Compare commits

..

1 Commits

Author SHA1 Message Date
sago35 30d684e637 atsamd5x: add SPI.TxN() 2021-03-29 13:31:36 +09:00
205 changed files with 2196 additions and 9491 deletions
+12 -35
View File
@@ -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:
@@ -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,10 +167,9 @@ 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
@@ -184,11 +177,6 @@ commands:
- 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:
@@ -225,15 +213,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 +226,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
@@ -298,36 +282,29 @@ commands:
- go-cache-macos-v2-{{ checksum "go.mod" }}
- restore_cache:
keys:
- llvm-source-11-macos-v2
- llvm-source-11-macos-v1
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
key: llvm-source-11-macos-v2
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-v3
- 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-v3
key: llvm-build-11-macos-v2
paths:
llvm-build
- restore_cache:
+7 -19
View File
@@ -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)
@@ -180,7 +180,7 @@ tinygo:
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
test: wasi-libc
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./cgo ./compileopts ./compiler ./interp ./transform .
# Test known-working standard library packages.
# TODO: do this in one command, parallelize, and only show failing tests (no
@@ -226,6 +226,8 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit-v2 examples/microbit-blink
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
@@ -254,10 +256,6 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit-s110v8 examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit-v2 examples/microbit-blink
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit-v2-s113v7 examples/microbit-blink
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nrf52840-mdk examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1
@@ -290,8 +288,6 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.gba -target=gameboy-advance examples/gba-display
@$(MD5SUM) test.gba
$(TINYGO) build -size short -o test.hex -target=grandcentral-m4 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m4 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
@@ -338,10 +334,6 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=p1am-100 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=atsame54-xpro examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-m4-can examples/blinky1
@$(MD5SUM) test.hex
# test pwm
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
@$(MD5SUM) test.hex
@@ -349,6 +341,8 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pyportal examples/pwm
@$(MD5SUM) test.hex
ifneq ($(STM32), 0)
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
@$(MD5SUM) test.hex
@@ -360,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
@@ -382,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
+1 -3
View File
@@ -43,14 +43,13 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
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)
+76 -224
View File
@@ -17,6 +17,7 @@ import (
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
"github.com/tinygo-org/tinygo/compileopts"
@@ -52,17 +53,13 @@ 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
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,19 @@ 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,
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)
@@ -193,7 +179,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// The package has not yet been compiled, so create a job to do so.
job := &compileJob{
description: "compile package " + pkg.ImportPath,
run: func(*compileJob) error {
run: func() error {
// Compile AST to IR. The compiler.CompilePackage function will
// build the SSA as needed.
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
@@ -204,72 +190,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.
pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
if pkgInit.IsNil() {
panic("init not found for " + pkg.Pkg.Path())
}
err := interp.RunFunc(pkgInit, config.DumpSSA())
if err != nil {
return err
}
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification error after interpreting " + pkgInit.Name())
}
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 +220,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)
@@ -319,7 +233,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
programJob := &compileJob{
description: "link+optimize packages (LTO)",
dependencies: packageJobs,
run: func(*compileJob) error {
run: func() error {
// Load and link all the bitcode files. This does not yet optimize
// anything, it only links the bitcode files together.
ctx := llvm.NewContext()
@@ -439,8 +353,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
outputObjectFileJob := &compileJob{
description: "generate output file",
dependencies: []*compileJob{programJob},
result: objfile,
run: func(*compileJob) error {
run: func() error {
llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil {
return err
@@ -454,32 +367,40 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
linkerDependencies := []*compileJob{outputObjectFileJob}
executable := filepath.Join(dir, "main")
tmppath := executable // final file
ldflags := append(config.LDFlags(), "-o", executable)
ldflags := append(config.LDFlags(), "-o", executable, objfile)
// Add compiler-rt dependency if needed. Usually this is a simple load from
// a cache.
if config.Target.RTLib == "compiler-rt" {
job, err := CompilerRT.load(config.Triple(), config.CPU(), dir)
path, job, err := CompilerRT.load(config.Triple(), config.CPU(), dir)
if err != nil {
return err
}
jobs = append(jobs, job.dependencies...)
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
if job != nil {
// The library was not loaded from cache so needs to be compiled
// (and then stored in the cache).
jobs = append(jobs, job.dependencies...)
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
}
ldflags = append(ldflags, path)
}
// Add libc dependency if needed.
root := goenv.Get("TINYGOROOT")
switch config.Target.Libc {
case "picolibc":
job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
path, job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
if err != nil {
return err
}
// The library needs to be compiled (cache miss).
jobs = append(jobs, job.dependencies...)
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
if job != nil {
// The library needs to be compiled (cache miss).
jobs = append(jobs, job.dependencies...)
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
}
ldflags = append(ldflags, path)
case "wasi-libc":
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
if _, err := os.Stat(path); os.IsNotExist(err) {
@@ -495,37 +416,44 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Add jobs to compile extra files. These files are in C or assembly and
// contain things like the interrupt vector table and low level operations
// such as stack switching.
for _, path := range config.ExtraFiles() {
for i, path := range config.ExtraFiles() {
abspath := filepath.Join(root, path)
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
job := &compileJob{
description: "compile extra file " + path,
run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config.Options.PrintCommands)
job.result = result
return err
run: func() error {
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
if err != nil {
return &commandError{"failed to build", path, err}
}
return nil
},
}
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
ldflags = append(ldflags, outpath)
}
// Add jobs to compile C files in all packages. This is part of CGo.
// TODO: do this as part of building the package to be able to link the
// bitcode files together.
for _, pkg := range lprogram.Sorted() {
pkg := pkg
for _, filename := range pkg.CFiles {
abspath := filepath.Join(pkg.Dir, filename)
for i, pkg := range lprogram.Sorted() {
for j, filename := range pkg.CFiles {
file := filepath.Join(pkg.Dir, filename)
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"."+strconv.Itoa(j)+"-"+filepath.Base(file)+".o")
job := &compileJob{
description: "compile CGo file " + abspath,
run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config.Options.PrintCommands)
job.result = result
return err
description: "compile CGo file " + file,
run: func() error {
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
if err != nil {
return &commandError{"failed to build", file, err}
}
return nil
},
}
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job)
ldflags = append(ldflags, outpath)
}
}
@@ -540,16 +468,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
jobs = append(jobs, &compileJob{
description: "link",
dependencies: linkerDependencies,
run: func(job *compileJob) error {
for _, dependency := range job.dependencies {
if dependency.result == "" {
return errors.New("dependency without result: " + dependency.description)
}
ldflags = append(ldflags, dependency.result)
}
if config.Options.PrintCommands {
fmt.Printf("%s %s\n", config.Target.Linker, strings.Join(ldflags, " "))
}
run: func() error {
err = link(config.Target.Linker, ldflags...)
if err != nil {
return &commandError{"failed to link", executable, err}
@@ -656,29 +575,14 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
if err != nil {
return err
}
if config.VerifyIR() {
// Only verify if we really need it.
// The IR has already been verified before writing the bitcode to disk
// and the interp function above doesn't need to do a lot as most of the
// package initializers have already run. Additionally, verifying this
// linked IR is _expensive_ because dead code hasn't been removed yet,
// easily costing a few hundred milliseconds. Therefore, only do it when
// specifically requested.
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification error after interpreting runtime.initAll")
}
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
return errors.New("verification error after interpreting runtime.initAll")
}
if config.GOOS() != "darwin" {
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 +597,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 +633,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 {
-309
View File
@@ -1,309 +0,0 @@
package builder
// This file implements a wrapper around the C compiler (Clang) which uses a
// build cache.
import (
"crypto/sha512"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"unicode"
"github.com/tinygo-org/tinygo/goenv"
"tinygo.org/x/go-llvm"
)
// compileAndCacheCFile compiles a C or assembly file using a build cache.
// Compiling the same file again (if nothing changed, including included header
// files) the output is loaded from the build cache instead.
//
// Its operation is a bit complex (more complex than Go package build caching)
// because the list of file dependencies is only known after the file is
// compiled. However, luckily compilers have a flag to write a list of file
// dependencies in Makefile syntax which can be used for caching.
//
// Because of this complexity, every file has in fact two cached build outputs:
// the file itself, and the list of dependencies. Its operation is as follows:
//
// depfile = hash(path, compiler, cflags, ...)
// if depfile exists:
// outfile = hash of all files and depfile name
// if outfile exists:
// # cache hit
// return outfile
// # cache miss
// tmpfile = compile file
// read dependencies (side effect of compile)
// write depfile
// outfile = hash of all files and depfile name
// rename tmpfile to outfile
//
// There are a few edge cases that are not handled:
// - If a file is added to an include path, that file may be included instead of
// some other file. This would be fixed by also including lookup failures in the
// dependencies file, but I'm not aware of a compiler which does that.
// - The Makefile syntax that compilers output has issues, see readDepFile for
// details.
// - A header file may be changed to add/remove an include. This invalidates the
// depfile but without invalidating its name. For this reason, the depfile is
// written on each new compilation (even when it seems unnecessary). However, it
// could in rare cases lead to a stale file fetched from the cache.
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands bool) (string, error) {
// Hash input file.
fileHash, err := hashFile(abspath)
if err != nil {
return "", err
}
// Create cache key for the dependencies file.
buf, err := json.Marshal(struct {
Path string
Hash string
Flags []string
LLVMVersion string
}{
Path: abspath,
Hash: fileHash,
Flags: cflags,
LLVMVersion: llvm.Version,
})
if err != nil {
panic(err) // shouldn't happen
}
depfileNameHashBuf := sha512.Sum512_224(buf)
depfileNameHash := hex.EncodeToString(depfileNameHashBuf[:])
// Load dependencies file, if possible.
depfileName := "dep-" + depfileNameHash + ".json"
depfileCachePath := filepath.Join(goenv.Get("GOCACHE"), depfileName)
depfileBuf, err := ioutil.ReadFile(depfileCachePath)
var dependencies []string // sorted list of dependency paths
if err == nil {
// There is a dependency file, that's great!
// Parse it first.
err := json.Unmarshal(depfileBuf, &dependencies)
if err != nil {
return "", fmt.Errorf("could not parse dependencies JSON: %w", err)
}
// Obtain hashes of all the files listed as a dependency.
outpath, err := makeCFileCachePath(dependencies, depfileNameHash)
if err == nil {
if _, err := os.Stat(outpath); err == nil {
return outpath, nil
} else if !os.IsNotExist(err) {
return "", err
}
}
} else if !os.IsNotExist(err) {
// expected either nil or IsNotExist
return "", err
}
objTmpFile, err := ioutil.TempFile(goenv.Get("GOCACHE"), "tmp-*.o")
if err != nil {
return "", err
}
objTmpFile.Close()
depTmpFile, err := ioutil.TempFile(tmpdir, "dep-*.d")
if err != nil {
return "", err
}
depTmpFile.Close()
flags := append([]string{}, cflags...) // copy cflags
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
if 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 printCommands {
fmt.Printf("clang %s\n", strings.Join(flags, " "))
}
err = runCCompiler(flags...)
if err != nil {
return "", &commandError{"failed to build", abspath, err}
}
// Create sorted and uniqued slice of dependencies.
dependencyPaths, err := readDepFile(depTmpFile.Name())
if err != nil {
return "", err
}
dependencyPaths = append(dependencyPaths, abspath) // necessary for .s files
dependencySet := make(map[string]struct{}, len(dependencyPaths))
var dependencySlice []string
for _, path := range dependencyPaths {
if _, ok := dependencySet[path]; ok {
continue
}
dependencySet[path] = struct{}{}
dependencySlice = append(dependencySlice, path)
}
sort.Strings(dependencySlice)
// Write dependencies file.
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
buf, err = json.MarshalIndent(dependencySlice, "", "\t")
if err != nil {
panic(err) // shouldn't happen
}
_, err = f.Write(buf)
if err != nil {
return "", err
}
err = f.Close()
if err != nil {
return "", err
}
err = os.Rename(f.Name(), depfileCachePath)
if err != nil {
return "", err
}
// Move temporary object file to final location.
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash)
if err != nil {
return "", err
}
err = os.Rename(objTmpFile.Name(), outpath)
if err != nil {
return "", err
}
return outpath, nil
}
// Create a cache path (a path in GOCACHE) to store the output of a compiler
// job. This path is based on the dep file name (which is a hash of metadata
// including compiler flags) and the hash of all input files in the paths slice.
func makeCFileCachePath(paths []string, depfileNameHash string) (string, error) {
// Hash all input files.
fileHashes := make(map[string]string, len(paths))
for _, path := range paths {
hash, err := hashFile(path)
if err != nil {
return "", err
}
fileHashes[path] = hash
}
// Calculate a cache key based on the above hashes.
buf, err := json.Marshal(struct {
DepfileHash string
FileHashes map[string]string
}{
DepfileHash: depfileNameHash,
FileHashes: fileHashes,
})
if err != nil {
panic(err) // shouldn't happen
}
outFileNameBuf := sha512.Sum512_224(buf)
cacheKey := hex.EncodeToString(outFileNameBuf[:])
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+".o")
return outpath, nil
}
// hashFile hashes the given file path and returns the hash as a hex string.
func hashFile(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", fmt.Errorf("failed to hash file: %w", err)
}
defer f.Close()
fileHasher := sha512.New512_224()
_, err = io.Copy(fileHasher, f)
if err != nil {
return "", fmt.Errorf("failed to hash file: %w", err)
}
return hex.EncodeToString(fileHasher.Sum(nil)), nil
}
// readDepFile reads a dependency file in NMake (Visual Studio make) format. The
// file is assumed to have a single target named deps.
//
// There are roughly three make syntax variants:
// - BSD make, which doesn't support any escaping. This means that many special
// characters are not supported in file names.
// - GNU make, which supports escaping using a backslash but when it fails to
// find a file it tries to fall back with the literal path name (to match BSD
// make).
// - NMake (Visual Studio) and Jom, which simply quote the string if there are
// any weird characters.
// Clang supports two variants: a format that's a compromise between BSD and GNU
// make (and is buggy to match GCC which is equally buggy), and NMake/Jom, which
// is at least somewhat sane. This last format isn't perfect either: it does not
// correctly handle filenames with quote marks in them. Those are generally not
// allowed on Windows, but of course can be used on POSIX like systems. Still,
// it's the most sane of any of the formats so readDepFile will use that format.
func readDepFile(filename string) ([]string, error) {
buf, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
if len(buf) == 0 {
return nil, nil
}
return parseDepFile(string(buf))
}
func parseDepFile(s string) ([]string, error) {
// This function makes no attempt at parsing anything other than Clang -MD
// -MV output.
// For Windows: replace CRLF with LF to make the logic below simpler.
s = strings.ReplaceAll(s, "\r\n", "\n")
// Collapse all lines ending in a backslash. These backslashes are really
// just a way to continue a line without making very long lines.
s = strings.ReplaceAll(s, "\\\n", " ")
// Only use the first line, which is expected to begin with "deps:".
line := strings.SplitN(s, "\n", 2)[0]
if !strings.HasPrefix(line, "deps:") {
return nil, errors.New("readDepFile: expected 'deps:' prefix")
}
line = strings.TrimSpace(line[len("deps:"):])
var deps []string
for line != "" {
if line[0] == '"' {
// File path is quoted. Path ends with double quote.
// This does not handle double quotes in path names, which is a
// problem on non-Windows systems.
line = line[1:]
end := strings.IndexByte(line, '"')
if end < 0 {
return nil, errors.New("readDepFile: path is incorrectly quoted")
}
dep := line[:end]
line = strings.TrimSpace(line[end+1:])
deps = append(deps, dep)
} else {
// File path is not quoted. Path ends in space or EOL.
end := strings.IndexFunc(line, unicode.IsSpace)
if end < 0 {
// last dependency
deps = append(deps, line)
break
}
dep := line[:end]
line = strings.TrimSpace(line[end:])
deps = append(deps, dep)
}
}
return deps, nil
}
-33
View File
@@ -1,33 +0,0 @@
package builder
import (
"reflect"
"testing"
)
func TestSplitDepFile(t *testing.T) {
for i, tc := range []struct {
in string
out []string
}{
{`deps: foo bar`, []string{"foo", "bar"}},
{`deps: foo "bar"`, []string{"foo", "bar"}},
{`deps: "foo" bar`, []string{"foo", "bar"}},
{`deps: "foo bar"`, []string{"foo bar"}},
{`deps: "foo bar" `, []string{"foo bar"}},
{"deps: foo\nbar", []string{"foo"}},
{"deps: foo \\\nbar", []string{"foo", "bar"}},
{"deps: foo\\bar \\\nbaz", []string{"foo\\bar", "baz"}},
{"deps: foo\\bar \\\r\n baz", []string{"foo\\bar", "baz"}}, // Windows uses CRLF line endings
} {
out, err := parseDepFile(tc.in)
if err != nil {
t.Errorf("test #%d failed: %v", i, err)
continue
}
if !reflect.DeepEqual(out, tc.out) {
t.Errorf("test #%d failed: expected %#v but got %#v", i, tc.out, out)
continue
}
}
}
+4 -17
View File
@@ -27,23 +27,12 @@ const (
type compileJob struct {
description string // description, only used for logging
dependencies []*compileJob
result string // result (path)
run func(*compileJob) (err error)
run func() error
state jobState
err error // error if finished
duration time.Duration // how long it took to run this job (only set after finishing)
}
// dummyCompileJob returns a new *compileJob that produces an output without
// doing anything. This can be useful where a *compileJob producing an output is
// expected but nothing needs to be done, for example for a load from a cache.
func dummyCompileJob(result string) *compileJob {
return &compileJob{
description: "<dummy>",
result: result,
}
}
// readyToRun returns whether this job is ready to run: it is itself not yet
// started and all dependencies are finished.
func (job *compileJob) readyToRun() bool {
@@ -161,11 +150,9 @@ func nextJob(jobs []*compileJob) *compileJob {
func jobWorker(workerChan, doneChan chan *compileJob) {
for job := range workerChan {
start := time.Now()
if job.run != nil {
err := job.run(job)
if err != nil {
job.err = err
}
err := job.run()
if err != nil {
job.err = err
}
job.duration = time.Since(start)
doneChan <- job
+18 -17
View File
@@ -42,28 +42,30 @@ func (l *Library) sourcePaths(target string) []string {
// The resulting file is stored in the provided tmpdir, which is expected to be
// removed after the Load call.
func (l *Library) Load(target, tmpdir string) (path string, err error) {
job, err := l.load(target, "", tmpdir)
path, job, err := l.load(target, "", tmpdir)
if err != nil {
return "", err
}
jobs := append([]*compileJob{job}, job.dependencies...)
err = runJobs(jobs)
return job.result, err
if job != nil {
jobs := append([]*compileJob{job}, job.dependencies...)
err = runJobs(jobs)
}
return path, err
}
// load returns a compile job to build this library file for the given target
// and CPU. It may return a dummy compileJob if the library build is already
// cached. The path is stored as job.result but is only valid if the job and
// job.dependencies have been run.
// load returns a path to the library file for the given target, loading it from
// cache if possible. It will return a non-zero compiler job if the library
// wasn't cached, this job (and its dependencies) must be run before the library
// path is valid.
// The provided tmpdir will be used to store intermediary files and possibly the
// output archive file, it is expected to be removed after use.
func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error) {
func (l *Library) load(target, cpu, tmpdir string) (path string, job *compileJob, err error) {
// Try to load a precompiled library.
precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, l.name+".a")
if _, err := os.Stat(precompiledPath); err == nil {
// Found a precompiled library for this OS/architecture. Return the path
// directly.
return dummyCompileJob(precompiledPath), nil
return precompiledPath, nil, nil
}
var outfile string
@@ -76,7 +78,7 @@ func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error)
// Try to fetch this library from the cache.
if path, err := cacheLoad(outfile, l.sourcePaths(target)); path != "" || err != nil {
// Cache hit.
return dummyCompileJob(path), nil
return path, nil, err
}
// Cache miss, build it now.
@@ -84,7 +86,7 @@ func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error)
dir := filepath.Join(tmpdir, "build-lib-"+l.name)
err = os.Mkdir(dir, 0777)
if err != nil {
return nil, err
return "", nil, err
}
// Precalculate the flags to the compiler invocation.
@@ -111,8 +113,7 @@ func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error)
arpath := filepath.Join(dir, l.name+".a")
job = &compileJob{
description: "ar " + l.name + ".a",
result: arpath,
run: func(*compileJob) error {
run: func() error {
// Create an archive of all object files.
err := makeArchive(arpath, objs)
if err != nil {
@@ -132,11 +133,11 @@ func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error)
objs = append(objs, objpath)
job.dependencies = append(job.dependencies, &compileJob{
description: "compile " + srcpath,
run: func(*compileJob) error {
run: func() 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}
}
@@ -145,5 +146,5 @@ func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error)
})
}
return job, nil
return arpath, job, nil
}
+13 -4
View File
@@ -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.
+14 -17
View File
@@ -41,8 +41,7 @@ type cgoPackage struct {
elaboratedTypes map[string]*elaboratedTypeInfo
enums map[string]enumInfo
anonStructNum int
cflags []string // CFlags from #cgo lines
ldflags []string // LDFlags from #cgo lines
ldflags []string
visitedFiles map[string][]byte
}
@@ -158,10 +157,10 @@ typedef unsigned long long _Cgo_ulonglong;
// Process extracts `import "C"` statements from the AST, parses the comment
// with libclang, and modifies the AST to use this information. It returns a
// newly created *ast.File that should be added to the list of to-be-parsed
// files, the CFLAGS and LDFLAGS found in #cgo lines, and a map of file hashes
// of the accessed C header files. If there is one or more error, it returns
// these in the []error slice but still modifies the AST.
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []string, map[string][]byte, []error) {
// files, the LDFLAGS for this package, and a map of file hashes of the accessed
// C header files. If there is one or more error, it returns these in the
// []error slice but still modifies the AST.
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, map[string][]byte, []error) {
p := &cgoPackage{
dir: dir,
fset: fset,
@@ -176,6 +175,11 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
visitedFiles: map[string][]byte{},
}
// Disable _FORTIFY_SOURCE as it causes problems on macOS.
// Note that it is only disabled for memcpy (etc) calls made from Go, which
// have better alternatives anyway.
cflags = append(cflags, "-D_FORTIFY_SOURCE=0")
// Add a new location for the following file.
generatedTokenPos := p.fset.AddFile(dir+"/!cgo.go", -1, 0)
generatedTokenPos.SetLines([]int{0})
@@ -184,7 +188,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
// Find the absolute path for this package.
packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
if err != nil {
return nil, nil, nil, nil, []error{
return nil, nil, nil, []error{
scanner.Error{
Pos: fset.Position(files[0].Pos()),
Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error
@@ -359,7 +363,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
continue
}
makePathsAbsolute(flags, packagePath)
p.cflags = append(p.cflags, flags...)
cflags = append(cflags, flags...)
case "LDFLAGS":
flags, err := shlex.Split(value)
if err != nil {
@@ -382,13 +386,6 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
}
}
// Define CFlags that will be used while parsing the package.
// Disable _FORTIFY_SOURCE as it causes problems on macOS.
// Note that it is only disabled for memcpy (etc) calls made from Go, which
// have better alternatives anyway.
cflagsForCGo := append([]string{"-D_FORTIFY_SOURCE=0"}, cflags...)
cflagsForCGo = append(cflagsForCGo, p.cflags...)
// Process all CGo imports.
for _, genDecl := range statements {
cgoComment := genDecl.Doc.Text()
@@ -398,7 +395,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
pos = genDecl.Doc.Pos()
}
position := fset.PositionFor(pos, true)
p.parseFragment(cgoComment+cgoTypes, cflagsForCGo, position.Filename, position.Line)
p.parseFragment(cgoComment+cgoTypes, cflags, position.Filename, position.Line)
}
// Declare functions found by libclang.
@@ -433,7 +430,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
// Print the newly generated in-memory AST, for debugging.
//ast.Print(fset, p.generated)
return p.generated, p.cflags, p.ldflags, p.visitedFiles, p.errors
return p.generated, p.ldflags, p.visitedFiles, p.errors
}
// makePathsAbsolute converts some common path compiler flags (-I, -L) from
+1 -1
View File
@@ -65,7 +65,7 @@ func TestCGo(t *testing.T) {
}
// Process the AST with CGo.
cgoAST, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
cgoAST, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
// Check the AST for type errors.
var typecheckErrors []error
+2 -23
View File
@@ -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
View File
@@ -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
}
+2 -6
View File
@@ -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"`
@@ -229,13 +230,7 @@ func LoadTarget(target string) (*TargetSpec, error) {
}
}
// WindowsBuildNotSupportedErr is being thrown, when goos is windows and no target has been specified.
var WindowsBuildNotSupportedErr = errors.New("Building Windows binaries is currently not supported. Try specifying a different target")
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
if goos == "windows" {
return nil, WindowsBuildNotSupportedErr
}
// No target spec available. Use the default one, useful on most systems
// with a regular OS.
spec := TargetSpec{
@@ -243,6 +238,7 @@ 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"},
+10 -22
View File
@@ -58,10 +58,10 @@ func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm
// Expand an argument type to a list that can be used in a function call
// parameter list.
func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
func expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
switch t.TypeKind() {
case llvm.StructTypeKind:
fieldInfos := c.flattenAggregateType(t, name, goType)
fieldInfos := flattenAggregateType(t, name, goType)
if len(fieldInfos) <= maxFieldsPerParam {
return fieldInfos
} else {
@@ -105,7 +105,7 @@ func (b *builder) expandFormalParamOffsets(t llvm.Type) []uint64 {
func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
switch v.Type().TypeKind() {
case llvm.StructTypeKind:
fieldInfos := b.flattenAggregateType(v.Type(), "", nil)
fieldInfos := flattenAggregateType(v.Type(), "", nil)
if len(fieldInfos) <= maxFieldsPerParam {
fields := b.flattenAggregate(v)
if len(fields) != len(fieldInfos) {
@@ -124,15 +124,12 @@ func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
// Try to flatten a struct type to a list of types. Returns a 1-element slice
// with the passed in type if this is not possible.
func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
func flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
typeFlags := getTypeFlags(goType)
switch t.TypeKind() {
case llvm.StructTypeKind:
var paramInfos []paramInfo
paramInfos := make([]paramInfo, 0, t.StructElementTypesCount())
for i, subfield := range t.StructElementTypes() {
if c.targetData.TypeAllocSize(subfield) == 0 {
continue
}
suffix := strconv.Itoa(i)
if goType != nil {
// Try to come up with a good suffix for this struct field,
@@ -155,7 +152,7 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
suffix = []string{"context", "funcptr"}[i]
}
}
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
subInfos := flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
for i := range subInfos {
subInfos[i].flags |= typeFlags
}
@@ -221,11 +218,8 @@ func extractSubfield(t types.Type, field int) types.Type {
func (c *compilerContext) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
switch t.TypeKind() {
case llvm.StructTypeKind:
var fields []uint64
fields := make([]uint64, 0, t.StructElementTypesCount())
for fieldIndex, field := range t.StructElementTypes() {
if c.targetData.TypeAllocSize(field) == 0 {
continue
}
suboffsets := c.flattenAggregateTypeOffsets(field)
offset := c.targetData.ElementOffset(t, fieldIndex)
for i := range suboffsets {
@@ -244,11 +238,8 @@ func (c *compilerContext) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
func (b *builder) flattenAggregate(v llvm.Value) []llvm.Value {
switch v.Type().TypeKind() {
case llvm.StructTypeKind:
var fields []llvm.Value
for i, field := range v.Type().StructElementTypes() {
if b.targetData.TypeAllocSize(field) == 0 {
continue
}
fields := make([]llvm.Value, 0, v.Type().StructElementTypesCount())
for i := range v.Type().StructElementTypes() {
subfield := b.CreateExtractValue(v, i, "")
subfields := b.flattenAggregate(subfield)
fields = append(fields, subfields...)
@@ -275,13 +266,10 @@ func (b *builder) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Val
func (b *builder) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
switch t.TypeKind() {
case llvm.StructTypeKind:
flattened := b.flattenAggregateType(t, "", nil)
flattened := flattenAggregateType(t, "", nil)
if len(flattened) <= maxFieldsPerParam {
value := llvm.ConstNull(t)
for i, subtyp := range t.StructElementTypes() {
if b.targetData.TypeAllocSize(subtyp) == 0 {
continue
}
structField, remaining := b.collapseFormalParamInternal(subtyp, fields)
fields = remaining
value = b.CreateInsertValue(value, structField, i, "")
+11 -17
View File
@@ -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 = 5 // last change: add method set to interface types
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
@@ -829,7 +829,7 @@ func (b *builder) createFunction() {
for _, param := range b.fn.Params {
llvmType := b.getLLVMType(param.Type())
fields := make([]llvm.Value, 0, 1)
for _, info := range b.expandFormalParamType(llvmType, param.Name(), param.Type()) {
for _, info := range expandFormalParamType(llvmType, param.Name(), param.Type()) {
param := b.llvmFn.Param(llvmParamIndex)
param.SetName(info.name)
fields = append(fields, param)
@@ -2258,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 {
+1 -7
View File
@@ -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()
+5 -17
View File
@@ -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:
@@ -136,13 +124,13 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
// The receiver is not an interface, but a i8* type.
recv = c.i8ptrType
}
for _, info := range c.expandFormalParamType(recv, "", nil) {
for _, info := range expandFormalParamType(recv, "", nil) {
paramTypes = append(paramTypes, info.llvmType)
}
}
for i := 0; i < typ.Params().Len(); i++ {
subType := c.getLLVMType(typ.Params().At(i).Type())
for _, info := range c.expandFormalParamType(subType, "", nil) {
for _, info := range expandFormalParamType(subType, "", nil) {
paramTypes = append(paramTypes, info.llvmType)
}
}
+17 -21
View File
@@ -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.
@@ -460,7 +456,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
// Get the expanded receiver type.
receiverType := c.getLLVMType(fn.Signature.Recv().Type())
var expandedReceiverType []llvm.Type
for _, info := range c.expandFormalParamType(receiverType, "", nil) {
for _, info := range expandFormalParamType(receiverType, "", nil) {
expandedReceiverType = append(expandedReceiverType, info.llvmType)
}
+6 -19
View File
@@ -74,7 +74,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
var paramInfos []paramInfo
for _, param := range getParams(fn.Signature) {
paramType := c.getLLVMType(param.Type())
paramFragmentInfos := c.expandFormalParamType(paramType, param.Name(), param.Type())
paramFragmentInfos := expandFormalParamType(paramType, param.Name(), param.Type())
paramInfos = append(paramInfos, paramFragmentInfos...)
}
@@ -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 {
-12
View File
@@ -1,12 +0,0 @@
package main
func foo(callback func(int)) {
callback(3)
}
func bar() {
foo(someFunc)
}
func someFunc(int) {
}
-47
View File
@@ -1,47 +0,0 @@
; ModuleID = 'func.go'
source_filename = "func.go"
target datalayout = "e-m:e-p:32:32-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
}
+10 -11
View File
@@ -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
+2 -2
View File
@@ -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*)
-8
View File
@@ -1,13 +1,5 @@
package main
func someString() string {
return "foo"
}
func zeroLengthString() string {
return ""
}
func stringLen(s string) int {
return len(s)
}
-14
View File
@@ -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
+1 -1
View File
@@ -12,5 +12,5 @@ require (
go.bug.st/serial v1.1.2
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4
)
-2
View File
@@ -59,5 +59,3 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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
View File
@@ -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
View File
@@ -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
-5
View File
@@ -20,11 +20,6 @@ var (
errMapAlreadyCreated = errors.New("interp: map already created")
)
// This is one of the errors that can be returned from toLLVMValue when the
// passed type does not fit the data to serialize. It is recoverable by
// serializing without a type (using rawValue.rawLLVMValue).
var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not equal pointer size")
func isRecoverableError(err error) bool {
return err == errIntegerAsPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
}
+5 -124
View File
@@ -11,12 +11,6 @@ import (
"tinygo.org/x/go-llvm"
)
// Version of the interp package. It must be incremented whenever the interp
// package is changed in a way that affects the output so that cached package
// builds will be invalidated.
// This version is independent of the TinyGo version number.
const Version = 1
// Enable extra checks, which should be disabled by default.
// This may help track down bugs by adding a few more sanity checks.
const checks = true
@@ -38,7 +32,9 @@ type runner struct {
callsExecuted uint64
}
func newRunner(mod llvm.Module, debug bool) *runner {
// Run evaluates runtime.initAll function as much as possible at compile time.
// Set debug to true if it should print output while running.
func Run(mod llvm.Module, debug bool) error {
r := runner{
mod: mod,
targetData: llvm.NewTargetData(mod.DataLayout()),
@@ -51,13 +47,6 @@ func newRunner(mod llvm.Module, debug bool) *runner {
r.pointerSize = uint32(r.targetData.PointerSize())
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0)
r.maxAlign = r.targetData.PrefTypeAlignment(r.i8ptrType) // assume pointers are maximally aligned (this is not always the case)
return &r
}
// Run evaluates runtime.initAll function as much as possible at compile time.
// Set debug to true if it should print output while running.
func Run(mod llvm.Module, debug bool) error {
r := newRunner(mod, debug)
initAll := mod.NamedFunction("runtime.initAll")
bb := initAll.EntryBasicBlock()
@@ -128,7 +117,7 @@ func Run(mod llvm.Module, debug bool) error {
r.pkgName = ""
// Update all global variables in the LLVM module.
mem := memoryView{r: r}
mem := memoryView{r: &r}
for _, obj := range r.objects {
if obj.llvmGlobal.IsNil() {
continue
@@ -136,34 +125,7 @@ func Run(mod llvm.Module, debug bool) error {
if obj.buffer == nil {
continue
}
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
if err == errInvalidPtrToIntSize {
// This can happen when a previous interp run did not have the
// correct LLVM type for a global and made something up. In that
// case, some fields could be written out as a series of (null)
// bytes even though they actually contain a pointer value.
// As a fallback, use asRawValue to get something of the correct
// memory layout.
initializer, err := obj.buffer.asRawValue(r).rawLLVMValue(&mem)
if err != nil {
return err
}
initializerType := initializer.Type()
newGlobal := llvm.AddGlobal(mod, initializerType, obj.llvmGlobal.Name()+".tmp")
newGlobal.SetInitializer(initializer)
newGlobal.SetLinkage(obj.llvmGlobal.Linkage())
newGlobal.SetAlignment(obj.llvmGlobal.Alignment())
// TODO: copy debug info, unnamed_addr, ...
bitcast := llvm.ConstBitCast(newGlobal, obj.llvmGlobal.Type())
obj.llvmGlobal.ReplaceAllUsesWith(bitcast)
name := obj.llvmGlobal.Name()
obj.llvmGlobal.EraseFromParentAsGlobal()
newGlobal.SetName(name)
continue
}
if err != nil {
return err
}
initializer := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
if checks && initializer.Type() != obj.llvmGlobal.Type().ElementType() {
panic("initializer type mismatch")
}
@@ -173,87 +135,6 @@ func Run(mod llvm.Module, debug bool) error {
return nil
}
// RunFunc evaluates a single package initializer at compile time.
// Set debug to true if it should print output while running.
func RunFunc(fn llvm.Value, debug bool) error {
// Create and initialize *runner object.
mod := fn.GlobalParent()
r := newRunner(mod, debug)
initName := fn.Name()
if !strings.HasSuffix(initName, ".init") {
return errorAt(fn, "interp: unexpected function name (expected *.init)")
}
r.pkgName = initName[:len(initName)-len(".init")]
// Create new function with the interp result.
newFn := llvm.AddFunction(mod, fn.Name()+".tmp", fn.Type().ElementType())
newFn.SetLinkage(fn.Linkage())
newFn.SetVisibility(fn.Visibility())
entry := mod.Context().AddBasicBlock(newFn, "entry")
// Create a builder, to insert instructions that could not be evaluated at
// compile time.
r.builder = mod.Context().NewBuilder()
defer r.builder.Dispose()
r.builder.SetInsertPointAtEnd(entry)
// Copy debug information.
subprogram := fn.Subprogram()
if !subprogram.IsNil() {
newFn.SetSubprogram(subprogram)
r.builder.SetCurrentDebugLocation(subprogram.SubprogramLine(), 0, subprogram, llvm.Metadata{})
}
// Run the initializer, filling the .init.tmp function.
if r.debug {
fmt.Fprintln(os.Stderr, "interp:", fn.Name())
}
_, pkgMem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
if callErr != nil {
if isRecoverableError(callErr.Err) {
// Could not finish, but could recover from it.
if r.debug {
fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error())
}
newFn.EraseFromParentAsFunction()
return nil
}
return callErr
}
for index, obj := range pkgMem.objects {
r.objects[index] = obj
}
// Update globals with values determined while running the initializer above.
mem := memoryView{r: r}
for _, obj := range r.objects {
if obj.llvmGlobal.IsNil() {
continue
}
if obj.buffer == nil {
continue
}
initializer, err := obj.buffer.toLLVMValue(obj.llvmGlobal.Type().ElementType(), &mem)
if err != nil {
return err
}
if checks && initializer.Type() != obj.llvmGlobal.Type().ElementType() {
panic("initializer type mismatch")
}
obj.llvmGlobal.SetInitializer(initializer)
}
// Finalize: remove the old init function and replace it with the new
// (.init.tmp) function.
r.builder.CreateRetVoid()
fnName := fn.Name()
fn.ReplaceAllUsesWith(newFn)
fn.EraseFromParentAsFunction()
newFn.SetName(fnName)
return nil
}
// getFunction returns the compiled version of the given LLVM function. It
// compiles the function if necessary and caches the result.
func (r *runner) getFunction(llvmFn llvm.Value) *function {
+1 -1
View File
@@ -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 -100
View File
@@ -33,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
@@ -147,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.
@@ -196,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.)
@@ -204,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
@@ -326,11 +282,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
fmt.Fprintln(os.Stderr, indent+"call (reflect.rawType).elem:", operands[1:])
}
// Extract the type code global from the first parameter.
typecodeIDPtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
typecodeID := typecodeIDPtrToInt.Operand(0)
typecodeID := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem).Operand(0)
// Get the type class.
// See also: getClassAndValueFromTypeCode in transform/reflect.go.
@@ -363,16 +315,10 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
if r.debug {
fmt.Fprintln(os.Stderr, indent+"typeassert:", operands[1:])
}
assertedType, err := operands[2].toLLVMValue(inst.llvmInst.Operand(1).Type(), &mem)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
actualTypePtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
assertedType := operands[2].toLLVMValue(inst.llvmInst.Operand(1).Type(), &mem)
actualTypePtrToInt := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
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)}
@@ -431,11 +377,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
// Load the first param, which is the type code (ptrtoint of the
// type code global).
typecodeIDPtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
typecodeID := typecodeIDPtrToInt.Operand(0).Initializer()
typecodeID := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem).Operand(0).Initializer()
// Load the method set, which is part of the typecodeID object.
methodSet := llvm.ConstExtractValue(typecodeID, []uint32{2}).Operand(0).Initializer()
@@ -459,6 +401,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
@@ -524,13 +534,6 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
continue
}
result := mem.load(ptr, uint32(size))
if result == nil {
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
if err != nil {
return nil, mem, err
}
continue
}
if r.debug {
fmt.Fprintln(os.Stderr, indent+"load:", ptr, "->", result)
}
@@ -553,14 +556,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
if r.debug {
fmt.Fprintln(os.Stderr, indent+"store:", val, ptr)
}
ok := mem.store(val, ptr)
if !ok {
// Could not store the value, do it at runtime.
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
if err != nil {
return nil, mem, err
}
}
mem.store(val, ptr)
case llvm.Alloca:
// Alloca normally allocates some stack memory. In the interpreter,
// it allocates a global instead.
@@ -892,11 +888,7 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
for i := 0; i < numOperands; i++ {
operand := inst.llvmInst.Operand(i)
if !operand.IsAInstruction().IsNil() || !operand.IsAArgument().IsNil() {
var err error
operand, err = locals[fn.locals[operand]].toLLVMValue(operand.Type(), mem)
if err != nil {
return r.errorAt(inst, err)
}
operand = locals[fn.locals[operand]].toLLVMValue(operand.Type(), mem)
}
operands[i] = operand
}
+322 -89
View File
@@ -259,17 +259,12 @@ func (mv *memoryView) put(index uint32, obj object) {
mv.objects[index] = obj
}
// Load the value behind the given pointer. Returns nil if the pointer points to
// an external global.
// Load the value behind the given pointer.
func (mv *memoryView) load(p pointerValue, size uint32) value {
if checks && mv.hasExternalStore(p) {
panic("interp: load from object with external store")
}
obj := mv.get(p.index())
if obj.buffer == nil {
// External global, return nil.
return nil
}
if p.offset() == 0 && size == obj.size {
return obj.buffer.clone()
}
@@ -285,17 +280,12 @@ func (mv *memoryView) load(p pointerValue, size uint32) value {
// Store to the value behind the given pointer. This overwrites the value in the
// memory view, so that the changed value is discarded when the memory view is
// reverted. Returns true on success, false if the object to store to is
// external.
func (mv *memoryView) store(v value, p pointerValue) bool {
// reverted.
func (mv *memoryView) store(v value, p pointerValue) {
if checks && mv.hasExternalLoadOrStore(p) {
panic("interp: store to object with external load/store")
}
obj := mv.get(p.index())
if obj.buffer == nil {
// External global, return false (for a failure).
return false
}
if checks && p.offset()+v.len(mv.r) > obj.size {
panic("interp: store out of bounds")
}
@@ -311,7 +301,6 @@ func (mv *memoryView) store(v value, p pointerValue) bool {
}
}
mv.put(p.index(), obj)
return true // success
}
// value is some sort of value, comparable to a LLVM constant. It can be
@@ -325,7 +314,7 @@ type value interface {
asRawValue(*runner) rawValue
Uint() uint64
Int() int64
toLLVMValue(llvm.Type, *memoryView) (llvm.Value, error)
toLLVMValue(llvm.Type, *memoryView) llvm.Value
String() string
}
@@ -416,25 +405,25 @@ func (v literalValue) Int() int64 {
}
}
func (v literalValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
func (v literalValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
switch llvmType.TypeKind() {
case llvm.IntegerTypeKind:
switch value := v.value.(type) {
case uint64:
return llvm.ConstInt(llvmType, value, false), nil
return llvm.ConstInt(llvmType, value, false)
case uint32:
return llvm.ConstInt(llvmType, uint64(value), false), nil
return llvm.ConstInt(llvmType, uint64(value), false)
case uint16:
return llvm.ConstInt(llvmType, uint64(value), false), nil
return llvm.ConstInt(llvmType, uint64(value), false)
case uint8:
return llvm.ConstInt(llvmType, uint64(value), false), nil
return llvm.ConstInt(llvmType, uint64(value), false)
default:
return llvm.Value{}, errors.New("interp: unknown literal type")
panic("inpterp: unknown literal type")
}
case llvm.DoubleTypeKind:
return llvm.ConstFloat(llvmType, math.Float64frombits(v.value.(uint64))), nil
return llvm.ConstFloat(llvmType, math.Float64frombits(v.value.(uint64)))
case llvm.FloatTypeKind:
return llvm.ConstFloat(llvmType, float64(math.Float32frombits(v.value.(uint32)))), nil
return llvm.ConstFloat(llvmType, float64(math.Float32frombits(v.value.(uint32))))
default:
return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
}
@@ -518,7 +507,7 @@ func (v pointerValue) llvmValue(mem *memoryView) llvm.Value {
// toLLVMValue returns the LLVM value for this pointer, which may be a GEP or
// bitcast. The llvm.Type parameter is optional, if omitted the pointer type may
// be different than expected.
func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
// Obtain the llvmValue, creating it if it doesn't exist yet.
llvmValue := v.llvmValue(mem)
if llvmValue.IsNil() {
@@ -529,10 +518,7 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
if obj.llvmType.IsNil() {
// Create an initializer without knowing the global type.
// This is probably the result of a runtime.alloc call.
initializer, err := obj.buffer.asRawValue(mem.r).rawLLVMValue(mem)
if err != nil {
return llvm.Value{}, err
}
initializer := obj.buffer.asRawValue(mem.r).rawLLVMValue(mem)
globalType := initializer.Type()
llvmValue = llvm.AddGlobal(mem.r.mod, globalType, obj.globalName)
llvmValue.SetInitializer(initializer)
@@ -551,12 +537,9 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
// Set the initializer for the global. Do this after creation to avoid
// infinite recursion between creating the global and creating the
// contents of the global (if the global contains itself).
initializer, err := obj.buffer.toLLVMValue(globalType, mem)
if err != nil {
return llvm.Value{}, err
}
initializer := obj.buffer.toLLVMValue(globalType, mem)
if checks && initializer.Type() != globalType {
return llvm.Value{}, errors.New("interp: allocated value does not match allocated type")
panic("allocated value does not match allocated type")
}
llvmValue.SetInitializer(initializer)
}
@@ -569,7 +552,7 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
}
if llvmType.IsNil() {
return llvmValue, nil
return llvmValue
}
if llvmType.TypeKind() != llvm.PointerTypeKind {
@@ -581,7 +564,7 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
// This can be worked around by simply converting to a raw value,
// rawValue knows how to create such structs.
if v.offset() != 0 {
return llvm.Value{}, errors.New("interp: offset set without known pointer type")
panic("offset set without known pointer type")
}
return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
}
@@ -592,14 +575,14 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
if v.offset() != 0 {
// This should never happen, if offset is non-zero, the types
// shouldn't match.
return llvm.Value{}, errors.New("interp: offset set while there is no way to convert the type")
panic("offset set while there is no way to convert the type")
}
return llvmValue, nil
return llvmValue
}
if v.offset() == 0 {
// Offset is zero, so we can just bitcast to get a correct pointer.
return llvm.ConstBitCast(llvmValue, llvmType), nil
return llvm.ConstBitCast(llvmValue, llvmType)
}
// We need to make a constant GEP for pointer arithmetic.
@@ -623,11 +606,11 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
offset -= int64(mem.r.targetData.ElementOffset(objectElementType, element))
objectElementType = objectElementType.StructElementTypes()[element]
default:
return llvm.Value{}, errors.New("interp: pointer index with something other than a struct or array?")
panic("pointer index with something other than a struct or array?")
}
}
if offset < 0 {
return llvm.Value{}, errors.New("interp: offset has somehow gone negative, this should be impossible")
panic("offset has somehow gone negative, this should be impossible")
}
// Finally do the gep, using the above computed indices.
@@ -635,9 +618,283 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
// the bits of the pointer are now correct, just not the type).
gep := llvm.ConstInBoundsGEP(llvmValue, indices)
if gep.Type() != llvmType {
return llvm.ConstBitCast(gep, llvmType), nil
return llvm.ConstBitCast(gep, llvmType)
}
return gep, nil
return gep
}
// 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 {
keyValues = append(keyValues, key.rawLLVMValue(mem))
}
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 {
valueValues = append(valueValues, value.rawLLVMValue(mem))
}
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 {
if !v.hashmap.IsNil() {
return v.hashmap
}
// 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() {
panic("todo: map key with pointer")
}
data = key.buf
keyValue = key
default:
panic("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
}
// 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
@@ -761,7 +1018,7 @@ func (v rawValue) equal(rhs rawValue) bool {
// goes. The resulting value does not have a specified type, but it will be the
// same size and have the same bytes if it was created with a provided LLVM type
// (through toLLVMValue).
func (v rawValue) rawLLVMValue(mem *memoryView) (llvm.Value, error) {
func (v rawValue) rawLLVMValue(mem *memoryView) llvm.Value {
var structFields []llvm.Value
ctx := mem.r.mod.Context()
int8Type := ctx.Int8Type()
@@ -785,10 +1042,7 @@ func (v rawValue) rawLLVMValue(mem *memoryView) (llvm.Value, error) {
for i := uint32(0); i < uint32(len(v.buf)); {
if v.buf[i] > 255 {
addBytes()
field, err := pointerValue{v.buf[i]}.toLLVMValue(llvm.Type{}, mem)
if err != nil {
return llvm.Value{}, err
}
field := pointerValue{v.buf[i]}.toLLVMValue(llvm.Type{}, mem)
elementType := field.Type().ElementType()
if elementType.TypeKind() == llvm.StructTypeKind {
// There are some special pointer types that should be used as a
@@ -811,12 +1065,12 @@ func (v rawValue) rawLLVMValue(mem *memoryView) (llvm.Value, error) {
// Return the created data.
if len(structFields) == 1 {
return structFields[0], nil
return structFields[0]
}
return ctx.ConstStruct(structFields, false), nil
return ctx.ConstStruct(structFields, false)
}
func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
isZero := true
for _, p := range v.buf {
if p != 0 {
@@ -825,7 +1079,7 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value,
}
}
if isZero {
return llvm.ConstNull(llvmType), nil
return llvm.ConstNull(llvmType)
}
switch llvmType.TypeKind() {
case llvm.IntegerTypeKind:
@@ -834,17 +1088,7 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value,
if err != nil {
panic(err)
}
if checks && mem.r.targetData.TypeAllocSize(llvmType) != mem.r.targetData.TypeAllocSize(mem.r.i8ptrType) {
// Probably trying to serialize a pointer to a byte array,
// perhaps as a result of rawLLVMValue() in a previous interp
// run.
return llvm.Value{}, errInvalidPtrToIntSize
}
v, err := ptr.toLLVMValue(llvm.Type{}, mem)
if err != nil {
return llvm.Value{}, err
}
return llvm.ConstPtrToInt(v, llvmType), nil
return llvm.ConstPtrToInt(ptr.toLLVMValue(llvm.Type{}, mem), llvmType)
}
var n uint64
switch llvmType.IntTypeWidth() {
@@ -864,7 +1108,7 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value,
default:
panic("unknown integer size")
}
return llvm.ConstInt(llvmType, n, false), nil
return llvm.ConstInt(llvmType, n, false)
case llvm.StructTypeKind:
fieldTypes := llvmType.StructElementTypes()
fields := make([]llvm.Value, len(fieldTypes))
@@ -873,16 +1117,12 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value,
field := rawValue{
buf: v.buf[offset:],
}
var err error
fields[i], err = field.toLLVMValue(fieldType, mem)
if err != nil {
return llvm.Value{}, err
}
fields[i] = field.toLLVMValue(fieldType, mem)
}
if llvmType.StructName() != "" {
return llvm.ConstNamedStruct(llvmType, fields), nil
return llvm.ConstNamedStruct(llvmType, fields)
}
return llvmType.Context().ConstStruct(fields, false), nil
return llvmType.Context().ConstStruct(fields, false)
case llvm.ArrayTypeKind:
numElements := llvmType.ArrayLength()
childType := llvmType.ElementType()
@@ -893,34 +1133,27 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value,
field := rawValue{
buf: v.buf[offset:],
}
var err error
fields[i], err = field.toLLVMValue(childType, mem)
if err != nil {
return llvm.Value{}, err
}
fields[i] = field.toLLVMValue(childType, mem)
if checks && fields[i].Type() != childType {
panic("child type doesn't match")
}
}
return llvm.ConstArray(childType, fields), nil
return llvm.ConstArray(childType, fields)
case llvm.PointerTypeKind:
if v.buf[0] > 255 {
// This is a regular pointer.
llvmValue, err := pointerValue{v.buf[0]}.toLLVMValue(llvm.Type{}, mem)
if err != nil {
return llvm.Value{}, err
}
llvmValue := pointerValue{v.buf[0]}.toLLVMValue(llvm.Type{}, mem)
if llvmValue.Type() != llvmType {
llvmValue = llvm.ConstBitCast(llvmValue, llvmType)
}
return llvmValue, nil
return llvmValue
}
// This is either a null pointer or a raw pointer for memory-mapped I/O
// (such as 0xe000ed00).
ptr := rawValue{v.buf[:mem.r.pointerSize]}.Uint()
if ptr == 0 {
// Null pointer.
return llvm.ConstNull(llvmType), nil
return llvm.ConstNull(llvmType)
}
var ptrValue llvm.Value // the underlying int
switch mem.r.pointerSize {
@@ -931,19 +1164,19 @@ func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value,
case 2:
ptrValue = llvm.ConstInt(llvmType.Context().Int16Type(), ptr, false)
default:
return llvm.Value{}, errors.New("interp: unknown pointer size")
panic("unknown pointer size")
}
return llvm.ConstIntToPtr(ptrValue, llvmType), nil
return llvm.ConstIntToPtr(ptrValue, llvmType)
case llvm.DoubleTypeKind:
b := rawValue{v.buf[:8]}.Uint()
f := math.Float64frombits(b)
return llvm.ConstFloat(llvmType, f), nil
return llvm.ConstFloat(llvmType, f)
case llvm.FloatTypeKind:
b := uint32(rawValue{v.buf[:4]}.Uint())
f := math.Float32frombits(b)
return llvm.ConstFloat(llvmType, float64(f)), nil
return llvm.ConstFloat(llvmType, float64(f))
default:
return llvm.Value{}, errors.New("interp: todo: raw value to LLVM value: " + llvmType.String())
panic("todo: raw value to LLVM value: " + llvmType.String())
}
}
@@ -1132,8 +1365,8 @@ func (v localValue) Int() int64 {
panic("interp: localValue.Int")
}
func (v localValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
return v.value, nil
func (v localValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) llvm.Value {
return v.value
}
func (r *runner) getValue(llvmValue llvm.Value) value {
-25
View File
@@ -65,12 +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)
ret void
}
@@ -93,22 +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
}
-23
View File
@@ -25,8 +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)
ret void
}
@@ -46,24 +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
}
+2 -2
View File
@@ -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
}
+74
View File
@@ -0,0 +1,74 @@
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "armv6m-none-eabi"
%runtime._string = type { i8*, i32 }
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
@main.m = global %runtime.hashmap* null
@main.binaryMap = global %runtime.hashmap* null
@main.stringMap = global %runtime.hashmap* null
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
declare %runtime.hashmap* @runtime.hashmapMake(i8, i8, i32, i8* %context, i8* %parentHandle)
declare void @runtime.hashmapBinarySet(%runtime.hashmap*, i8*, i8*, i8* %context, i8* %parentHandle)
declare void @runtime.hashmapStringSet(%runtime.hashmap*, i8*, i32, i8*, i8* %context, i8* %parentHandle)
declare void @llvm.lifetime.end.p0i8(i64, i8*)
declare void @llvm.lifetime.start.p0i8(i64, i8*)
define void @runtime.initAll() unnamed_addr {
entry:
call void @main.init(i8* undef, i8* null)
ret void
}
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
entry:
; Test that hashmap optimizations generally work (even with lifetimes).
%hashmap.key = alloca i8
%hashmap.value = alloca %runtime._string
%0 = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 8, i32 1, i8* undef, i8* null)
%hashmap.value.bitcast = bitcast %runtime._string* %hashmap.value to i8*
call void @llvm.lifetime.start.p0i8(i64 8, i8* %hashmap.value.bitcast)
store %runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string* %hashmap.value
call void @llvm.lifetime.start.p0i8(i64 1, i8* %hashmap.key)
store i8 1, i8* %hashmap.key
call void @runtime.hashmapBinarySet(%runtime.hashmap* %0, i8* %hashmap.key, i8* %hashmap.value.bitcast, i8* undef, i8* null)
call void @llvm.lifetime.end.p0i8(i64 1, i8* %hashmap.key)
call void @llvm.lifetime.end.p0i8(i64 8, i8* %hashmap.value.bitcast)
store %runtime.hashmap* %0, %runtime.hashmap** @main.m
; Other tests, that can be done in a separate function.
call void @main.testNonConstantBinarySet()
call void @main.testNonConstantStringSet()
ret void
}
; Test that a map loaded from a global can still be used for mapassign
; operations (with binary keys).
define internal void @main.testNonConstantBinarySet() {
%hashmap.key = alloca i8
%hashmap.value = alloca i8
; Create hashmap from global.
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 1, i32 1, i8* undef, i8* null)
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.binaryMap
%map = load %runtime.hashmap*, %runtime.hashmap** @main.binaryMap
; Do the binary set to the newly loaded map.
store i8 1, i8* %hashmap.key
store i8 2, i8* %hashmap.value
call void @runtime.hashmapBinarySet(%runtime.hashmap* %map, i8* %hashmap.key, i8* %hashmap.value, i8* undef, i8* null)
ret void
}
; Test that a map loaded from a global can still be used for mapassign
; operations (with string keys).
define internal void @main.testNonConstantStringSet() {
%hashmap.value = alloca i8
; Create hashmap from global.
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 8, i8 1, i32 1, i8* undef, i8* null)
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.stringMap
%map = load %runtime.hashmap*, %runtime.hashmap** @main.stringMap
; Do the string set to the newly loaded map.
store i8 2, i8* %hashmap.value
call void @runtime.hashmapStringSet(%runtime.hashmap* %map, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7, i8* %hashmap.value, i8* undef, i8* null)
ret void
}
+20
View File
@@ -0,0 +1,20 @@
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "armv6m-none-eabi"
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
@main.m = local_unnamed_addr global %runtime.hashmap* @"main$map"
@main.binaryMap = local_unnamed_addr global %runtime.hashmap* @"main$map.1"
@main.stringMap = local_unnamed_addr global %runtime.hashmap* @"main$map.3"
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
@"main$map" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } }, { [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } }* @"main$mapbucket", i32 0, i32 0, i32 0), i32 1, i8 1, i8 8, i8 0 }
@"main$mapbucket" = internal unnamed_addr global { [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, { i8, [7 x i8] } { i8 1, [7 x i8] zeroinitializer }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } { { [7 x i8]*, [4 x i8] } { [7 x i8]* @main.init.string, [4 x i8] c"\07\00\00\00" }, [56 x i8] zeroinitializer } }
@"main$map.1" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } }, { [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } }* @"main$mapbucket.2", i32 0, i32 0, i32 0), i32 1, i8 1, i8 1, i8 0 }
@"main$mapbucket.2" = internal unnamed_addr global { [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, { i8, [7 x i8] } { i8 1, [7 x i8] zeroinitializer }, { i8, [7 x i8] } { i8 2, [7 x i8] zeroinitializer } }
@"main$map.3" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } }, { [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } }* @"main$mapbucket.4", i32 0, i32 0, i32 0), i32 1, i8 8, i8 1, i8 0 }
@"main$mapbucket.4" = internal unnamed_addr global { [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } } { [8 x i8] c"x\00\00\00\00\00\00\00", i8* null, { { [7 x i8]*, [4 x i8] }, [56 x i8] } { { [7 x i8]*, [4 x i8] } { [7 x i8]* @main.init.string, [4 x i8] c"\07\00\00\00" }, [56 x i8] zeroinitializer }, { i8, [7 x i8] } { i8 2, [7 x i8] zeroinitializer } }
define void @runtime.initAll() unnamed_addr {
entry:
ret void
}
-31
View File
@@ -1,31 +0,0 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
@main.phiNodesResultA = global i8 0
@main.phiNodesResultB = global i8 0
define void @runtime.initAll() {
call void @main.init()
ret void
}
; PHI nodes always use the value from the previous block, even in a loop. This
; means that the loop below should swap the values %a and %b on each iteration.
; Previously there was a bug which resulted in %b getting the value 3 on the
; second iteration while it should have gotten 1 (from the first iteration of
; %for.loop).
define internal void @main.init() {
entry:
br label %for.loop
for.loop:
%a = phi i8 [ 1, %entry ], [ %b, %for.loop ]
%b = phi i8 [ 3, %entry ], [ %a, %for.loop ]
%icmp = icmp eq i8 %a, 3
br i1 %icmp, label %for.done, label %for.loop
for.done:
store i8 %a, i8* @main.phiNodesResultA
store i8 %b, i8* @main.phiNodesResultB
ret void
}
-9
View File
@@ -1,9 +0,0 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
@main.phiNodesResultA = local_unnamed_addr global i8 3
@main.phiNodesResultB = local_unnamed_addr global i8 1
define void @runtime.initAll() local_unnamed_addr {
ret void
}
+6 -6
View File
@@ -382,14 +382,14 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
// Do CGo processing.
if len(p.CgoFiles) != 0 {
var initialCFlags []string
initialCFlags = append(initialCFlags, p.program.config.CFlags()...)
initialCFlags = append(initialCFlags, "-I"+p.Dir)
var cflags []string
cflags = append(cflags, p.program.config.CFlags()...)
cflags = append(cflags, "-I"+p.Dir)
if p.program.clangHeaders != "" {
initialCFlags = append(initialCFlags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
}
generated, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.program.fset, initialCFlags)
p.CFlags = append(initialCFlags, cflags...)
p.CFlags = cflags
generated, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.program.fset, cflags)
for path, hash := range accessedFiles {
p.FileHashes[path] = hash
}
+40 -118
View File
@@ -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 (
@@ -96,7 +93,7 @@ func copyFile(src, dst string) error {
// executeCommand is a simple wrapper to exec.Cmd
func executeCommand(options *compileopts.Options, name string, arg ...string) *exec.Cmd {
if options.PrintCommands {
fmt.Printf("%s %s\n", name, strings.Join(arg, " "))
fmt.Printf("%s %s\n ", name, strings.Join(arg, " "))
}
return exec.Command(name, arg...)
}
@@ -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 -112
View File
@@ -13,6 +13,7 @@ import (
"os/exec"
"path/filepath"
"runtime"
"sort"
"strings"
"sync"
"testing"
@@ -28,43 +29,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 +72,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,72 +104,27 @@ 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)
})
}
}
@@ -187,28 +141,10 @@ func runBuild(src, out string, opts *compileopts.Options) error {
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 +165,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 = runBuild("./"+path, binary, config)
if err != nil {
printCompilerError(t.Log, err)
t.Fail()
@@ -244,7 +191,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 +200,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...)
}
-71
View File
@@ -1,71 +0,0 @@
// Hand created file. DO NOT DELETE.
// atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go
// +build sam,atsame5x
// These are the supported pchctrl function numberings on the atsamd51x
// See http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf
// table 14-9
package sam
const (
PCHCTRL_GCLK_OSCCTRL_DFLL48 = 0 // DFLL48 input clock source
PCHCTRL_GCLK_OSCCTRL_FDPLL0 = 1 // Reference clock for FDPLL0
PCHCTRL_GCLK_OSCCTRL_FDPLL1 = 2 // Reference clock for FDPLL1
PCHCTRL_GCLK_OSCCTRL_FDPLL0_32K = 3 // FDPLL0 = 3 // 32KHz clock for internal lock timer
PCHCTRL_GCLK_OSCCTRL_FDPLL1_32K = 3 // FDPLL1 = 3 // 32KHz clock for internal lock timer
PCHCTRL_GCLK_SDHC0_SLOW = 3 // SDHC0 = 3 // Slow
PCHCTRL_GCLK_SDHC1_SLOW = 3 // SDHC1 = 3 // Slow
PCHCTRL_GCLK_SERCOMX_SLOW = 3 // GCLK_SERCOM[0..7]_SLOW = 3
PCHCTRL_GCLK_EIC = 4
PCHCTRL_GCLK_FREQM_MSR = 5 // FREQM Measure
PCHCTRL_GCLK_FREQM_REF = 6 // FREQM Reference
PCHCTRL_GCLK_SERCOM0_CORE = 7 // SERCOM0 Core
PCHCTRL_GCLK_SERCOM1_CORE = 8 // SERCOM1 Core
PCHCTRL_GCLK_TC0 = 9
PCHCTRL_GCLK_TC1 = 9 // TC0, TC1
PCHCTRL_GCLK_USB = 10 // USB
PCHCTRL_GCLK_EVSYS0 = 11
PCHCTRL_GCLK_EVSYS1 = 12
PCHCTRL_GCLK_EVSYS2 = 13
PCHCTRL_GCLK_EVSYS3 = 14
PCHCTRL_GCLK_EVSYS4 = 15
PCHCTRL_GCLK_EVSYS5 = 16
PCHCTRL_GCLK_EVSYS6 = 17
PCHCTRL_GCLK_EVSYS7 = 18
PCHCTRL_GCLK_EVSYS8 = 19
PCHCTRL_GCLK_EVSYS9 = 20
PCHCTRL_GCLK_EVSYS10 = 21
PCHCTRL_GCLK_EVSYS11 = 22
PCHCTRL_GCLK_SERCOM2_CORE = 23 // SERCOM2 Core
PCHCTRL_GCLK_SERCOM3_CORE = 24 // SERCOM3 Core
PCHCTRL_GCLK_TCC0 = 25
PCHCTRL_GCLK_TCC1 = 25 // TCC0, TCC1
PCHCTRL_GCLK_TC2 = 26
PCHCTRL_GCLK_TC3 = 26 // TC2, TC3
PCHCTRL_GCLK_CAN0 = 27 // CAN0
PCHCTRL_GCLK_CAN1 = 28 // CAN1
PCHCTRL_GCLK_TCC2 = 29
PCHCTRL_GCLK_TCC3 = 29 // TCC2, TCC3
PCHCTRL_GCLK_TC4 = 30
PCHCTRL_GCLK_TC5 = 30 // TC4, TC5
PCHCTRL_GCLK_PDEC = 31 // PDEC
PCHCTRL_GCLK_AC = 32 // AC
PCHCTRL_GCLK_CCL = 33 // CCL
PCHCTRL_GCLK_SERCOM4_CORE = 34 // SERCOM4 Core
PCHCTRL_GCLK_SERCOM5_CORE = 35 // SERCOM5 Core
PCHCTRL_GCLK_SERCOM6_CORE = 36 // SERCOM6 Core
PCHCTRL_GCLK_SERCOM7_CORE = 37 // SERCOM7 Core
PCHCTRL_GCLK_TCC4 = 38 // TCC4
PCHCTRL_GCLK_TC6 = 39
PCHCTRL_GCLK_TC7 = 39 // TC6, TC7
PCHCTRL_GCLK_ADC0 = 40 // ADC0
PCHCTRL_GCLK_ADC1 = 41 // ADC1
PCHCTRL_GCLK_DAC = 42 // DAC
PCHCTRL_GCLK_I2S0 = 43
PCHCTRL_GCLK_I2S1 = 44
PCHCTRL_GCLK_SDHC0 = 45 // SDHC0
PCHCTRL_GCLK_SDHC1 = 46 // SDHC1
PCHCTRL_GCLK_CM4_TRACE = 47 // CM4 Trace
)
-12
View File
@@ -1,12 +0,0 @@
// +build arduino_mega1280
package main
import "machine"
var (
// Configuration on an Arduino Uno.
pwm = machine.Timer3
pinA = machine.PH3 // pin 6 on the Mega
pinB = machine.PH4 // pin 7 on the Mega
)
-12
View File
@@ -1,12 +0,0 @@
// +build arduino
package main
import "machine"
var (
// Configuration on an Arduino Uno.
pwm = machine.Timer2
pinA = machine.PB3 // pin 11 on the Uno
pinB = machine.PD3 // pin 3 on the Uno
)
-11
View File
@@ -1,11 +0,0 @@
// +build feather_m4
package main
import "machine"
var (
pwm = machine.TCC0
pinA = machine.D12
pinB = machine.D13
)
-11
View File
@@ -1,11 +0,0 @@
// +build itsybitsy_m0
package main
import "machine"
var (
pwm = machine.TCC0
pinA = machine.D3
pinB = machine.D4
)
-11
View File
@@ -1,11 +0,0 @@
// +build itsybitsy_m4
package main
import "machine"
var (
pwm = machine.TCC0
pinA = machine.D12
pinB = machine.D13
)
+54 -64
View File
@@ -1,74 +1,64 @@
package main
// 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()
}
}
-104
View File
@@ -1,104 +0,0 @@
// +build arduino_mega1280
package machine
// Return the current CPU frequency in hertz.
func CPUFrequency() uint32 {
return 16000000
}
const (
AREF Pin = NoPin
LED Pin = PB7
A0 Pin = PF0
A1 Pin = PF1
A2 Pin = PF2
A3 Pin = PF3
A4 Pin = PF4
A5 Pin = PF5
A6 Pin = PF6
A7 Pin = PF7
A8 Pin = PK0
A9 Pin = PK1
A10 Pin = PK2
A11 Pin = PK3
A12 Pin = PK4
A13 Pin = PK5
A14 Pin = PK6
A15 Pin = PK7
// Analog Input
ADC0 Pin = PF0
ADC1 Pin = PF1
ADC2 Pin = PF2
ADC3 Pin = PF3
ADC4 Pin = PF4
ADC5 Pin = PF5
ADC6 Pin = PF6
ADC7 Pin = PF7
ADC8 Pin = PK0
ADC9 Pin = PK1
ADC10 Pin = PK2
ADC11 Pin = PK3
ADC12 Pin = PK4
ADC13 Pin = PK5
ADC14 Pin = PK6
ADC15 Pin = PK7
// Digital pins
D0 Pin = PE0
D1 Pin = PE1
D2 Pin = PE4
D3 Pin = PE5
D4 Pin = PG5
D5 Pin = PE3
D6 Pin = PH3
D7 Pin = PH4
D8 Pin = PH5
D9 Pin = PH6
D10 Pin = PB4
D11 Pin = PB5
D12 Pin = PB6
D13 Pin = PB7
D14 Pin = PJ1
D15 Pin = PJ0
D16 Pin = PH1
D17 Pin = PH0
D18 Pin = PD3
D19 Pin = PD2
D20 Pin = PD1
D21 Pin = PD0
D22 Pin = PA0
D23 Pin = PA1
D24 Pin = PA2
D25 Pin = PA3
D26 Pin = PA4
D27 Pin = PA5
D28 Pin = PA6
D29 Pin = PA7
D30 Pin = PC7
D31 Pin = PC6
D32 Pin = PC5
D33 Pin = PC4
D34 Pin = PC3
D35 Pin = PC2
D36 Pin = PC1
D37 Pin = PC0
D38 Pin = PD7
D39 Pin = PG2
D40 Pin = PG1
D41 Pin = PG0
D42 Pin = PL7
D43 Pin = PL6
D44 Pin = PL5
D45 Pin = PL4
D46 Pin = PL3
D47 Pin = PL2
D48 Pin = PL1
D49 Pin = PL0
D50 Pin = PB3
D51 Pin = PB2
D52 Pin = PB1
D53 Pin = PB0
)
-330
View File
@@ -1,330 +0,0 @@
// +build atsame54_xpro
package machine
import (
"device/sam"
"runtime/interrupt"
)
// Definition for compatibility, but not used
const RESET_MAGIC_VALUE = 0x00000000
const (
LED = PC18
BUTTON = PB31
)
const (
// https://ww1.microchip.com/downloads/en/DeviceDoc/70005321A.pdf
// Extension Header EXT1
EXT1_PIN3_ADC_P = PB04
EXT1_PIN4_ADC_N = PB05
EXT1_PIN5_GPIO1 = PA06
EXT1_PIN6_GPIO2 = PA07
EXT1_PIN7_PWM_P = PB08
EXT1_PIN8_PWM_N = PB09
EXT1_PIN9_IRQ = PB07
EXT1_PIN9_GPIO = PB07
EXT1_PIN10_SPI_SS_B = PA27
EXT1_PIN10_GPIO = PA27
EXT1_PIN11_TWI_SDA = PA22
EXT1_PIN12_TWI_SCL = PA23
EXT1_PIN13_UART_RX = PA05
EXT1_PIN14_UART_TX = PA04
EXT1_PIN15_SPI_SS_A = PB28
EXT1_PIN16_SPI_SDO = PB27
EXT1_PIN17_SPI_SDI = PB29
EXT1_PIN18_SPI_SCK = PB26
// Extension Header EXT2
EXT2_PIN3_ADC_P = PB00
EXT2_PIN4_ADC_N = PA03
EXT2_PIN5_GPIO1 = PB01
EXT2_PIN6_GPIO2 = PB06
EXT2_PIN7_PWM_P = PB14
EXT2_PIN8_PWM_N = PB15
EXT2_PIN9_IRQ = PD00
EXT2_PIN9_GPIO = PD00
EXT2_PIN10_SPI_SS_B = PB02
EXT2_PIN10_GPIO = PB02
EXT2_PIN11_TWI_SDA = PD08
EXT2_PIN12_TWI_SCL = PD09
EXT2_PIN13_UART_RX = PB17
EXT2_PIN14_UART_TX = PB16
EXT2_PIN15_SPI_SS_A = PC06
EXT2_PIN16_SPI_SDO = PC04
EXT2_PIN17_SPI_SDI = PC07
EXT2_PIN18_SPI_SCK = PC05
// Extension Header EXT3
EXT3_PIN3_ADC_P = PC02
EXT3_PIN4_ADC_N = PC03
EXT3_PIN5_GPIO1 = PC01
EXT3_PIN6_GPIO2 = PC10
EXT3_PIN7_PWM_P = PD10
EXT3_PIN8_PWM_N = PD11
EXT3_PIN9_IRQ = PC30
EXT3_PIN9_GPIO = PC30
EXT3_PIN10_SPI_SS_B = PC31
EXT3_PIN10_GPIO = PC31
EXT3_PIN11_TWI_SDA = PD08
EXT3_PIN12_TWI_SCL = PD09
EXT3_PIN13_UART_RX = PC23
EXT3_PIN14_UART_TX = PC22
EXT3_PIN15_SPI_SS_A = PC14
EXT3_PIN16_SPI_SDO = PC04
EXT3_PIN17_SPI_SDI = PC07
EXT3_PIN18_SPI_SCK = PC05
// SD_CARD
SD_CARD_MCDA0 = PB18
SD_CARD_MCDA1 = PB19
SD_CARD_MCDA2 = PB20
SD_CARD_MCDA3 = PB21
SD_CARD_MCCK = PA21
SD_CARD_MCCDA = PA20
SD_CARD_DETECT = PD20
SD_CARD_PROTECT = PD21
// I2C
I2C_SDA = PD08
I2C_SCL = PD09
// CAN
CAN0_TX = PA22
CAN0_RX = PA23
CAN1_STANDBY = PC13
CAN1_TX = PB12
CAN1_RX = PB13
CAN_STANDBY = CAN1_STANDBY
CAN_TX = CAN1_TX
CAN_RX = CAN1_RX
// PDEC
PDEC_PHASE_A = PC16
PDEC_PHASE_B = PC17
PDEC_INDEX = PC18
// PCC
PCC_I2C_SDA = PD08
PCC_I2C_SCL = PD09
PCC_VSYNC_DEN1 = PA12
PCC_HSYNC_DEN2 = PA13
PCC_CLK = PA14
PCC_XCLK = PA15
PCC_DATA00 = PA16
PCC_DATA01 = PA17
PCC_DATA02 = PA18
PCC_DATA03 = PA19
PCC_DATA04 = PA20
PCC_DATA05 = PA21
PCC_DATA06 = PA22
PCC_DATA07 = PA23
PCC_DATA08 = PB14
PCC_DATA09 = PB15
PCC_RESET = PC12
PCC_PWDN = PC11
// Ethernet
ETHERNET_TXCK = PA14
ETHERNET_TXEN = PA17
ETHERNET_TX0 = PA18
ETHERNET_TX1 = PA19
ETHERNET_RXER = PA15
ETHERNET_RX0 = PA13
ETHERNET_RX1 = PA12
ETHERNET_RXDV = PC20
ETHERNET_MDIO = PC12
ETHERNET_MDC = PC11
ETHERNET_INT = PD12
ETHERNET_RESET = PC21
PIN_QT_BUTTON = PA16
PIN_BTN0 = PB31
PIN_ETH_LED = PC15
PIN_LED0 = PC18
PIN_ADC_DAC = PA02
PIN_VBUS_DETECT = PC00
PIN_USB_ID = PC19
)
// UART0 aka USBCDC pins
const (
USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25
)
// UART pins
const (
// Extension Header EXT1
UART_TX_PIN = PA04 // TX : SERCOM0/PAD[0]
UART_RX_PIN = PA05 // RX : SERCOM0/PAD[1]
// Extension Header EXT2
UART2_TX_PIN = PB16 // TX : SERCOM5/PAD[0]
UART2_RX_PIN = PB17 // RX : SERCOM5/PAD[1]
// Extension Header EXT3
UART3_TX_PIN = PC22 // TX : SERCOM1/PAD[0]
UART3_RX_PIN = PC23 // RX : SERCOM1/PAD[1]
// Virtual COM Port
UART4_TX_PIN = PB25 // TX : SERCOM2/PAD[0]
UART4_RX_PIN = PB24 // RX : SERCOM2/PAD[1]
)
// I2C pins
const (
// Extension Header EXT1
SDA0_PIN = PA22 // SDA: SERCOM3/PAD[0]
SCL0_PIN = PA23 // SCL: SERCOM3/PAD[1]
// Extension Header EXT2
SDA1_PIN = PD08 // SDA: SERCOM7/PAD[0]
SCL1_PIN = PD09 // SCL: SERCOM7/PAD[1]
// Extension Header EXT3
SDA2_PIN = PD08 // SDA: SERCOM7/PAD[0]
SCL2_PIN = PD09 // SCL: SERCOM7/PAD[1]
// Data Gateway Interface
SDA_DGI_PIN = PD08 // SDA: SERCOM7/PAD[0]
SCL_DGI_PIN = PD09 // SCL: SERCOM7/PAD[1]
SDA_PIN = SDA0_PIN
SCL_PIN = SCL0_PIN
)
// SPI pins
const (
// Extension Header EXT1
SPI0_SCK_PIN = PB26 // SCK: SERCOM4/PAD[1]
SPI0_SDO_PIN = PB27 // SDO: SERCOM4/PAD[0]
SPI0_SDI_PIN = PB29 // SDI: SERCOM4/PAD[3]
SPI0_SS_PIN = PB28 // SS : SERCOM4/PAD[2]
// Extension Header EXT2
SPI1_SCK_PIN = PC05 // SCK: SERCOM6/PAD[1]
SPI1_SDO_PIN = PC04 // SDO: SERCOM6/PAD[0]
SPI1_SDI_PIN = PC07 // SDI: SERCOM6/PAD[3]
SPI1_SS_PIN = PC06 // SS : SERCOM6/PAD[2]
// Extension Header EXT3
SPI2_SCK_PIN = PC05 // SCK: SERCOM6/PAD[1]
SPI2_SDO_PIN = PC04 // SDO: SERCOM6/PAD[0]
SPI2_SDI_PIN = PC07 // SDI: SERCOM6/PAD[3]
SPI2_SS_PIN = PC14 // SS : GPIO
// Data Gateway Interface
SPI_DGI_SCK_PIN = PC05 // SCK: SERCOM6/PAD[1]
SPI_DGI_SDO_PIN = PC04 // SDO: SERCOM6/PAD[0]
SPI_DGI_SDI_PIN = PC07 // SDI: SERCOM6/PAD[3]
SPI_DGI_SS_PIN = PD01 // SS : GPIO
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "SAM E54 Xplained Pro"
usb_STRING_MANUFACTURER = "Atmel"
)
var (
usb_VID uint16 = 0x03EB
usb_PID uint16 = 0x2404
)
// UART on the SAM E54 Xplained Pro
var (
// Extension Header EXT1
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT,
SERCOM: 0,
}
// Extension Header EXT2
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM5_USART_INT,
SERCOM: 5,
}
// Extension Header EXT3
UART3 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM1_USART_INT,
SERCOM: 1,
}
// EDBG Virtual COM Port
UART4 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM2_USART_INT,
SERCOM: 2,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART2.handleInterrupt)
UART3.Interrupt = interrupt.New(sam.IRQ_SERCOM1_2, UART3.handleInterrupt)
UART4.Interrupt = interrupt.New(sam.IRQ_SERCOM2_2, UART4.handleInterrupt)
}
// I2C on the SAM E54 Xplained Pro
var (
// Extension Header EXT1
I2C0 = I2C{
Bus: sam.SERCOM3_I2CM,
SERCOM: 3,
}
// Extension Header EXT2
I2C1 = I2C{
Bus: sam.SERCOM7_I2CM,
SERCOM: 7,
}
// Extension Header EXT3
I2C2 = I2C{
Bus: sam.SERCOM7_I2CM,
SERCOM: 7,
}
// Data Gateway Interface
I2C3 = I2C{
Bus: sam.SERCOM7_I2CM,
SERCOM: 7,
}
)
// SPI on the SAM E54 Xplained Pro
var (
// Extension Header EXT1
SPI0 = SPI{
Bus: sam.SERCOM4_SPIM,
SERCOM: 4,
}
// Extension Header EXT2
SPI1 = SPI{
Bus: sam.SERCOM6_SPIM,
SERCOM: 6,
}
// Extension Header EXT3
SPI2 = SPI{
Bus: sam.SERCOM6_SPIM,
SERCOM: 6,
}
// Data Gateway Interface
SPI3 = SPI{
Bus: sam.SERCOM6_SPIM,
SERCOM: 6,
}
)
+2 -2
View File
@@ -25,8 +25,8 @@ const (
// Analog Pins
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
-142
View File
@@ -1,142 +0,0 @@
// +build feather_m4_can
package machine
import (
"device/sam"
"runtime/interrupt"
)
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
// GPIO Pins
const (
D0 = PB17 // UART0 RX/PWM available
D1 = PB16 // UART0 TX/PWM available
D4 = PA14 // PWM available
D5 = PA16 // PWM available
D6 = PA18 // PWM available
D7 = PB03 // neopixel power
D8 = PB02 // built-in neopixel
D9 = PA19 // PWM available
D10 = PA20 // can be used for PWM or UART1 TX
D11 = PA21 // can be used for PWM or UART1 RX
D12 = PA22 // PWM available
D13 = PA23 // PWM available
D21 = PA13 // PWM available
D22 = PA12 // PWM available
D23 = PB22 // PWM available
D24 = PB23 // PWM available
D25 = PA17 // PWM available
)
// Analog pins
const (
A0 = PA02 // ADC/AIN[0]
A1 = PA05 // ADC/AIN[2]
A2 = PB08 // ADC/AIN[3]
A3 = PB09 // ADC/AIN[4]
A4 = PA04 // ADC/AIN[5]
A5 = PA06 // ADC/AIN[10]
)
const (
LED = D13
NEOPIXELS = D8
)
// UART0 aka USBCDC pins
const (
USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25
)
const (
UART_TX_PIN = D1
UART_RX_PIN = D0
)
const (
UART2_TX_PIN = A4
UART2_RX_PIN = A5
)
// I2C pins
const (
SDA_PIN = D22 // SDA: SERCOM2/PAD[0]
SCL_PIN = D21 // SCL: SERCOM2/PAD[1]
)
// SPI pins
const (
SPI0_SCK_PIN = D25 // SCK: SERCOM1/PAD[1]
SPI0_SDO_PIN = D24 // SDO: SERCOM1/PAD[3]
SPI0_SDI_PIN = D23 // SDI: SERCOM1/PAD[2]
)
// CAN pins
const (
CAN0_TX = PA22
CAN0_RX = PA23
CAN1_STANDBY = PB12
CAN1_TX = PB14
CAN1_RX = PB15
BOOST_EN = PB13 // power control of CAN1's TCAN1051HGV (H: enable)
CAN_STANDBY = CAN1_STANDBY
CAN_S = CAN1_STANDBY
CAN_TX = CAN1_TX
CAN_RX = CAN1_RX
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Adafruit Feather M4 CAN"
usb_STRING_MANUFACTURER = "Adafruit"
)
var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x80CD
)
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM5_USART_INT,
SERCOM: 5,
}
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT,
SERCOM: 0,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
// turn on neopixel
D7.Configure(PinConfig{Mode: PinOutput})
D7.High()
}
// I2C on the Feather M4.
var (
I2C0 = &I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI on the Feather M4.
var (
SPI0 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
+2 -13
View File
@@ -41,17 +41,13 @@ const (
// LORA RFM95 Radio
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
View File
@@ -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
-92
View File
@@ -1,92 +0,0 @@
// +build nucleol031k6
package machine
import (
"device/stm32"
"runtime/interrupt"
)
const (
LED = LED_BUILTIN
LED_BUILTIN = LED_GREEN
LED_GREEN = PB3
)
const (
// Arduino Pins
A0 = PA0 // ADC_IN0
A1 = PA1 // ADC_IN1
A2 = PA3 // ADC_IN3
A3 = PA4 // ADC_IN4
A4 = PA5 // ADC_IN5 || I2C1_SDA
A5 = PA6 // ADC_IN6 || I2C1_SCL
A6 = PA7 // ADC_IN7
A7 = PA2 // ADC_IN2
D0 = PA10 // USART1_TX
D1 = PA9 // USART1_RX
D2 = PA12
D3 = PB0 // TIM2_CH3
D4 = PB7
D5 = PB6 // TIM16_CH1N
D6 = PB1 // TIM14_CH1
D9 = PA8 // TIM1_CH1
D10 = PA11 // SPI_CS || TIM1_CH4
D11 = PB5 // SPI1_MOSI || TIM3_CH2
D12 = PB4 // SPI1_MISO
D13 = PB3 // SPI1_SCK
)
const (
// UART pins
// PA2 and PA15 are connected to the ST-Link Virtual Com Port (VCP)
UART_TX_PIN = PA2
UART_RX_PIN = PA15
// SPI
SPI1_SCK_PIN = PB3
SPI1_SDI_PIN = PB5
SPI1_SDO_PIN = PB4
SPI0_SCK_PIN = SPI1_SCK_PIN
SPI0_SDI_PIN = SPI1_SDI_PIN
SPI0_SDO_PIN = SPI1_SDO_PIN
// I2C pins
// PB6 and PB7 are mapped to CN4 pin 7 and CN4 pin 8 respectively with the
// default solder bridge settings
I2C0_SCL_PIN = PB7
I2C0_SDA_PIN = PB6
I2C0_ALT_FUNC = 1
)
var (
// USART2 is the hardware serial port connected to the onboard ST-LINK
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
// Both UART0 and UART1 refer to USART2.
UART0 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART2,
TxAltFuncSelector: 4,
RxAltFuncSelector: 4,
}
UART1 = &UART0
// I2C1 is documented, alias to I2C0 as well
I2C1 = &I2C{
Bus: stm32.I2C1,
AltFuncSelector: 1,
}
I2C0 = I2C1
// SPI
SPI0 = SPI{
Bus: stm32.SPI1,
AltFuncSelector: 0,
}
SPI1 = &SPI0
)
func init() {
UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
}
+6 -44
View File
@@ -13,53 +13,20 @@ const (
LED_GREEN = PB3
)
// 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
View File
@@ -1,4 +1,4 @@
// +build atmega nrf sam stm32 fe310 k210
// +build atmega nrf sam stm32,!stm32l0 fe310 k210
package machine
+4
View File
@@ -37,6 +37,10 @@ func (p Pin) Low() {
p.Set(false)
}
type PWM struct {
Pin Pin
}
type ADC struct {
Pin Pin
}
-931
View File
@@ -1,931 +0,0 @@
// +build avr,atmega1280
package machine
import (
"device/avr"
"runtime/interrupt"
"runtime/volatile"
)
const irq_USART0_RX = avr.IRQ_USART0_RX
const (
portA Pin = iota * 8
portB
portC
portD
portE
portF
portG
portH
portJ
portK
portL
)
const (
PA0 = portA + 0
PA1 = portA + 1
PA2 = portA + 2
PA3 = portA + 3
PA4 = portA + 4
PA5 = portA + 5
PA6 = portA + 6
PA7 = portA + 7
PB0 = portB + 0
PB1 = portB + 1
PB2 = portB + 2
PB3 = portB + 3
PB4 = portB + 4
PB5 = portB + 5
PB6 = portB + 6
PB7 = portB + 7
PC0 = portC + 0
PC1 = portC + 1
PC2 = portC + 2
PC3 = portC + 3
PC4 = portC + 4
PC5 = portC + 5
PC6 = portC + 6
PC7 = portC + 7
PD0 = portD + 0
PD1 = portD + 1
PD2 = portD + 2
PD3 = portD + 3
PD7 = portD + 7
PE0 = portE + 0
PE1 = portE + 1
PE3 = portE + 3
PE4 = portE + 4
PE5 = portE + 5
PE6 = portE + 6
PF0 = portF + 0
PF1 = portF + 1
PF2 = portF + 2
PF3 = portF + 3
PF4 = portF + 4
PF5 = portF + 5
PF6 = portF + 6
PF7 = portF + 7
PG0 = portG + 0
PG1 = portG + 1
PG2 = portG + 2
PG5 = portG + 5
PH0 = portH + 0
PH1 = portH + 1
PH3 = portH + 3
PH4 = portH + 4
PH5 = portH + 5
PH6 = portH + 6
PJ0 = portJ + 0
PJ1 = portJ + 1
PK0 = portK + 0
PK1 = portK + 1
PK2 = portK + 2
PK3 = portK + 3
PK4 = portH + 4
PK5 = portH + 5
PK6 = portH + 6
PK7 = portH + 7
PL0 = portL + 0
PL1 = portL + 1
PL2 = portL + 2
PL3 = portL + 3
PL4 = portL + 4
PL5 = portL + 5
PL6 = portL + 6
PL7 = portL + 7
)
// getPortMask returns the PORTx register and mask for the pin.
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
switch {
case p >= PA0 && p <= PA7:
return avr.PORTA, 1 << uint8(p-portA)
case p >= PB0 && p <= PB7:
return avr.PORTB, 1 << uint8(p-portB)
case p >= PC0 && p <= PC7:
return avr.PORTC, 1 << uint8(p-portC)
case p >= PD0 && p <= PD7:
return avr.PORTD, 1 << uint8(p-portD)
case p >= PE0 && p <= PE6:
return avr.PORTE, 1 << uint8(p-portE)
case p >= PF0 && p <= PF7:
return avr.PORTF, 1 << uint8(p-portF)
case p >= PG0 && p <= PG5:
return avr.PORTG, 1 << uint8(p-portG)
case p >= PH0 && p <= PH6:
return avr.PORTH, 1 << uint8(p-portH)
case p >= PJ0 && p <= PJ1:
return avr.PORTJ, 1 << uint8(p-portJ)
case p >= PK0 && p <= PK7:
return avr.PORTK, 1 << uint8(p-portK)
case p >= PL0 && p <= PL7:
return avr.PORTL, 1 << uint8(p-portL)
default:
return avr.PORTA, 255
}
}
// PWM is one PWM peripheral, which consists of a counter and two output
// channels (that can be connected to two fixed pins). You can set the frequency
// using SetPeriod, but only for all the channels in this PWM peripheral at
// once.
type PWM struct {
num uint8
}
var (
Timer0 = PWM{0} // 8 bit timer for PB7 and PG5
Timer1 = PWM{1} // 16 bit timer for PB5 and PB6
Timer2 = PWM{2} // 8 bit timer for PB4 and PH6
Timer3 = PWM{3} // 16 bit timer for PE3, PE4 and PE5
Timer4 = PWM{4} // 16 bit timer for PH3, PH4 and PH5
Timer5 = PWM{5} // 16 bit timer for PL3, PL4 and PL5
)
// Configure enables and configures this PWM.
//
// For the two 8 bit timers, there is only a limited number of periods
// available, namely the CPU frequency divided by 256 and again divided by 1, 8,
// 64, 256, or 1024. For a MCU running at 16MHz, this would be a period of 16µs,
// 128µs, 1024µs, 4096µs, or 16384µs.
func (pwm PWM) Configure(config PWMConfig) error {
switch pwm.num {
case 0, 2: // 8-bit timers (Timer/counter 0 and Timer/counter 2)
// Calculate the timer prescaler.
// While we could configure a flexible top, that would sacrifice one of
// the PWM output compare registers and thus a PWM channel. I've chosen
// to instead limit this timer to a fixed number of frequencies.
var prescaler uint8
switch config.Period {
case 0, (uint64(1e9) * 256 * 1) / uint64(CPUFrequency()):
prescaler = 1
case (uint64(1e9) * 256 * 8) / uint64(CPUFrequency()):
prescaler = 2
case (uint64(1e9) * 256 * 64) / uint64(CPUFrequency()):
prescaler = 3
case (uint64(1e9) * 256 * 256) / uint64(CPUFrequency()):
prescaler = 4
case (uint64(1e9) * 256 * 1024) / uint64(CPUFrequency()):
prescaler = 5
default:
return ErrPWMPeriodTooLong
}
if pwm.num == 0 {
avr.TCCR0B.Set(prescaler)
// Set the PWM mode to fast PWM (mode = 3).
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
} else {
avr.TCCR2B.Set(prescaler)
// Set the PWM mode to fast PWM (mode = 3).
avr.TCCR2A.Set(avr.TCCR2A_WGM20 | avr.TCCR2A_WGM21)
}
case 1, 3, 4, 5:
// The top value is the number of PWM ticks a PWM period takes. It is
// initially picked assuming an unlimited counter top and no PWM
// prescaler.
var top uint64
if config.Period == 0 {
// Use a top appropriate for LEDs. Picking a relatively low period
// here (0xff) for consistency with the other timers.
top = 0xff
} else {
// The formula below calculates the following formula, optimized:
// top = period * (CPUFrequency() / 1e9)
// By dividing the CPU frequency first (an operation that is easily
// optimized away) the period has less chance of overflowing.
top = config.Period * (uint64(CPUFrequency()) / 1000000) / 1000
}
// The ideal PWM period may be larger than would fit in the PWM counter,
// which is 16 bits (see maxTop). Therefore, try to make the PWM clock
// speed lower with a prescaler to make the top value fit the maximum
// top value.
const maxTop = 0x10000
var prescalingTop uint8
switch {
case top <= maxTop:
prescalingTop = 3<<3 | 1 // no prescaling
case top/8 <= maxTop:
prescalingTop = 3<<3 | 2 // divide by 8
top /= 8
case top/64 <= maxTop:
prescalingTop = 3<<3 | 3 // divide by 64
top /= 64
case top/256 <= maxTop:
prescalingTop = 3<<3 | 4 // divide by 256
top /= 256
case top/1024 <= maxTop:
prescalingTop = 3<<3 | 5 // divide by 1024
top /= 1024
default:
return ErrPWMPeriodTooLong
}
// A top of 0x10000 is at 100% duty cycle. Subtract one because the
// counter counts from 0, not 1 (avoiding an off-by-one).
top -= 1
switch pwm.num {
case 1:
avr.TCCR1A.Set(avr.TCCR1A_WGM11)
avr.TCCR1B.Set(prescalingTop)
avr.ICR1H.Set(uint8(top >> 8))
avr.ICR1L.Set(uint8(top))
case 3:
avr.TCCR3A.Set(avr.TCCR3A_WGM31)
avr.TCCR3B.Set(prescalingTop)
avr.ICR3H.Set(uint8(top >> 8))
avr.ICR3L.Set(uint8(top))
case 4:
avr.TCCR4A.Set(avr.TCCR4A_WGM41)
avr.TCCR4B.Set(prescalingTop)
avr.ICR4H.Set(uint8(top >> 8))
avr.ICR4L.Set(uint8(top))
case 5:
avr.TCCR5A.Set(avr.TCCR5A_WGM51)
avr.TCCR5B.Set(prescalingTop)
avr.ICR5H.Set(uint8(top >> 8))
avr.ICR5L.Set(uint8(top))
}
}
return nil
}
// SetPeriod updates the period of this PWM peripheral.
// To set a particular frequency, use the following formula:
//
// period = 1e9 / frequency
//
// If you use a period of 0, a period that works well for LEDs will be picked.
//
// SetPeriod will not change the prescaler, but also won't change the current
// value in any of the channels. This means that you may need to update the
// value for the particular channel.
//
// Note that you cannot pick any arbitrary period after the PWM peripheral has
// been configured. If you want to switch between frequencies, pick the lowest
// frequency (longest period) once when calling Configure and adjust the
// frequency here as needed.
func (pwm PWM) SetPeriod(period uint64) error {
if pwm.num == 0 || pwm.num == 2 {
return ErrPWMPeriodTooLong // TODO better error message
}
// The top value is the number of PWM ticks a PWM period takes. It is
// initially picked assuming an unlimited counter top and no PWM
// prescaler.
var top uint64
if period == 0 {
// Use a top appropriate for LEDs. Picking a relatively low period
// here (0xff) for consistency with the other timers.
top = 0xff
} else {
// The formula below calculates the following formula, optimized:
// top = period * (CPUFrequency() / 1e9)
// By dividing the CPU frequency first (an operation that is easily
// optimized away) the period has less chance of overflowing.
top = period * (uint64(CPUFrequency()) / 1000000) / 1000
}
var prescaler uint8
switch pwm.num {
case 1:
prescaler = avr.TCCR1B.Get() & 0x7
case 3:
prescaler = avr.TCCR3B.Get() & 0x7
case 4:
prescaler = avr.TCCR4B.Get() & 0x7
case 5:
prescaler = avr.TCCR5B.Get() & 0x7
}
switch prescaler {
case 1:
top /= 1
case 2:
top /= 8
case 3:
top /= 64
case 4:
top /= 256
case 5:
top /= 1024
}
// A top of 0x10000 is at 100% duty cycle. Subtract one because the counter
// counts from 0, not 1 (avoiding an off-by-one).
top -= 1
if top > 0xffff {
return ErrPWMPeriodTooLong
}
switch pwm.num {
case 1:
// Warning: this change is not atomic!
avr.ICR1H.Set(uint8(top >> 8))
avr.ICR1L.Set(uint8(top))
// ... and because of that, set the counter back to zero to avoid most of
// the effects of this non-atomicity.
avr.TCNT1H.Set(0)
avr.TCNT1L.Set(0)
case 3:
// Warning: this change is not atomic!
avr.ICR3H.Set(uint8(top >> 8))
avr.ICR3L.Set(uint8(top))
// ... and because of that, set the counter back to zero to avoid most of
// the effects of this non-atomicity.
avr.TCNT3H.Set(0)
avr.TCNT3L.Set(0)
case 4:
// Warning: this change is not atomic!
avr.ICR4H.Set(uint8(top >> 8))
avr.ICR4L.Set(uint8(top))
// ... and because of that, set the counter back to zero to avoid most of
// the effects of this non-atomicity.
avr.TCNT4H.Set(0)
avr.TCNT4L.Set(0)
case 5:
// Warning: this change is not atomic!
avr.ICR5H.Set(uint8(top >> 8))
avr.ICR5L.Set(uint8(top))
// ... and because of that, set the counter back to zero to avoid most of
// the effects of this non-atomicity.
avr.TCNT5H.Set(0)
avr.TCNT5L.Set(0)
}
return nil
}
// Top returns the current counter top, for use in duty cycle calculation. It
// will only change with a call to Configure or SetPeriod, otherwise it is
// constant.
//
// The value returned here is hardware dependent. In general, it's best to treat
// it as an opaque value that can be divided by some number and passed to Set
// (see Set documentation for more information).
func (pwm PWM) Top() uint32 {
switch pwm.num {
case 1:
// Timer 1 has a configurable top value.
low := avr.ICR1L.Get()
high := avr.ICR1H.Get()
return uint32(high)<<8 | uint32(low) + 1
case 3:
// Timer 3 has a configurable top value.
low := avr.ICR3L.Get()
high := avr.ICR3H.Get()
return uint32(high)<<8 | uint32(low) + 1
case 4:
// Timer 4 has a configurable top value.
low := avr.ICR4L.Get()
high := avr.ICR4H.Get()
return uint32(high)<<8 | uint32(low) + 1
case 5:
// Timer 5 has a configurable top value.
low := avr.ICR5L.Get()
high := avr.ICR5H.Get()
return uint32(high)<<8 | uint32(low) + 1
}
// Other timers go from 0 to 0xff (0x100 or 256 in total).
return 256
}
// Counter returns the current counter value of the timer in this PWM
// peripheral. It may be useful for debugging.
func (pwm PWM) Counter() uint32 {
switch pwm.num {
case 0:
return uint32(avr.TCNT0.Get())
case 1:
mask := interrupt.Disable()
low := avr.TCNT1L.Get()
high := avr.TCNT1H.Get()
interrupt.Restore(mask)
return uint32(high)<<8 | uint32(low)
case 2:
return uint32(avr.TCNT2.Get())
case 3:
mask := interrupt.Disable()
low := avr.TCNT3L.Get()
high := avr.TCNT3H.Get()
interrupt.Restore(mask)
return uint32(high)<<8 | uint32(low)
case 4:
mask := interrupt.Disable()
low := avr.TCNT4L.Get()
high := avr.TCNT4H.Get()
interrupt.Restore(mask)
return uint32(high)<<8 | uint32(low)
case 5:
mask := interrupt.Disable()
low := avr.TCNT5L.Get()
high := avr.TCNT5H.Get()
interrupt.Restore(mask)
return uint32(high)<<8 | uint32(low)
}
// Unknown PWM.
return 0
}
// Period returns the used PWM period in nanoseconds. It might deviate slightly
// from the configured period due to rounding.
func (pwm PWM) Period() uint64 {
var prescaler uint8
switch pwm.num {
case 0:
prescaler = avr.TCCR0B.Get() & 0x7
case 1:
prescaler = avr.TCCR1B.Get() & 0x7
case 2:
prescaler = avr.TCCR2B.Get() & 0x7
case 3:
prescaler = avr.TCCR3B.Get() & 0x7
case 4:
prescaler = avr.TCCR4B.Get() & 0x7
case 5:
prescaler = avr.TCCR5B.Get() & 0x7
}
top := uint64(pwm.Top())
switch prescaler {
case 1: // prescaler 1
return 1 * top * 1000 / uint64(CPUFrequency()/1e6)
case 2: // prescaler 8
return 8 * top * 1000 / uint64(CPUFrequency()/1e6)
case 3: // prescaler 64
return 64 * top * 1000 / uint64(CPUFrequency()/1e6)
case 4: // prescaler 256
return 256 * top * 1000 / uint64(CPUFrequency()/1e6)
case 5: // prescaler 1024
return 1024 * top * 1000 / uint64(CPUFrequency()/1e6)
default: // unknown clock source
return 0
}
}
// Channel returns a PWM channel for the given pin.
func (pwm PWM) Channel(pin Pin) (uint8, error) {
pin.Configure(PinConfig{Mode: PinOutput})
pin.Low()
switch pwm.num {
case 0:
switch pin {
case PB7: // channel A
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
return 0, nil
case PG5: // channel B
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
return 1, nil
}
case 1:
switch pin {
case PB5: // channel A
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
return 0, nil
case PB6: // channel B
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
return 1, nil
}
case 2:
switch pin {
case PB4: // channel A
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
return 0, nil
case PH6: // channel B
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
return 1, nil
}
case 3:
switch pin {
case PE3: // channel A
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1)
return 0, nil
case PE4: //channel B
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1)
return 1, nil
case PE5: //channel C
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1)
return 2, nil
}
case 4:
switch pin {
case PH3: // channel A
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1)
return 0, nil
case PH4: //channel B
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1)
return 1, nil
case PH5: //channel C
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1)
return 2, nil
}
case 5:
switch pin {
case PL3: // channel A
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1)
return 0, nil
case PL4: //channel B
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1)
return 1, nil
case PL5: //channel C
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1)
return 2, nil
}
}
return 0, ErrInvalidOutputPin
}
// SetInverting sets whether to invert the output of this channel.
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
// the time and low for the rest. Inverting flips the output as if a NOT gate
// was placed at the output, meaning that the output would be 25% low and 75%
// high with a duty cycle of 25%.
//
// Note: the invert state may not be applied on the AVR until the next call to
// ch.Set().
func (pwm PWM) SetInverting(channel uint8, inverting bool) {
switch pwm.num {
case 0:
switch channel {
case 0: // channel A, PB7
if inverting {
avr.PORTB.SetBits(1 << 7) // PB7 high
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A0)
} else {
avr.PORTB.ClearBits(1 << 7) // PB7 low
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A0)
}
case 1: // channel B, PG5
if inverting {
avr.PORTG.SetBits(1 << 5) // PG5 high
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B0)
} else {
avr.PORTG.ClearBits(1 << 5) // PG5 low
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B0)
}
}
case 1:
// Note: the COM1A0/COM1B0 bit is not set with the configuration below.
// It will be set the following call to Set(), however.
switch channel {
case 0: // channel A, PB5
if inverting {
avr.PORTB.SetBits(1 << 5) // PB5 high
} else {
avr.PORTB.ClearBits(1 << 5) // PB5 low
}
case 1: // channel B, PB6
if inverting {
avr.PORTB.SetBits(1 << 6) // PB6 high
} else {
avr.PORTB.ClearBits(1 << 6) // PB6 low
}
}
case 2:
switch channel {
case 0: // channel A, PB4
if inverting {
avr.PORTB.SetBits(1 << 4) // PB4 high
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A0)
} else {
avr.PORTB.ClearBits(1 << 4) // PB4 low
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A0)
}
case 1: // channel B, PH6
if inverting {
avr.PORTH.SetBits(1 << 6) // PH6 high
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B0)
} else {
avr.PORTH.ClearBits(1 << 6) // PH6 low
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B0)
}
}
case 3:
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
// It will be set the following call to Set(), however.
switch channel {
case 0: // channel A, PE3
if inverting {
avr.PORTE.SetBits(1 << 3) // PE3 high
} else {
avr.PORTE.ClearBits(1 << 3) // PE3 low
}
case 1: // channel B, PE4
if inverting {
avr.PORTE.SetBits(1 << 4) // PE4 high
} else {
avr.PORTE.ClearBits(1 << 4) // PE4 low
}
case 2: // channel C, PE5
if inverting {
avr.PORTE.SetBits(1 << 5) // PE4 high
} else {
avr.PORTE.ClearBits(1 << 5) // PE4 low
}
}
case 4:
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
// It will be set the following call to Set(), however.
switch channel {
case 0: // channel A, PH3
if inverting {
avr.PORTH.SetBits(1 << 3) // PH3 high
} else {
avr.PORTH.ClearBits(1 << 3) // PH3 low
}
case 1: // channel B, PH4
if inverting {
avr.PORTH.SetBits(1 << 4) // PH4 high
} else {
avr.PORTH.ClearBits(1 << 4) // PH4 low
}
case 2: // channel C, PH5
if inverting {
avr.PORTH.SetBits(1 << 5) // PH4 high
} else {
avr.PORTH.ClearBits(1 << 5) // PH4 low
}
}
case 5:
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
// It will be set the following call to Set(), however.
switch channel {
case 0: // channel A, PL3
if inverting {
avr.PORTL.SetBits(1 << 3) // PL3 high
} else {
avr.PORTL.ClearBits(1 << 3) // PL3 low
}
case 1: // channel B, PL4
if inverting {
avr.PORTL.SetBits(1 << 4) // PL4 high
} else {
avr.PORTL.ClearBits(1 << 4) // PL4 low
}
case 2: // channel C, PH5
if inverting {
avr.PORTL.SetBits(1 << 5) // PL4 high
} else {
avr.PORTL.ClearBits(1 << 5) // PL4 low
}
}
}
}
// Set updates the channel value. This is used to control the channel duty
// cycle, in other words the fraction of time the channel output is high (or low
// when inverted). For example, to set it to a 25% duty cycle, use:
//
// pwm.Set(channel, pwm.Top() / 4)
//
// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
// pwm.Top()) will set the output to high, assuming the output isn't inverted.
func (pwm PWM) Set(channel uint8, value uint32) {
switch pwm.num {
case 0:
value := uint16(value)
switch channel {
case 0: // channel A
if value == 0 {
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A1)
} else {
avr.OCR0A.Set(uint8(value - 1))
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
}
case 1: // channel B
if value == 0 {
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B1)
} else {
avr.OCR0B.Set(uint8(value) - 1)
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
}
}
case 1:
mask := interrupt.Disable()
switch channel {
case 0: // channel A, PB5
if value == 0 {
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR1AH.Set(uint8(value >> 8))
avr.OCR1AL.Set(uint8(value))
if avr.PORTB.HasBits(1 << 5) { // is PB1 high?
// Yes, set the inverting bit.
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
} else {
// No, output is non-inverting.
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
}
}
case 1: // channel B, PB6
if value == 0 {
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR1BH.Set(uint8(value >> 8))
avr.OCR1BL.Set(uint8(value))
if avr.PORTB.HasBits(1 << 6) { // is PB6 high?
// Yes, set the inverting bit.
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
} else {
// No, output is non-inverting.
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
}
}
}
interrupt.Restore(mask)
case 2:
value := uint16(value)
switch channel {
case 0: // channel A
if value == 0 {
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A1)
} else {
avr.OCR2A.Set(uint8(value - 1))
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
}
case 1: // channel B
if value == 0 {
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B1)
} else {
avr.OCR2B.Set(uint8(value - 1))
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
}
}
case 3:
mask := interrupt.Disable()
switch channel {
case 0: // channel A, PE3
if value == 0 {
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3A1 | avr.TCCR3A_COM3A0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR3AH.Set(uint8(value >> 8))
avr.OCR3AL.Set(uint8(value))
if avr.PORTE.HasBits(1 << 3) { // is PE3 high?
// Yes, set the inverting bit.
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1 | avr.TCCR3A_COM3A0)
} else {
// No, output is non-inverting.
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1)
}
}
case 1: // channel B, PE4
if value == 0 {
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3B1 | avr.TCCR3A_COM3B0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR3BH.Set(uint8(value >> 8))
avr.OCR3BL.Set(uint8(value))
if avr.PORTE.HasBits(1 << 4) { // is PE4 high?
// Yes, set the inverting bit.
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1 | avr.TCCR3A_COM3B0)
} else {
// No, output is non-inverting.
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1)
}
}
case 2: // channel C, PE5
if value == 0 {
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3C1 | avr.TCCR3A_COM3C0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR3CH.Set(uint8(value >> 8))
avr.OCR3CL.Set(uint8(value))
if avr.PORTE.HasBits(1 << 5) { // is PE5 high?
// Yes, set the inverting bit.
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1 | avr.TCCR3A_COM3C0)
} else {
// No, output is non-inverting.
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1)
}
}
}
interrupt.Restore(mask)
case 4:
mask := interrupt.Disable()
switch channel {
case 0: // channel A, PH3
if value == 0 {
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4A1 | avr.TCCR4A_COM4A0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR4AH.Set(uint8(value >> 8))
avr.OCR4AL.Set(uint8(value))
if avr.PORTH.HasBits(1 << 3) { // is PH3 high?
// Yes, set the inverting bit.
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1 | avr.TCCR4A_COM4A0)
} else {
// No, output is non-inverting.
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1)
}
}
case 1: // channel B, PH4
if value == 0 {
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4B1 | avr.TCCR4A_COM4B0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR4BH.Set(uint8(value >> 8))
avr.OCR4BL.Set(uint8(value))
if avr.PORTH.HasBits(1 << 4) { // is PH4 high?
// Yes, set the inverting bit.
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1 | avr.TCCR4A_COM4B0)
} else {
// No, output is non-inverting.
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1)
}
}
case 2: // channel C, PH5
if value == 0 {
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4C1 | avr.TCCR4A_COM4C0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR4CH.Set(uint8(value >> 8))
avr.OCR4CL.Set(uint8(value))
if avr.PORTH.HasBits(1 << 5) { // is PH5 high?
// Yes, set the inverting bit.
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1 | avr.TCCR4A_COM4C0)
} else {
// No, output is non-inverting.
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1)
}
}
}
interrupt.Restore(mask)
case 5:
mask := interrupt.Disable()
switch channel {
case 0: // channel A, PL3
if value == 0 {
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5A1 | avr.TCCR5A_COM5A0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR5AH.Set(uint8(value >> 8))
avr.OCR5AL.Set(uint8(value))
if avr.PORTL.HasBits(1 << 3) { // is PL3 high?
// Yes, set the inverting bit.
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1 | avr.TCCR5A_COM5A0)
} else {
// No, output is non-inverting.
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1)
}
}
case 1: // channel B, PL4
if value == 0 {
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5B1 | avr.TCCR5A_COM5B0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR5BH.Set(uint8(value >> 8))
avr.OCR5BL.Set(uint8(value))
if avr.PORTL.HasBits(1 << 4) { // is PL4 high?
// Yes, set the inverting bit.
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1 | avr.TCCR5A_COM5B0)
} else {
// No, output is non-inverting.
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1)
}
}
case 2: // channel C, PL5
if value == 0 {
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5C1 | avr.TCCR5A_COM5C0)
} else {
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
avr.OCR5CH.Set(uint8(value >> 8))
avr.OCR5CL.Set(uint8(value))
if avr.PORTL.HasBits(1 << 5) { // is PL5 high?
// Yes, set the inverting bit.
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1 | avr.TCCR5A_COM5C0)
} else {
// No, output is non-inverting.
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1)
}
}
}
interrupt.Restore(mask)
}
}
// SPI configuration
var SPI0 = SPI{
spcr: avr.SPCR,
spdr: avr.SPDR,
spsr: avr.SPSR,
sck: PB1,
sdo: PB2,
sdi: PB3,
cs: PB0}
+59 -421
View File
@@ -4,7 +4,6 @@ package machine
import (
"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")
}
}
+187 -364
View File
@@ -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
)
@@ -1421,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
}
}
@@ -1818,7 +1646,6 @@ func (usbcdc *USBCDC) Flush() error {
if usbcdc.waitTxc {
// waiting for the next flush(), because the transmission is not complete
usbcdc.waitTxcRetryCount++
return nil
}
usbcdc.waitTxc = true
@@ -1873,7 +1700,7 @@ func (usbcdc *USBCDC) WriteByte(c byte) error {
mask := interrupt.Disable()
UART0.waitTxc = false
UART0.waitTxcRetryCount = 0
UART0.TxIdx.Set(0)
usbcdc.TxIdx.Set(0)
usbLineInfo.lineState = 0
interrupt.Restore(mask)
break
@@ -2041,7 +1868,6 @@ func handleUSB(intr interrupt.Interrupt) {
// Start of frame
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
UART0.Flush()
// if you want to blink LED showing traffic, this would be the place...
}
@@ -2105,7 +1931,13 @@ func handleUSB(intr interrupt.Interrupt) {
}
}
}
if i == usb_CDC_ENDPOINT_IN && UART0.waitTxc {
UART0.waitTxcRetryCount++
}
}
UART0.Flush()
}
func initEndpoint(ep, config uint32) {
@@ -2298,7 +2130,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)
@@ -2307,18 +2139,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]
@@ -2365,9 +2193,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]))))
@@ -2382,7 +2208,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
timeout--
if timeout == 0 {
return b, errUSBCDCReadTimeout
return []byte{}
}
}
@@ -2391,7 +2217,7 @@ func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 {
timeout--
if timeout == 0 {
return b, errUSBCDCReadTimeout
return []byte{}
}
}
@@ -2399,13 +2225,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) {
+311 -340
View File
@@ -40,9 +40,9 @@ 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
)
@@ -1560,6 +1560,76 @@ func (spi SPI) txrx(tx, rx []byte) {
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
}
// TxN handles read/write operation for SPI interface. The difference with Tx() is that
// it repeats the process n times.
//
func (spi SPI) TxN(w, r []byte, n int) error {
switch {
case w == nil:
// read only, so write zero and read a result.
spi.rxn(r, n)
case r == nil:
// write only
spi.txn(w, n)
default:
// write/read
if len(w) != len(r) {
return ErrTxInvalidSliceSize
}
spi.txrxn(w, r, n)
}
return nil
}
func (spi SPI) txn(tx []byte, n int) {
for i := 0; i < len(tx)*n; i++ {
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
spi.Bus.DATA.Set(uint32(tx[i%len(tx)]))
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_TXC) {
}
// read to clear RXC register
for spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
spi.Bus.DATA.Get()
}
}
func (spi SPI) rxn(rx []byte, n int) {
spi.Bus.DATA.Set(0)
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
for i := 1; i < len(rx)*n; i++ {
spi.Bus.DATA.Set(0)
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
}
rx[(i-1)%len(rx)] = byte(spi.Bus.DATA.Get())
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
}
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
}
func (spi SPI) txrxn(tx, rx []byte, n int) {
spi.Bus.DATA.Set(uint32(tx[0]))
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
}
for i := 1; i < len(rx)*n; i++ {
spi.Bus.DATA.Set(uint32(tx[i%len(rx)]))
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
}
rx[(i-1)%len(rx)] = byte(spi.Bus.DATA.Get())
}
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
}
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
}
// The QSPI peripheral on ATSAMD51 is only available on the following pins
const (
QSPI_SCK = PB10
@@ -1570,350 +1640,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
}
}
@@ -1944,7 +1920,6 @@ func (usbcdc *USBCDC) Flush() error {
if usbcdc.waitTxc {
// waiting for the next flush(), because the transmission is not complete
usbcdc.waitTxcRetryCount++
return nil
}
usbcdc.waitTxc = true
@@ -1999,7 +1974,7 @@ func (usbcdc *USBCDC) WriteByte(c byte) error {
mask := interrupt.Disable()
UART0.waitTxc = false
UART0.waitTxcRetryCount = 0
UART0.TxIdx.Set(0)
usbcdc.TxIdx.Set(0)
usbLineInfo.lineState = 0
interrupt.Restore(mask)
break
@@ -2169,7 +2144,6 @@ func handleUSBIRQ(interrupt.Interrupt) {
// Start of frame
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
UART0.Flush()
// if you want to blink LED showing traffic, this would be the place...
}
@@ -2233,7 +2207,13 @@ func handleUSBIRQ(interrupt.Interrupt) {
}
}
}
if i == usb_CDC_ENDPOINT_IN && UART0.waitTxc {
UART0.waitTxcRetryCount++
}
}
UART0.Flush()
}
func initEndpoint(ep, config uint32) {
@@ -2426,7 +2406,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)
@@ -2435,18 +2415,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]
@@ -2493,9 +2469,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]))))
@@ -2510,7 +2484,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{}
}
}
@@ -2519,7 +2493,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{}
}
}
@@ -2527,13 +2501,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) {
+34 -28
View File
@@ -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
}
}
+51 -40
View File
@@ -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
}
}
+51 -40
View File
@@ -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
}
}
+51 -40
View File
@@ -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
}
}
+51 -40
View File
@@ -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
}
}
-59
View File
@@ -1,59 +0,0 @@
// +build sam,atsame51,atsame51j19
// Peripheral abstraction layer for the atsame51.
//
// Datasheet:
// http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf
//
package machine
import "device/sam"
const HSRAM_SIZE = 0x00030000
// This chip has five TCC peripherals, which have PWM as one feature.
var (
TCC0 = (*TCC)(sam.TCC0)
TCC1 = (*TCC)(sam.TCC1)
TCC2 = (*TCC)(sam.TCC2)
TCC3 = (*TCC)(sam.TCC3)
TCC4 = (*TCC)(sam.TCC4)
)
func (tcc *TCC) configureClock() {
// Turn on timer clocks used for the TCC and use generic clock generator 0.
switch tcc.timer() {
case sam.TCC0:
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC1:
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC2:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC3:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC4:
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
}
}
func (tcc *TCC) timerNum() uint8 {
switch tcc.timer() {
case sam.TCC0:
return 0
case sam.TCC1:
return 1
case sam.TCC2:
return 2
case sam.TCC3:
return 3
case sam.TCC4:
return 4
default:
return 0x0f // should not happen
}
}
-59
View File
@@ -1,59 +0,0 @@
// +build sam,atsame5x,atsame54p20
// Peripheral abstraction layer for the atsame54.
//
// Datasheet:
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
//
package machine
import "device/sam"
const HSRAM_SIZE = 0x00040000
// This chip has five TCC peripherals, which have PWM as one feature.
var (
TCC0 = (*TCC)(sam.TCC0)
TCC1 = (*TCC)(sam.TCC1)
TCC2 = (*TCC)(sam.TCC2)
TCC3 = (*TCC)(sam.TCC3)
TCC4 = (*TCC)(sam.TCC4)
)
func (tcc *TCC) configureClock() {
// Turn on timer clocks used for TCC and use generic clock generator 0.
switch tcc.timer() {
case sam.TCC0:
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC1:
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC2:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC3:
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
case sam.TCC4:
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
}
}
func (tcc *TCC) timerNum() uint8 {
switch tcc.timer() {
case sam.TCC0:
return 0
case sam.TCC1:
return 1
case sam.TCC2:
return 2
case sam.TCC3:
return 3
case sam.TCC4:
return 4
default:
return 0x0f // should not happen
}
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -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 {
+18
View File
@@ -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
+3 -3
View File
@@ -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
)
+3 -4
View File
@@ -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
)
+3 -4
View File
@@ -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
)
+3 -4
View File
@@ -65,7 +65,7 @@ func (usbcdc *USBCDC) Flush() error {
}
// WriteByte writes a byte of data to the USB CDC interface.
func (usbcdc *USBCDC) WriteByte(c byte) error {
func (usbcdc USBCDC) WriteByte(c byte) error {
// Supposedly to handle problem with Windows USB serial ports?
if usbLineInfo.lineState > 0 {
ok := false
@@ -199,7 +199,6 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) {
if nrf.USBD.EVENTS_SOF.Get() == 1 {
nrf.USBD.EVENTS_SOF.Set(0)
USB.Flush()
// if you want to blink LED showing traffic, this would be the place...
}
// USBD ready event
@@ -445,7 +444,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 +453,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
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
// +build stm32l5 stm32f7 stm32l4 stm32l0
// +build stm32l5 stm32f7 stm32l4
package machine
+4 -6
View File
@@ -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:
+4 -12
View File
@@ -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.
-4
View File
@@ -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
-4
View File
@@ -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)
-4
View File
@@ -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
+45 -14
View File
@@ -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
}
-52
View File
@@ -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)
}
}
-54
View File
@@ -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)
}
}
-64
View File
@@ -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)
}
-21
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
}
-3
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
-5
View File
@@ -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

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