mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-27 23:28:42 +00:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a3f96b221 | |||
| b661882391 | |||
| 321488dcfe | |||
| 80caf2dab2 | |||
| c466465c32 | |||
| 404b65941a | |||
| 25f3adb47e | |||
| b9043b649d | |||
| 7b761fac78 | |||
| c47cdfa66f | |||
| 768a15c1dd | |||
| c1c3be1aa7 | |||
| b2e72c96f4 | |||
| f706219996 | |||
| 6152a661e8 | |||
| f1a5743f77 | |||
| 9f52fe4e4a | |||
| cb886a35c9 | |||
| 5707022951 | |||
| bd212cc000 | |||
| 6dd5666ed1 | |||
| f145663464 | |||
| 0d66475e10 | |||
| 22b143eba8 | |||
| 3fe13a72bd | |||
| d919905c96 | |||
| 96b1b76483 | |||
| f234df7a50 | |||
| e6d5c26df5 | |||
| ae59e7703e | |||
| e587b1d1b4 | |||
| 57271d7eaa | |||
| 8383552552 | |||
| aa8e12c464 | |||
| e7a05b6e74 | |||
| 2fd8f103ab | |||
| 33f76d1c2e | |||
| ea8f7ba1f9 | |||
| 0ffe5ac2fa | |||
| 7bac93aab3 | |||
| b61751e429 | |||
| 25dac32a88 | |||
| 56cf69a66b | |||
| 04d12bf2ba | |||
| 0b7957d612 | |||
| 61243f6c57 | |||
| 49ec3eb58e | |||
| fa6c1b69ce |
+30
-12
@@ -68,14 +68,17 @@ commands:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-11-v1
|
||||
- llvm-source-11-v2
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-11-v1
|
||||
key: llvm-source-11-v2
|
||||
paths:
|
||||
- llvm-project
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
- llvm-project/lld/include
|
||||
- llvm-project/llvm/include
|
||||
build-wasi-libc:
|
||||
steps:
|
||||
- restore_cache:
|
||||
@@ -154,12 +157,15 @@ commands:
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-linux-v2-assert
|
||||
- llvm-build-11-linux-v3-assert
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# install dependencies
|
||||
sudo apt-get install cmake ninja-build
|
||||
# hack ninja to use less jobs
|
||||
@@ -167,9 +173,10 @@ commands:
|
||||
chmod +x /go/bin/ninja
|
||||
# build!
|
||||
make ASSERT=1 llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-linux-v2-assert
|
||||
key: llvm-build-11-linux-v3-assert
|
||||
paths:
|
||||
llvm-build
|
||||
- run: make ASSERT=1
|
||||
@@ -218,12 +225,15 @@ commands:
|
||||
- llvm-source-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-linux-v2-noassert
|
||||
- llvm-build-11-linux-v3-noassert
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# install dependencies
|
||||
sudo apt-get install cmake ninja-build
|
||||
# hack ninja to use less jobs
|
||||
@@ -231,9 +241,10 @@ commands:
|
||||
chmod +x /go/bin/ninja
|
||||
# build!
|
||||
make llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-linux-v2-noassert
|
||||
key: llvm-build-11-linux-v3-noassert
|
||||
paths:
|
||||
llvm-build
|
||||
- build-wasi-libc
|
||||
@@ -287,29 +298,36 @@ commands:
|
||||
- go-cache-macos-v2-{{ checksum "go.mod" }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-11-macos-v1
|
||||
- llvm-source-11-macos-v2
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-11-macos-v1
|
||||
key: llvm-source-11-macos-v2
|
||||
paths:
|
||||
- llvm-project
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
- llvm-project/lld/include
|
||||
- llvm-project/llvm/include
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-macos-v2
|
||||
- llvm-build-11-macos-v3
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
if [ ! -f llvm-build/lib/liblldELF.a ]
|
||||
then
|
||||
# fetch LLVM source
|
||||
rm -rf llvm-project
|
||||
make llvm-source
|
||||
# install dependencies
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake ninja
|
||||
# build!
|
||||
make llvm-build
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-macos-v2
|
||||
key: llvm-build-11-macos-v3
|
||||
paths:
|
||||
llvm-build
|
||||
- restore_cache:
|
||||
|
||||
@@ -152,9 +152,9 @@ gen-device-stm32: build/gen-device-svd
|
||||
|
||||
|
||||
# Get LLVM sources.
|
||||
$(LLVM_PROJECTDIR)/README.md:
|
||||
$(LLVM_PROJECTDIR)/llvm:
|
||||
git clone -b xtensa_release_11.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||
llvm-source: $(LLVM_PROJECTDIR)/README.md
|
||||
llvm-source: $(LLVM_PROJECTDIR)/llvm
|
||||
|
||||
# Configure LLVM.
|
||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||
@@ -226,8 +226,6 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit-v2 examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
|
||||
@@ -256,6 +254,10 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit-s110v8 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit-v2 examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=microbit-v2-s113v7 examples/microbit-blink
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nrf52840-mdk examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1
|
||||
@@ -288,6 +290,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.gba -target=gameboy-advance examples/gba-display
|
||||
@$(MD5SUM) test.gba
|
||||
$(TINYGO) build -size short -o test.hex -target=grandcentral-m4 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m4 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
|
||||
@@ -334,6 +338,10 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=p1am-100 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=atsame54-xpro examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4-can examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -374,6 +382,10 @@ ifneq ($(AVR), 0)
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-mega1280 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-mega1280 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-nano examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
|
||||
|
||||
@@ -43,13 +43,14 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
The following 55 microcontroller boards are currently supported:
|
||||
The following 57 microcontroller boards are currently supported:
|
||||
|
||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||
* [Adafruit CLUE](https://www.adafruit.com/product/4500)
|
||||
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
||||
* [Adafruit Feather M4](https://www.adafruit.com/product/3857)
|
||||
* [Adafruit Feather M4 CAN](https://www.adafruit.com/product/4759)
|
||||
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
|
||||
* [Adafruit Feather STM32F405 Express](https://www.adafruit.com/product/4382)
|
||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||
@@ -77,6 +78,7 @@ The following 55 microcontroller boards are currently supported:
|
||||
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
||||
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
|
||||
* [Makerdiary nRF52840-MDK USB Dongle](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/)
|
||||
* [Microchip SAM E54 Xplained Pro](https://www.microchip.com/developmenttools/productdetails/atsame54-xpro)
|
||||
* [nice!nano](https://docs.nicekeyboards.com/#/nice!nano/)
|
||||
* [Nintendo Switch](https://www.nintendo.com/switch/)
|
||||
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle)
|
||||
|
||||
+162
-34
@@ -52,14 +52,17 @@ type BuildResult struct {
|
||||
// key, avoiding the need for recompiling all dependencies when only the
|
||||
// implementation of an imported package changes.
|
||||
type packageAction struct {
|
||||
ImportPath string
|
||||
CompilerVersion int // compiler.Version
|
||||
InterpVersion int // interp.Version
|
||||
LLVMVersion string
|
||||
Config *compiler.Config
|
||||
CFlags []string
|
||||
FileHashes map[string]string // hash of every file that's part of the package
|
||||
Imports map[string]string // map from imported package to action ID hash
|
||||
ImportPath string
|
||||
CompilerVersion int // compiler.Version
|
||||
InterpVersion int // interp.Version
|
||||
LLVMVersion string
|
||||
Config *compiler.Config
|
||||
CFlags []string
|
||||
FileHashes map[string]string // hash of every file that's part of the package
|
||||
Imports map[string]string // map from imported package to action ID hash
|
||||
OptLevel int // LLVM optimization level (0-3)
|
||||
SizeLevel int // LLVM optimization for size level (0-2)
|
||||
UndefinedGlobals []string // globals that are left as external globals (no initializer)
|
||||
}
|
||||
|
||||
// Build performs a single package to executable Go build. It takes in a package
|
||||
@@ -127,20 +130,30 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
var packageJobs []*compileJob
|
||||
packageBitcodePaths := make(map[string]string)
|
||||
packageActionIDs := make(map[string]string)
|
||||
optLevel, sizeLevel, _ := config.OptLevels()
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkg := pkg // necessary to avoid a race condition
|
||||
|
||||
var undefinedGlobals []string
|
||||
for name := range config.Options.GlobalValues[pkg.Pkg.Path()] {
|
||||
undefinedGlobals = append(undefinedGlobals, name)
|
||||
}
|
||||
sort.Strings(undefinedGlobals)
|
||||
|
||||
// Create a cache key: a hash from the action ID below that contains all
|
||||
// the parameters for the build.
|
||||
actionID := packageAction{
|
||||
ImportPath: pkg.ImportPath,
|
||||
CompilerVersion: compiler.Version,
|
||||
InterpVersion: interp.Version,
|
||||
LLVMVersion: llvm.Version,
|
||||
Config: compilerConfig,
|
||||
CFlags: pkg.CFlags,
|
||||
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||
ImportPath: pkg.ImportPath,
|
||||
CompilerVersion: compiler.Version,
|
||||
InterpVersion: interp.Version,
|
||||
LLVMVersion: llvm.Version,
|
||||
Config: compilerConfig,
|
||||
CFlags: pkg.CFlags,
|
||||
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||
OptLevel: optLevel,
|
||||
SizeLevel: sizeLevel,
|
||||
UndefinedGlobals: undefinedGlobals,
|
||||
}
|
||||
for filePath, hash := range pkg.FileHashes {
|
||||
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
||||
@@ -191,6 +204,25 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return errors.New("verification error after compiling package " + pkg.ImportPath)
|
||||
}
|
||||
|
||||
// Erase all globals that are part of the undefinedGlobals list.
|
||||
// This list comes from the -ldflags="-X pkg.foo=val" option.
|
||||
// Instead of setting the value directly in the AST (which would
|
||||
// mean the value, which may be a secret, is stored in the build
|
||||
// cache), the global itself is left external (undefined) and is
|
||||
// only set at the end of the compilation.
|
||||
for _, name := range undefinedGlobals {
|
||||
globalName := pkg.Pkg.Path() + "." + name
|
||||
global := mod.NamedGlobal(globalName)
|
||||
if global.IsNil() {
|
||||
return errors.New("global not found: " + globalName)
|
||||
}
|
||||
name := global.Name()
|
||||
newGlobal := llvm.AddGlobal(mod, global.Type().ElementType(), name+".tmp")
|
||||
global.ReplaceAllUsesWith(newGlobal)
|
||||
global.EraseFromParentAsGlobal()
|
||||
newGlobal.SetName(name)
|
||||
}
|
||||
|
||||
// Try to interpret package initializers at compile time.
|
||||
// It may only be possible to do this partially, in which case
|
||||
// it is completed after all IR files are linked.
|
||||
@@ -206,6 +238,38 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return errors.New("verification error after interpreting " + pkgInit.Name())
|
||||
}
|
||||
|
||||
if sizeLevel >= 2 {
|
||||
// Set the "optsize" attribute to make slightly smaller
|
||||
// binaries at the cost of some performance.
|
||||
kind := llvm.AttributeKindID("optsize")
|
||||
attr := mod.Context().CreateEnumAttribute(kind, 0)
|
||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
fn.AddFunctionAttr(attr)
|
||||
}
|
||||
}
|
||||
|
||||
// Run function passes for each function in the module.
|
||||
// These passes are intended to be run on each function right
|
||||
// after they're created to reduce IR size (and maybe also for
|
||||
// cache locality to improve performance), but for now they're
|
||||
// run here for each function in turn. Maybe this can be
|
||||
// improved in the future.
|
||||
builder := llvm.NewPassManagerBuilder()
|
||||
defer builder.Dispose()
|
||||
builder.SetOptLevel(optLevel)
|
||||
builder.SetSizeLevel(sizeLevel)
|
||||
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
|
||||
defer funcPasses.Dispose()
|
||||
builder.PopulateFunc(funcPasses)
|
||||
funcPasses.InitializeFunc()
|
||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
if fn.IsDeclaration() {
|
||||
continue
|
||||
}
|
||||
funcPasses.RunFunc(fn)
|
||||
}
|
||||
funcPasses.FinalizeFunc()
|
||||
|
||||
// Serialize the LLVM module as a bitcode file.
|
||||
// Write to a temporary path that is renamed to the destination
|
||||
// file to avoid race conditions with other TinyGo invocatiosn
|
||||
@@ -236,7 +300,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(f.Name(), bitcodePath)
|
||||
|
||||
// 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
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
@@ -430,7 +500,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
job := &compileJob{
|
||||
description: "compile extra file " + path,
|
||||
run: func(job *compileJob) error {
|
||||
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config)
|
||||
result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config.Options.PrintCommands)
|
||||
job.result = result
|
||||
return err
|
||||
},
|
||||
@@ -449,7 +519,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
job := &compileJob{
|
||||
description: "compile CGo file " + abspath,
|
||||
run: func(job *compileJob) error {
|
||||
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config)
|
||||
result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config.Options.PrintCommands)
|
||||
job.result = result
|
||||
return err
|
||||
},
|
||||
@@ -603,6 +673,12 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
transform.ApplyFunctionSections(mod) // -ffunction-sections
|
||||
}
|
||||
|
||||
// Insert values from -ldflags="-X ..." into the IR.
|
||||
err = setGlobalValues(mod, config.Options.GlobalValues)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Browsers cannot handle external functions that have type i64 because it
|
||||
// cannot be represented exactly in JavaScript (JS only has doubles). To
|
||||
// keep functions interoperable, pass int64 types as pointers to
|
||||
@@ -617,21 +693,8 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
|
||||
// Optimization levels here are roughly the same as Clang, but probably not
|
||||
// exactly.
|
||||
var errs []error
|
||||
switch config.Options.Opt {
|
||||
case "none", "0":
|
||||
errs = transform.Optimize(mod, config, 0, 0, 0) // -O0
|
||||
case "1":
|
||||
errs = transform.Optimize(mod, config, 1, 0, 0) // -O1
|
||||
case "2":
|
||||
errs = transform.Optimize(mod, config, 2, 0, 225) // -O2
|
||||
case "s":
|
||||
errs = transform.Optimize(mod, config, 2, 1, 225) // -Os
|
||||
case "z":
|
||||
errs = transform.Optimize(mod, config, 2, 2, 5) // -Oz, default
|
||||
default:
|
||||
return errors.New("unknown optimization level: -opt=" + config.Options.Opt)
|
||||
}
|
||||
optLevel, sizeLevel, inlinerThreshold := config.OptLevels()
|
||||
errs := transform.Optimize(mod, config, optLevel, sizeLevel, inlinerThreshold)
|
||||
if len(errs) > 0 {
|
||||
return newMultiError(errs)
|
||||
}
|
||||
@@ -653,6 +716,71 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// setGlobalValues sets the global values from the -ldflags="-X ..." compiler
|
||||
// option in the given module. An error may be returned if the global is not of
|
||||
// the expected type.
|
||||
func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) error {
|
||||
var pkgPaths []string
|
||||
for pkgPath := range globals {
|
||||
pkgPaths = append(pkgPaths, pkgPath)
|
||||
}
|
||||
sort.Strings(pkgPaths)
|
||||
for _, pkgPath := range pkgPaths {
|
||||
pkg := globals[pkgPath]
|
||||
var names []string
|
||||
for name := range pkg {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
value := pkg[name]
|
||||
globalName := pkgPath + "." + name
|
||||
global := mod.NamedGlobal(globalName)
|
||||
if global.IsNil() || !global.Initializer().IsNil() {
|
||||
// The global either does not exist (optimized away?) or has
|
||||
// some value, in which case it has already been initialized at
|
||||
// package init time.
|
||||
continue
|
||||
}
|
||||
|
||||
// A strin is a {ptr, len} pair. We need these types to build the
|
||||
// initializer.
|
||||
initializerType := global.Type().ElementType()
|
||||
if initializerType.TypeKind() != llvm.StructTypeKind || initializerType.StructName() == "" {
|
||||
return fmt.Errorf("%s: not a string", globalName)
|
||||
}
|
||||
elementTypes := initializerType.StructElementTypes()
|
||||
if len(elementTypes) != 2 {
|
||||
return fmt.Errorf("%s: not a string", globalName)
|
||||
}
|
||||
|
||||
// Create a buffer for the string contents.
|
||||
bufInitializer := mod.Context().ConstString(value, false)
|
||||
buf := llvm.AddGlobal(mod, bufInitializer.Type(), ".string")
|
||||
buf.SetInitializer(bufInitializer)
|
||||
buf.SetAlignment(1)
|
||||
buf.SetUnnamedAddr(true)
|
||||
buf.SetLinkage(llvm.PrivateLinkage)
|
||||
|
||||
// Create the string value, which is a {ptr, len} pair.
|
||||
zero := llvm.ConstInt(mod.Context().Int32Type(), 0, false)
|
||||
ptr := llvm.ConstGEP(buf, []llvm.Value{zero, zero})
|
||||
if ptr.Type() != elementTypes[0] {
|
||||
return fmt.Errorf("%s: not a string", globalName)
|
||||
}
|
||||
length := llvm.ConstInt(elementTypes[1], uint64(len(value)), false)
|
||||
initializer := llvm.ConstNamedStruct(initializerType, []llvm.Value{
|
||||
ptr,
|
||||
length,
|
||||
})
|
||||
|
||||
// Set the initializer. No initializer should be set at this point.
|
||||
global.SetInitializer(initializer)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// functionStackSizes keeps stack size information about a single function
|
||||
// (usually a goroutine).
|
||||
type functionStackSize struct {
|
||||
|
||||
+12
-8
@@ -17,7 +17,6 @@ import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
@@ -57,7 +56,7 @@ import (
|
||||
// depfile but without invalidating its name. For this reason, the depfile is
|
||||
// written on each new compilation (even when it seems unnecessary). However, it
|
||||
// could in rare cases lead to a stale file fetched from the cache.
|
||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, config *compileopts.Config) (string, error) {
|
||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands bool) (string, error) {
|
||||
// Hash input file.
|
||||
fileHash, err := hashFile(abspath)
|
||||
if err != nil {
|
||||
@@ -68,14 +67,12 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, config *compi
|
||||
buf, err := json.Marshal(struct {
|
||||
Path string
|
||||
Hash string
|
||||
Compiler string
|
||||
Flags []string
|
||||
LLVMVersion string
|
||||
}{
|
||||
Path: abspath,
|
||||
Hash: fileHash,
|
||||
Compiler: config.Target.Compiler,
|
||||
Flags: config.CFlags(),
|
||||
Flags: cflags,
|
||||
LLVMVersion: llvm.Version,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -124,10 +121,17 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, config *compi
|
||||
flags := append([]string{}, cflags...) // copy cflags
|
||||
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
|
||||
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
|
||||
if config.Options.PrintCommands {
|
||||
fmt.Printf("%s %s\n", config.Target.Compiler, strings.Join(flags, " "))
|
||||
if strings.ToLower(filepath.Ext(abspath)) == ".s" {
|
||||
// If this is an assembly file (.s or .S, lowercase or uppercase), then
|
||||
// we'll need to add -Qunused-arguments because many parameters are
|
||||
// relevant to C, not assembly. And with -Werror, having meaningless
|
||||
// flags (for the assembler) is a compiler error.
|
||||
flags = append(flags, "-Qunused-arguments")
|
||||
}
|
||||
err = runCCompiler(config.Target.Compiler, flags...)
|
||||
if printCommands {
|
||||
fmt.Printf("clang %s\n", strings.Join(flags, " "))
|
||||
}
|
||||
err = runCCompiler(flags...)
|
||||
if err != nil {
|
||||
return "", &commandError{"failed to build", abspath, err}
|
||||
}
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@ func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error)
|
||||
var compileArgs []string
|
||||
compileArgs = append(compileArgs, args...)
|
||||
compileArgs = append(compileArgs, "-o", objpath, srcpath)
|
||||
err := runCCompiler("clang", compileArgs...)
|
||||
err := runCCompiler(compileArgs...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", srcpath, err}
|
||||
}
|
||||
|
||||
+4
-13
@@ -9,8 +9,8 @@ import (
|
||||
)
|
||||
|
||||
// runCCompiler invokes a C compiler with the given arguments.
|
||||
func runCCompiler(command string, flags ...string) error {
|
||||
if hasBuiltinTools && command == "clang" {
|
||||
func runCCompiler(flags ...string) error {
|
||||
if hasBuiltinTools {
|
||||
// Compile this with the internal Clang compiler.
|
||||
headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
if headerPath == "" {
|
||||
@@ -23,17 +23,8 @@ func runCCompiler(command string, flags ...string) error {
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// Running some other compiler. Maybe it has been defined in the
|
||||
// commands map (unlikely).
|
||||
if cmdNames, ok := commands[command]; ok {
|
||||
return execCommand(cmdNames, flags...)
|
||||
}
|
||||
|
||||
// Alternatively, run the compiler directly.
|
||||
cmd := exec.Command(command, flags...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
// Compile this with an external invocation of the Clang compiler.
|
||||
return execCommand(commands["clang"], flags...)
|
||||
}
|
||||
|
||||
// link invokes a linker with the given name and flags.
|
||||
|
||||
+23
-2
@@ -118,6 +118,27 @@ func (c *Config) Scheduler() string {
|
||||
return "coroutines"
|
||||
}
|
||||
|
||||
// OptLevels returns the optimization level (0-2), size level (0-2), and inliner
|
||||
// threshold as used in the LLVM optimization pipeline.
|
||||
func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
|
||||
switch c.Options.Opt {
|
||||
case "none", "0":
|
||||
return 0, 0, 0 // -O0
|
||||
case "1":
|
||||
return 1, 0, 0 // -O1
|
||||
case "2":
|
||||
return 2, 0, 225 // -O2
|
||||
case "s":
|
||||
return 2, 1, 225 // -Os
|
||||
case "z":
|
||||
return 2, 2, 5 // -Oz, default
|
||||
default:
|
||||
// This is not shown to the user: valid choices are already checked as
|
||||
// part of Options.Verify(). It is here as a sanity check.
|
||||
panic("unknown optimization level: -opt=" + c.Options.Opt)
|
||||
}
|
||||
}
|
||||
|
||||
// FuncImplementation picks an appropriate func value implementation for the
|
||||
// target.
|
||||
func (c *Config) FuncImplementation() string {
|
||||
@@ -163,7 +184,7 @@ func (c *Config) AutomaticStackSize() bool {
|
||||
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
||||
// preprocessing.
|
||||
func (c *Config) CFlags() []string {
|
||||
cflags := append([]string{}, c.Options.CFlags...)
|
||||
var cflags []string
|
||||
for _, flag := range c.Target.CFlags {
|
||||
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
|
||||
}
|
||||
@@ -184,7 +205,7 @@ func (c *Config) CFlags() []string {
|
||||
func (c *Config) LDFlags() []string {
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
// Merge and adjust LDFlags.
|
||||
ldflags := append([]string{}, c.Options.LDFlags...)
|
||||
var ldflags []string
|
||||
for _, flag := range c.Target.LDFlags {
|
||||
ldflags = append(ldflags, strings.ReplaceAll(flag, "{root}", root))
|
||||
}
|
||||
|
||||
+10
-2
@@ -2,6 +2,7 @@ package compileopts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -10,6 +11,7 @@ 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
|
||||
@@ -26,11 +28,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
|
||||
}
|
||||
@@ -73,6 +75,12 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
}
|
||||
|
||||
if o.Opt != "" {
|
||||
if !isInArray(validOptOptions, o.Opt) {
|
||||
return fmt.Errorf("invalid -opt=%s: valid values are %s", o.Opt, strings.Join(validOptOptions, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ type TargetSpec struct {
|
||||
BuildTags []string `json:"build-tags"`
|
||||
GC string `json:"gc"`
|
||||
Scheduler string `json:"scheduler"`
|
||||
Compiler string `json:"compiler"`
|
||||
Linker string `json:"linker"`
|
||||
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
|
||||
Libc string `json:"libc"`
|
||||
@@ -244,7 +243,6 @@ 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"},
|
||||
|
||||
+16
-10
@@ -23,7 +23,7 @@ import (
|
||||
// Version of the compiler pacakge. Must be incremented each time the compiler
|
||||
// package changes in a way that affects the generated LLVM module.
|
||||
// This version is independent of the TinyGo version number.
|
||||
const Version = 6 // last change: fix issue 1304
|
||||
const Version = 9 // last change: implement reflect.New()
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargets()
|
||||
@@ -298,7 +298,7 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
||||
c.dibuilder.Finalize()
|
||||
}
|
||||
|
||||
return c.mod, nil
|
||||
return c.mod, c.diagnostics
|
||||
}
|
||||
|
||||
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
||||
@@ -2258,14 +2258,20 @@ func (b *builder) createConst(prefix string, expr *ssa.Const) llvm.Value {
|
||||
} else if typ.Info()&types.IsString != 0 {
|
||||
str := constant.StringVal(expr.Value)
|
||||
strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
|
||||
objname := prefix + "$string"
|
||||
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
|
||||
global.SetInitializer(b.ctx.ConstString(str, false))
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
strPtr := b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
||||
var strPtr llvm.Value
|
||||
if str != "" {
|
||||
objname := prefix + "$string"
|
||||
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
|
||||
global.SetInitializer(b.ctx.ConstString(str, false))
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
global.SetAlignment(1)
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
strPtr = b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
||||
} else {
|
||||
strPtr = llvm.ConstNull(b.i8ptrType)
|
||||
}
|
||||
strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
|
||||
return strObj
|
||||
} else if typ.Kind() == types.UnsafePointer {
|
||||
|
||||
@@ -62,6 +62,7 @@ func TestCompiler(t *testing.T) {
|
||||
"string.go",
|
||||
"float.go",
|
||||
"interface.go",
|
||||
"func.go",
|
||||
}
|
||||
|
||||
for _, testCase := range tests {
|
||||
@@ -84,11 +85,16 @@ func TestCompiler(t *testing.T) {
|
||||
mod, errs := CompilePackage(testCase, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
||||
if errs != nil {
|
||||
for _, err := range errs {
|
||||
t.Log("error:", err)
|
||||
t.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
err = llvm.VerifyModule(mod, llvm.PrintMessageAction)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Optimize IR a little.
|
||||
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
|
||||
defer funcPasses.Dispose()
|
||||
|
||||
+15
-3
@@ -25,14 +25,13 @@ func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context
|
||||
// Closure is: {context, function pointer}
|
||||
funcValueScalar = funcPtr
|
||||
case "switch":
|
||||
sigGlobal := c.getTypeCode(sig)
|
||||
funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
|
||||
funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
|
||||
if funcValueWithSignatureGlobal.IsNil() {
|
||||
funcValueWithSignatureType := c.getLLVMRuntimeType("funcValueWithSignature")
|
||||
funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{
|
||||
llvm.ConstPtrToInt(funcPtr, c.uintptrType),
|
||||
sigGlobal,
|
||||
c.getFuncSignatureID(sig),
|
||||
})
|
||||
funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
|
||||
funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
|
||||
@@ -50,6 +49,19 @@ func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context
|
||||
return funcValue
|
||||
}
|
||||
|
||||
// getFuncSignatureID returns a new external global for a given signature. This
|
||||
// global reference is not real, it is only used during func lowering to assign
|
||||
// signature types to functions and will then be removed.
|
||||
func (c *compilerContext) getFuncSignatureID(sig *types.Signature) llvm.Value {
|
||||
sigGlobalName := "reflect/types.funcid:" + getTypeCodeName(sig)
|
||||
sigGlobal := c.mod.NamedGlobal(sigGlobalName)
|
||||
if sigGlobal.IsNil() {
|
||||
sigGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), sigGlobalName)
|
||||
sigGlobal.SetGlobalConstant(true)
|
||||
}
|
||||
return sigGlobal
|
||||
}
|
||||
|
||||
// extractFuncScalar returns some scalar that can be used in comparisons. It is
|
||||
// a cheap operation.
|
||||
func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value {
|
||||
@@ -71,7 +83,7 @@ func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (f
|
||||
funcPtr = b.CreateExtractValue(funcValue, 1, "")
|
||||
case "switch":
|
||||
llvmSig := b.getRawFuncType(sig)
|
||||
sigGlobal := b.getTypeCode(sig)
|
||||
sigGlobal := b.getFuncSignatureID(sig)
|
||||
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
||||
funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "")
|
||||
default:
|
||||
|
||||
+20
-16
@@ -46,6 +46,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
var references llvm.Value
|
||||
var length int64
|
||||
var methodSet llvm.Value
|
||||
var ptrTo llvm.Value
|
||||
switch typ := typ.(type) {
|
||||
case *types.Named:
|
||||
references = c.getTypeCode(typ.Underlying())
|
||||
@@ -69,22 +70,25 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
if _, ok := typ.Underlying().(*types.Interface); !ok {
|
||||
methodSet = c.getTypeMethodSet(typ)
|
||||
}
|
||||
if !references.IsNil() || length != 0 || !methodSet.IsNil() {
|
||||
// Set the 'references' field of the runtime.typecodeID struct.
|
||||
globalValue := llvm.ConstNull(global.Type().ElementType())
|
||||
if !references.IsNil() {
|
||||
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
|
||||
}
|
||||
if length != 0 {
|
||||
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
|
||||
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
|
||||
}
|
||||
if !methodSet.IsNil() {
|
||||
globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2})
|
||||
}
|
||||
global.SetInitializer(globalValue)
|
||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
if _, ok := typ.Underlying().(*types.Pointer); !ok {
|
||||
ptrTo = c.getTypeCode(types.NewPointer(typ))
|
||||
}
|
||||
globalValue := llvm.ConstNull(global.Type().ElementType())
|
||||
if !references.IsNil() {
|
||||
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
|
||||
}
|
||||
if length != 0 {
|
||||
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
|
||||
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
|
||||
}
|
||||
if !methodSet.IsNil() {
|
||||
globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2})
|
||||
}
|
||||
if !ptrTo.IsNil() {
|
||||
globalValue = llvm.ConstInsertValue(globalValue, ptrTo, []uint32{3})
|
||||
}
|
||||
global.SetInitializer(globalValue)
|
||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
}
|
||||
return global
|
||||
@@ -341,7 +345,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
commaOk = b.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "")
|
||||
|
||||
} else {
|
||||
globalName := "reflect/types.type:" + getTypeCodeName(expr.AssertedType) + "$id"
|
||||
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
||||
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
||||
if assertedTypeCodeGlobal.IsNil() {
|
||||
// Create a new typecode global.
|
||||
|
||||
+18
-5
@@ -139,6 +139,17 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
for _, attrName := range []string{"noalias", "nonnull"} {
|
||||
llvmFn.AddAttributeAtIndex(0, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(attrName), 0))
|
||||
}
|
||||
case "runtime.sliceAppend":
|
||||
// Appending a slice will only read the to-be-appended slice, it won't
|
||||
// be modified.
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
case "runtime.sliceCopy":
|
||||
// Copying a slice won't capture any of the parameters.
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("writeonly"), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
case "runtime.trackPointer":
|
||||
// This function is necessary for tracking pointers on the stack in a
|
||||
// portable way (see gc_stack_portable.go). Indicate to the optimizer
|
||||
@@ -355,16 +366,18 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
||||
|
||||
// Set alignment from the //go:align comment.
|
||||
var alignInBits uint32
|
||||
if info.align < 0 || info.align&(info.align-1) != 0 {
|
||||
alignment := c.targetData.ABITypeAlignment(llvmType)
|
||||
if info.align > alignment {
|
||||
alignment = info.align
|
||||
}
|
||||
if alignment <= 0 || alignment&(alignment-1) != 0 {
|
||||
// Check for power-of-two (or 0).
|
||||
// See: https://stackoverflow.com/a/108360
|
||||
c.addError(g.Pos(), "global variable alignment must be a positive power of two")
|
||||
} else {
|
||||
// Set the alignment only when it is a power of two.
|
||||
alignInBits = uint32(info.align) ^ uint32(info.align-1)
|
||||
if info.align > c.targetData.ABITypeAlignment(llvmType) {
|
||||
llvmGlobal.SetAlignment(info.align)
|
||||
}
|
||||
alignInBits = uint32(alignment) ^ uint32(alignment-1)
|
||||
llvmGlobal.SetAlignment(alignment)
|
||||
}
|
||||
|
||||
if c.Debug && !info.extern {
|
||||
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func foo(callback func(int)) {
|
||||
callback(3)
|
||||
}
|
||||
|
||||
func bar() {
|
||||
foo(someFunc)
|
||||
}
|
||||
|
||||
func someFunc(int) {
|
||||
}
|
||||
Vendored
+47
@@ -0,0 +1,47 @@
|
||||
; ModuleID = 'func.go'
|
||||
source_filename = "func.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
%runtime.funcValueWithSignature = type { i32, i8* }
|
||||
|
||||
@"reflect/types.funcid:func:{basic:int}{}" = external constant i8
|
||||
@"main.someFunc$withSignature" = linkonce_odr constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @main.someFunc to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.foo(i8* %callback.context, i32 %callback.funcptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call i32 @runtime.getFuncPtr(i8* %callback.context, i32 %callback.funcptr, i8* nonnull @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null)
|
||||
%1 = icmp eq i32 %0, 0
|
||||
br i1 %1, label %fpcall.throw, label %fpcall.next
|
||||
|
||||
fpcall.throw: ; preds = %entry
|
||||
call void @runtime.nilPanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
fpcall.next: ; preds = %entry
|
||||
%2 = inttoptr i32 %0 to void (i32, i8*, i8*)*
|
||||
call void %2(i32 3, i8* %callback.context, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @runtime.getFuncPtr(i8*, i32, i8* dereferenceable_or_null(1), i8*, i8*)
|
||||
|
||||
declare void @runtime.nilPanic(i8*, i8*)
|
||||
|
||||
define hidden void @main.bar(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
call void @main.foo(i8* undef, i32 ptrtoint (%runtime.funcValueWithSignature* @"main.someFunc$withSignature" to i32), i8* undef, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.someFunc(i32 %arg0, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
Vendored
+11
-10
@@ -3,23 +3,24 @@ 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 = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID* }
|
||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||
%runtime._interface = type { i32, i8* }
|
||||
%runtime._string = type { i8*, i32 }
|
||||
|
||||
@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID zeroinitializer
|
||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:int" }
|
||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:named:error" }
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }
|
||||
@"func Error() string" = external constant i8
|
||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
|
||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }
|
||||
@"func String() string" = external constant i8
|
||||
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"]
|
||||
@"reflect/types.type:basic:int$id" = external constant i8
|
||||
@"reflect/types.typeid:basic:int" = external constant i8
|
||||
@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
@@ -51,7 +52,7 @@ entry:
|
||||
|
||||
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.type:basic:int$id", i8* undef, i8* null)
|
||||
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.typeid:basic:int", i8* undef, i8* null)
|
||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.ok: ; preds = %entry
|
||||
|
||||
Vendored
+2
-2
@@ -60,7 +60,7 @@ entry:
|
||||
ret { i32*, i32, i32 } %7
|
||||
}
|
||||
|
||||
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8*, i32, i32, i32, i32, i8*, i8*)
|
||||
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8* nocapture readonly, i32, i32, i32, i32, i8*, i8*)
|
||||
|
||||
define hidden { i32*, i32, i32 } @main.sliceAppendSlice(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32* %added.data, i32 %added.len, i32 %added.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
@@ -85,4 +85,4 @@ entry:
|
||||
ret i32 %copy.n
|
||||
}
|
||||
|
||||
declare i32 @runtime.sliceCopy(i8*, i8*, i32, i32, i32, i8*, i8*)
|
||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||
|
||||
Vendored
+8
@@ -1,5 +1,13 @@
|
||||
package main
|
||||
|
||||
func someString() string {
|
||||
return "foo"
|
||||
}
|
||||
|
||||
func zeroLengthString() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func stringLen(s string) int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
Vendored
+14
@@ -3,6 +3,10 @@ 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 {
|
||||
@@ -10,6 +14,16 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden %runtime._string @main.someString(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret %runtime._string { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"main.someString$string", i32 0, i32 0), i32 3 }
|
||||
}
|
||||
|
||||
define hidden %runtime._string @main.zeroLengthString(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret %runtime._string zeroinitializer
|
||||
}
|
||||
|
||||
define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret i32 %s.len
|
||||
|
||||
+3
-2
@@ -171,8 +171,9 @@ func getGoroot() string {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
candidates = []string{
|
||||
"/usr/local/go", // manually installed
|
||||
"/usr/lib/go", // from the distribution
|
||||
"/usr/local/go", // manually installed
|
||||
"/usr/lib/go", // from the distribution
|
||||
"/snap/go/current/", // installed using snap
|
||||
}
|
||||
case "darwin":
|
||||
candidates = []string{
|
||||
|
||||
+17
-1
@@ -25,6 +25,7 @@ type function struct {
|
||||
// basicBlock represents a LLVM basic block and contains a slice of
|
||||
// instructions. The last instruction must be a terminator instruction.
|
||||
type basicBlock struct {
|
||||
phiNodes []instruction
|
||||
instructions []instruction
|
||||
}
|
||||
|
||||
@@ -135,6 +136,15 @@ func (r *runner) compileFunction(llvmFn llvm.Value) *function {
|
||||
default:
|
||||
panic("unknown number of operands")
|
||||
}
|
||||
case llvm.Switch:
|
||||
// A switch is an array of (value, label) pairs, of which the
|
||||
// first one indicates the to-switch value and the default
|
||||
// label.
|
||||
numOperands := llvmInst.OperandsCount()
|
||||
for i := 0; i < numOperands; i += 2 {
|
||||
inst.operands = append(inst.operands, r.getValue(llvmInst.Operand(i)))
|
||||
inst.operands = append(inst.operands, literalValue{uint32(blockIndices[llvmInst.Operand(i+1)])})
|
||||
}
|
||||
case llvm.PHI:
|
||||
inst.name = llvmInst.Name()
|
||||
incomingCount := inst.llvmInst.IncomingCount()
|
||||
@@ -337,7 +347,13 @@ func (r *runner) compileFunction(llvmFn llvm.Value) *function {
|
||||
// This error is handled when actually trying to interpret this
|
||||
// instruction (to not trigger on code that won't be executed).
|
||||
}
|
||||
bb.instructions = append(bb.instructions, inst)
|
||||
if inst.opcode == llvm.PHI {
|
||||
// PHI nodes need to be treated specially, see the comment in
|
||||
// interpreter.go for an explanation.
|
||||
bb.phiNodes = append(bb.phiNodes, inst)
|
||||
} else {
|
||||
bb.instructions = append(bb.instructions, inst)
|
||||
}
|
||||
}
|
||||
}
|
||||
return fn
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
func TestInterp(t *testing.T) {
|
||||
for _, name := range []string{
|
||||
"basic",
|
||||
"phi",
|
||||
"slice-copy",
|
||||
"consteval",
|
||||
"map",
|
||||
"interface",
|
||||
} {
|
||||
name := name // make tc local to this closure
|
||||
|
||||
+62
-86
@@ -33,6 +33,50 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
lastBB := -1 // last basic block is undefined, only defined after a branch
|
||||
var operands []value
|
||||
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
||||
if instIndex == 0 {
|
||||
// This is the start of a new basic block.
|
||||
// There may be PHI nodes that need to be resolved. Resolve all PHI
|
||||
// nodes before continuing with regular instructions.
|
||||
// PHI nodes need to be treated specially because they can have a
|
||||
// mutual dependency:
|
||||
// for.loop:
|
||||
// %a = phi i8 [ 1, %entry ], [ %b, %for.loop ]
|
||||
// %b = phi i8 [ 3, %entry ], [ %a, %for.loop ]
|
||||
// If these PHI nodes are processed like a regular instruction, %a
|
||||
// and %b are both 3 on the second iteration of the loop because %b
|
||||
// loads the value of %a from the second iteration, while it should
|
||||
// load the value from the previous iteration. The correct behavior
|
||||
// is that these two values swap each others place on each
|
||||
// iteration.
|
||||
var phiValues []value
|
||||
var phiIndices []int
|
||||
for _, inst := range bb.phiNodes {
|
||||
var result value
|
||||
for i := 0; i < len(inst.operands); i += 2 {
|
||||
if int(inst.operands[i].(literalValue).value.(uint32)) == lastBB {
|
||||
incoming := inst.operands[i+1]
|
||||
if local, ok := incoming.(localValue); ok {
|
||||
result = locals[fn.locals[local.value]]
|
||||
} else {
|
||||
result = incoming
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"phi", inst.operands, "->", result)
|
||||
}
|
||||
if result == nil {
|
||||
panic("could not find PHI input")
|
||||
}
|
||||
phiValues = append(phiValues, result)
|
||||
phiIndices = append(phiIndices, inst.localIndex)
|
||||
}
|
||||
for i, value := range phiValues {
|
||||
locals[phiIndices[i]] = value
|
||||
}
|
||||
}
|
||||
|
||||
inst := bb.instructions[instIndex]
|
||||
operands = operands[:0]
|
||||
isRuntimeInst := false
|
||||
@@ -103,27 +147,24 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
default:
|
||||
panic("unknown operands length")
|
||||
}
|
||||
break // continue with next block
|
||||
case llvm.PHI:
|
||||
var result value
|
||||
for i := 0; i < len(inst.operands); i += 2 {
|
||||
if int(inst.operands[i].(literalValue).value.(uint32)) == lastBB {
|
||||
incoming := inst.operands[i+1]
|
||||
if local, ok := incoming.(localValue); ok {
|
||||
result = locals[fn.locals[local.value]]
|
||||
} else {
|
||||
result = incoming
|
||||
}
|
||||
case llvm.Switch:
|
||||
// Switch statement: [value, defaultLabel, case0, label0, case1, label1, ...]
|
||||
value := operands[0].Uint()
|
||||
targetLabel := operands[1].Uint() // default label
|
||||
// Do a lazy switch by iterating over all cases.
|
||||
for i := 2; i < len(operands); i += 2 {
|
||||
if value == operands[i].Uint() {
|
||||
targetLabel = operands[i+1].Uint()
|
||||
break
|
||||
}
|
||||
}
|
||||
lastBB = currentBB
|
||||
currentBB = int(targetLabel)
|
||||
bb = fn.blocks[currentBB]
|
||||
instIndex = -1 // start at 0 the next cycle
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"phi", inst.operands, "->", result)
|
||||
fmt.Fprintln(os.Stderr, indent+"switch", operands, "->", currentBB)
|
||||
}
|
||||
if result == nil {
|
||||
panic("could not find PHI input")
|
||||
}
|
||||
locals[inst.localIndex] = result
|
||||
case llvm.Select:
|
||||
// Select is much like a ternary operator: it picks a result from
|
||||
// the second and third operand based on the boolean first operand.
|
||||
@@ -155,7 +196,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// which case this call won't even get to this point but will
|
||||
// already be emitted in initAll.
|
||||
continue
|
||||
case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet":
|
||||
case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet" || callFn.name == "os.runtime_args":
|
||||
// These functions should be run at runtime. Specifically:
|
||||
// * Print and panic functions are best emitted directly without
|
||||
// interpreting them, otherwise we get a ton of putchar (etc.)
|
||||
@@ -163,6 +204,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// * runtime.hashmapGet tries to access the map value directly.
|
||||
// This is not possible as the map value is treated as a special
|
||||
// kind of object in this package.
|
||||
// * os.runtime_args reads globals that are initialized outside
|
||||
// the view of the interp package so it always needs to be run
|
||||
// at runtime.
|
||||
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||
if err != nil {
|
||||
return nil, mem, err
|
||||
@@ -328,7 +372,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
actualType := actualTypePtrToInt.Operand(0)
|
||||
if actualType.Name()+"$id" == assertedType.Name() {
|
||||
if strings.TrimPrefix(actualType.Name(), "reflect/types.type:") == strings.TrimPrefix(assertedType.Name(), "reflect/types.typeid:") {
|
||||
locals[inst.localIndex] = literalValue{uint8(1)}
|
||||
} else {
|
||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||
@@ -415,74 +459,6 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
return nil, mem, r.errorAt(inst, errors.New("could not find method: "+signature.Name()))
|
||||
}
|
||||
locals[inst.localIndex] = r.getValue(method)
|
||||
case callFn.name == "runtime.hashmapMake":
|
||||
// Create a new map.
|
||||
hashmapPointerType := inst.llvmInst.Type()
|
||||
keySize := uint32(operands[1].Uint())
|
||||
valueSize := uint32(operands[2].Uint())
|
||||
m := newMapValue(r, hashmapPointerType, keySize, valueSize)
|
||||
alloc := object{
|
||||
llvmType: hashmapPointerType,
|
||||
globalName: r.pkgName + "$map",
|
||||
buffer: m,
|
||||
size: m.len(r),
|
||||
}
|
||||
index := len(r.objects)
|
||||
r.objects = append(r.objects, alloc)
|
||||
|
||||
// Create a pointer to this map. Maps are reference types, so
|
||||
// are implemented as pointers.
|
||||
ptr := newPointerValue(r, index, 0)
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"runtime.hashmapMake:", keySize, valueSize, "->", ptr)
|
||||
}
|
||||
locals[inst.localIndex] = ptr
|
||||
case callFn.name == "runtime.hashmapBinarySet":
|
||||
// Do a mapassign operation with a binary key (that is, without
|
||||
// a string key).
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"runtime.hashmapBinarySet:", operands[1:])
|
||||
}
|
||||
mapPtr, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
m := mem.getWritable(mapPtr.index()).buffer.(*mapValue)
|
||||
keyPtr, err := operands[2].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
valuePtr, err := operands[3].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
err = m.putBinary(&mem, keyPtr, valuePtr)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
case callFn.name == "runtime.hashmapStringSet":
|
||||
// Do a mapassign operation with a string key.
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"runtime.hashmapBinarySet:", operands[1:])
|
||||
}
|
||||
mapPtr, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
m := mem.getWritable(mapPtr.index()).buffer.(*mapValue)
|
||||
stringPtr, err := operands[2].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
stringLen := operands[3].Uint()
|
||||
valuePtr, err := operands[4].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
err = m.putString(&mem, stringPtr, stringLen, valuePtr)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
default:
|
||||
if len(callFn.blocks) == 0 {
|
||||
// Call to a function declaration without a definition
|
||||
|
||||
@@ -640,288 +640,6 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
||||
return gep, nil
|
||||
}
|
||||
|
||||
// mapValue implements a Go map which is created at compile time and stored as a
|
||||
// global variable.
|
||||
// The value itself is only used as part of an object (object.buffer). Maps are
|
||||
// reference types aka pointers, so it can only be used as a pointerValue, not
|
||||
// directly.
|
||||
type mapValue struct {
|
||||
r *runner
|
||||
pkgName string
|
||||
size uint32 // byte size of runtime.hashmap
|
||||
hashmap llvm.Value
|
||||
keyIsString bool
|
||||
keys []interface{} // either rawValue (for binary key) or mapStringKey (for string key)
|
||||
values []rawValue
|
||||
keySize uint32
|
||||
valueSize uint32
|
||||
}
|
||||
|
||||
type mapStringKey struct {
|
||||
buf pointerValue
|
||||
size uint64
|
||||
data []uint64
|
||||
}
|
||||
|
||||
func newMapValue(r *runner, hashmapPointerType llvm.Type, keySize, valueSize uint32) *mapValue {
|
||||
size := uint32(r.targetData.TypeAllocSize(hashmapPointerType.ElementType()))
|
||||
return &mapValue{
|
||||
r: r,
|
||||
pkgName: r.pkgName,
|
||||
size: size,
|
||||
keySize: keySize,
|
||||
valueSize: valueSize,
|
||||
}
|
||||
}
|
||||
|
||||
func (v *mapValue) len(r *runner) uint32 {
|
||||
return v.size
|
||||
}
|
||||
|
||||
func (v *mapValue) clone() value {
|
||||
// Return a copy of mapValue.
|
||||
clone := *v
|
||||
clone.keys = append([]interface{}{}, clone.keys...)
|
||||
clone.values = append([]rawValue{}, clone.values...)
|
||||
return &clone
|
||||
}
|
||||
|
||||
func (v *mapValue) asPointer(r *runner) (pointerValue, error) {
|
||||
panic("interp: mapValue.asPointer")
|
||||
}
|
||||
|
||||
func (v *mapValue) asRawValue(r *runner) rawValue {
|
||||
panic("interp: mapValue.asRawValue")
|
||||
}
|
||||
|
||||
func (v *mapValue) Uint() uint64 {
|
||||
panic("interp: mapValue.Uint")
|
||||
}
|
||||
|
||||
func (v *mapValue) Int() int64 {
|
||||
panic("interp: mapValue.Int")
|
||||
}
|
||||
|
||||
// Temporary struct to collect data before turning this into a hashmap bucket
|
||||
// LLVM value.
|
||||
type mapBucket struct {
|
||||
m *mapValue
|
||||
tophash [8]uint8
|
||||
keys []rawValue // can have up to 8 keys
|
||||
values []rawValue // can have up to 8 values, len(keys) == len(values)
|
||||
}
|
||||
|
||||
// create returns a (pointer to a) buffer structurally equivalent to
|
||||
// runtime.hashmapBucket.
|
||||
func (b *mapBucket) create(ctx llvm.Context, nextBucket llvm.Value, mem *memoryView) llvm.Value {
|
||||
// Create tophash array.
|
||||
int8Type := ctx.Int8Type()
|
||||
tophashValues := make([]llvm.Value, 8)
|
||||
for i := range tophashValues {
|
||||
tophashValues[i] = llvm.ConstInt(int8Type, uint64(b.tophash[i]), false)
|
||||
}
|
||||
tophash := llvm.ConstArray(int8Type, tophashValues)
|
||||
|
||||
// Create next pointer (if not set).
|
||||
if nextBucket.IsNil() {
|
||||
nextBucket = llvm.ConstNull(llvm.PointerType(int8Type, 0))
|
||||
}
|
||||
|
||||
// Create data for keys.
|
||||
var keyValues []llvm.Value
|
||||
for _, key := range b.keys {
|
||||
keyValue, err := key.rawLLVMValue(mem)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
keyValues = append(keyValues, keyValue)
|
||||
}
|
||||
if len(b.keys) < 8 {
|
||||
keyValues = append(keyValues, llvm.ConstNull(llvm.ArrayType(int8Type, int(b.m.keySize)*(8-len(b.keys)))))
|
||||
}
|
||||
keyValue := ctx.ConstStruct(keyValues, false)
|
||||
if checks && uint32(b.m.r.targetData.TypeAllocSize(keyValue.Type())) != b.m.keySize*8 {
|
||||
panic("key size invalid")
|
||||
}
|
||||
|
||||
// Create data for values.
|
||||
var valueValues []llvm.Value
|
||||
for _, value := range b.values {
|
||||
v, err := value.rawLLVMValue(mem)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
valueValues = append(valueValues, v)
|
||||
}
|
||||
if len(b.values) < 8 {
|
||||
valueValues = append(valueValues, llvm.ConstNull(llvm.ArrayType(int8Type, int(b.m.valueSize)*(8-len(b.values)))))
|
||||
}
|
||||
valueValue := ctx.ConstStruct(valueValues, false)
|
||||
if checks && uint32(b.m.r.targetData.TypeAllocSize(valueValue.Type())) != b.m.valueSize*8 {
|
||||
panic("value size invalid")
|
||||
}
|
||||
|
||||
// Create the bucket.
|
||||
bucketInitializer := ctx.ConstStruct([]llvm.Value{
|
||||
tophash,
|
||||
nextBucket,
|
||||
keyValue,
|
||||
valueValue,
|
||||
}, false)
|
||||
bucket := llvm.AddGlobal(b.m.r.mod, bucketInitializer.Type(), b.m.pkgName+"$mapbucket")
|
||||
bucket.SetInitializer(bucketInitializer)
|
||||
bucket.SetLinkage(llvm.InternalLinkage)
|
||||
bucket.SetUnnamedAddr(true)
|
||||
return bucket
|
||||
}
|
||||
|
||||
func (v *mapValue) toLLVMValue(hashmapType llvm.Type, mem *memoryView) (llvm.Value, error) {
|
||||
if !v.hashmap.IsNil() {
|
||||
return v.hashmap, nil
|
||||
}
|
||||
|
||||
// Create a slice of buckets with all the keys and values in the hashmap.
|
||||
var buckets []*mapBucket
|
||||
var bucket *mapBucket
|
||||
for i, key := range v.keys {
|
||||
var data []uint64
|
||||
var keyValue rawValue
|
||||
switch key := key.(type) {
|
||||
case mapStringKey:
|
||||
data = key.data
|
||||
keyValue = newRawValue(v.keySize)
|
||||
// runtime._string is {ptr, length}
|
||||
for i := uint32(0); i < v.keySize/2; i++ {
|
||||
keyValue.buf[i] = key.buf.pointer
|
||||
}
|
||||
copy(keyValue.buf[v.keySize/2:], literalValue{key.size}.asRawValue(v.r).buf)
|
||||
case rawValue:
|
||||
if key.hasPointer() {
|
||||
return llvm.Value{}, errors.New("interp: todo: map key with pointer")
|
||||
}
|
||||
data = key.buf
|
||||
keyValue = key
|
||||
default:
|
||||
return llvm.Value{}, errors.New("interp: unknown map key type")
|
||||
}
|
||||
buf := make([]byte, len(data))
|
||||
for i, p := range data {
|
||||
buf[i] = byte(p)
|
||||
}
|
||||
hash := v.hash(buf)
|
||||
|
||||
if i%8 == 0 {
|
||||
bucket = &mapBucket{m: v}
|
||||
buckets = append(buckets, bucket)
|
||||
}
|
||||
bucket.tophash[i%8] = v.topHash(hash)
|
||||
bucket.keys = append(bucket.keys, keyValue)
|
||||
bucket.values = append(bucket.values, v.values[i])
|
||||
}
|
||||
|
||||
// Convert these buckets into LLVM global variables.
|
||||
ctx := v.r.mod.Context()
|
||||
var nextBucket llvm.Value
|
||||
for i := len(buckets) - 1; i >= 0; i-- {
|
||||
bucket = buckets[i]
|
||||
bucketValue := bucket.create(ctx, nextBucket, mem)
|
||||
nextBucket = bucketValue
|
||||
}
|
||||
firstBucket := nextBucket
|
||||
if firstBucket.IsNil() {
|
||||
firstBucket = llvm.ConstNull(mem.r.i8ptrType)
|
||||
} else {
|
||||
firstBucket = llvm.ConstBitCast(firstBucket, mem.r.i8ptrType)
|
||||
}
|
||||
|
||||
// Create the hashmap itself, pointing to these buckets.
|
||||
hashmapPointerType := llvm.PointerType(hashmapType, 0)
|
||||
hashmap := llvm.ConstNamedStruct(hashmapType, []llvm.Value{
|
||||
llvm.ConstPointerNull(hashmapPointerType), // next
|
||||
firstBucket, // buckets
|
||||
llvm.ConstInt(hashmapType.StructElementTypes()[2], uint64(len(v.keys)), false), // count
|
||||
llvm.ConstInt(ctx.Int8Type(), uint64(v.keySize), false), // keySize
|
||||
llvm.ConstInt(ctx.Int8Type(), uint64(v.valueSize), false), // valueSize
|
||||
llvm.ConstInt(ctx.Int8Type(), 0, false), // bucketBits
|
||||
})
|
||||
|
||||
v.hashmap = hashmap
|
||||
return v.hashmap, nil
|
||||
}
|
||||
|
||||
// putString does a map assign operation, assuming that the map is of type
|
||||
// map[string]T.
|
||||
func (v *mapValue) putString(mem *memoryView, stringBuf pointerValue, stringLen uint64, valuePtr pointerValue) error {
|
||||
if !v.hashmap.IsNil() {
|
||||
return errMapAlreadyCreated
|
||||
}
|
||||
|
||||
value := mem.load(valuePtr, v.valueSize)
|
||||
stringValue := mem.load(stringBuf, uint32(stringLen)).asRawValue(v.r)
|
||||
if stringValue.hasPointer() {
|
||||
panic("interp: string contains pointer")
|
||||
}
|
||||
|
||||
// TODO: avoid duplicate keys
|
||||
v.keys = append(v.keys, mapStringKey{stringBuf, stringLen, stringValue.buf})
|
||||
v.values = append(v.values, value.asRawValue(v.r))
|
||||
v.keyIsString = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// putBinary does a map assign operation for binary data (e.g. [3]int etc). The
|
||||
// key must not contain pointer values.
|
||||
func (v *mapValue) putBinary(mem *memoryView, keyPtr, valuePtr pointerValue) error {
|
||||
if !v.hashmap.IsNil() {
|
||||
return errMapAlreadyCreated
|
||||
}
|
||||
|
||||
key := mem.load(keyPtr, v.keySize)
|
||||
value := mem.load(valuePtr, v.valueSize)
|
||||
|
||||
// Sanity checks.
|
||||
if v.keySize != key.len(mem.r) || v.valueSize != value.len(mem.r) {
|
||||
// This is a bug (not unhandled input), so panic.
|
||||
panic("interp: key or value size mismatch")
|
||||
}
|
||||
if v.keyIsString {
|
||||
panic("cannot put binary keys in string map")
|
||||
}
|
||||
|
||||
// TODO: avoid duplicate keys
|
||||
v.keys = append(v.keys, key.asRawValue(v.r))
|
||||
v.values = append(v.values, value.asRawValue(v.r))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get FNV-1a hash of this string.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
|
||||
func (v *mapValue) hash(data []byte) uint32 {
|
||||
var result uint32 = 2166136261 // FNV offset basis
|
||||
for _, c := range data {
|
||||
result ^= uint32(c)
|
||||
result *= 16777619 // FNV prime
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Get the topmost 8 bits of the hash, without using a special value (like 0).
|
||||
func (v *mapValue) topHash(hash uint32) uint8 {
|
||||
tophash := uint8(hash >> 24)
|
||||
if tophash < 1 {
|
||||
// 0 means empty slot, so make it bigger.
|
||||
tophash++
|
||||
}
|
||||
return tophash
|
||||
}
|
||||
|
||||
func (v *mapValue) String() string {
|
||||
return "<map keySize=" + strconv.Itoa(int(v.keySize)) + " valueSize=" + strconv.Itoa(int(v.valueSize)) + ">"
|
||||
}
|
||||
|
||||
// rawValue is a raw memory buffer that can store either pointers or regular
|
||||
// data. This is the fallback data for everything that isn't clearly a
|
||||
// literalValue or pointerValue.
|
||||
|
||||
Vendored
+25
@@ -65,6 +65,12 @@ 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
|
||||
}
|
||||
|
||||
@@ -87,3 +93,22 @@ 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
|
||||
}
|
||||
|
||||
Vendored
+23
@@ -25,6 +25,8 @@ 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
|
||||
}
|
||||
|
||||
@@ -44,3 +46,24 @@ 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
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -6,7 +6,7 @@ target triple = "x86_64--linux"
|
||||
|
||||
@main.v1 = global i1 0
|
||||
@"reflect/types.type:named:main.foo" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i64 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:named:main.foo$id" = external constant i8
|
||||
@"reflect/types.typeid:named:main.foo" = external constant i8
|
||||
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ entry:
|
||||
define internal void @main.init() unnamed_addr {
|
||||
entry:
|
||||
; Test type asserts.
|
||||
%typecode = call i1 @runtime.typeAssert(i64 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:main.foo" to i64), i8* @"reflect/types.type:named:main.foo$id", i8* undef, i8* null)
|
||||
%typecode = call i1 @runtime.typeAssert(i64 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:main.foo" to i64), i8* @"reflect/types.typeid:named:main.foo", i8* undef, i8* null)
|
||||
store i1 %typecode, i1* @main.v1
|
||||
ret void
|
||||
}
|
||||
|
||||
Vendored
-74
@@ -1,74 +0,0 @@
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv6m-none-eabi"
|
||||
|
||||
%runtime._string = type { i8*, i32 }
|
||||
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
|
||||
|
||||
@main.m = global %runtime.hashmap* null
|
||||
@main.binaryMap = global %runtime.hashmap* null
|
||||
@main.stringMap = global %runtime.hashmap* null
|
||||
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
|
||||
|
||||
declare %runtime.hashmap* @runtime.hashmapMake(i8, i8, i32, i8* %context, i8* %parentHandle)
|
||||
declare void @runtime.hashmapBinarySet(%runtime.hashmap*, i8*, i8*, i8* %context, i8* %parentHandle)
|
||||
declare void @runtime.hashmapStringSet(%runtime.hashmap*, i8*, i32, i8*, i8* %context, i8* %parentHandle)
|
||||
declare void @llvm.lifetime.end.p0i8(i64, i8*)
|
||||
declare void @llvm.lifetime.start.p0i8(i64, i8*)
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @main.init(i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
; Test that hashmap optimizations generally work (even with lifetimes).
|
||||
%hashmap.key = alloca i8
|
||||
%hashmap.value = alloca %runtime._string
|
||||
%0 = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 8, i32 1, i8* undef, i8* null)
|
||||
%hashmap.value.bitcast = bitcast %runtime._string* %hashmap.value to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 8, i8* %hashmap.value.bitcast)
|
||||
store %runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string* %hashmap.value
|
||||
call void @llvm.lifetime.start.p0i8(i64 1, i8* %hashmap.key)
|
||||
store i8 1, i8* %hashmap.key
|
||||
call void @runtime.hashmapBinarySet(%runtime.hashmap* %0, i8* %hashmap.key, i8* %hashmap.value.bitcast, i8* undef, i8* null)
|
||||
call void @llvm.lifetime.end.p0i8(i64 1, i8* %hashmap.key)
|
||||
call void @llvm.lifetime.end.p0i8(i64 8, i8* %hashmap.value.bitcast)
|
||||
store %runtime.hashmap* %0, %runtime.hashmap** @main.m
|
||||
|
||||
; Other tests, that can be done in a separate function.
|
||||
call void @main.testNonConstantBinarySet()
|
||||
call void @main.testNonConstantStringSet()
|
||||
ret void
|
||||
}
|
||||
|
||||
; Test that a map loaded from a global can still be used for mapassign
|
||||
; operations (with binary keys).
|
||||
define internal void @main.testNonConstantBinarySet() {
|
||||
%hashmap.key = alloca i8
|
||||
%hashmap.value = alloca i8
|
||||
; Create hashmap from global.
|
||||
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 1, i32 1, i8* undef, i8* null)
|
||||
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.binaryMap
|
||||
%map = load %runtime.hashmap*, %runtime.hashmap** @main.binaryMap
|
||||
; Do the binary set to the newly loaded map.
|
||||
store i8 1, i8* %hashmap.key
|
||||
store i8 2, i8* %hashmap.value
|
||||
call void @runtime.hashmapBinarySet(%runtime.hashmap* %map, i8* %hashmap.key, i8* %hashmap.value, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Test that a map loaded from a global can still be used for mapassign
|
||||
; operations (with string keys).
|
||||
define internal void @main.testNonConstantStringSet() {
|
||||
%hashmap.value = alloca i8
|
||||
; Create hashmap from global.
|
||||
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 8, i8 1, i32 1, i8* undef, i8* null)
|
||||
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.stringMap
|
||||
%map = load %runtime.hashmap*, %runtime.hashmap** @main.stringMap
|
||||
; Do the string set to the newly loaded map.
|
||||
store i8 2, i8* %hashmap.value
|
||||
call void @runtime.hashmapStringSet(%runtime.hashmap* %map, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7, i8* %hashmap.value, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
Vendored
-20
@@ -1,20 +0,0 @@
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv6m-none-eabi"
|
||||
|
||||
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
|
||||
|
||||
@main.m = local_unnamed_addr global %runtime.hashmap* @"main$map"
|
||||
@main.binaryMap = local_unnamed_addr global %runtime.hashmap* @"main$map.1"
|
||||
@main.stringMap = local_unnamed_addr global %runtime.hashmap* @"main$map.3"
|
||||
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
|
||||
@"main$map" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } }, { [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } }* @"main$mapbucket", i32 0, i32 0, i32 0), i32 1, i8 1, i8 8, i8 0 }
|
||||
@"main$mapbucket" = internal unnamed_addr global { [8 x i8], i8*, { i8, [7 x i8] }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, { i8, [7 x i8] } { i8 1, [7 x i8] zeroinitializer }, { { [7 x i8]*, [4 x i8] }, [56 x i8] } { { [7 x i8]*, [4 x i8] } { [7 x i8]* @main.init.string, [4 x i8] c"\07\00\00\00" }, [56 x i8] zeroinitializer } }
|
||||
@"main$map.1" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } }, { [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } }* @"main$mapbucket.2", i32 0, i32 0, i32 0), i32 1, i8 1, i8 1, i8 0 }
|
||||
@"main$mapbucket.2" = internal unnamed_addr global { [8 x i8], i8*, { i8, [7 x i8] }, { i8, [7 x i8] } } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, { i8, [7 x i8] } { i8 1, [7 x i8] zeroinitializer }, { i8, [7 x i8] } { i8 2, [7 x i8] zeroinitializer } }
|
||||
@"main$map.3" = internal global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } }, { [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } }* @"main$mapbucket.4", i32 0, i32 0, i32 0), i32 1, i8 8, i8 1, i8 0 }
|
||||
@"main$mapbucket.4" = internal unnamed_addr global { [8 x i8], i8*, { { [7 x i8]*, [4 x i8] }, [56 x i8] }, { i8, [7 x i8] } } { [8 x i8] c"x\00\00\00\00\00\00\00", i8* null, { { [7 x i8]*, [4 x i8] }, [56 x i8] } { { [7 x i8]*, [4 x i8] } { [7 x i8]* @main.init.string, [4 x i8] c"\07\00\00\00" }, [56 x i8] zeroinitializer }, { i8, [7 x i8] } { i8 2, [7 x i8] zeroinitializer } }
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
@main.phiNodesResultA = global i8 0
|
||||
@main.phiNodesResultB = global i8 0
|
||||
|
||||
define void @runtime.initAll() {
|
||||
call void @main.init()
|
||||
ret void
|
||||
}
|
||||
|
||||
; PHI nodes always use the value from the previous block, even in a loop. This
|
||||
; means that the loop below should swap the values %a and %b on each iteration.
|
||||
; Previously there was a bug which resulted in %b getting the value 3 on the
|
||||
; second iteration while it should have gotten 1 (from the first iteration of
|
||||
; %for.loop).
|
||||
define internal void @main.init() {
|
||||
entry:
|
||||
br label %for.loop
|
||||
|
||||
for.loop:
|
||||
%a = phi i8 [ 1, %entry ], [ %b, %for.loop ]
|
||||
%b = phi i8 [ 3, %entry ], [ %a, %for.loop ]
|
||||
%icmp = icmp eq i8 %a, 3
|
||||
br i1 %icmp, label %for.done, label %for.loop
|
||||
|
||||
for.done:
|
||||
store i8 %a, i8* @main.phiNodesResultA
|
||||
store i8 %b, i8* @main.phiNodesResultB
|
||||
ret void
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
@main.phiNodesResultA = local_unnamed_addr global i8 3
|
||||
@main.phiNodesResultB = local_unnamed_addr global i8 1
|
||||
|
||||
define void @runtime.initAll() local_unnamed_addr {
|
||||
ret void
|
||||
}
|
||||
+1
-1
Submodule lib/cmsis-svd updated: d9b58694ce...2fb66c5735
+1
-1
Submodule lib/stm32-svd updated: c6b5be976f...3a0b0829b7
@@ -13,11 +13,13 @@ import (
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/google/shlex"
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/tinygo-org/tinygo/builder"
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
@@ -28,6 +30,7 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
|
||||
"go.bug.st/serial"
|
||||
"go.bug.st/serial/enumerator"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -240,15 +243,12 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
|
||||
// do we need port reset to put MCU into bootloader mode?
|
||||
if config.Target.PortReset == "true" && flashMethod != "openocd" {
|
||||
if port == "" {
|
||||
var err error
|
||||
port, err = getDefaultPort()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
port, err := getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' }))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := touchSerialPortAt1200bps(port)
|
||||
err = touchSerialPortAt1200bps(port)
|
||||
if err != nil {
|
||||
return &commandError{"failed to reset port", result.Binary, err}
|
||||
}
|
||||
@@ -264,9 +264,9 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
fileToken := "{" + fileExt[1:] + "}"
|
||||
flashCmd = strings.ReplaceAll(flashCmd, fileToken, result.Binary)
|
||||
|
||||
if port == "" && strings.Contains(flashCmd, "{port}") {
|
||||
if strings.Contains(flashCmd, "{port}") {
|
||||
var err error
|
||||
port, err = getDefaultPort()
|
||||
port, err = getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' }))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -665,41 +665,66 @@ func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, e
|
||||
}
|
||||
|
||||
// getDefaultPort returns the default serial port depending on the operating system.
|
||||
func getDefaultPort() (port string, err error) {
|
||||
var portPath string
|
||||
func getDefaultPort(portCandidates []string) (port string, err error) {
|
||||
if len(portCandidates) == 1 {
|
||||
return portCandidates[0], nil
|
||||
}
|
||||
|
||||
var ports []string
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
portPath = "/dev/cu.usb*"
|
||||
case "linux":
|
||||
portPath = "/dev/ttyACM*"
|
||||
case "freebsd":
|
||||
portPath = "/dev/cuaU*"
|
||||
case "windows":
|
||||
ports, err := serial.GetPortsList()
|
||||
ports, err = filepath.Glob("/dev/cuaU*")
|
||||
case "darwin", "linux", "windows":
|
||||
var portsList []*enumerator.PortDetails
|
||||
portsList, err = enumerator.GetDetailedPortsList()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(ports) == 0 {
|
||||
return "", errors.New("no serial ports available")
|
||||
} else if len(ports) > 1 {
|
||||
return "", errors.New("multiple serial ports available - use -port flag")
|
||||
for _, p := range portsList {
|
||||
ports = append(ports, p.Name)
|
||||
}
|
||||
|
||||
return ports[0], nil
|
||||
if ports == nil || len(ports) == 0 {
|
||||
// fallback
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
ports, err = filepath.Glob("/dev/cu.usb*")
|
||||
case "linux":
|
||||
ports, err = filepath.Glob("/dev/ttyACM*")
|
||||
case "windows":
|
||||
ports, err = serial.GetPortsList()
|
||||
}
|
||||
}
|
||||
default:
|
||||
return "", errors.New("unable to search for a default USB device to be flashed on this OS")
|
||||
}
|
||||
|
||||
d, err := filepath.Glob(portPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if d == nil {
|
||||
} else if ports == nil {
|
||||
return "", errors.New("unable to locate a serial port")
|
||||
} else if len(ports) == 0 {
|
||||
return "", errors.New("no serial ports available")
|
||||
}
|
||||
|
||||
return d[0], nil
|
||||
if len(portCandidates) == 0 {
|
||||
if len(ports) == 1 {
|
||||
return ports[0], nil
|
||||
} else {
|
||||
return "", errors.New("multiple serial ports available - use -port flag, available ports are " + strings.Join(ports, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
for _, ps := range portCandidates {
|
||||
for _, p := range ports {
|
||||
if p == ps {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("port you specified '" + strings.Join(portCandidates, ",") + "' does not exist, available ports are " + strings.Join(ports, ", "))
|
||||
}
|
||||
|
||||
func usage() {
|
||||
@@ -812,6 +837,52 @@ func handleCompilerError(err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// This is a special type for the -X flag to parse the pkgpath.Var=stringVal
|
||||
// format. It has to be a special type to allow multiple variables to be defined
|
||||
// this way.
|
||||
type globalValuesFlag map[string]map[string]string
|
||||
|
||||
func (m globalValuesFlag) String() string {
|
||||
return "pkgpath.Var=value"
|
||||
}
|
||||
|
||||
func (m globalValuesFlag) Set(value string) error {
|
||||
equalsIndex := strings.IndexByte(value, '=')
|
||||
if equalsIndex < 0 {
|
||||
return errors.New("expected format pkgpath.Var=value")
|
||||
}
|
||||
pathAndName := value[:equalsIndex]
|
||||
pointIndex := strings.LastIndexByte(pathAndName, '.')
|
||||
if pointIndex < 0 {
|
||||
return errors.New("expected format pkgpath.Var=value")
|
||||
}
|
||||
path := pathAndName[:pointIndex]
|
||||
name := pathAndName[pointIndex+1:]
|
||||
stringValue := value[equalsIndex+1:]
|
||||
if m[path] == nil {
|
||||
m[path] = make(map[string]string)
|
||||
}
|
||||
m[path][name] = stringValue
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseGoLinkFlag parses the -ldflags parameter. Its primary purpose right now
|
||||
// is the -X flag, for setting the value of global string variables.
|
||||
func parseGoLinkFlag(flagsString string) (map[string]map[string]string, error) {
|
||||
set := flag.NewFlagSet("link", flag.ExitOnError)
|
||||
globalVarValues := make(globalValuesFlag)
|
||||
set.Var(globalVarValues, "X", "Set the value of the string variable to the given value.")
|
||||
flags, err := shlex.Split(flagsString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = set.Parse(flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]map[string]string(globalVarValues), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
|
||||
@@ -831,13 +902,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")
|
||||
port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)")
|
||||
programmer := flag.String("programmer", "", "which hardware programmer to use")
|
||||
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
||||
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
||||
ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags")
|
||||
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic")
|
||||
|
||||
var flagJSON, flagDeps *bool
|
||||
@@ -867,6 +938,19 @@ 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,
|
||||
@@ -879,23 +963,17 @@ 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()
|
||||
|
||||
+112
-55
@@ -13,7 +13,6 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -29,41 +28,43 @@ const TESTDATA = "testdata"
|
||||
var testTarget = flag.String("target", "", "override test target")
|
||||
|
||||
func TestCompiler(t *testing.T) {
|
||||
matches, err := filepath.Glob(filepath.Join(TESTDATA, "*.go"))
|
||||
if err != nil {
|
||||
t.Fatal("could not read test files:", err)
|
||||
tests := []string{
|
||||
"alias.go",
|
||||
"atomic.go",
|
||||
"binop.go",
|
||||
"calls.go",
|
||||
"cgo/",
|
||||
"channel.go",
|
||||
"coroutines.go",
|
||||
"float.go",
|
||||
"gc.go",
|
||||
"init.go",
|
||||
"init_multi.go",
|
||||
"interface.go",
|
||||
"json.go",
|
||||
"map.go",
|
||||
"math.go",
|
||||
"print.go",
|
||||
"reflect.go",
|
||||
"slice.go",
|
||||
"sort.go",
|
||||
"stdlib.go",
|
||||
"string.go",
|
||||
"structs.go",
|
||||
"zeroalloc.go",
|
||||
}
|
||||
|
||||
dirMatches, err := filepath.Glob(filepath.Join(TESTDATA, "*", "main.go"))
|
||||
if err != nil {
|
||||
t.Fatal("could not read test packages:", err)
|
||||
}
|
||||
if len(matches) == 0 || len(dirMatches) == 0 {
|
||||
t.Fatal("no test files found")
|
||||
}
|
||||
for _, m := range dirMatches {
|
||||
matches = append(matches, filepath.Dir(m)+string(filepath.Separator))
|
||||
}
|
||||
|
||||
sort.Strings(matches)
|
||||
|
||||
if *testTarget != "" {
|
||||
// This makes it possible to run one specific test (instead of all),
|
||||
// which is especially useful to quickly check whether some changes
|
||||
// affect a particular target architecture.
|
||||
runPlatTests(*testTarget, matches, t)
|
||||
runPlatTests(*testTarget, tests, t)
|
||||
return
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
t.Run("Host", func(t *testing.T) {
|
||||
runPlatTests("", matches, t)
|
||||
if runtime.GOOS == "darwin" {
|
||||
runTest("testdata/libc/filesystem.go", "", t,
|
||||
nil, nil)
|
||||
runTest("testdata/libc/env.go", "", t,
|
||||
[]string{"ENV1=VALUE1", "ENV2=VALUE2"}, nil)
|
||||
}
|
||||
runPlatTests("", tests, t)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -72,26 +73,26 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("EmulatedCortexM3", func(t *testing.T) {
|
||||
runPlatTests("cortex-m-qemu", matches, t)
|
||||
runPlatTests("cortex-m-qemu", tests, t)
|
||||
})
|
||||
|
||||
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
|
||||
// Note: running only on Windows and macOS because Linux (as of 2020)
|
||||
// usually has an outdated QEMU version that doesn't support RISC-V yet.
|
||||
t.Run("EmulatedRISCV", func(t *testing.T) {
|
||||
runPlatTests("riscv-qemu", matches, t)
|
||||
runPlatTests("riscv-qemu", tests, t)
|
||||
})
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
t.Run("X86Linux", func(t *testing.T) {
|
||||
runPlatTests("i386--linux-gnu", matches, t)
|
||||
runPlatTests("i386--linux-gnu", tests, t)
|
||||
})
|
||||
t.Run("ARMLinux", func(t *testing.T) {
|
||||
runPlatTests("arm--linux-gnueabihf", matches, t)
|
||||
runPlatTests("arm--linux-gnueabihf", tests, t)
|
||||
})
|
||||
t.Run("ARM64Linux", func(t *testing.T) {
|
||||
runPlatTests("aarch64--linux-gnu", matches, t)
|
||||
runPlatTests("aarch64--linux-gnu", tests, t)
|
||||
})
|
||||
goVersion, err := goenv.GorootVersionString(goenv.Get("GOROOT"))
|
||||
if err != nil {
|
||||
@@ -104,27 +105,72 @@ func TestCompiler(t *testing.T) {
|
||||
// below that are also not supported but still seem to pass, so
|
||||
// include them in the tests for now.
|
||||
t.Run("WebAssembly", func(t *testing.T) {
|
||||
runPlatTests("wasm", matches, t)
|
||||
runPlatTests("wasm", tests, t)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("WASI", func(t *testing.T) {
|
||||
runPlatTests("wasi", matches, t)
|
||||
runTest("testdata/libc/env.go", "wasi", t,
|
||||
[]string{"--env", "ENV1=VALUE1", "--env", "ENV2=VALUE2"}, nil)
|
||||
runTest("testdata/libc/filesystem.go", "wasi", t, nil, []string{"--dir=."})
|
||||
runPlatTests("wasi", tests, t)
|
||||
})
|
||||
}
|
||||
|
||||
// Test a few build options.
|
||||
t.Run("build-options", func(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// These tests assume a host that is supported by TinyGo.
|
||||
t.Skip("can't test build options on Windows")
|
||||
}
|
||||
t.Parallel()
|
||||
|
||||
// Test with few optimizations enabled (no inlining, etc).
|
||||
t.Run("opt=1", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTestWithConfig("stdlib.go", "", t, &compileopts.Options{
|
||||
Opt: "1",
|
||||
}, nil, nil)
|
||||
})
|
||||
|
||||
// Test with only the bare minimum of optimizations enabled.
|
||||
// TODO: fix this for stdlib.go, which currently fails.
|
||||
t.Run("opt=0", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTestWithConfig("print.go", "", t, &compileopts.Options{
|
||||
Opt: "0",
|
||||
}, nil, nil)
|
||||
})
|
||||
|
||||
t.Run("ldflags", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTestWithConfig("ldflags.go", "", t, &compileopts.Options{
|
||||
Opt: "z",
|
||||
GlobalValues: map[string]map[string]string{
|
||||
"main": {
|
||||
"someGlobal": "foobar",
|
||||
},
|
||||
},
|
||||
}, nil, nil)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func runPlatTests(target string, matches []string, t *testing.T) {
|
||||
func runPlatTests(target string, tests []string, t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, path := range matches {
|
||||
path := path // redefine to avoid race condition
|
||||
t.Run(filepath.Base(path), func(t *testing.T) {
|
||||
for _, name := range tests {
|
||||
name := name // redefine to avoid race condition
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest(path, target, t, nil, nil)
|
||||
runTest(name, target, t, nil, nil)
|
||||
})
|
||||
}
|
||||
if target == "wasi" || target == "" {
|
||||
t.Run("filesystem.go", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest("filesystem.go", target, t, nil, nil)
|
||||
})
|
||||
t.Run("env.go", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest("env.go", target, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -141,10 +187,28 @@ func runBuild(src, out string, opts *compileopts.Options) error {
|
||||
return Build(src, out, opts)
|
||||
}
|
||||
|
||||
func runTest(path, target string, t *testing.T, environmentVars []string, additionalArgs []string) {
|
||||
func runTest(name, target string, t *testing.T, cmdArgs, environmentVars []string) {
|
||||
options := &compileopts.Options{
|
||||
Target: target,
|
||||
Opt: "z",
|
||||
PrintIR: false,
|
||||
DumpSSA: false,
|
||||
VerifyIR: true,
|
||||
Debug: true,
|
||||
PrintSizes: "",
|
||||
WasmAbi: "",
|
||||
}
|
||||
runTestWithConfig(name, target, t, options, cmdArgs, environmentVars)
|
||||
}
|
||||
|
||||
func runTestWithConfig(name, target string, t *testing.T, options *compileopts.Options, cmdArgs, environmentVars []string) {
|
||||
// Get the expected output for this test.
|
||||
// Note: not using filepath.Join as it strips the path separator at the end
|
||||
// of the path.
|
||||
path := TESTDATA + "/" + name
|
||||
// Get the expected output for this test.
|
||||
txtpath := path[:len(path)-3] + ".txt"
|
||||
if path[len(path)-1] == os.PathSeparator {
|
||||
if path[len(path)-1] == '/' {
|
||||
txtpath = path + "out.txt"
|
||||
}
|
||||
expected, err := ioutil.ReadFile(txtpath)
|
||||
@@ -165,19 +229,8 @@ func runTest(path, target string, t *testing.T, environmentVars []string, additi
|
||||
}()
|
||||
|
||||
// Build the test binary.
|
||||
config := &compileopts.Options{
|
||||
Target: target,
|
||||
Opt: "z",
|
||||
PrintIR: false,
|
||||
DumpSSA: false,
|
||||
VerifyIR: true,
|
||||
Debug: true,
|
||||
PrintSizes: "",
|
||||
WasmAbi: "",
|
||||
}
|
||||
|
||||
binary := filepath.Join(tmpdir, "test")
|
||||
err = runBuild("./"+path, binary, config)
|
||||
err = runBuild("./"+path, binary, options)
|
||||
if err != nil {
|
||||
printCompilerError(t.Log, err)
|
||||
t.Fail()
|
||||
@@ -191,6 +244,7 @@ func runTest(path, target string, t *testing.T, environmentVars []string, additi
|
||||
if target == "" {
|
||||
cmd = exec.Command(binary)
|
||||
cmd.Env = append(cmd.Env, environmentVars...)
|
||||
cmd.Args = append(cmd.Args, cmdArgs...)
|
||||
} else {
|
||||
spec, err := compileopts.LoadTarget(target)
|
||||
if err != nil {
|
||||
@@ -200,13 +254,16 @@ func runTest(path, target string, t *testing.T, environmentVars []string, additi
|
||||
cmd = exec.Command(binary)
|
||||
} else {
|
||||
args := append(spec.Emulator[1:], binary)
|
||||
cmd = exec.Command(spec.Emulator[0], append(args, additionalArgs...)...)
|
||||
cmd = exec.Command(spec.Emulator[0], args...)
|
||||
}
|
||||
|
||||
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
||||
// Allow reading from the current directory.
|
||||
cmd.Args = append(cmd.Args, "--dir=.")
|
||||
for _, v := range environmentVars {
|
||||
cmd.Args = append(cmd.Args, "--env", v)
|
||||
}
|
||||
cmd.Args = append(cmd.Args, cmdArgs...)
|
||||
} else {
|
||||
cmd.Env = append(cmd.Env, environmentVars...)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go
|
||||
|
||||
// +build sam,atsame5x
|
||||
|
||||
// These are the supported pchctrl function numberings on the atsamd51x
|
||||
// See http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf
|
||||
// table 14-9
|
||||
|
||||
package sam
|
||||
|
||||
const (
|
||||
PCHCTRL_GCLK_OSCCTRL_DFLL48 = 0 // DFLL48 input clock source
|
||||
PCHCTRL_GCLK_OSCCTRL_FDPLL0 = 1 // Reference clock for FDPLL0
|
||||
PCHCTRL_GCLK_OSCCTRL_FDPLL1 = 2 // Reference clock for FDPLL1
|
||||
PCHCTRL_GCLK_OSCCTRL_FDPLL0_32K = 3 // FDPLL0 = 3 // 32KHz clock for internal lock timer
|
||||
PCHCTRL_GCLK_OSCCTRL_FDPLL1_32K = 3 // FDPLL1 = 3 // 32KHz clock for internal lock timer
|
||||
PCHCTRL_GCLK_SDHC0_SLOW = 3 // SDHC0 = 3 // Slow
|
||||
PCHCTRL_GCLK_SDHC1_SLOW = 3 // SDHC1 = 3 // Slow
|
||||
PCHCTRL_GCLK_SERCOMX_SLOW = 3 // GCLK_SERCOM[0..7]_SLOW = 3
|
||||
PCHCTRL_GCLK_EIC = 4
|
||||
PCHCTRL_GCLK_FREQM_MSR = 5 // FREQM Measure
|
||||
PCHCTRL_GCLK_FREQM_REF = 6 // FREQM Reference
|
||||
PCHCTRL_GCLK_SERCOM0_CORE = 7 // SERCOM0 Core
|
||||
PCHCTRL_GCLK_SERCOM1_CORE = 8 // SERCOM1 Core
|
||||
PCHCTRL_GCLK_TC0 = 9
|
||||
PCHCTRL_GCLK_TC1 = 9 // TC0, TC1
|
||||
PCHCTRL_GCLK_USB = 10 // USB
|
||||
PCHCTRL_GCLK_EVSYS0 = 11
|
||||
PCHCTRL_GCLK_EVSYS1 = 12
|
||||
PCHCTRL_GCLK_EVSYS2 = 13
|
||||
PCHCTRL_GCLK_EVSYS3 = 14
|
||||
PCHCTRL_GCLK_EVSYS4 = 15
|
||||
PCHCTRL_GCLK_EVSYS5 = 16
|
||||
PCHCTRL_GCLK_EVSYS6 = 17
|
||||
PCHCTRL_GCLK_EVSYS7 = 18
|
||||
PCHCTRL_GCLK_EVSYS8 = 19
|
||||
PCHCTRL_GCLK_EVSYS9 = 20
|
||||
PCHCTRL_GCLK_EVSYS10 = 21
|
||||
PCHCTRL_GCLK_EVSYS11 = 22
|
||||
PCHCTRL_GCLK_SERCOM2_CORE = 23 // SERCOM2 Core
|
||||
PCHCTRL_GCLK_SERCOM3_CORE = 24 // SERCOM3 Core
|
||||
PCHCTRL_GCLK_TCC0 = 25
|
||||
PCHCTRL_GCLK_TCC1 = 25 // TCC0, TCC1
|
||||
PCHCTRL_GCLK_TC2 = 26
|
||||
PCHCTRL_GCLK_TC3 = 26 // TC2, TC3
|
||||
PCHCTRL_GCLK_CAN0 = 27 // CAN0
|
||||
PCHCTRL_GCLK_CAN1 = 28 // CAN1
|
||||
PCHCTRL_GCLK_TCC2 = 29
|
||||
PCHCTRL_GCLK_TCC3 = 29 // TCC2, TCC3
|
||||
PCHCTRL_GCLK_TC4 = 30
|
||||
PCHCTRL_GCLK_TC5 = 30 // TC4, TC5
|
||||
PCHCTRL_GCLK_PDEC = 31 // PDEC
|
||||
PCHCTRL_GCLK_AC = 32 // AC
|
||||
PCHCTRL_GCLK_CCL = 33 // CCL
|
||||
PCHCTRL_GCLK_SERCOM4_CORE = 34 // SERCOM4 Core
|
||||
PCHCTRL_GCLK_SERCOM5_CORE = 35 // SERCOM5 Core
|
||||
PCHCTRL_GCLK_SERCOM6_CORE = 36 // SERCOM6 Core
|
||||
PCHCTRL_GCLK_SERCOM7_CORE = 37 // SERCOM7 Core
|
||||
PCHCTRL_GCLK_TCC4 = 38 // TCC4
|
||||
PCHCTRL_GCLK_TC6 = 39
|
||||
PCHCTRL_GCLK_TC7 = 39 // TC6, TC7
|
||||
PCHCTRL_GCLK_ADC0 = 40 // ADC0
|
||||
PCHCTRL_GCLK_ADC1 = 41 // ADC1
|
||||
PCHCTRL_GCLK_DAC = 42 // DAC
|
||||
PCHCTRL_GCLK_I2S0 = 43
|
||||
PCHCTRL_GCLK_I2S1 = 44
|
||||
PCHCTRL_GCLK_SDHC0 = 45 // SDHC0
|
||||
PCHCTRL_GCLK_SDHC1 = 46 // SDHC1
|
||||
PCHCTRL_GCLK_CM4_TRACE = 47 // CM4 Trace
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
// +build arduino_mega1280
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
// Configuration on an Arduino Uno.
|
||||
pwm = machine.Timer3
|
||||
pinA = machine.PH3 // pin 6 on the Mega
|
||||
pinB = machine.PH4 // pin 7 on the Mega
|
||||
)
|
||||
@@ -0,0 +1,104 @@
|
||||
// +build arduino_mega1280
|
||||
|
||||
package machine
|
||||
|
||||
// Return the current CPU frequency in hertz.
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
const (
|
||||
AREF Pin = NoPin
|
||||
LED Pin = PB7
|
||||
|
||||
A0 Pin = PF0
|
||||
A1 Pin = PF1
|
||||
A2 Pin = PF2
|
||||
A3 Pin = PF3
|
||||
A4 Pin = PF4
|
||||
A5 Pin = PF5
|
||||
A6 Pin = PF6
|
||||
A7 Pin = PF7
|
||||
A8 Pin = PK0
|
||||
A9 Pin = PK1
|
||||
A10 Pin = PK2
|
||||
A11 Pin = PK3
|
||||
A12 Pin = PK4
|
||||
A13 Pin = PK5
|
||||
A14 Pin = PK6
|
||||
A15 Pin = PK7
|
||||
|
||||
// Analog Input
|
||||
ADC0 Pin = PF0
|
||||
ADC1 Pin = PF1
|
||||
ADC2 Pin = PF2
|
||||
ADC3 Pin = PF3
|
||||
ADC4 Pin = PF4
|
||||
ADC5 Pin = PF5
|
||||
ADC6 Pin = PF6
|
||||
ADC7 Pin = PF7
|
||||
ADC8 Pin = PK0
|
||||
ADC9 Pin = PK1
|
||||
ADC10 Pin = PK2
|
||||
ADC11 Pin = PK3
|
||||
ADC12 Pin = PK4
|
||||
ADC13 Pin = PK5
|
||||
ADC14 Pin = PK6
|
||||
ADC15 Pin = PK7
|
||||
|
||||
// Digital pins
|
||||
D0 Pin = PE0
|
||||
D1 Pin = PE1
|
||||
D2 Pin = PE4
|
||||
D3 Pin = PE5
|
||||
D4 Pin = PG5
|
||||
D5 Pin = PE3
|
||||
D6 Pin = PH3
|
||||
D7 Pin = PH4
|
||||
D8 Pin = PH5
|
||||
D9 Pin = PH6
|
||||
D10 Pin = PB4
|
||||
D11 Pin = PB5
|
||||
D12 Pin = PB6
|
||||
D13 Pin = PB7
|
||||
D14 Pin = PJ1
|
||||
D15 Pin = PJ0
|
||||
D16 Pin = PH1
|
||||
D17 Pin = PH0
|
||||
D18 Pin = PD3
|
||||
D19 Pin = PD2
|
||||
D20 Pin = PD1
|
||||
D21 Pin = PD0
|
||||
D22 Pin = PA0
|
||||
D23 Pin = PA1
|
||||
D24 Pin = PA2
|
||||
D25 Pin = PA3
|
||||
D26 Pin = PA4
|
||||
D27 Pin = PA5
|
||||
D28 Pin = PA6
|
||||
D29 Pin = PA7
|
||||
D30 Pin = PC7
|
||||
D31 Pin = PC6
|
||||
D32 Pin = PC5
|
||||
D33 Pin = PC4
|
||||
D34 Pin = PC3
|
||||
D35 Pin = PC2
|
||||
D36 Pin = PC1
|
||||
D37 Pin = PC0
|
||||
D38 Pin = PD7
|
||||
D39 Pin = PG2
|
||||
D40 Pin = PG1
|
||||
D41 Pin = PG0
|
||||
D42 Pin = PL7
|
||||
D43 Pin = PL6
|
||||
D44 Pin = PL5
|
||||
D45 Pin = PL4
|
||||
D46 Pin = PL3
|
||||
D47 Pin = PL2
|
||||
D48 Pin = PL1
|
||||
D49 Pin = PL0
|
||||
D50 Pin = PB3
|
||||
D51 Pin = PB2
|
||||
D52 Pin = PB1
|
||||
D53 Pin = PB0
|
||||
)
|
||||
@@ -0,0 +1,330 @@
|
||||
// +build atsame54_xpro
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// Definition for compatibility, but not used
|
||||
const RESET_MAGIC_VALUE = 0x00000000
|
||||
|
||||
const (
|
||||
LED = PC18
|
||||
BUTTON = PB31
|
||||
)
|
||||
|
||||
const (
|
||||
// https://ww1.microchip.com/downloads/en/DeviceDoc/70005321A.pdf
|
||||
|
||||
// Extension Header EXT1
|
||||
EXT1_PIN3_ADC_P = PB04
|
||||
EXT1_PIN4_ADC_N = PB05
|
||||
EXT1_PIN5_GPIO1 = PA06
|
||||
EXT1_PIN6_GPIO2 = PA07
|
||||
EXT1_PIN7_PWM_P = PB08
|
||||
EXT1_PIN8_PWM_N = PB09
|
||||
EXT1_PIN9_IRQ = PB07
|
||||
EXT1_PIN9_GPIO = PB07
|
||||
EXT1_PIN10_SPI_SS_B = PA27
|
||||
EXT1_PIN10_GPIO = PA27
|
||||
EXT1_PIN11_TWI_SDA = PA22
|
||||
EXT1_PIN12_TWI_SCL = PA23
|
||||
EXT1_PIN13_UART_RX = PA05
|
||||
EXT1_PIN14_UART_TX = PA04
|
||||
EXT1_PIN15_SPI_SS_A = PB28
|
||||
EXT1_PIN16_SPI_SDO = PB27
|
||||
EXT1_PIN17_SPI_SDI = PB29
|
||||
EXT1_PIN18_SPI_SCK = PB26
|
||||
|
||||
// Extension Header EXT2
|
||||
EXT2_PIN3_ADC_P = PB00
|
||||
EXT2_PIN4_ADC_N = PA03
|
||||
EXT2_PIN5_GPIO1 = PB01
|
||||
EXT2_PIN6_GPIO2 = PB06
|
||||
EXT2_PIN7_PWM_P = PB14
|
||||
EXT2_PIN8_PWM_N = PB15
|
||||
EXT2_PIN9_IRQ = PD00
|
||||
EXT2_PIN9_GPIO = PD00
|
||||
EXT2_PIN10_SPI_SS_B = PB02
|
||||
EXT2_PIN10_GPIO = PB02
|
||||
EXT2_PIN11_TWI_SDA = PD08
|
||||
EXT2_PIN12_TWI_SCL = PD09
|
||||
EXT2_PIN13_UART_RX = PB17
|
||||
EXT2_PIN14_UART_TX = PB16
|
||||
EXT2_PIN15_SPI_SS_A = PC06
|
||||
EXT2_PIN16_SPI_SDO = PC04
|
||||
EXT2_PIN17_SPI_SDI = PC07
|
||||
EXT2_PIN18_SPI_SCK = PC05
|
||||
|
||||
// Extension Header EXT3
|
||||
EXT3_PIN3_ADC_P = PC02
|
||||
EXT3_PIN4_ADC_N = PC03
|
||||
EXT3_PIN5_GPIO1 = PC01
|
||||
EXT3_PIN6_GPIO2 = PC10
|
||||
EXT3_PIN7_PWM_P = PD10
|
||||
EXT3_PIN8_PWM_N = PD11
|
||||
EXT3_PIN9_IRQ = PC30
|
||||
EXT3_PIN9_GPIO = PC30
|
||||
EXT3_PIN10_SPI_SS_B = PC31
|
||||
EXT3_PIN10_GPIO = PC31
|
||||
EXT3_PIN11_TWI_SDA = PD08
|
||||
EXT3_PIN12_TWI_SCL = PD09
|
||||
EXT3_PIN13_UART_RX = PC23
|
||||
EXT3_PIN14_UART_TX = PC22
|
||||
EXT3_PIN15_SPI_SS_A = PC14
|
||||
EXT3_PIN16_SPI_SDO = PC04
|
||||
EXT3_PIN17_SPI_SDI = PC07
|
||||
EXT3_PIN18_SPI_SCK = PC05
|
||||
|
||||
// SD_CARD
|
||||
SD_CARD_MCDA0 = PB18
|
||||
SD_CARD_MCDA1 = PB19
|
||||
SD_CARD_MCDA2 = PB20
|
||||
SD_CARD_MCDA3 = PB21
|
||||
SD_CARD_MCCK = PA21
|
||||
SD_CARD_MCCDA = PA20
|
||||
SD_CARD_DETECT = PD20
|
||||
SD_CARD_PROTECT = PD21
|
||||
|
||||
// I2C
|
||||
I2C_SDA = PD08
|
||||
I2C_SCL = PD09
|
||||
|
||||
// CAN
|
||||
CAN0_TX = PA22
|
||||
CAN0_RX = PA23
|
||||
|
||||
CAN1_STANDBY = PC13
|
||||
CAN1_TX = PB12
|
||||
CAN1_RX = PB13
|
||||
|
||||
CAN_STANDBY = CAN1_STANDBY
|
||||
CAN_TX = CAN1_TX
|
||||
CAN_RX = CAN1_RX
|
||||
|
||||
// PDEC
|
||||
PDEC_PHASE_A = PC16
|
||||
PDEC_PHASE_B = PC17
|
||||
PDEC_INDEX = PC18
|
||||
|
||||
// PCC
|
||||
PCC_I2C_SDA = PD08
|
||||
PCC_I2C_SCL = PD09
|
||||
PCC_VSYNC_DEN1 = PA12
|
||||
PCC_HSYNC_DEN2 = PA13
|
||||
PCC_CLK = PA14
|
||||
PCC_XCLK = PA15
|
||||
PCC_DATA00 = PA16
|
||||
PCC_DATA01 = PA17
|
||||
PCC_DATA02 = PA18
|
||||
PCC_DATA03 = PA19
|
||||
PCC_DATA04 = PA20
|
||||
PCC_DATA05 = PA21
|
||||
PCC_DATA06 = PA22
|
||||
PCC_DATA07 = PA23
|
||||
PCC_DATA08 = PB14
|
||||
PCC_DATA09 = PB15
|
||||
PCC_RESET = PC12
|
||||
PCC_PWDN = PC11
|
||||
|
||||
// Ethernet
|
||||
ETHERNET_TXCK = PA14
|
||||
ETHERNET_TXEN = PA17
|
||||
ETHERNET_TX0 = PA18
|
||||
ETHERNET_TX1 = PA19
|
||||
ETHERNET_RXER = PA15
|
||||
ETHERNET_RX0 = PA13
|
||||
ETHERNET_RX1 = PA12
|
||||
ETHERNET_RXDV = PC20
|
||||
ETHERNET_MDIO = PC12
|
||||
ETHERNET_MDC = PC11
|
||||
ETHERNET_INT = PD12
|
||||
ETHERNET_RESET = PC21
|
||||
|
||||
PIN_QT_BUTTON = PA16
|
||||
PIN_BTN0 = PB31
|
||||
PIN_ETH_LED = PC15
|
||||
PIN_LED0 = PC18
|
||||
PIN_ADC_DAC = PA02
|
||||
PIN_VBUS_DETECT = PC00
|
||||
PIN_USB_ID = PC19
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
// Extension Header EXT1
|
||||
UART_TX_PIN = PA04 // TX : SERCOM0/PAD[0]
|
||||
UART_RX_PIN = PA05 // RX : SERCOM0/PAD[1]
|
||||
|
||||
// Extension Header EXT2
|
||||
UART2_TX_PIN = PB16 // TX : SERCOM5/PAD[0]
|
||||
UART2_RX_PIN = PB17 // RX : SERCOM5/PAD[1]
|
||||
|
||||
// Extension Header EXT3
|
||||
UART3_TX_PIN = PC22 // TX : SERCOM1/PAD[0]
|
||||
UART3_RX_PIN = PC23 // RX : SERCOM1/PAD[1]
|
||||
|
||||
// Virtual COM Port
|
||||
UART4_TX_PIN = PB25 // TX : SERCOM2/PAD[0]
|
||||
UART4_RX_PIN = PB24 // RX : SERCOM2/PAD[1]
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
// Extension Header EXT1
|
||||
SDA0_PIN = PA22 // SDA: SERCOM3/PAD[0]
|
||||
SCL0_PIN = PA23 // SCL: SERCOM3/PAD[1]
|
||||
|
||||
// Extension Header EXT2
|
||||
SDA1_PIN = PD08 // SDA: SERCOM7/PAD[0]
|
||||
SCL1_PIN = PD09 // SCL: SERCOM7/PAD[1]
|
||||
|
||||
// Extension Header EXT3
|
||||
SDA2_PIN = PD08 // SDA: SERCOM7/PAD[0]
|
||||
SCL2_PIN = PD09 // SCL: SERCOM7/PAD[1]
|
||||
|
||||
// Data Gateway Interface
|
||||
SDA_DGI_PIN = PD08 // SDA: SERCOM7/PAD[0]
|
||||
SCL_DGI_PIN = PD09 // SCL: SERCOM7/PAD[1]
|
||||
|
||||
SDA_PIN = SDA0_PIN
|
||||
SCL_PIN = SCL0_PIN
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
// Extension Header EXT1
|
||||
SPI0_SCK_PIN = PB26 // SCK: SERCOM4/PAD[1]
|
||||
SPI0_SDO_PIN = PB27 // SDO: SERCOM4/PAD[0]
|
||||
SPI0_SDI_PIN = PB29 // SDI: SERCOM4/PAD[3]
|
||||
SPI0_SS_PIN = PB28 // SS : SERCOM4/PAD[2]
|
||||
|
||||
// Extension Header EXT2
|
||||
SPI1_SCK_PIN = PC05 // SCK: SERCOM6/PAD[1]
|
||||
SPI1_SDO_PIN = PC04 // SDO: SERCOM6/PAD[0]
|
||||
SPI1_SDI_PIN = PC07 // SDI: SERCOM6/PAD[3]
|
||||
SPI1_SS_PIN = PC06 // SS : SERCOM6/PAD[2]
|
||||
|
||||
// Extension Header EXT3
|
||||
SPI2_SCK_PIN = PC05 // SCK: SERCOM6/PAD[1]
|
||||
SPI2_SDO_PIN = PC04 // SDO: SERCOM6/PAD[0]
|
||||
SPI2_SDI_PIN = PC07 // SDI: SERCOM6/PAD[3]
|
||||
SPI2_SS_PIN = PC14 // SS : GPIO
|
||||
|
||||
// Data Gateway Interface
|
||||
SPI_DGI_SCK_PIN = PC05 // SCK: SERCOM6/PAD[1]
|
||||
SPI_DGI_SDO_PIN = PC04 // SDO: SERCOM6/PAD[0]
|
||||
SPI_DGI_SDI_PIN = PC07 // SDI: SERCOM6/PAD[3]
|
||||
SPI_DGI_SS_PIN = PD01 // SS : GPIO
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "SAM E54 Xplained Pro"
|
||||
usb_STRING_MANUFACTURER = "Atmel"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x03EB
|
||||
usb_PID uint16 = 0x2404
|
||||
)
|
||||
|
||||
// UART on the SAM E54 Xplained Pro
|
||||
var (
|
||||
// Extension Header EXT1
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
}
|
||||
|
||||
// Extension Header EXT2
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM5_USART_INT,
|
||||
SERCOM: 5,
|
||||
}
|
||||
|
||||
// Extension Header EXT3
|
||||
UART3 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM1_USART_INT,
|
||||
SERCOM: 1,
|
||||
}
|
||||
|
||||
// EDBG Virtual COM Port
|
||||
UART4 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM2_USART_INT,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART2.handleInterrupt)
|
||||
UART3.Interrupt = interrupt.New(sam.IRQ_SERCOM1_2, UART3.handleInterrupt)
|
||||
UART4.Interrupt = interrupt.New(sam.IRQ_SERCOM2_2, UART4.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the SAM E54 Xplained Pro
|
||||
var (
|
||||
// Extension Header EXT1
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM3_I2CM,
|
||||
SERCOM: 3,
|
||||
}
|
||||
|
||||
// Extension Header EXT2
|
||||
I2C1 = I2C{
|
||||
Bus: sam.SERCOM7_I2CM,
|
||||
SERCOM: 7,
|
||||
}
|
||||
|
||||
// Extension Header EXT3
|
||||
I2C2 = I2C{
|
||||
Bus: sam.SERCOM7_I2CM,
|
||||
SERCOM: 7,
|
||||
}
|
||||
|
||||
// Data Gateway Interface
|
||||
I2C3 = I2C{
|
||||
Bus: sam.SERCOM7_I2CM,
|
||||
SERCOM: 7,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the SAM E54 Xplained Pro
|
||||
var (
|
||||
// Extension Header EXT1
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM4_SPIM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
|
||||
// Extension Header EXT2
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM6_SPIM,
|
||||
SERCOM: 6,
|
||||
}
|
||||
|
||||
// Extension Header EXT3
|
||||
SPI2 = SPI{
|
||||
Bus: sam.SERCOM6_SPIM,
|
||||
SERCOM: 6,
|
||||
}
|
||||
|
||||
// Data Gateway Interface
|
||||
SPI3 = SPI{
|
||||
Bus: sam.SERCOM6_SPIM,
|
||||
SERCOM: 6,
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,142 @@
|
||||
// +build feather_m4_can
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = PB17 // UART0 RX/PWM available
|
||||
D1 = PB16 // UART0 TX/PWM available
|
||||
D4 = PA14 // PWM available
|
||||
D5 = PA16 // PWM available
|
||||
D6 = PA18 // PWM available
|
||||
D7 = PB03 // neopixel power
|
||||
D8 = PB02 // built-in neopixel
|
||||
D9 = PA19 // PWM available
|
||||
D10 = PA20 // can be used for PWM or UART1 TX
|
||||
D11 = PA21 // can be used for PWM or UART1 RX
|
||||
D12 = PA22 // PWM available
|
||||
D13 = PA23 // PWM available
|
||||
D21 = PA13 // PWM available
|
||||
D22 = PA12 // PWM available
|
||||
D23 = PB22 // PWM available
|
||||
D24 = PB23 // PWM available
|
||||
D25 = PA17 // PWM available
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = PA02 // ADC/AIN[0]
|
||||
A1 = PA05 // ADC/AIN[2]
|
||||
A2 = PB08 // ADC/AIN[3]
|
||||
A3 = PB09 // ADC/AIN[4]
|
||||
A4 = PA04 // ADC/AIN[5]
|
||||
A5 = PA06 // ADC/AIN[10]
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
NEOPIXELS = D8
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
const (
|
||||
UART_TX_PIN = D1
|
||||
UART_RX_PIN = D0
|
||||
)
|
||||
|
||||
const (
|
||||
UART2_TX_PIN = A4
|
||||
UART2_RX_PIN = A5
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = D22 // SDA: SERCOM2/PAD[0]
|
||||
SCL_PIN = D21 // SCL: SERCOM2/PAD[1]
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = D25 // SCK: SERCOM1/PAD[1]
|
||||
SPI0_SDO_PIN = D24 // SDO: SERCOM1/PAD[3]
|
||||
SPI0_SDI_PIN = D23 // SDI: SERCOM1/PAD[2]
|
||||
)
|
||||
|
||||
// CAN pins
|
||||
const (
|
||||
CAN0_TX = PA22
|
||||
CAN0_RX = PA23
|
||||
|
||||
CAN1_STANDBY = PB12
|
||||
CAN1_TX = PB14
|
||||
CAN1_RX = PB15
|
||||
BOOST_EN = PB13 // power control of CAN1's TCAN1051HGV (H: enable)
|
||||
|
||||
CAN_STANDBY = CAN1_STANDBY
|
||||
CAN_S = CAN1_STANDBY
|
||||
CAN_TX = CAN1_TX
|
||||
CAN_RX = CAN1_RX
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Adafruit Feather M4 CAN"
|
||||
usb_STRING_MANUFACTURER = "Adafruit"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x80CD
|
||||
)
|
||||
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM5_USART_INT,
|
||||
SERCOM: 5,
|
||||
}
|
||||
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
SERCOM: 0,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
|
||||
|
||||
// turn on neopixel
|
||||
D7.Configure(PinConfig{Mode: PinOutput})
|
||||
D7.High()
|
||||
}
|
||||
|
||||
// I2C on the Feather M4.
|
||||
var (
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the Feather M4.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
@@ -5,11 +5,6 @@ package machine
|
||||
// The micro:bit does not have a 32kHz crystal on board.
|
||||
const HasLowFrequencyCrystal = false
|
||||
|
||||
const (
|
||||
LED = P13
|
||||
LED1 = LED
|
||||
)
|
||||
|
||||
// Buttons on the micro:bit v2 (A and B)
|
||||
const (
|
||||
BUTTON Pin = BUTTONA
|
||||
|
||||
@@ -13,20 +13,53 @@ const (
|
||||
LED_GREEN = PB3
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
// Arduino Pins
|
||||
A0 = PA0
|
||||
A1 = PA1
|
||||
A2 = PA3
|
||||
A3 = PA4
|
||||
A4 = PA5
|
||||
A5 = PA6
|
||||
A6 = PA7
|
||||
A7 = PA2
|
||||
|
||||
D0 = PA10
|
||||
D1 = PA9
|
||||
D2 = PA12
|
||||
D3 = PB0
|
||||
D4 = PB7
|
||||
D5 = PB6
|
||||
D6 = PB1
|
||||
D7 = PC14
|
||||
D8 = PC15
|
||||
D9 = PA8
|
||||
D10 = PA11
|
||||
D11 = PB5
|
||||
D12 = PB4
|
||||
D13 = PB3
|
||||
)
|
||||
|
||||
const (
|
||||
// UART pins
|
||||
// PA2 and PA15 are connected to the ST-Link Virtual Com Port (VCP)
|
||||
UART_TX_PIN = PA2
|
||||
UART_RX_PIN = PA15
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
// I2C pins
|
||||
// With default solder bridge settings:
|
||||
// PB6 / Arduino D5 / CN3 Pin 8 is SCL
|
||||
// PB7 / Arduino D4 / CN3 Pin 7 is SDA
|
||||
I2C0_SCL_PIN = PB6
|
||||
I2C0_SDA_PIN = PB7
|
||||
|
||||
// SPI pins
|
||||
SPI1_SCK_PIN = PB3
|
||||
SPI1_SDI_PIN = PB5
|
||||
SPI1_SDO_PIN = PB4
|
||||
SPI0_SCK_PIN = SPI1_SCK_PIN
|
||||
SPI0_SDI_PIN = SPI1_SDI_PIN
|
||||
SPI0_SDO_PIN = SPI1_SDO_PIN
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -40,15 +73,20 @@ var (
|
||||
RxAltFuncSelector: 3,
|
||||
}
|
||||
UART1 = &UART0
|
||||
)
|
||||
|
||||
var (
|
||||
// I2C1 is documented, alias to I2C0 as well
|
||||
I2C1 = &I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: 4,
|
||||
}
|
||||
I2C0 = I2C1
|
||||
|
||||
// SPI1 is documented, alias to SPI0 as well
|
||||
SPI1 = &SPI{
|
||||
Bus: stm32.SPI1,
|
||||
AltFuncSelector: 5,
|
||||
}
|
||||
SPI0 = SPI1
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -0,0 +1,931 @@
|
||||
// +build avr,atmega1280
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const irq_USART0_RX = avr.IRQ_USART0_RX
|
||||
|
||||
const (
|
||||
portA Pin = iota * 8
|
||||
portB
|
||||
portC
|
||||
portD
|
||||
portE
|
||||
portF
|
||||
portG
|
||||
portH
|
||||
portJ
|
||||
portK
|
||||
portL
|
||||
)
|
||||
|
||||
const (
|
||||
PA0 = portA + 0
|
||||
PA1 = portA + 1
|
||||
PA2 = portA + 2
|
||||
PA3 = portA + 3
|
||||
PA4 = portA + 4
|
||||
PA5 = portA + 5
|
||||
PA6 = portA + 6
|
||||
PA7 = portA + 7
|
||||
PB0 = portB + 0
|
||||
PB1 = portB + 1
|
||||
PB2 = portB + 2
|
||||
PB3 = portB + 3
|
||||
PB4 = portB + 4
|
||||
PB5 = portB + 5
|
||||
PB6 = portB + 6
|
||||
PB7 = portB + 7
|
||||
PC0 = portC + 0
|
||||
PC1 = portC + 1
|
||||
PC2 = portC + 2
|
||||
PC3 = portC + 3
|
||||
PC4 = portC + 4
|
||||
PC5 = portC + 5
|
||||
PC6 = portC + 6
|
||||
PC7 = portC + 7
|
||||
PD0 = portD + 0
|
||||
PD1 = portD + 1
|
||||
PD2 = portD + 2
|
||||
PD3 = portD + 3
|
||||
PD7 = portD + 7
|
||||
PE0 = portE + 0
|
||||
PE1 = portE + 1
|
||||
PE3 = portE + 3
|
||||
PE4 = portE + 4
|
||||
PE5 = portE + 5
|
||||
PE6 = portE + 6
|
||||
PF0 = portF + 0
|
||||
PF1 = portF + 1
|
||||
PF2 = portF + 2
|
||||
PF3 = portF + 3
|
||||
PF4 = portF + 4
|
||||
PF5 = portF + 5
|
||||
PF6 = portF + 6
|
||||
PF7 = portF + 7
|
||||
PG0 = portG + 0
|
||||
PG1 = portG + 1
|
||||
PG2 = portG + 2
|
||||
PG5 = portG + 5
|
||||
PH0 = portH + 0
|
||||
PH1 = portH + 1
|
||||
PH3 = portH + 3
|
||||
PH4 = portH + 4
|
||||
PH5 = portH + 5
|
||||
PH6 = portH + 6
|
||||
PJ0 = portJ + 0
|
||||
PJ1 = portJ + 1
|
||||
PK0 = portK + 0
|
||||
PK1 = portK + 1
|
||||
PK2 = portK + 2
|
||||
PK3 = portK + 3
|
||||
PK4 = portH + 4
|
||||
PK5 = portH + 5
|
||||
PK6 = portH + 6
|
||||
PK7 = portH + 7
|
||||
PL0 = portL + 0
|
||||
PL1 = portL + 1
|
||||
PL2 = portL + 2
|
||||
PL3 = portL + 3
|
||||
PL4 = portL + 4
|
||||
PL5 = portL + 5
|
||||
PL6 = portL + 6
|
||||
PL7 = portL + 7
|
||||
)
|
||||
|
||||
// getPortMask returns the PORTx register and mask for the pin.
|
||||
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||
switch {
|
||||
case p >= PA0 && p <= PA7:
|
||||
return avr.PORTA, 1 << uint8(p-portA)
|
||||
case p >= PB0 && p <= PB7:
|
||||
return avr.PORTB, 1 << uint8(p-portB)
|
||||
case p >= PC0 && p <= PC7:
|
||||
return avr.PORTC, 1 << uint8(p-portC)
|
||||
case p >= PD0 && p <= PD7:
|
||||
return avr.PORTD, 1 << uint8(p-portD)
|
||||
case p >= PE0 && p <= PE6:
|
||||
return avr.PORTE, 1 << uint8(p-portE)
|
||||
case p >= PF0 && p <= PF7:
|
||||
return avr.PORTF, 1 << uint8(p-portF)
|
||||
case p >= PG0 && p <= PG5:
|
||||
return avr.PORTG, 1 << uint8(p-portG)
|
||||
case p >= PH0 && p <= PH6:
|
||||
return avr.PORTH, 1 << uint8(p-portH)
|
||||
case p >= PJ0 && p <= PJ1:
|
||||
return avr.PORTJ, 1 << uint8(p-portJ)
|
||||
case p >= PK0 && p <= PK7:
|
||||
return avr.PORTK, 1 << uint8(p-portK)
|
||||
case p >= PL0 && p <= PL7:
|
||||
return avr.PORTL, 1 << uint8(p-portL)
|
||||
default:
|
||||
return avr.PORTA, 255
|
||||
}
|
||||
}
|
||||
|
||||
// PWM is one PWM peripheral, which consists of a counter and two output
|
||||
// channels (that can be connected to two fixed pins). You can set the frequency
|
||||
// using SetPeriod, but only for all the channels in this PWM peripheral at
|
||||
// once.
|
||||
type PWM struct {
|
||||
num uint8
|
||||
}
|
||||
|
||||
var (
|
||||
Timer0 = PWM{0} // 8 bit timer for PB7 and PG5
|
||||
Timer1 = PWM{1} // 16 bit timer for PB5 and PB6
|
||||
Timer2 = PWM{2} // 8 bit timer for PB4 and PH6
|
||||
Timer3 = PWM{3} // 16 bit timer for PE3, PE4 and PE5
|
||||
Timer4 = PWM{4} // 16 bit timer for PH3, PH4 and PH5
|
||||
Timer5 = PWM{5} // 16 bit timer for PL3, PL4 and PL5
|
||||
)
|
||||
|
||||
// Configure enables and configures this PWM.
|
||||
//
|
||||
// For the two 8 bit timers, there is only a limited number of periods
|
||||
// available, namely the CPU frequency divided by 256 and again divided by 1, 8,
|
||||
// 64, 256, or 1024. For a MCU running at 16MHz, this would be a period of 16µs,
|
||||
// 128µs, 1024µs, 4096µs, or 16384µs.
|
||||
func (pwm PWM) Configure(config PWMConfig) error {
|
||||
|
||||
switch pwm.num {
|
||||
case 0, 2: // 8-bit timers (Timer/counter 0 and Timer/counter 2)
|
||||
// Calculate the timer prescaler.
|
||||
// While we could configure a flexible top, that would sacrifice one of
|
||||
// the PWM output compare registers and thus a PWM channel. I've chosen
|
||||
// to instead limit this timer to a fixed number of frequencies.
|
||||
var prescaler uint8
|
||||
switch config.Period {
|
||||
case 0, (uint64(1e9) * 256 * 1) / uint64(CPUFrequency()):
|
||||
prescaler = 1
|
||||
case (uint64(1e9) * 256 * 8) / uint64(CPUFrequency()):
|
||||
prescaler = 2
|
||||
case (uint64(1e9) * 256 * 64) / uint64(CPUFrequency()):
|
||||
prescaler = 3
|
||||
case (uint64(1e9) * 256 * 256) / uint64(CPUFrequency()):
|
||||
prescaler = 4
|
||||
case (uint64(1e9) * 256 * 1024) / uint64(CPUFrequency()):
|
||||
prescaler = 5
|
||||
default:
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
if pwm.num == 0 {
|
||||
avr.TCCR0B.Set(prescaler)
|
||||
// Set the PWM mode to fast PWM (mode = 3).
|
||||
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
|
||||
} else {
|
||||
avr.TCCR2B.Set(prescaler)
|
||||
// Set the PWM mode to fast PWM (mode = 3).
|
||||
avr.TCCR2A.Set(avr.TCCR2A_WGM20 | avr.TCCR2A_WGM21)
|
||||
}
|
||||
case 1, 3, 4, 5:
|
||||
// The top value is the number of PWM ticks a PWM period takes. It is
|
||||
// initially picked assuming an unlimited counter top and no PWM
|
||||
// prescaler.
|
||||
var top uint64
|
||||
if config.Period == 0 {
|
||||
// Use a top appropriate for LEDs. Picking a relatively low period
|
||||
// here (0xff) for consistency with the other timers.
|
||||
top = 0xff
|
||||
} else {
|
||||
// The formula below calculates the following formula, optimized:
|
||||
// top = period * (CPUFrequency() / 1e9)
|
||||
// By dividing the CPU frequency first (an operation that is easily
|
||||
// optimized away) the period has less chance of overflowing.
|
||||
top = config.Period * (uint64(CPUFrequency()) / 1000000) / 1000
|
||||
}
|
||||
|
||||
// The ideal PWM period may be larger than would fit in the PWM counter,
|
||||
// which is 16 bits (see maxTop). Therefore, try to make the PWM clock
|
||||
// speed lower with a prescaler to make the top value fit the maximum
|
||||
// top value.
|
||||
|
||||
const maxTop = 0x10000
|
||||
var prescalingTop uint8
|
||||
switch {
|
||||
case top <= maxTop:
|
||||
prescalingTop = 3<<3 | 1 // no prescaling
|
||||
case top/8 <= maxTop:
|
||||
prescalingTop = 3<<3 | 2 // divide by 8
|
||||
top /= 8
|
||||
case top/64 <= maxTop:
|
||||
prescalingTop = 3<<3 | 3 // divide by 64
|
||||
top /= 64
|
||||
case top/256 <= maxTop:
|
||||
prescalingTop = 3<<3 | 4 // divide by 256
|
||||
top /= 256
|
||||
case top/1024 <= maxTop:
|
||||
prescalingTop = 3<<3 | 5 // divide by 1024
|
||||
top /= 1024
|
||||
default:
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
// A top of 0x10000 is at 100% duty cycle. Subtract one because the
|
||||
// counter counts from 0, not 1 (avoiding an off-by-one).
|
||||
top -= 1
|
||||
|
||||
switch pwm.num {
|
||||
case 1:
|
||||
avr.TCCR1A.Set(avr.TCCR1A_WGM11)
|
||||
avr.TCCR1B.Set(prescalingTop)
|
||||
avr.ICR1H.Set(uint8(top >> 8))
|
||||
avr.ICR1L.Set(uint8(top))
|
||||
case 3:
|
||||
avr.TCCR3A.Set(avr.TCCR3A_WGM31)
|
||||
avr.TCCR3B.Set(prescalingTop)
|
||||
avr.ICR3H.Set(uint8(top >> 8))
|
||||
avr.ICR3L.Set(uint8(top))
|
||||
case 4:
|
||||
avr.TCCR4A.Set(avr.TCCR4A_WGM41)
|
||||
avr.TCCR4B.Set(prescalingTop)
|
||||
avr.ICR4H.Set(uint8(top >> 8))
|
||||
avr.ICR4L.Set(uint8(top))
|
||||
case 5:
|
||||
avr.TCCR5A.Set(avr.TCCR5A_WGM51)
|
||||
avr.TCCR5B.Set(prescalingTop)
|
||||
avr.ICR5H.Set(uint8(top >> 8))
|
||||
avr.ICR5L.Set(uint8(top))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPeriod updates the period of this PWM peripheral.
|
||||
// To set a particular frequency, use the following formula:
|
||||
//
|
||||
// period = 1e9 / frequency
|
||||
//
|
||||
// If you use a period of 0, a period that works well for LEDs will be picked.
|
||||
//
|
||||
// SetPeriod will not change the prescaler, but also won't change the current
|
||||
// value in any of the channels. This means that you may need to update the
|
||||
// value for the particular channel.
|
||||
//
|
||||
// Note that you cannot pick any arbitrary period after the PWM peripheral has
|
||||
// been configured. If you want to switch between frequencies, pick the lowest
|
||||
// frequency (longest period) once when calling Configure and adjust the
|
||||
// frequency here as needed.
|
||||
func (pwm PWM) SetPeriod(period uint64) error {
|
||||
if pwm.num == 0 || pwm.num == 2 {
|
||||
return ErrPWMPeriodTooLong // TODO better error message
|
||||
}
|
||||
|
||||
// The top value is the number of PWM ticks a PWM period takes. It is
|
||||
// initially picked assuming an unlimited counter top and no PWM
|
||||
// prescaler.
|
||||
var top uint64
|
||||
if period == 0 {
|
||||
// Use a top appropriate for LEDs. Picking a relatively low period
|
||||
// here (0xff) for consistency with the other timers.
|
||||
top = 0xff
|
||||
} else {
|
||||
// The formula below calculates the following formula, optimized:
|
||||
// top = period * (CPUFrequency() / 1e9)
|
||||
// By dividing the CPU frequency first (an operation that is easily
|
||||
// optimized away) the period has less chance of overflowing.
|
||||
top = period * (uint64(CPUFrequency()) / 1000000) / 1000
|
||||
}
|
||||
|
||||
var prescaler uint8
|
||||
|
||||
switch pwm.num {
|
||||
case 1:
|
||||
prescaler = avr.TCCR1B.Get() & 0x7
|
||||
case 3:
|
||||
prescaler = avr.TCCR3B.Get() & 0x7
|
||||
case 4:
|
||||
prescaler = avr.TCCR4B.Get() & 0x7
|
||||
case 5:
|
||||
prescaler = avr.TCCR5B.Get() & 0x7
|
||||
}
|
||||
|
||||
switch prescaler {
|
||||
case 1:
|
||||
top /= 1
|
||||
case 2:
|
||||
top /= 8
|
||||
case 3:
|
||||
top /= 64
|
||||
case 4:
|
||||
top /= 256
|
||||
case 5:
|
||||
top /= 1024
|
||||
}
|
||||
|
||||
// A top of 0x10000 is at 100% duty cycle. Subtract one because the counter
|
||||
// counts from 0, not 1 (avoiding an off-by-one).
|
||||
top -= 1
|
||||
|
||||
if top > 0xffff {
|
||||
return ErrPWMPeriodTooLong
|
||||
}
|
||||
|
||||
switch pwm.num {
|
||||
case 1:
|
||||
// Warning: this change is not atomic!
|
||||
avr.ICR1H.Set(uint8(top >> 8))
|
||||
avr.ICR1L.Set(uint8(top))
|
||||
|
||||
// ... and because of that, set the counter back to zero to avoid most of
|
||||
// the effects of this non-atomicity.
|
||||
avr.TCNT1H.Set(0)
|
||||
avr.TCNT1L.Set(0)
|
||||
case 3:
|
||||
// Warning: this change is not atomic!
|
||||
avr.ICR3H.Set(uint8(top >> 8))
|
||||
avr.ICR3L.Set(uint8(top))
|
||||
|
||||
// ... and because of that, set the counter back to zero to avoid most of
|
||||
// the effects of this non-atomicity.
|
||||
avr.TCNT3H.Set(0)
|
||||
avr.TCNT3L.Set(0)
|
||||
case 4:
|
||||
// Warning: this change is not atomic!
|
||||
avr.ICR4H.Set(uint8(top >> 8))
|
||||
avr.ICR4L.Set(uint8(top))
|
||||
|
||||
// ... and because of that, set the counter back to zero to avoid most of
|
||||
// the effects of this non-atomicity.
|
||||
avr.TCNT4H.Set(0)
|
||||
avr.TCNT4L.Set(0)
|
||||
case 5:
|
||||
// Warning: this change is not atomic!
|
||||
avr.ICR5H.Set(uint8(top >> 8))
|
||||
avr.ICR5L.Set(uint8(top))
|
||||
|
||||
// ... and because of that, set the counter back to zero to avoid most of
|
||||
// the effects of this non-atomicity.
|
||||
avr.TCNT5H.Set(0)
|
||||
avr.TCNT5L.Set(0)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Top returns the current counter top, for use in duty cycle calculation. It
|
||||
// will only change with a call to Configure or SetPeriod, otherwise it is
|
||||
// constant.
|
||||
//
|
||||
// The value returned here is hardware dependent. In general, it's best to treat
|
||||
// it as an opaque value that can be divided by some number and passed to Set
|
||||
// (see Set documentation for more information).
|
||||
func (pwm PWM) Top() uint32 {
|
||||
switch pwm.num {
|
||||
case 1:
|
||||
// Timer 1 has a configurable top value.
|
||||
low := avr.ICR1L.Get()
|
||||
high := avr.ICR1H.Get()
|
||||
return uint32(high)<<8 | uint32(low) + 1
|
||||
case 3:
|
||||
// Timer 3 has a configurable top value.
|
||||
low := avr.ICR3L.Get()
|
||||
high := avr.ICR3H.Get()
|
||||
return uint32(high)<<8 | uint32(low) + 1
|
||||
case 4:
|
||||
// Timer 4 has a configurable top value.
|
||||
low := avr.ICR4L.Get()
|
||||
high := avr.ICR4H.Get()
|
||||
return uint32(high)<<8 | uint32(low) + 1
|
||||
case 5:
|
||||
// Timer 5 has a configurable top value.
|
||||
low := avr.ICR5L.Get()
|
||||
high := avr.ICR5H.Get()
|
||||
return uint32(high)<<8 | uint32(low) + 1
|
||||
}
|
||||
|
||||
// Other timers go from 0 to 0xff (0x100 or 256 in total).
|
||||
return 256
|
||||
}
|
||||
|
||||
// Counter returns the current counter value of the timer in this PWM
|
||||
// peripheral. It may be useful for debugging.
|
||||
func (pwm PWM) Counter() uint32 {
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
return uint32(avr.TCNT0.Get())
|
||||
case 1:
|
||||
mask := interrupt.Disable()
|
||||
low := avr.TCNT1L.Get()
|
||||
high := avr.TCNT1H.Get()
|
||||
interrupt.Restore(mask)
|
||||
return uint32(high)<<8 | uint32(low)
|
||||
case 2:
|
||||
return uint32(avr.TCNT2.Get())
|
||||
case 3:
|
||||
mask := interrupt.Disable()
|
||||
low := avr.TCNT3L.Get()
|
||||
high := avr.TCNT3H.Get()
|
||||
interrupt.Restore(mask)
|
||||
return uint32(high)<<8 | uint32(low)
|
||||
case 4:
|
||||
mask := interrupt.Disable()
|
||||
low := avr.TCNT4L.Get()
|
||||
high := avr.TCNT4H.Get()
|
||||
interrupt.Restore(mask)
|
||||
return uint32(high)<<8 | uint32(low)
|
||||
case 5:
|
||||
mask := interrupt.Disable()
|
||||
low := avr.TCNT5L.Get()
|
||||
high := avr.TCNT5H.Get()
|
||||
interrupt.Restore(mask)
|
||||
return uint32(high)<<8 | uint32(low)
|
||||
}
|
||||
|
||||
// Unknown PWM.
|
||||
return 0
|
||||
}
|
||||
|
||||
// Period returns the used PWM period in nanoseconds. It might deviate slightly
|
||||
// from the configured period due to rounding.
|
||||
func (pwm PWM) Period() uint64 {
|
||||
var prescaler uint8
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
prescaler = avr.TCCR0B.Get() & 0x7
|
||||
case 1:
|
||||
prescaler = avr.TCCR1B.Get() & 0x7
|
||||
case 2:
|
||||
prescaler = avr.TCCR2B.Get() & 0x7
|
||||
case 3:
|
||||
prescaler = avr.TCCR3B.Get() & 0x7
|
||||
case 4:
|
||||
prescaler = avr.TCCR4B.Get() & 0x7
|
||||
case 5:
|
||||
prescaler = avr.TCCR5B.Get() & 0x7
|
||||
}
|
||||
top := uint64(pwm.Top())
|
||||
switch prescaler {
|
||||
case 1: // prescaler 1
|
||||
return 1 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 2: // prescaler 8
|
||||
return 8 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 3: // prescaler 64
|
||||
return 64 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 4: // prescaler 256
|
||||
return 256 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
case 5: // prescaler 1024
|
||||
return 1024 * top * 1000 / uint64(CPUFrequency()/1e6)
|
||||
default: // unknown clock source
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Channel returns a PWM channel for the given pin.
|
||||
func (pwm PWM) Channel(pin Pin) (uint8, error) {
|
||||
pin.Configure(PinConfig{Mode: PinOutput})
|
||||
pin.Low()
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
switch pin {
|
||||
case PB7: // channel A
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
return 0, nil
|
||||
case PG5: // channel B
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
return 1, nil
|
||||
}
|
||||
case 1:
|
||||
switch pin {
|
||||
case PB5: // channel A
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
return 0, nil
|
||||
case PB6: // channel B
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
return 1, nil
|
||||
}
|
||||
case 2:
|
||||
switch pin {
|
||||
case PB4: // channel A
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
return 0, nil
|
||||
case PH6: // channel B
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
return 1, nil
|
||||
}
|
||||
case 3:
|
||||
switch pin {
|
||||
case PE3: // channel A
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1)
|
||||
return 0, nil
|
||||
case PE4: //channel B
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1)
|
||||
return 1, nil
|
||||
case PE5: //channel C
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1)
|
||||
return 2, nil
|
||||
}
|
||||
case 4:
|
||||
switch pin {
|
||||
case PH3: // channel A
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1)
|
||||
return 0, nil
|
||||
case PH4: //channel B
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1)
|
||||
return 1, nil
|
||||
case PH5: //channel C
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1)
|
||||
return 2, nil
|
||||
}
|
||||
case 5:
|
||||
switch pin {
|
||||
case PL3: // channel A
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1)
|
||||
return 0, nil
|
||||
case PL4: //channel B
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1)
|
||||
return 1, nil
|
||||
case PL5: //channel C
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1)
|
||||
return 2, nil
|
||||
}
|
||||
}
|
||||
return 0, ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
// SetInverting sets whether to invert the output of this channel.
|
||||
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
||||
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
||||
// was placed at the output, meaning that the output would be 25% low and 75%
|
||||
// high with a duty cycle of 25%.
|
||||
//
|
||||
// Note: the invert state may not be applied on the AVR until the next call to
|
||||
// ch.Set().
|
||||
func (pwm PWM) SetInverting(channel uint8, inverting bool) {
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
switch channel {
|
||||
case 0: // channel A, PB7
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 7) // PB7 high
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A0)
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 7) // PB7 low
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A0)
|
||||
}
|
||||
case 1: // channel B, PG5
|
||||
if inverting {
|
||||
avr.PORTG.SetBits(1 << 5) // PG5 high
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B0)
|
||||
} else {
|
||||
avr.PORTG.ClearBits(1 << 5) // PG5 low
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B0)
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
// Note: the COM1A0/COM1B0 bit is not set with the configuration below.
|
||||
// It will be set the following call to Set(), however.
|
||||
switch channel {
|
||||
case 0: // channel A, PB5
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 5) // PB5 high
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 5) // PB5 low
|
||||
}
|
||||
case 1: // channel B, PB6
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 6) // PB6 high
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 6) // PB6 low
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
switch channel {
|
||||
case 0: // channel A, PB4
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 4) // PB4 high
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A0)
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 4) // PB4 low
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A0)
|
||||
}
|
||||
case 1: // channel B, PH6
|
||||
if inverting {
|
||||
avr.PORTH.SetBits(1 << 6) // PH6 high
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B0)
|
||||
} else {
|
||||
avr.PORTH.ClearBits(1 << 6) // PH6 low
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B0)
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
|
||||
// It will be set the following call to Set(), however.
|
||||
switch channel {
|
||||
case 0: // channel A, PE3
|
||||
if inverting {
|
||||
avr.PORTE.SetBits(1 << 3) // PE3 high
|
||||
} else {
|
||||
avr.PORTE.ClearBits(1 << 3) // PE3 low
|
||||
}
|
||||
case 1: // channel B, PE4
|
||||
if inverting {
|
||||
avr.PORTE.SetBits(1 << 4) // PE4 high
|
||||
} else {
|
||||
avr.PORTE.ClearBits(1 << 4) // PE4 low
|
||||
}
|
||||
case 2: // channel C, PE5
|
||||
if inverting {
|
||||
avr.PORTE.SetBits(1 << 5) // PE4 high
|
||||
} else {
|
||||
avr.PORTE.ClearBits(1 << 5) // PE4 low
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
|
||||
// It will be set the following call to Set(), however.
|
||||
switch channel {
|
||||
case 0: // channel A, PH3
|
||||
if inverting {
|
||||
avr.PORTH.SetBits(1 << 3) // PH3 high
|
||||
} else {
|
||||
avr.PORTH.ClearBits(1 << 3) // PH3 low
|
||||
}
|
||||
case 1: // channel B, PH4
|
||||
if inverting {
|
||||
avr.PORTH.SetBits(1 << 4) // PH4 high
|
||||
} else {
|
||||
avr.PORTH.ClearBits(1 << 4) // PH4 low
|
||||
}
|
||||
case 2: // channel C, PH5
|
||||
if inverting {
|
||||
avr.PORTH.SetBits(1 << 5) // PH4 high
|
||||
} else {
|
||||
avr.PORTH.ClearBits(1 << 5) // PH4 low
|
||||
}
|
||||
}
|
||||
case 5:
|
||||
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
|
||||
// It will be set the following call to Set(), however.
|
||||
switch channel {
|
||||
case 0: // channel A, PL3
|
||||
if inverting {
|
||||
avr.PORTL.SetBits(1 << 3) // PL3 high
|
||||
} else {
|
||||
avr.PORTL.ClearBits(1 << 3) // PL3 low
|
||||
}
|
||||
case 1: // channel B, PL4
|
||||
if inverting {
|
||||
avr.PORTL.SetBits(1 << 4) // PL4 high
|
||||
} else {
|
||||
avr.PORTL.ClearBits(1 << 4) // PL4 low
|
||||
}
|
||||
case 2: // channel C, PH5
|
||||
if inverting {
|
||||
avr.PORTL.SetBits(1 << 5) // PL4 high
|
||||
} else {
|
||||
avr.PORTL.ClearBits(1 << 5) // PL4 low
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set updates the channel value. This is used to control the channel duty
|
||||
// cycle, in other words the fraction of time the channel output is high (or low
|
||||
// when inverted). For example, to set it to a 25% duty cycle, use:
|
||||
//
|
||||
// pwm.Set(channel, pwm.Top() / 4)
|
||||
//
|
||||
// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
|
||||
// pwm.Top()) will set the output to high, assuming the output isn't inverted.
|
||||
func (pwm PWM) Set(channel uint8, value uint32) {
|
||||
switch pwm.num {
|
||||
case 0:
|
||||
value := uint16(value)
|
||||
switch channel {
|
||||
case 0: // channel A
|
||||
if value == 0 {
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A1)
|
||||
} else {
|
||||
avr.OCR0A.Set(uint8(value - 1))
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
}
|
||||
case 1: // channel B
|
||||
if value == 0 {
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B1)
|
||||
} else {
|
||||
avr.OCR0B.Set(uint8(value) - 1)
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PB5
|
||||
if value == 0 {
|
||||
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR1AH.Set(uint8(value >> 8))
|
||||
avr.OCR1AL.Set(uint8(value))
|
||||
if avr.PORTB.HasBits(1 << 5) { // is PB1 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
}
|
||||
}
|
||||
case 1: // channel B, PB6
|
||||
if value == 0 {
|
||||
avr.TCCR1A.ClearBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR1BH.Set(uint8(value >> 8))
|
||||
avr.OCR1BL.Set(uint8(value))
|
||||
if avr.PORTB.HasBits(1 << 6) { // is PB6 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
case 2:
|
||||
value := uint16(value)
|
||||
switch channel {
|
||||
case 0: // channel A
|
||||
if value == 0 {
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A1)
|
||||
} else {
|
||||
avr.OCR2A.Set(uint8(value - 1))
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
}
|
||||
case 1: // channel B
|
||||
if value == 0 {
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B1)
|
||||
} else {
|
||||
avr.OCR2B.Set(uint8(value - 1))
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PE3
|
||||
if value == 0 {
|
||||
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3A1 | avr.TCCR3A_COM3A0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR3AH.Set(uint8(value >> 8))
|
||||
avr.OCR3AL.Set(uint8(value))
|
||||
if avr.PORTE.HasBits(1 << 3) { // is PE3 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1 | avr.TCCR3A_COM3A0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1)
|
||||
}
|
||||
}
|
||||
case 1: // channel B, PE4
|
||||
if value == 0 {
|
||||
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3B1 | avr.TCCR3A_COM3B0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR3BH.Set(uint8(value >> 8))
|
||||
avr.OCR3BL.Set(uint8(value))
|
||||
if avr.PORTE.HasBits(1 << 4) { // is PE4 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1 | avr.TCCR3A_COM3B0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1)
|
||||
}
|
||||
}
|
||||
case 2: // channel C, PE5
|
||||
if value == 0 {
|
||||
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3C1 | avr.TCCR3A_COM3C0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR3CH.Set(uint8(value >> 8))
|
||||
avr.OCR3CL.Set(uint8(value))
|
||||
if avr.PORTE.HasBits(1 << 5) { // is PE5 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1 | avr.TCCR3A_COM3C0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1)
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
case 4:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PH3
|
||||
if value == 0 {
|
||||
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4A1 | avr.TCCR4A_COM4A0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR4AH.Set(uint8(value >> 8))
|
||||
avr.OCR4AL.Set(uint8(value))
|
||||
if avr.PORTH.HasBits(1 << 3) { // is PH3 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1 | avr.TCCR4A_COM4A0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1)
|
||||
}
|
||||
}
|
||||
case 1: // channel B, PH4
|
||||
if value == 0 {
|
||||
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4B1 | avr.TCCR4A_COM4B0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR4BH.Set(uint8(value >> 8))
|
||||
avr.OCR4BL.Set(uint8(value))
|
||||
if avr.PORTH.HasBits(1 << 4) { // is PH4 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1 | avr.TCCR4A_COM4B0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1)
|
||||
}
|
||||
}
|
||||
case 2: // channel C, PH5
|
||||
if value == 0 {
|
||||
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4C1 | avr.TCCR4A_COM4C0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR4CH.Set(uint8(value >> 8))
|
||||
avr.OCR4CL.Set(uint8(value))
|
||||
if avr.PORTH.HasBits(1 << 5) { // is PH5 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1 | avr.TCCR4A_COM4C0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1)
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
case 5:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PL3
|
||||
if value == 0 {
|
||||
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5A1 | avr.TCCR5A_COM5A0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR5AH.Set(uint8(value >> 8))
|
||||
avr.OCR5AL.Set(uint8(value))
|
||||
if avr.PORTL.HasBits(1 << 3) { // is PL3 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1 | avr.TCCR5A_COM5A0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1)
|
||||
}
|
||||
}
|
||||
case 1: // channel B, PL4
|
||||
if value == 0 {
|
||||
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5B1 | avr.TCCR5A_COM5B0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR5BH.Set(uint8(value >> 8))
|
||||
avr.OCR5BL.Set(uint8(value))
|
||||
if avr.PORTL.HasBits(1 << 4) { // is PL4 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1 | avr.TCCR5A_COM5B0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1)
|
||||
}
|
||||
}
|
||||
case 2: // channel C, PL5
|
||||
if value == 0 {
|
||||
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5C1 | avr.TCCR5A_COM5C0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR5CH.Set(uint8(value >> 8))
|
||||
avr.OCR5CL.Set(uint8(value))
|
||||
if avr.PORTL.HasBits(1 << 5) { // is PL5 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1 | avr.TCCR5A_COM5C0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1)
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
}
|
||||
|
||||
// SPI configuration
|
||||
var SPI0 = SPI{
|
||||
spcr: avr.SPCR,
|
||||
spdr: avr.SPDR,
|
||||
spsr: avr.SPSR,
|
||||
sck: PB1,
|
||||
sdo: PB2,
|
||||
sdi: PB3,
|
||||
cs: PB0}
|
||||
@@ -2298,7 +2298,7 @@ func handleStandardSetup(setup usbSetup) bool {
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
b := make([]byte, 7)
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
@@ -2307,14 +2307,18 @@ func cdcSetup(setup usbSetup) bool {
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, b)
|
||||
sendUSBPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
||||
b := receiveUSBControlPacket()
|
||||
b, err := receiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
@@ -2361,7 +2365,9 @@ func sendUSBPacket(ep uint32, data []byte) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos))
|
||||
}
|
||||
|
||||
func receiveUSBControlPacket() []byte {
|
||||
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
@@ -2376,7 +2382,7 @@ func receiveUSBControlPacket() []byte {
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return []byte{}
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2385,7 +2391,7 @@ func receiveUSBControlPacket() []byte {
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return []byte{}
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2393,10 +2399,13 @@ func receiveUSBControlPacket() []byte {
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
data := make([]byte, bytesread)
|
||||
copy(data, udd_ep_out_cache_buffer[0][:])
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
|
||||
return data
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpoint(ep uint32) {
|
||||
|
||||
@@ -2426,7 +2426,7 @@ func handleStandardSetup(setup usbSetup) bool {
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
b := make([]byte, 7)
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
@@ -2435,14 +2435,18 @@ func cdcSetup(setup usbSetup) bool {
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, b)
|
||||
sendUSBPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
||||
b := receiveUSBControlPacket()
|
||||
b, err := receiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
@@ -2489,7 +2493,9 @@ func sendUSBPacket(ep uint32, data []byte) {
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos))
|
||||
}
|
||||
|
||||
func receiveUSBControlPacket() []byte {
|
||||
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
@@ -2504,7 +2510,7 @@ func receiveUSBControlPacket() []byte {
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return []byte{}
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2513,7 +2519,7 @@ func receiveUSBControlPacket() []byte {
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return []byte{}
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2521,10 +2527,13 @@ func receiveUSBControlPacket() []byte {
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
data := make([]byte, bytesread)
|
||||
copy(data, udd_ep_out_cache_buffer[0][:])
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
|
||||
return data
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpoint(ep uint32) {
|
||||
|
||||
@@ -11,60 +11,49 @@ import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00040000
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// turn on timer clocks used for PWM
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
TCC1 = (*TCC)(sam.TCC1)
|
||||
TCC2 = (*TCC)(sam.TCC2)
|
||||
TCC3 = (*TCC)(sam.TCC3)
|
||||
TCC4 = (*TCC)(sam.TCC4)
|
||||
)
|
||||
|
||||
//use clock generator 0
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case PC18:
|
||||
return sam.TCC0
|
||||
case PC19:
|
||||
return sam.TCC0
|
||||
case PC20:
|
||||
return sam.TCC0
|
||||
case PC21:
|
||||
return sam.TCC0
|
||||
case PD20:
|
||||
return sam.TCC1
|
||||
case PD21:
|
||||
return sam.TCC1
|
||||
case PB18:
|
||||
return sam.TCC1
|
||||
case PB12:
|
||||
return sam.TCC3
|
||||
case PB13:
|
||||
return sam.TCC3
|
||||
case PA15:
|
||||
return sam.TCC2
|
||||
case PC17:
|
||||
return sam.TCC0
|
||||
case PC16:
|
||||
return sam.TCC0
|
||||
case PA14:
|
||||
return sam.TCC2
|
||||
case PB15:
|
||||
return sam.TCC4
|
||||
case PB14:
|
||||
return sam.TCC4
|
||||
case PB20:
|
||||
return sam.TCC1
|
||||
case PB21:
|
||||
return sam.TCC1
|
||||
default:
|
||||
return nil // not supported on this pin
|
||||
func (tcc *TCC) configureClock() {
|
||||
// Turn on timer clocks used for TCC and use generic clock generator 0.
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC1:
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC2:
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC3:
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC4:
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
}
|
||||
|
||||
func (tcc *TCC) timerNum() uint8 {
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
return 0
|
||||
case sam.TCC1:
|
||||
return 1
|
||||
case sam.TCC2:
|
||||
return 2
|
||||
case sam.TCC3:
|
||||
return 3
|
||||
case sam.TCC4:
|
||||
return 4
|
||||
default:
|
||||
return 0x0f // should not happen
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// +build sam,atsame51,atsame51j19
|
||||
|
||||
// Peripheral abstraction layer for the atsame51.
|
||||
//
|
||||
// Datasheet:
|
||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf
|
||||
//
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00030000
|
||||
|
||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
TCC1 = (*TCC)(sam.TCC1)
|
||||
TCC2 = (*TCC)(sam.TCC2)
|
||||
TCC3 = (*TCC)(sam.TCC3)
|
||||
TCC4 = (*TCC)(sam.TCC4)
|
||||
)
|
||||
|
||||
func (tcc *TCC) configureClock() {
|
||||
// Turn on timer clocks used for the TCC and use generic clock generator 0.
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC1:
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC2:
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC3:
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC4:
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
}
|
||||
|
||||
func (tcc *TCC) timerNum() uint8 {
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
return 0
|
||||
case sam.TCC1:
|
||||
return 1
|
||||
case sam.TCC2:
|
||||
return 2
|
||||
case sam.TCC3:
|
||||
return 3
|
||||
case sam.TCC4:
|
||||
return 4
|
||||
default:
|
||||
return 0x0f // should not happen
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// +build sam,atsame5x,atsame54p20
|
||||
|
||||
// Peripheral abstraction layer for the atsame54.
|
||||
//
|
||||
// Datasheet:
|
||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
|
||||
//
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00040000
|
||||
|
||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
TCC1 = (*TCC)(sam.TCC1)
|
||||
TCC2 = (*TCC)(sam.TCC2)
|
||||
TCC3 = (*TCC)(sam.TCC3)
|
||||
TCC4 = (*TCC)(sam.TCC4)
|
||||
)
|
||||
|
||||
func (tcc *TCC) configureClock() {
|
||||
// Turn on timer clocks used for TCC and use generic clock generator 0.
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC1:
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC2:
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC3:
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC3_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC3].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
case sam.TCC4:
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
}
|
||||
|
||||
func (tcc *TCC) timerNum() uint8 {
|
||||
switch tcc.timer() {
|
||||
case sam.TCC0:
|
||||
return 0
|
||||
case sam.TCC1:
|
||||
return 1
|
||||
case sam.TCC2:
|
||||
return 2
|
||||
case sam.TCC3:
|
||||
return 3
|
||||
case sam.TCC4:
|
||||
return 4
|
||||
default:
|
||||
return 0x0f // should not happen
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)&0x1f | uint16(c.G)&0x1f<<5 | uint16(c.B)&0x1f<<10)
|
||||
d.port[y][x].Set((uint16(c.R) >> 3) | ((uint16(c.G) >> 3) << 5) | ((uint16(c.B) >> 3) << 10))
|
||||
}
|
||||
|
||||
func (d FramebufDisplay) Display() error {
|
||||
|
||||
@@ -83,7 +83,8 @@ func (i2c *I2C) setPins(scl, sda Pin) {
|
||||
|
||||
// PWM
|
||||
var (
|
||||
pwmChannelPins = [4]uint32{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}
|
||||
pwms = [4]*nrf.PWM_Type{nrf.PWM0, nrf.PWM1, nrf.PWM2, nrf.PWM3}
|
||||
pwmChannelSequence [4]uint16
|
||||
PWM0 = &PWM{PWM: nrf.PWM0}
|
||||
PWM1 = &PWM{PWM: nrf.PWM1}
|
||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||
PWM3 = &PWM{PWM: nrf.PWM3}
|
||||
)
|
||||
|
||||
@@ -445,7 +445,7 @@ func handleStandardSetup(setup usbSetup) bool {
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
b := make([]byte, 7)
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
@@ -454,7 +454,7 @@ func cdcSetup(setup usbSetup) bool {
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, b)
|
||||
sendUSBPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,9 +55,11 @@ const (
|
||||
gpioPullMask = 0x3
|
||||
|
||||
// OSPEED bitfields.
|
||||
gpioOutputSpeedHigh = 2
|
||||
gpioOutputSpeedLow = 0
|
||||
gpioOutputSpeedMask = 0x3
|
||||
gpioOutputSpeedVeryHigh = 3
|
||||
gpioOutputSpeedHigh = 2
|
||||
gpioOutputSpeedMedium = 1
|
||||
gpioOutputSpeedLow = 0
|
||||
gpioOutputSpeedMask = 0x3
|
||||
)
|
||||
|
||||
// Configure this pin with the given configuration
|
||||
@@ -120,7 +122,7 @@ func (p Pin) ConfigureAltFunc(config PinConfig, altFunc uint8) {
|
||||
// SPI
|
||||
case PinModeSPICLK:
|
||||
port.MODER.ReplaceBits(gpioModeAlternate, gpioModeMask, pos)
|
||||
port.OSPEEDR.ReplaceBits(gpioOutputSpeedLow, gpioOutputSpeedMask, pos)
|
||||
port.OSPEEDR.ReplaceBits(gpioOutputSpeedHigh, gpioOutputSpeedMask, pos)
|
||||
port.PUPDR.ReplaceBits(gpioPullFloating, gpioPullMask, pos)
|
||||
p.SetAltFunc(altFunc)
|
||||
case PinModeSPISDO:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build stm32,!stm32f7x2,!stm32l5x2,!stm32l4x2
|
||||
// +build stm32,!stm32f7x2,!stm32l5x2
|
||||
|
||||
package machine
|
||||
|
||||
@@ -6,6 +6,7 @@ package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -86,7 +87,12 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
|
||||
// now set the configuration
|
||||
spi.Bus.CR1.Set(conf)
|
||||
spi.Bus.CR2.SetBits((conf & stm32.SPI_CR1_SSM_Msk) >> 16)
|
||||
|
||||
// Series-specific configuration to set 8-bit transfer mode
|
||||
spi.config8Bits()
|
||||
|
||||
// enable SPI
|
||||
spi.Bus.CR1.SetBits(stm32.SPI_CR1_SPE)
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
@@ -103,8 +109,10 @@ 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.
|
||||
spi.Bus.DR.Set(uint32(w))
|
||||
// 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)
|
||||
|
||||
// wait for SPI bus receive buffer not empty bit (RXNE) to be set.
|
||||
// warning: blocks forever until this condition is met.
|
||||
|
||||
@@ -161,6 +161,10 @@ 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
|
||||
|
||||
@@ -67,6 +67,10 @@ type SPI struct {
|
||||
AltFuncSelector uint8
|
||||
}
|
||||
|
||||
func (spi SPI) config8Bits() {
|
||||
// no-op on this series
|
||||
}
|
||||
|
||||
func (spi SPI) configurePins(config SPIConfig) {
|
||||
config.SCK.ConfigureAltFunc(PinConfig{Mode: PinModeSPICLK}, spi.AltFuncSelector)
|
||||
config.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector)
|
||||
|
||||
@@ -70,6 +70,10 @@ 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
|
||||
|
||||
@@ -184,6 +184,10 @@ 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
|
||||
|
||||
@@ -182,3 +182,67 @@ func enableAltFuncClock(bus unsafe.Pointer) {
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM1EN)
|
||||
}
|
||||
}
|
||||
|
||||
//---------- SPI related types and code
|
||||
|
||||
// SPI on the STM32Fxxx using MODER / alternate function pins
|
||||
type SPI struct {
|
||||
Bus *stm32.SPI_Type
|
||||
AltFuncSelector uint8
|
||||
}
|
||||
|
||||
func (spi SPI) config8Bits() {
|
||||
// Set rx threshold to 8-bits, so RXNE flag is set for 1 byte
|
||||
// (common STM32 SPI implementation does 8-bit transfers only)
|
||||
spi.Bus.CR2.SetBits(stm32.SPI_CR2_FRXTH)
|
||||
}
|
||||
|
||||
// Set baud rate for SPI
|
||||
func (spi SPI) getBaudRate(config SPIConfig) uint32 {
|
||||
var conf uint32
|
||||
|
||||
// Default
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = 4e6
|
||||
}
|
||||
|
||||
localFrequency := config.Frequency
|
||||
|
||||
// set frequency dependent on PCLK prescaler. Since these are rather weird
|
||||
// speeds due to the CPU freqency, pick a range up to that frquency for
|
||||
// clients to use more human-understandable numbers, e.g. nearest 100KHz
|
||||
|
||||
// These are based on 80MHz peripheral clock frquency
|
||||
switch {
|
||||
case localFrequency < 312500:
|
||||
conf = stm32.SPI_CR1_BR_Div256
|
||||
case localFrequency < 625000:
|
||||
conf = stm32.SPI_CR1_BR_Div128
|
||||
case localFrequency < 1250000:
|
||||
conf = stm32.SPI_CR1_BR_Div64
|
||||
case localFrequency < 2500000:
|
||||
conf = stm32.SPI_CR1_BR_Div32
|
||||
case localFrequency < 5000000:
|
||||
conf = stm32.SPI_CR1_BR_Div16
|
||||
case localFrequency < 10000000:
|
||||
conf = stm32.SPI_CR1_BR_Div8
|
||||
// NOTE: many SPI components won't operate reliably (or at all) above 10MHz
|
||||
// Check the datasheet of the part
|
||||
case localFrequency < 20000000:
|
||||
conf = stm32.SPI_CR1_BR_Div4
|
||||
case localFrequency < 40000000:
|
||||
conf = stm32.SPI_CR1_BR_Div2
|
||||
default:
|
||||
// None of the specific baudrates were selected; choose the lowest speed
|
||||
conf = stm32.SPI_CR1_BR_Div256
|
||||
}
|
||||
|
||||
return conf << stm32.SPI_CR1_BR_Pos
|
||||
}
|
||||
|
||||
// Configure SPI pins for input output and clock
|
||||
func (spi SPI) configurePins(config SPIConfig) {
|
||||
config.SCK.ConfigureAltFunc(PinConfig{Mode: PinModeSPICLK}, spi.AltFuncSelector)
|
||||
config.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector)
|
||||
config.SDI.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDI}, spi.AltFuncSelector)
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build !baremetal stm32,!stm32f7x2,!stm32l5x2,!stm32l4x2 fe310 k210 atmega
|
||||
// +build !baremetal stm32,!stm32f7x2,!stm32l5x2 fe310 k210 atmega
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
+75
-35
@@ -12,6 +12,8 @@ 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.
|
||||
@@ -43,8 +45,8 @@ func NewDeviceDescriptor(class, subClass, proto, packetSize0 uint8, vid, pid, ve
|
||||
}
|
||||
|
||||
// Bytes returns DeviceDescriptor data
|
||||
func (d DeviceDescriptor) Bytes() []byte {
|
||||
b := make([]byte, deviceDescriptorSize)
|
||||
func (d DeviceDescriptor) Bytes() [deviceDescriptorSize]byte {
|
||||
var b [deviceDescriptorSize]byte
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bcdUSB)
|
||||
@@ -91,8 +93,8 @@ func NewConfigDescriptor(totalLength uint16, interfaces uint8) ConfigDescriptor
|
||||
}
|
||||
|
||||
// Bytes returns ConfigDescriptor data.
|
||||
func (d ConfigDescriptor) Bytes() []byte {
|
||||
b := make([]byte, configDescriptorSize)
|
||||
func (d ConfigDescriptor) Bytes() [configDescriptorSize]byte {
|
||||
var b [configDescriptorSize]byte
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.wTotalLength)
|
||||
@@ -131,8 +133,8 @@ func NewInterfaceDescriptor(n, numEndpoints, class, subClass, protocol uint8) In
|
||||
}
|
||||
|
||||
// Bytes returns InterfaceDescriptor data.
|
||||
func (d InterfaceDescriptor) Bytes() []byte {
|
||||
b := make([]byte, interfaceDescriptorSize)
|
||||
func (d InterfaceDescriptor) Bytes() [interfaceDescriptorSize]byte {
|
||||
var b [interfaceDescriptorSize]byte
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bInterfaceNumber)
|
||||
@@ -167,8 +169,8 @@ func NewEndpointDescriptor(addr, attr uint8, packetSize uint16, interval uint8)
|
||||
}
|
||||
|
||||
// Bytes returns EndpointDescriptor data.
|
||||
func (d EndpointDescriptor) Bytes() []byte {
|
||||
b := make([]byte, endpointDescriptorSize)
|
||||
func (d EndpointDescriptor) Bytes() [endpointDescriptorSize]byte {
|
||||
var b [endpointDescriptorSize]byte
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bEndpointAddress)
|
||||
@@ -205,8 +207,8 @@ func NewIADDescriptor(firstInterface, count, class, subClass, protocol uint8) IA
|
||||
}
|
||||
|
||||
// Bytes returns IADDescriptor data.
|
||||
func (d IADDescriptor) Bytes() []byte {
|
||||
b := make([]byte, iadDescriptorSize)
|
||||
func (d IADDescriptor) Bytes() [iadDescriptorSize]byte {
|
||||
var b [iadDescriptorSize]byte
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bFirstInterface)
|
||||
@@ -235,8 +237,8 @@ func NewCDCCSInterfaceDescriptor(subtype, d0, d1 uint8) CDCCSInterfaceDescriptor
|
||||
}
|
||||
|
||||
// Bytes returns CDCCSInterfaceDescriptor data.
|
||||
func (d CDCCSInterfaceDescriptor) Bytes() []byte {
|
||||
b := make([]byte, cdcCSInterfaceDescriptorSize)
|
||||
func (d CDCCSInterfaceDescriptor) Bytes() [cdcCSInterfaceDescriptorSize]byte {
|
||||
var b [cdcCSInterfaceDescriptorSize]byte
|
||||
b[0] = byte(d.len)
|
||||
b[1] = byte(d.dtype)
|
||||
b[2] = byte(d.subtype)
|
||||
@@ -262,8 +264,8 @@ func NewCMFunctionalDescriptor(subtype, d0, d1 uint8) CMFunctionalDescriptor {
|
||||
}
|
||||
|
||||
// Bytes returns the CMFunctionalDescriptor data.
|
||||
func (d CMFunctionalDescriptor) Bytes() []byte {
|
||||
b := make([]byte, cmFunctionalDescriptorSize)
|
||||
func (d CMFunctionalDescriptor) Bytes() [cmFunctionalDescriptorSize]byte {
|
||||
var b [cmFunctionalDescriptorSize]byte
|
||||
b[0] = byte(d.bFunctionLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bDescriptorSubtype)
|
||||
@@ -288,8 +290,8 @@ func NewACMFunctionalDescriptor(subtype, d0 uint8) ACMFunctionalDescriptor {
|
||||
}
|
||||
|
||||
// Bytes returns the ACMFunctionalDescriptor data.
|
||||
func (d ACMFunctionalDescriptor) Bytes() []byte {
|
||||
b := make([]byte, acmFunctionalDescriptorSize)
|
||||
func (d ACMFunctionalDescriptor) Bytes() [acmFunctionalDescriptorSize]byte {
|
||||
var b [acmFunctionalDescriptorSize]byte
|
||||
b[0] = byte(d.len)
|
||||
b[1] = byte(d.dtype)
|
||||
b[2] = byte(d.subtype)
|
||||
@@ -351,19 +353,51 @@ const cdcSize = iadDescriptorSize +
|
||||
endpointDescriptorSize
|
||||
|
||||
// Bytes returns CDCDescriptor data.
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
// MSCDescriptor is not used yet.
|
||||
@@ -373,6 +407,8 @@ type MSCDescriptor struct {
|
||||
out EndpointDescriptor
|
||||
}
|
||||
|
||||
const cdcLineInfoSize = 7
|
||||
|
||||
type cdcLineInfo struct {
|
||||
dwDTERate uint32
|
||||
bCharFormat uint8
|
||||
@@ -633,7 +669,8 @@ func sendDescriptor(setup usbSetup) {
|
||||
if setup.wLength < deviceDescriptorSize {
|
||||
l = int(setup.wLength)
|
||||
}
|
||||
sendUSBPacket(0, dd.Bytes()[:l])
|
||||
buf := dd.Bytes()
|
||||
sendUSBPacket(0, buf[:l])
|
||||
return
|
||||
|
||||
case usb_STRING_DESCRIPTOR_TYPE:
|
||||
@@ -669,7 +706,8 @@ func sendConfiguration(setup usbSetup) {
|
||||
if setup.wLength == 9 {
|
||||
sz := uint16(configDescriptorSize + cdcSize)
|
||||
config := NewConfigDescriptor(sz, 2)
|
||||
sendUSBPacket(0, config.Bytes())
|
||||
configBuf := config.Bytes()
|
||||
sendUSBPacket(0, configBuf[:])
|
||||
} else {
|
||||
iad := NewIADDescriptor(0, 2, usb_CDC_COMMUNICATION_INTERFACE_CLASS, usb_CDC_ABSTRACT_CONTROL_MODEL, 0)
|
||||
|
||||
@@ -705,10 +743,12 @@ func sendConfiguration(setup usbSetup) {
|
||||
sz := uint16(configDescriptorSize + cdcSize)
|
||||
config := NewConfigDescriptor(sz, 2)
|
||||
|
||||
buf := make([]byte, 0, sz)
|
||||
buf = append(buf, config.Bytes()...)
|
||||
buf = append(buf, cdc.Bytes()...)
|
||||
configBuf := config.Bytes()
|
||||
cdcBuf := cdc.Bytes()
|
||||
var buf [configDescriptorSize + cdcSize]byte
|
||||
copy(buf[0:], configBuf[:])
|
||||
copy(buf[configDescriptorSize:], cdcBuf[:])
|
||||
|
||||
sendUSBPacket(0, buf)
|
||||
sendUSBPacket(0, buf[:])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,6 +322,9 @@ 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")
|
||||
|
||||
+10
-1
@@ -724,8 +724,14 @@ 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 {
|
||||
panic("unimplemented: reflect.New()")
|
||||
return Value{
|
||||
typecode: PtrTo(typ).(rawType),
|
||||
value: alloc(typ.Size()),
|
||||
flags: valueFlagExported,
|
||||
}
|
||||
}
|
||||
|
||||
type funcHeader struct {
|
||||
@@ -756,6 +762,9 @@ func (e *ValueError) Error() string {
|
||||
// llvm.memcpy.p0i8.p0i8.i32().
|
||||
func memcpy(dst, src unsafe.Pointer, size uintptr)
|
||||
|
||||
//go:linkname alloc runtime.alloc
|
||||
func alloc(size uintptr) unsafe.Pointer
|
||||
|
||||
// Copy copies the contents of src into dst until either
|
||||
// dst has been filled or src has been exhausted.
|
||||
func Copy(dst, src Value) int {
|
||||
|
||||
+3
-3
@@ -16,13 +16,13 @@ type funcValue struct {
|
||||
|
||||
// funcValueWithSignature is used before the func lowering pass.
|
||||
type funcValueWithSignature struct {
|
||||
funcPtr uintptr // ptrtoint of the actual function pointer
|
||||
signature *typecodeID // pointer to identify this signature (the value is undef)
|
||||
funcPtr uintptr // ptrtoint of the actual function pointer
|
||||
signature *uint8 // external *i8 with a name identifying the function signature
|
||||
}
|
||||
|
||||
// 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 *typecodeID) uintptr {
|
||||
func getFuncPtr(val funcValue, signature *uint8) uintptr {
|
||||
return (*funcValueWithSignature)(unsafe.Pointer(val.id)).funcPtr
|
||||
}
|
||||
|
||||
@@ -112,6 +112,11 @@ type typecodeID struct {
|
||||
length uintptr
|
||||
|
||||
methodSet *interfaceMethodInfo // nil or a GEP of an array
|
||||
|
||||
// The type that's a pointer to this type, nil if it is already a pointer.
|
||||
// Keeping the type struct alive here is important so that values from
|
||||
// reflect.New (which uses reflect.PtrTo) can be used in type asserts etc.
|
||||
ptrTo *typecodeID
|
||||
}
|
||||
|
||||
// structField is used by the compiler to pass information to the interface
|
||||
|
||||
@@ -23,7 +23,9 @@ func GOROOT() string {
|
||||
return "/usr/local/go"
|
||||
}
|
||||
|
||||
// TODO: fill with real args.
|
||||
// This is the default set of arguments, if nothing else has been set.
|
||||
// This may be overriden by modifying this global at runtime init (for example,
|
||||
// on Linux where there are real command line arguments).
|
||||
var args = []string{"/proc/self/exe"}
|
||||
|
||||
//go:linkname os_runtime_args os.runtime_args
|
||||
@@ -47,6 +49,9 @@ func memmove(dst, src unsafe.Pointer, size uintptr)
|
||||
// llvm.memset.p0i8.i32(ptr, 0, size, false).
|
||||
func memzero(ptr unsafe.Pointer, size uintptr)
|
||||
|
||||
//export strlen
|
||||
func strlen(ptr unsafe.Pointer) uintptr
|
||||
|
||||
// Compare two same-size buffers for equality.
|
||||
func memequal(x, y unsafe.Pointer, n uintptr) bool {
|
||||
for i := uintptr(0); i < n; i++ {
|
||||
@@ -89,8 +94,3 @@ func AdjustTimeOffset(offset int64) {
|
||||
func os_sigpipe() {
|
||||
runtimePanic("too many writes on closed pipe")
|
||||
}
|
||||
|
||||
//go:linkname syscall_runtime_envs syscall.runtime_envs
|
||||
func syscall_runtime_envs() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// +build sam,atsame51,atsame51j19
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
)
|
||||
|
||||
func initSERCOMClocks() {
|
||||
// Turn on clock to SERCOM0 for UART0
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// sets the "slow" clock shared by all SERCOM
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOMX_SLOW].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM1
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM2
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM3
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM4
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM5
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// +build sam,atsame5x,atsame54p20
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
)
|
||||
|
||||
func initSERCOMClocks() {
|
||||
// Turn on clock to SERCOM0 for UART0
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// sets the "slow" clock shared by all SERCOM
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOMX_SLOW].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM1
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM2
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM3
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM4
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM5
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM6
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM6_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM6_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
|
||||
// Turn on clock to SERCOM7
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM7_)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM7_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
// +build sam,atsame5x
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
type timeUnit int64
|
||||
|
||||
func postinit() {}
|
||||
|
||||
//export Reset_Handler
|
||||
func main() {
|
||||
preinit()
|
||||
run()
|
||||
abort()
|
||||
}
|
||||
|
||||
func init() {
|
||||
initClocks()
|
||||
initRTC()
|
||||
initSERCOMClocks()
|
||||
initUSBClock()
|
||||
initADCClock()
|
||||
|
||||
// connect to USB CDC interface
|
||||
machine.UART0.Configure(machine.UARTConfig{})
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
machine.UART0.WriteByte(c)
|
||||
}
|
||||
|
||||
func initClocks() {
|
||||
// set flash wait state
|
||||
sam.NVMCTRL.CTRLA.SetBits(0 << sam.NVMCTRL_CTRLA_RWS_Pos)
|
||||
|
||||
// software reset
|
||||
sam.GCLK.CTRLA.SetBits(sam.GCLK_CTRLA_SWRST)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_SWRST) {
|
||||
}
|
||||
|
||||
// Set OSCULP32K as source of Generic Clock Generator 3
|
||||
// GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_XOSC32K].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_OSCULP32K) | GCLK_GENCTRL_GENEN; //generic clock gen 3
|
||||
sam.GCLK.GENCTRL[3].Set((sam.GCLK_GENCTRL_SRC_OSCULP32K << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK_GENCTRL_GENEN)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK3) {
|
||||
}
|
||||
|
||||
// Set OSCULP32K as source of Generic Clock Generator 0
|
||||
sam.GCLK.GENCTRL[0].Set((sam.GCLK_GENCTRL_SRC_OSCULP32K << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK_GENCTRL_GENEN)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK0) {
|
||||
}
|
||||
|
||||
// Enable DFLL48M clock
|
||||
sam.OSCCTRL.DFLLCTRLA.Set(0)
|
||||
sam.OSCCTRL.DFLLMUL.Set((0x1 << sam.OSCCTRL_DFLLMUL_CSTEP_Pos) |
|
||||
(0x1 << sam.OSCCTRL_DFLLMUL_FSTEP_Pos) |
|
||||
(0x0 << sam.OSCCTRL_DFLLMUL_MUL_Pos))
|
||||
for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_DFLLMUL) {
|
||||
}
|
||||
|
||||
sam.OSCCTRL.DFLLCTRLB.Set(0)
|
||||
for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_DFLLCTRLB) {
|
||||
}
|
||||
|
||||
sam.OSCCTRL.DFLLCTRLA.SetBits(sam.OSCCTRL_DFLLCTRLA_ENABLE)
|
||||
for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_ENABLE) {
|
||||
}
|
||||
|
||||
sam.OSCCTRL.DFLLVAL.Set(sam.OSCCTRL.DFLLVAL.Get())
|
||||
for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_DFLLVAL) {
|
||||
}
|
||||
|
||||
sam.OSCCTRL.DFLLCTRLB.Set(sam.OSCCTRL_DFLLCTRLB_WAITLOCK |
|
||||
sam.OSCCTRL_DFLLCTRLB_CCDIS |
|
||||
sam.OSCCTRL_DFLLCTRLB_USBCRM)
|
||||
for !sam.OSCCTRL.STATUS.HasBits(sam.OSCCTRL_STATUS_DFLLRDY) {
|
||||
}
|
||||
|
||||
// set GCLK7 to run at 2MHz, using DFLL48M as clock source
|
||||
// GCLK7 = 48MHz / 24 = 2MHz
|
||||
sam.GCLK.GENCTRL[7].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
(24 << sam.GCLK_GENCTRL_DIV_Pos) |
|
||||
sam.GCLK_GENCTRL_GENEN)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK7) {
|
||||
}
|
||||
|
||||
// Set up the PLLs
|
||||
|
||||
// Set PLL0 to run at 120MHz, using GCLK7 as clock source
|
||||
sam.GCLK.PCHCTRL[1].Set(sam.GCLK_PCHCTRL_CHEN |
|
||||
(sam.GCLK_PCHCTRL_GEN_GCLK7 << sam.GCLK_PCHCTRL_GEN_Pos))
|
||||
|
||||
// multiplier = 59 + 1 + (0/32) = 60
|
||||
// PLL0 = 2MHz * 60 = 120MHz
|
||||
sam.OSCCTRL.DPLL[0].DPLLRATIO.Set((0x0 << sam.OSCCTRL_DPLL_DPLLRATIO_LDRFRAC_Pos) |
|
||||
(59 << sam.OSCCTRL_DPLL_DPLLRATIO_LDR_Pos))
|
||||
for sam.OSCCTRL.DPLL[0].DPLLSYNCBUSY.HasBits(sam.OSCCTRL_DPLL_DPLLSYNCBUSY_DPLLRATIO) {
|
||||
}
|
||||
|
||||
// MUST USE LBYPASS DUE TO BUG IN REV A OF SAMD51, via Adafruit lib.
|
||||
sam.OSCCTRL.DPLL[0].DPLLCTRLB.Set((sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_GCLK << sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_Pos) |
|
||||
sam.OSCCTRL_DPLL_DPLLCTRLB_LBYPASS)
|
||||
|
||||
sam.OSCCTRL.DPLL[0].DPLLCTRLA.Set(sam.OSCCTRL_DPLL_DPLLCTRLA_ENABLE)
|
||||
for !sam.OSCCTRL.DPLL[0].DPLLSTATUS.HasBits(sam.OSCCTRL_DPLL_DPLLSTATUS_CLKRDY) ||
|
||||
!sam.OSCCTRL.DPLL[0].DPLLSTATUS.HasBits(sam.OSCCTRL_DPLL_DPLLSTATUS_LOCK) {
|
||||
}
|
||||
|
||||
// Set PLL1 to run at 100MHz, using GCLK7 as clock source
|
||||
sam.GCLK.PCHCTRL[2].Set(sam.GCLK_PCHCTRL_CHEN |
|
||||
(sam.GCLK_PCHCTRL_GEN_GCLK7 << sam.GCLK_PCHCTRL_GEN_Pos))
|
||||
|
||||
// multiplier = 49 + 1 + (0/32) = 50
|
||||
// PLL1 = 2MHz * 50 = 100MHz
|
||||
sam.OSCCTRL.DPLL[1].DPLLRATIO.Set((0x0 << sam.OSCCTRL_DPLL_DPLLRATIO_LDRFRAC_Pos) |
|
||||
(49 << sam.OSCCTRL_DPLL_DPLLRATIO_LDR_Pos))
|
||||
for sam.OSCCTRL.DPLL[1].DPLLSYNCBUSY.HasBits(sam.OSCCTRL_DPLL_DPLLSYNCBUSY_DPLLRATIO) {
|
||||
}
|
||||
|
||||
// // MUST USE LBYPASS DUE TO BUG IN REV A OF SAMD51
|
||||
sam.OSCCTRL.DPLL[1].DPLLCTRLB.Set((sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_GCLK << sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_Pos) |
|
||||
sam.OSCCTRL_DPLL_DPLLCTRLB_LBYPASS)
|
||||
|
||||
sam.OSCCTRL.DPLL[1].DPLLCTRLA.Set(sam.OSCCTRL_DPLL_DPLLCTRLA_ENABLE)
|
||||
// for !sam.OSCCTRL.DPLLSTATUS1.HasBits(sam.OSCCTRL_DPLLSTATUS_CLKRDY) ||
|
||||
// !sam.OSCCTRL.DPLLSTATUS1.HasBits(sam.OSCCTRL_DPLLSTATUS_LOCK) {
|
||||
// }
|
||||
|
||||
// Set up the peripheral clocks
|
||||
// Set 48MHZ CLOCK FOR USB
|
||||
sam.GCLK.GENCTRL[1].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK_GENCTRL_IDC |
|
||||
sam.GCLK_GENCTRL_GENEN)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK1) {
|
||||
}
|
||||
|
||||
// // Set 100MHZ CLOCK FOR OTHER PERIPHERALS
|
||||
// sam.GCLK.GENCTRL2.Set((sam.GCLK_GENCTRL_SRC_DPLL1 << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
// sam.GCLK_GENCTRL_IDC |
|
||||
// sam.GCLK_GENCTRL_GENEN)
|
||||
// for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL2) {
|
||||
// }
|
||||
|
||||
// // Set 12MHZ CLOCK FOR DAC
|
||||
sam.GCLK.GENCTRL[4].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK_GENCTRL_IDC |
|
||||
(4 << sam.GCLK_GENCTRL_DIVSEL_Pos) |
|
||||
sam.GCLK_GENCTRL_GENEN)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK4) {
|
||||
}
|
||||
|
||||
// // Set up main clock
|
||||
sam.GCLK.GENCTRL[0].Set((sam.GCLK_GENCTRL_SRC_DPLL0 << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||
sam.GCLK_GENCTRL_IDC |
|
||||
sam.GCLK_GENCTRL_GENEN)
|
||||
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK0) {
|
||||
}
|
||||
|
||||
sam.MCLK.CPUDIV.Set(sam.MCLK_CPUDIV_DIV_DIV1)
|
||||
|
||||
// Use the LDO regulator by default
|
||||
sam.SUPC.VREG.ClearBits(sam.SUPC_VREG_SEL)
|
||||
|
||||
// Start up the "Debug Watchpoint and Trace" unit, so that we can use
|
||||
// it's 32bit cycle counter for timing.
|
||||
//CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
//DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
}
|
||||
|
||||
func initRTC() {
|
||||
// turn on digital interface clock
|
||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_RTC_)
|
||||
|
||||
// disable RTC
|
||||
sam.RTC_MODE0.CTRLA.ClearBits(sam.RTC_MODE0_CTRLA_ENABLE)
|
||||
//sam.RTC_MODE0.CTRLA.Set(0)
|
||||
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
// reset RTC
|
||||
sam.RTC_MODE0.CTRLA.SetBits(sam.RTC_MODE0_CTRLA_SWRST)
|
||||
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_SWRST) {
|
||||
}
|
||||
|
||||
// set to use ulp 32k oscillator
|
||||
sam.OSC32KCTRL.OSCULP32K.SetBits(sam.OSC32KCTRL_OSCULP32K_EN32K)
|
||||
sam.OSC32KCTRL.RTCCTRL.Set(sam.OSC32KCTRL_RTCCTRL_RTCSEL_ULP32K)
|
||||
|
||||
// set Mode0 to 32-bit counter (mode 0) with prescaler 1 and GCLK2 is 32KHz/1
|
||||
sam.RTC_MODE0.CTRLA.Set((sam.RTC_MODE0_CTRLA_MODE_COUNT32 << sam.RTC_MODE0_CTRLA_MODE_Pos) |
|
||||
(sam.RTC_MODE0_CTRLA_PRESCALER_DIV1 << sam.RTC_MODE0_CTRLA_PRESCALER_Pos) |
|
||||
(sam.RTC_MODE0_CTRLA_COUNTSYNC))
|
||||
|
||||
// re-enable RTC
|
||||
sam.RTC_MODE0.CTRLA.SetBits(sam.RTC_MODE0_CTRLA_ENABLE)
|
||||
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
irq := interrupt.New(sam.IRQ_RTC, func(interrupt.Interrupt) {
|
||||
// disable IRQ for CMP0 compare
|
||||
sam.RTC_MODE0.INTFLAG.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
|
||||
timerWakeup.Set(1)
|
||||
})
|
||||
irq.SetPriority(0xc0)
|
||||
irq.Enable()
|
||||
}
|
||||
|
||||
func waitForSync() {
|
||||
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_COUNT) {
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
timestamp timeUnit // ticks since boottime
|
||||
timerLastCounter uint64
|
||||
)
|
||||
|
||||
var timerWakeup volatile.Register8
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
// ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds.
|
||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||
// The following calculation is actually the following, but with both sides
|
||||
// reduced to reduce the risk of overflow:
|
||||
// ticks * 1e9 / 32768
|
||||
return int64(ticks) * 1953125 / 64
|
||||
}
|
||||
|
||||
// nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz).
|
||||
func nanosecondsToTicks(ns int64) timeUnit {
|
||||
// The following calculation is actually the following, but with both sides
|
||||
// reduced to reduce the risk of overflow:
|
||||
// ns * 32768 / 1e9
|
||||
return timeUnit(ns * 64 / 1953125)
|
||||
}
|
||||
|
||||
// sleepTicks should sleep for d number of microseconds.
|
||||
func sleepTicks(d timeUnit) {
|
||||
for d != 0 {
|
||||
ticks() // update timestamp
|
||||
ticks := uint32(d)
|
||||
if !timerSleep(ticks) {
|
||||
return
|
||||
}
|
||||
d -= timeUnit(ticks)
|
||||
}
|
||||
}
|
||||
|
||||
// ticks returns number of microseconds since start.
|
||||
func ticks() timeUnit {
|
||||
waitForSync()
|
||||
|
||||
rtcCounter := uint64(sam.RTC_MODE0.COUNT.Get())
|
||||
offset := (rtcCounter - timerLastCounter) // change since last measurement
|
||||
timerLastCounter = rtcCounter
|
||||
timestamp += timeUnit(offset)
|
||||
return timestamp
|
||||
}
|
||||
|
||||
// ticks are in microseconds
|
||||
// Returns true if the timer completed.
|
||||
// Returns false if another interrupt occured which requires an early return to scheduler.
|
||||
func timerSleep(ticks uint32) bool {
|
||||
timerWakeup.Set(0)
|
||||
if ticks < 8 {
|
||||
// due to delay waiting for the register value to sync, the minimum sleep value
|
||||
// for the SAMD51 is 260us.
|
||||
// For related info for SAMD21, see:
|
||||
// https://community.atmel.com/comment/2507091#comment-2507091
|
||||
ticks = 8
|
||||
}
|
||||
|
||||
// request read of count
|
||||
waitForSync()
|
||||
|
||||
// set compare value
|
||||
cnt := sam.RTC_MODE0.COUNT.Get()
|
||||
|
||||
sam.RTC_MODE0.COMP[0].Set(uint32(cnt) + ticks)
|
||||
|
||||
// enable IRQ for CMP0 compare
|
||||
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
|
||||
wait:
|
||||
waitForEvents()
|
||||
if timerWakeup.Get() != 0 {
|
||||
return true
|
||||
}
|
||||
if hasScheduler {
|
||||
// The interurpt may have awoken a goroutine, so bail out early.
|
||||
// Disable IRQ for CMP0 compare.
|
||||
sam.RTC_MODE0.INTENCLR.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
return false
|
||||
} else {
|
||||
// This is running without a scheduler.
|
||||
// The application expects this to sleep the whole time.
|
||||
goto wait
|
||||
}
|
||||
}
|
||||
|
||||
func initUSBClock() {
|
||||
// Turn on clock(s) for USB
|
||||
//MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB;
|
||||
//MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB;
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_USB_)
|
||||
sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_USB_)
|
||||
|
||||
// Put Generic Clock Generator 1 as source for USB
|
||||
//GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_USB].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
func initADCClock() {
|
||||
// Turn on clocks for ADC0/ADC1.
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_ADC0_)
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_ADC1_)
|
||||
|
||||
// Put Generic Clock Generator 1 as source for ADC0 and ADC1.
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_ADC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_ADC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
func waitForEvents() {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
@@ -27,7 +27,7 @@ func waitForEvents() {
|
||||
if nrf.DEVICE == "nrf51" {
|
||||
// sd_app_evt_wait: SOC_SVC_BASE_NOT_AVAILABLE + 29
|
||||
arm.SVCall0(0x2B + 29)
|
||||
} else if nrf.DEVICE == "nrf52" || nrf.DEVICE == "nrf52840" {
|
||||
} else if nrf.DEVICE == "nrf52" || nrf.DEVICE == "nrf52840" || nrf.DEVICE == "nrf52833" {
|
||||
// sd_app_evt_wait: SOC_SVC_BASE_NOT_AVAILABLE + 21
|
||||
arm.SVCall0(0x2C + 21)
|
||||
} else {
|
||||
|
||||
@@ -43,9 +43,34 @@ func postinit() {}
|
||||
|
||||
// Entry point for Go. Initialize all packages and call main.main().
|
||||
//export main
|
||||
func main() int {
|
||||
func main(argc int32, argv *unsafe.Pointer) int {
|
||||
preinit()
|
||||
|
||||
// Make args global big enough so that it can store all command line
|
||||
// arguments. Unfortunately this has to be done with some magic as the heap
|
||||
// is not yet initialized.
|
||||
argsSlice := (*struct {
|
||||
ptr unsafe.Pointer
|
||||
len uintptr
|
||||
cap uintptr
|
||||
})(unsafe.Pointer(&args))
|
||||
argsSlice.ptr = malloc(uintptr(argc) * (unsafe.Sizeof(uintptr(0))) * 3)
|
||||
argsSlice.len = 0
|
||||
argsSlice.cap = uintptr(argc)
|
||||
|
||||
// Initialize command line parameters.
|
||||
for *argv != nil {
|
||||
// Convert the C string to a Go string.
|
||||
length := strlen(*argv)
|
||||
argString := _string{
|
||||
length: length,
|
||||
ptr: (*byte)(*argv),
|
||||
}
|
||||
args = append(args, *(*string)(unsafe.Pointer(&argString)))
|
||||
// This is the Go equivalent of "argc++" in C.
|
||||
argv = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + unsafe.Sizeof(argv)))
|
||||
}
|
||||
|
||||
// Obtain the initial stack pointer right before calling the run() function.
|
||||
// The run function has been moved to a separate (non-inlined) function so
|
||||
// that the correct stack pointer is read.
|
||||
@@ -62,6 +87,37 @@ func runMain() {
|
||||
run()
|
||||
}
|
||||
|
||||
//go:extern environ
|
||||
var environ *unsafe.Pointer
|
||||
|
||||
//go:linkname syscall_runtime_envs syscall.runtime_envs
|
||||
func syscall_runtime_envs() []string {
|
||||
// Count how many environment variables there are.
|
||||
env := environ
|
||||
numEnvs := 0
|
||||
for *env != nil {
|
||||
numEnvs++
|
||||
env = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(env)) + unsafe.Sizeof(environ)))
|
||||
}
|
||||
|
||||
// Create a string slice of all environment variables.
|
||||
// This requires just a single heap allocation.
|
||||
env = environ
|
||||
envs := make([]string, 0, numEnvs)
|
||||
for *env != nil {
|
||||
ptr := *env
|
||||
length := strlen(ptr)
|
||||
s := _string{
|
||||
ptr: (*byte)(ptr),
|
||||
length: length,
|
||||
}
|
||||
envs = append(envs, *(*string)(unsafe.Pointer(&s)))
|
||||
env = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(env)) + unsafe.Sizeof(environ)))
|
||||
}
|
||||
|
||||
return envs
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
_putchar(int(c))
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@ type __wasi_iovec_t struct {
|
||||
//export fd_write
|
||||
func fd_write(id uint32, iovs *__wasi_iovec_t, iovs_len uint, nwritten *uint) (errno uint)
|
||||
|
||||
// See:
|
||||
// https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#-proc_exitrval-exitcode
|
||||
//go:wasm-module wasi_snapshot_preview1
|
||||
//export proc_exit
|
||||
func proc_exit(exitcode uint32)
|
||||
|
||||
func postinit() {}
|
||||
|
||||
const (
|
||||
@@ -49,6 +55,11 @@ func abort() {
|
||||
trap()
|
||||
}
|
||||
|
||||
//go:linkname syscall_Exit syscall.Exit
|
||||
func syscall_Exit(code int) {
|
||||
proc_exit(uint32(code))
|
||||
}
|
||||
|
||||
// TinyGo does not yet support any form of parallelism on WebAssembly, so these
|
||||
// can be left empty.
|
||||
|
||||
|
||||
@@ -15,6 +15,11 @@ func _start() {
|
||||
run()
|
||||
}
|
||||
|
||||
//go:linkname syscall_runtime_envs syscall.runtime_envs
|
||||
func syscall_runtime_envs() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
var handleEvent func()
|
||||
|
||||
//go:linkname setEventHandler syscall/js.setEventHandler
|
||||
|
||||
@@ -21,6 +21,33 @@ func _start() {
|
||||
run()
|
||||
}
|
||||
|
||||
// Read the command line arguments from WASI.
|
||||
// For example, they can be passed to a program with wasmtime like this:
|
||||
//
|
||||
// wasmtime ./program.wasm arg1 arg2
|
||||
func init() {
|
||||
// Read the number of args (argc) and the buffer size required to store all
|
||||
// these args (argv).
|
||||
var argc, argv_buf_size uint32
|
||||
args_sizes_get(&argc, &argv_buf_size)
|
||||
|
||||
// Obtain the command line arguments
|
||||
argsSlice := make([]unsafe.Pointer, argc)
|
||||
buf := make([]byte, argv_buf_size)
|
||||
args_get(&argsSlice[0], unsafe.Pointer(&buf[0]))
|
||||
|
||||
// Convert the array of C strings to an array of Go strings.
|
||||
args = make([]string, argc)
|
||||
for i, cstr := range argsSlice {
|
||||
length := strlen(cstr)
|
||||
argString := _string{
|
||||
length: length,
|
||||
ptr: (*byte)(cstr),
|
||||
}
|
||||
args[i] = *(*string)(unsafe.Pointer(&argString))
|
||||
}
|
||||
}
|
||||
|
||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||
return int64(ticks)
|
||||
}
|
||||
@@ -62,7 +89,15 @@ func ticks() timeUnit {
|
||||
return timeUnit(nano)
|
||||
}
|
||||
|
||||
// Implementations of wasi_unstable APIs
|
||||
// Implementations of WASI APIs
|
||||
|
||||
//go:wasm-module wasi_snapshot_preview1
|
||||
//export args_get
|
||||
func args_get(argv *unsafe.Pointer, argv_buf unsafe.Pointer) (errno uint16)
|
||||
|
||||
//go:wasm-module wasi_snapshot_preview1
|
||||
//export args_sizes_get
|
||||
func args_sizes_get(argc *uint32, argv_buf_size *uint32) (errno uint16)
|
||||
|
||||
//go:wasm-module wasi_snapshot_preview1
|
||||
//export clock_time_get
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"inherits": ["atmega1280"],
|
||||
"build-tags": ["arduino_mega1280"],
|
||||
"ldflags": [
|
||||
"-Wl,--defsym=_bootloader_size=4096"
|
||||
],
|
||||
"flash-command":"avrdude -c arduino -b 57600 -p atmega1280 -P {port} -U flash:w:{hex}:i -v -D"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"inherits": ["avr"],
|
||||
"cpu": "atmega1280",
|
||||
"build-tags": ["atmega1280", "atmega"],
|
||||
"cflags": [
|
||||
"-mmcu=atmega1280"
|
||||
],
|
||||
"ldflags": [
|
||||
"-mmcu=avr51",
|
||||
"-Wl,--defsym=_stack_size=512"
|
||||
],
|
||||
"linkerscript": "src/device/avr/atmega1280.ld",
|
||||
"extra-files": [
|
||||
"targets/avr.S",
|
||||
"src/device/avr/atmega1280.s"
|
||||
]
|
||||
}
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"inherits": ["cortex-m"],
|
||||
"llvm-target": "armv6m-none-eabi",
|
||||
"inherits": ["cortex-m0plus"],
|
||||
"build-tags": ["atsamd21e18a", "atsamd21e18", "atsamd21", "sam"],
|
||||
"cflags": [
|
||||
"--target=armv6m-none-eabi",
|
||||
"-Qunused-arguments"
|
||||
],
|
||||
"linkerscript": "targets/atsamd21.ld",
|
||||
"extra-files": [
|
||||
"src/device/sam/atsamd21e18a.s"
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"inherits": ["cortex-m"],
|
||||
"llvm-target": "armv6m-none-eabi",
|
||||
"inherits": ["cortex-m0plus"],
|
||||
"build-tags": ["atsamd21g18a", "atsamd21g18", "atsamd21", "sam"],
|
||||
"cflags": [
|
||||
"--target=armv6m-none-eabi",
|
||||
"-Qunused-arguments"
|
||||
],
|
||||
"linkerscript": "targets/atsamd21.ld",
|
||||
"extra-files": [
|
||||
"src/device/sam/atsamd21g18a.s"
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"inherits": ["cortex-m4"],
|
||||
"build-tags": ["atsamd51g19a", "atsamd51g19", "atsamd51", "sam"],
|
||||
"cflags": [
|
||||
"-Qunused-arguments"
|
||||
],
|
||||
"linkerscript": "targets/atsamd51.ld",
|
||||
"extra-files": [
|
||||
"src/device/sam/atsamd51g19a.s"
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"inherits": ["cortex-m4"],
|
||||
"build-tags": ["atsamd51j19a", "atsamd51j19", "atsamd51", "sam"],
|
||||
"cflags": [
|
||||
"-Qunused-arguments"
|
||||
],
|
||||
"linkerscript": "targets/atsamd51.ld",
|
||||
"extra-files": [
|
||||
"src/device/sam/atsamd51j19a.s"
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"inherits": ["cortex-m4"],
|
||||
"build-tags": ["sam", "atsamd51", "atsamd51j20", "atsamd51j20a"],
|
||||
"cflags": [
|
||||
"-Qunused-arguments"
|
||||
],
|
||||
"linkerscript": "targets/atsamd51j20a.ld",
|
||||
"extra-files": [
|
||||
"src/device/sam/atsamd51j20a.s"
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"inherits": ["cortex-m4"],
|
||||
"build-tags": ["atsamd51p19a", "atsamd51p19", "atsamd51", "sam"],
|
||||
"cflags": [
|
||||
"-Qunused-arguments"
|
||||
],
|
||||
"linkerscript": "targets/atsamd51.ld",
|
||||
"extra-files": [
|
||||
"src/device/sam/atsamd51p19a.s"
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"inherits": ["cortex-m4"],
|
||||
"build-tags": ["sam", "atsamd51", "atsamd51p20", "atsamd51p20a"],
|
||||
"cflags": [
|
||||
"-Qunused-arguments"
|
||||
],
|
||||
"linkerscript": "targets/atsamd51p20a.ld",
|
||||
"extra-files": [
|
||||
"src/device/sam/atsamd51p20a.s"
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"inherits": ["cortex-m4"],
|
||||
"build-tags": ["atsame51j19a", "atsame51j19", "atsame51", "atsame5x", "sam"],
|
||||
"linkerscript": "targets/atsame5xx19.ld",
|
||||
"extra-files": [
|
||||
"src/device/sam/atsame51j19a.s"
|
||||
],
|
||||
"openocd-transport": "swd",
|
||||
"openocd-target": "atsame5x"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"inherits": ["atsame54p20a"],
|
||||
"build-tags": ["atsame54_xpro"],
|
||||
"flash-method": "openocd",
|
||||
"openocd-interface": "cmsis-dap"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"inherits": ["cortex-m4"],
|
||||
"build-tags": ["sam", "atsame5x", "atsame54p20", "atsame54p20a"],
|
||||
"linkerscript": "targets/atsame5xx20-no-bootloader.ld",
|
||||
"extra-files": [
|
||||
"src/device/sam/atsame54p20a.s"
|
||||
],
|
||||
"openocd-transport": "swd",
|
||||
"openocd-target": "atsame5x"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
MEMORY
|
||||
{
|
||||
FLASH_TEXT (rw) : ORIGIN = 0x00000000+0x4000, LENGTH = 0x00080000-0x4000 /* First 16KB used by bootloader */
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x00030000
|
||||
}
|
||||
|
||||
_stack_size = 4K;
|
||||
|
||||
INCLUDE "targets/arm.ld"
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
MEMORY
|
||||
{
|
||||
FLASH_TEXT (rw) : ORIGIN = 0x00000000+0x0000, LENGTH = 0x00100000-0x0000
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x00040000
|
||||
}
|
||||
|
||||
_stack_size = 4K;
|
||||
|
||||
INCLUDE "targets/arm.ld"
|
||||
@@ -4,8 +4,7 @@
|
||||
"build-tags": ["attiny85", "attiny", "avr2", "avr25"],
|
||||
"cflags": [
|
||||
"-mmcu=attiny85",
|
||||
"-D__AVR_ARCH__=25",
|
||||
"-Qunused-arguments"
|
||||
"-D__AVR_ARCH__=25"
|
||||
],
|
||||
"ldflags": [
|
||||
"-mmcu=avr25"
|
||||
|
||||
+2
-2
@@ -3,13 +3,13 @@
|
||||
"build-tags": ["avr", "baremetal", "linux", "arm"],
|
||||
"goos": "linux",
|
||||
"goarch": "arm",
|
||||
"compiler": "clang",
|
||||
"gc": "conservative",
|
||||
"linker": "avr-gcc",
|
||||
"scheduler": "none",
|
||||
"default-stack-size": 256,
|
||||
"cflags": [
|
||||
"--target=avr-unknown-unknown"
|
||||
"--target=avr-unknown-unknown",
|
||||
"-Werror"
|
||||
],
|
||||
"ldflags": [
|
||||
"-T", "targets/avr.ld",
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"inherits": ["cortex-m"],
|
||||
"llvm-target": "armv7m-none-eabi",
|
||||
"inherits": ["cortex-m3"],
|
||||
"build-tags": ["bluepill", "stm32f103", "stm32f1", "stm32"],
|
||||
"cflags": [
|
||||
"--target=armv7m-none-eabi",
|
||||
"-Qunused-arguments"
|
||||
],
|
||||
"linkerscript": "targets/stm32.ld",
|
||||
"extra-files": [
|
||||
"src/device/stm32/stm32f103.s"
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"inherits": ["cortex-m"],
|
||||
"llvm-target": "armv7m-none-eabi",
|
||||
"inherits": ["cortex-m3"],
|
||||
"build-tags": ["qemu", "lm3s6965"],
|
||||
"cflags": [
|
||||
"--target=armv7m-none-eabi",
|
||||
"-Qunused-arguments"
|
||||
],
|
||||
"linkerscript": "targets/lm3s6965.ld",
|
||||
"extra-files": [
|
||||
"targets/cortex-m-qemu.s"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"build-tags": ["cortexm", "baremetal", "linux", "arm"],
|
||||
"goos": "linux",
|
||||
"goarch": "arm",
|
||||
"compiler": "clang",
|
||||
"gc": "conservative",
|
||||
"scheduler": "tasks",
|
||||
"linker": "ld.lld",
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"inherits": ["cortex-m"],
|
||||
"llvm-target": "armv6m-none-eabi",
|
||||
"cflags": [
|
||||
"--target=armv6m-none-eabi",
|
||||
"-Qunused-arguments"
|
||||
"--target=armv6m-none-eabi"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"inherits": ["cortex-m"],
|
||||
"llvm-target": "armv6m-none-eabi",
|
||||
"cflags": [
|
||||
"--target=armv6m-none-eabi"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"inherits": ["cortex-m"],
|
||||
"llvm-target": "armv7m-none-eabi",
|
||||
"cflags": [
|
||||
"--target=armv7m-none-eabi"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
"llvm-target": "armv7m-none-eabi",
|
||||
"cflags": [
|
||||
"--target=armv7m-none-eabi",
|
||||
"-mfloat-abi=soft",
|
||||
"-Qunused-arguments"
|
||||
"-mfloat-abi=soft"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"llvm-target": "armv7em-none-eabi",
|
||||
"cflags": [
|
||||
"--target=armv7em-none-eabi",
|
||||
"-mfloat-abi=soft",
|
||||
"-Qunused-arguments"
|
||||
"-mfloat-abi=soft"
|
||||
]
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user