mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-27 07:08:42 +00:00
Compare commits
30 Commits
bitcode-path
...
v0.18.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 1646326164 | |||
| e44cb7e519 | |||
| d1e96fd0a8 | |||
| ff5d0c9886 | |||
| d7ec9ddcd2 | |||
| e7c6bd3730 | |||
| 9f5066aa6f | |||
| 3b24fedf92 | |||
| 8cd2a462b9 | |||
| 25b045d0a7 | |||
| 78acbdf0d9 | |||
| 617e2791ef | |||
| a0908ff55b | |||
| 2f1f8fb075 | |||
| 6f61b83ad5 | |||
| 8dfefb46d1 | |||
| 959442dc82 | |||
| c1aa152a63 | |||
| dd3d8a363a | |||
| bb509ec91d | |||
| 944f022060 | |||
| cd517a30af | |||
| 52d8655eec | |||
| f5786941e5 | |||
| 1f73941c43 | |||
| abeab51d00 | |||
| 9ef75f17bf | |||
| c3992bd77b | |||
| f79e66ac2e | |||
| 4eac212695 |
+10
-10
@@ -294,16 +294,16 @@ commands:
|
||||
variant: "macos"
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-macos-v2-{{ checksum "go.mod" }}
|
||||
- go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-macos-v3-{{ checksum "go.mod" }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-source-11-macos-v2
|
||||
- llvm-source-11-macos-v3
|
||||
- run:
|
||||
name: "Fetch LLVM source"
|
||||
command: make llvm-source
|
||||
- save_cache:
|
||||
key: llvm-source-11-macos-v2
|
||||
key: llvm-source-11-macos-v3
|
||||
paths:
|
||||
- llvm-project/clang/lib/Headers
|
||||
- llvm-project/clang/include
|
||||
@@ -311,7 +311,7 @@ commands:
|
||||
- llvm-project/llvm/include
|
||||
- restore_cache:
|
||||
keys:
|
||||
- llvm-build-11-macos-v3
|
||||
- llvm-build-11-macos-v4
|
||||
- run:
|
||||
name: "Build LLVM"
|
||||
command: |
|
||||
@@ -327,17 +327,17 @@ commands:
|
||||
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
|
||||
fi
|
||||
- save_cache:
|
||||
key: llvm-build-11-macos-v3
|
||||
key: llvm-build-11-macos-v4
|
||||
paths:
|
||||
llvm-build
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-macos-v3
|
||||
- wasi-libc-sysroot-macos-v4
|
||||
- run:
|
||||
name: "Build wasi-libc"
|
||||
command: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-macos-v3
|
||||
key: wasi-libc-sysroot-macos-v4
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- run:
|
||||
@@ -359,7 +359,7 @@ commands:
|
||||
tinygo version
|
||||
- run: make smoketest AVR=0
|
||||
- save_cache:
|
||||
key: go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
key: go-cache-macos-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
@@ -401,7 +401,7 @@ jobs:
|
||||
- build-linux
|
||||
build-macos:
|
||||
macos:
|
||||
xcode: "10.1.0"
|
||||
xcode: "11.1.0" # macOS 10.14
|
||||
steps:
|
||||
- build-macos
|
||||
|
||||
|
||||
+122
@@ -1,3 +1,125 @@
|
||||
0.18.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- drop support for Go 1.11 and 1.12
|
||||
- throw an error when no target is specified on Windows
|
||||
- improve error messages in `getDefaultPort()`, support for multiple ports
|
||||
- remove `-cflags` and `-ldflags` flags
|
||||
- implement `-ldflags="-X ..."`
|
||||
- add `-print-allocs` flag that lets you print all heap allocations
|
||||
- openocd commands in tinygo command line
|
||||
- add `-llvm-features` parameter
|
||||
- match `go test` output
|
||||
- discover USB ports only, this will ignore f.ex. bluetooth
|
||||
- use physicmal path instead of cached GOROOT in function getGoroot
|
||||
- add goroot for snap installs
|
||||
* **compiler**
|
||||
- `builder`: add support for `-opt=0`
|
||||
- `builder`, `compiler`: compile and cache packages in parallel
|
||||
- `builder`: run interp per package
|
||||
- `builder`: cache C and assembly file outputs
|
||||
- `builder`: add support for `-x` flag to print commands
|
||||
- `builder`: add optsize attribute while building the package
|
||||
- `builder`: run function passes per package
|
||||
- `builder`: hard code Clang compiler
|
||||
- `compiler`: do not use `llvm.GlobalContext()`
|
||||
- `compiler`: remove SimpleDCE pass
|
||||
- `compiler`: do not emit nil checks for `*ssa.Alloc` instructions
|
||||
- `compiler`: merge `runtime.typecodeID` and runtime.typeInInterface
|
||||
- `compiler`: do not check for impossible type asserts
|
||||
- `compiler`: fix use of global context: `llvm.Int32Type()`
|
||||
- `compiler`: add interface IR test
|
||||
- `compiler`: fix lack of method name in interface matching
|
||||
- `compiler`: fix "fragment covers entire variable" bug
|
||||
- `compiler`: optimize string literals and globals
|
||||
- `compiler`: decouple func lowering from interface type codes
|
||||
- `compiler`: add function attributes to some runtime calls
|
||||
- `compiler`: improve position information in error messages
|
||||
- `cgo`: add support for CFLAGS in .c files
|
||||
- `interp`: support GEP on fixed (MMIO) addresses
|
||||
- `interp`: handle `(reflect.Type).Elem()`
|
||||
- `interp`: add support for runtime.interfaceMethod
|
||||
- `interp`: make toLLVMValue return an error instead of panicking
|
||||
- `interp`: add support for switch statement
|
||||
- `interp`: fix phi instruction
|
||||
- `interp`: remove map support
|
||||
- `interp`: support extractvalue/insertvalue with multiple operands
|
||||
- `transform`: optimize string comparisons against ""
|
||||
- `transform`: optimize `reflect.Type` `Implements()` method
|
||||
- `transform`: fix bug in interface lowering when signatures are renamed
|
||||
- `transform`: don't rely on struct name of `runtime.typecodeID`
|
||||
- `transform`: use IPSCCP pass instead of the constant propagation pass
|
||||
- `transform`: fix func lowering assertion failure
|
||||
- `transform`: do not lower zero-sized alloc to alloca
|
||||
- `transform`: split interface and reflect lowering
|
||||
* **standard library**
|
||||
- `runtime`: add dummy debug package
|
||||
- `machine`: fix data shift/mask in newUSBSetup
|
||||
- `machine`: make `machine.I2C0` and similar objects pointers
|
||||
- `machine`: unify usbcdc code
|
||||
- `machine`: refactor PWM support
|
||||
- `machine`: avoid heap allocations in USB code
|
||||
- `reflect`: let `reflect.Type` be of interface type
|
||||
- `reflect`: implement a number of stub functions
|
||||
- `reflect`: check for access in the `Interface` method call
|
||||
- `reflect`: fix `AssignableTo` and `Implements` methods
|
||||
- `reflect`: implement `Value.CanAddr`
|
||||
- `reflect`: implement `Sizeof` and `Alignof` for func values
|
||||
- `reflect`: implement `New` function
|
||||
- `runtime`: implement command line arguments in hosted environments
|
||||
- `runtime`: implement environment variables for Linux
|
||||
- `runtime`: improve timers on nrf, and samd chips
|
||||
* **targets**
|
||||
- all: use -Qunused-arguments only for assembly files
|
||||
- `atmega1280`: add PWM support
|
||||
- `attiny`: remove dummy UART
|
||||
- `atsamd21`: improve SPI
|
||||
- `atsamd51`: fix PWM support in atsamd51p20
|
||||
- `atsamd5x`: improve SPI
|
||||
- `atsamd51`, `atsame5x`: unify samd51 and same5x
|
||||
- `atsamd51`, `atsamd21`: fix `ADC.Get()` value at 8bit and 10bit
|
||||
- `atsame5x`: add support for CAN
|
||||
- `avr`: remove I2C stubs from attiny support
|
||||
- `cortexm`: check for `arm-none-eabi-gdb` and `gdb-multiarch` commands
|
||||
- `cortexm`: add `__isr_vector` symbol
|
||||
- `cortexm`: disable FPU on Cortex-M4
|
||||
- `cortexm`: clean up Cortex-M target files
|
||||
- `fe310`: fix SPI read
|
||||
- `gameboy-advance`: Fix RGBA color interpretation
|
||||
- `nrf52833`: add PWM support
|
||||
- `stm32l0`: use unified UART logic
|
||||
- `stm32`: move f103 (bluepill) to common i2c code
|
||||
- `stm32`: separate altfunc selection for UART Tx/Rx
|
||||
- `stm32`: i2c implementation for F7, L5 and L4 MCUs
|
||||
- `stm32`: make SPI CLK fast to fix data issue
|
||||
- `stm32`: support SPI on L4 series
|
||||
- `unix`: avoid possible heap allocation with `-opt=0`
|
||||
- `unix`: use conservative GC by default
|
||||
- `unix`: use the tasks scheduler instead of coroutines
|
||||
- `wasi`: upgrade WASI version to wasi_snapshot_preview1
|
||||
- `wasi`: darwin: support basic file io based on libc
|
||||
- `wasm`: only export explicitly exported functions
|
||||
- `wasm`: use WASI ABI for exit function
|
||||
- `wasm`: scan globals conservatively
|
||||
* **boards**
|
||||
- `arduino-mega1280`: add support for the Arduino Mega 1280
|
||||
- `arduino-nano-new`: Add Arduino Nano w/ New Bootloader target
|
||||
- `atsame54-xpro`: add initial support this board
|
||||
- `feather-m4-can`: add initial support for this board
|
||||
- `grandcentral-m4`: add board support for Adafruit Grand Central M4 (SAMD51)
|
||||
- `lgt92`: update to new UART structure
|
||||
- `microbit`: remove LED constant
|
||||
- `microbit-v2`: add support for S113 SoftDevice
|
||||
- `nucleol432`: add support for this board
|
||||
- `nucleo-l031k6`: add this board
|
||||
- `pca10059`: initial support for this board
|
||||
- `qtpy`: fix msd-volume-name
|
||||
- `qtpy`: fix i2c setting
|
||||
- `teensy40`: move txBuffer allocation to UART declaration
|
||||
- `teensy40`: add UART0 as alias for UART1
|
||||
|
||||
|
||||
0.17.0
|
||||
|
||||
---
|
||||
|
||||
@@ -182,25 +182,28 @@ tinygo:
|
||||
test: wasi-libc
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
|
||||
|
||||
TEST_PACKAGES = \
|
||||
container/heap \
|
||||
container/list \
|
||||
container/ring \
|
||||
crypto/des \
|
||||
encoding \
|
||||
encoding/ascii85 \
|
||||
encoding/base32 \
|
||||
encoding/hex \
|
||||
hash/adler32 \
|
||||
hash/fnv \
|
||||
hash/crc64 \
|
||||
math \
|
||||
math/cmplx \
|
||||
text/scanner \
|
||||
unicode/utf8 \
|
||||
|
||||
# Test known-working standard library packages.
|
||||
# TODO: do this in one command, parallelize, and only show failing tests (no
|
||||
# implied -v flag).
|
||||
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
||||
.PHONY: tinygo-test
|
||||
tinygo-test:
|
||||
$(TINYGO) test container/heap
|
||||
$(TINYGO) test container/list
|
||||
$(TINYGO) test container/ring
|
||||
$(TINYGO) test crypto/des
|
||||
$(TINYGO) test encoding/ascii85
|
||||
$(TINYGO) test encoding/base32
|
||||
$(TINYGO) test encoding/hex
|
||||
$(TINYGO) test hash/adler32
|
||||
$(TINYGO) test hash/fnv
|
||||
$(TINYGO) test hash/crc64
|
||||
$(TINYGO) test math
|
||||
$(TINYGO) test math/cmplx
|
||||
$(TINYGO) test text/scanner
|
||||
$(TINYGO) test unicode/utf8
|
||||
$(TINYGO) test $(TEST_PACKAGES)
|
||||
|
||||
.PHONY: smoketest
|
||||
smoketest:
|
||||
@@ -340,8 +343,12 @@ smoketest:
|
||||
@$(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=atsame54-xpro examples/can
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4-can examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4-can examples/caninterrupt
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
|
||||
@@ -43,7 +43,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
The following 57 microcontroller boards are currently supported:
|
||||
The following 62 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)
|
||||
@@ -53,6 +53,7 @@ The following 57 microcontroller boards are currently supported:
|
||||
* [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 Grand Central M4](https://www.adafruit.com/product/4064)
|
||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
|
||||
* [Adafruit ItsyBitsy nRF52840](https://www.adafruit.com/product/4481)
|
||||
@@ -63,6 +64,7 @@ The following 57 microcontroller boards are currently supported:
|
||||
* [Adafruit PyPortal](https://www.adafruit.com/product/4116)
|
||||
* [Adafruit QT Py](https://www.adafruit.com/product/4600)
|
||||
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
||||
* [Arduino Mega 1280](https://www.arduino.cc/en/Main/arduinoBoardMega/)
|
||||
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
|
||||
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
|
||||
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
|
||||
@@ -84,6 +86,7 @@ The following 57 microcontroller boards are currently supported:
|
||||
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle)
|
||||
* [Nordic Semiconductor PCA10040](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK)
|
||||
* [Nordic Semiconductor PCA10056](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
|
||||
* [Nordic Semiconductor pca10059](https://www.nordicsemi.com/Software-and-tools/Development-Kits/nRF52840-Dongle)
|
||||
* [Particle Argon](https://docs.particle.io/datasheets/wi-fi/argon-datasheet/)
|
||||
* [Particle Boron](https://docs.particle.io/datasheets/cellular/boron-datasheet/)
|
||||
* [Particle Xenon](https://docs.particle.io/datasheets/discontinued/xenon-datasheet/)
|
||||
@@ -98,6 +101,8 @@ The following 57 microcontroller boards are currently supported:
|
||||
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
|
||||
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
|
||||
* [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html)
|
||||
* [ST Micro "Nucleo" L432KC](https://www.st.com/ja/evaluation-tools/nucleo-l432kc.html)
|
||||
* [ST Micro "Nucleo" L552ZE](https://www.st.com/en/evaluation-tools/nucleo-l552ze-q.html)
|
||||
* [ST Micro STM32F103XX "Bluepill"](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill)
|
||||
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
|
||||
|
||||
+9
-2
@@ -39,6 +39,11 @@ type BuildResult struct {
|
||||
// The directory of the main package. This is useful for testing as the test
|
||||
// binary must be run in the directory of the tested package.
|
||||
MainDir string
|
||||
|
||||
// ImportPath is the import path of the main package. This is useful for
|
||||
// correctly printing test results: the import path isn't always the same as
|
||||
// the path listed on the command line.
|
||||
ImportPath string
|
||||
}
|
||||
|
||||
// packageAction is the struct that is serialized to JSON and hashed, to work as
|
||||
@@ -94,6 +99,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
DefaultStackSize: config.Target.DefaultStackSize,
|
||||
NeedsStackObjects: config.NeedsStackObjects(),
|
||||
Debug: config.Debug(),
|
||||
LLVMFeatures: config.LLVMFeatures(),
|
||||
}
|
||||
|
||||
// Load the target machine, which is the LLVM object that contains all
|
||||
@@ -637,8 +643,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
|
||||
}
|
||||
return action(BuildResult{
|
||||
Binary: tmppath,
|
||||
MainDir: lprogram.MainPkg().Dir,
|
||||
Binary: tmppath,
|
||||
MainDir: lprogram.MainPkg().Dir,
|
||||
ImportPath: lprogram.MainPkg().ImportPath,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,18 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if options.OpenOCDCommands != nil {
|
||||
// Override the OpenOCDCommands from the target spec if specified on
|
||||
// the command-line
|
||||
spec.OpenOCDCommands = options.OpenOCDCommands
|
||||
}
|
||||
|
||||
goroot := goenv.Get("GOROOT")
|
||||
if goroot == "" {
|
||||
return nil, errors.New("cannot locate $GOROOT, please set it manually")
|
||||
}
|
||||
|
||||
major, minor, err := goenv.GetGorootVersion(goroot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||
@@ -28,7 +36,9 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
if major != 1 || minor < 13 || minor > 16 {
|
||||
return nil, fmt.Errorf("requires go version 1.13 through 1.16, got go%d.%d", major, minor)
|
||||
}
|
||||
|
||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
|
||||
return &compileopts.Config{
|
||||
Options: options,
|
||||
Target: spec,
|
||||
|
||||
@@ -80,12 +80,7 @@ func (c *Config) GC() string {
|
||||
if c.Target.GC != "" {
|
||||
return c.Target.GC
|
||||
}
|
||||
for _, tag := range c.Target.BuildTags {
|
||||
if tag == "baremetal" || tag == "wasm" {
|
||||
return "conservative"
|
||||
}
|
||||
}
|
||||
return "extalloc"
|
||||
return "conservative"
|
||||
}
|
||||
|
||||
// NeedsStackObjects returns true if the compiler should insert stack objects
|
||||
@@ -342,6 +337,10 @@ func (c *Config) WasmAbi() string {
|
||||
return c.Target.WasmAbi
|
||||
}
|
||||
|
||||
func (c *Config) LLVMFeatures() string {
|
||||
return c.Options.LLVMFeatures
|
||||
}
|
||||
|
||||
type TestConfig struct {
|
||||
CompileTestBinary bool
|
||||
// TODO: Filter the test functions to run, include verbose flag, etc
|
||||
|
||||
+20
-18
@@ -17,24 +17,26 @@ var (
|
||||
// Options contains extra options to give to the compiler. These options are
|
||||
// usually passed from the command line.
|
||||
type Options struct {
|
||||
Target string
|
||||
Opt string
|
||||
GC string
|
||||
PanicStrategy string
|
||||
Scheduler string
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
PrintCommands bool
|
||||
Debug bool
|
||||
PrintSizes string
|
||||
PrintAllocs *regexp.Regexp // regexp string
|
||||
PrintStacks bool
|
||||
Tags string
|
||||
WasmAbi string
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
Target string
|
||||
Opt string
|
||||
GC string
|
||||
PanicStrategy string
|
||||
Scheduler string
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
PrintCommands bool
|
||||
Debug bool
|
||||
PrintSizes string
|
||||
PrintAllocs *regexp.Regexp // regexp string
|
||||
PrintStacks bool
|
||||
Tags string
|
||||
WasmAbi string
|
||||
GlobalValues map[string]map[string]string // map[pkgpath]map[varname]value
|
||||
TestConfig TestConfig
|
||||
Programmer string
|
||||
OpenOCDCommands []string
|
||||
LLVMFeatures string
|
||||
}
|
||||
|
||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||
|
||||
+12
-8
@@ -239,22 +239,26 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
// No target spec available. Use the default one, useful on most systems
|
||||
// with a regular OS.
|
||||
spec := TargetSpec{
|
||||
Triple: triple,
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
BuildTags: []string{goos, goarch},
|
||||
Linker: "cc",
|
||||
CFlags: []string{"--target=" + triple},
|
||||
GDB: []string{"gdb"},
|
||||
PortReset: "false",
|
||||
Triple: triple,
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
BuildTags: []string{goos, goarch},
|
||||
Scheduler: "tasks",
|
||||
Linker: "cc",
|
||||
DefaultStackSize: 1024 * 64, // 64kB
|
||||
CFlags: []string{"--target=" + triple},
|
||||
GDB: []string{"gdb"},
|
||||
PortReset: "false",
|
||||
}
|
||||
if goos == "darwin" {
|
||||
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
||||
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
|
||||
} else {
|
||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||
}
|
||||
if goarch != "wasm" {
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+".S")
|
||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+".S")
|
||||
}
|
||||
if goarch != runtime.GOARCH {
|
||||
// Some educated guesses as to how to invoke helper programs.
|
||||
|
||||
+56
-2
@@ -59,6 +59,7 @@ type Config struct {
|
||||
DefaultStackSize uint64
|
||||
NeedsStackObjects bool
|
||||
Debug bool // Whether to emit debug information in the LLVM module.
|
||||
LLVMFeatures string
|
||||
}
|
||||
|
||||
// compilerContext contains function-independent data that should still be
|
||||
@@ -185,7 +186,12 @@ func NewTargetMachine(config *Config) (llvm.TargetMachine, error) {
|
||||
if err != nil {
|
||||
return llvm.TargetMachine{}, err
|
||||
}
|
||||
features := strings.Join(config.Features, ",")
|
||||
|
||||
feat := config.Features
|
||||
if len(config.LLVMFeatures) > 0 {
|
||||
feat = append(feat, config.LLVMFeatures)
|
||||
}
|
||||
features := strings.Join(feat, ",")
|
||||
|
||||
var codeModel llvm.CodeModel
|
||||
var relocationModel llvm.RelocMode
|
||||
@@ -964,11 +970,59 @@ func (b *builder) createFunction() {
|
||||
}
|
||||
}
|
||||
|
||||
// posser is an interface that's implemented by both ssa.Value and
|
||||
// ssa.Instruction. It is implemented by everything that has a Pos() method,
|
||||
// which is all that getPos() needs.
|
||||
type posser interface {
|
||||
Pos() token.Pos
|
||||
}
|
||||
|
||||
// getPos returns position information for a ssa.Value or ssa.Instruction.
|
||||
//
|
||||
// Not all instructions have position information, especially when they're
|
||||
// implicit (such as implicit casts or implicit returns at the end of a
|
||||
// function). In these cases, it makes sense to try a bit harder to guess what
|
||||
// the position really should be.
|
||||
func getPos(val posser) token.Pos {
|
||||
pos := val.Pos()
|
||||
if pos != token.NoPos {
|
||||
// Easy: position is known.
|
||||
return pos
|
||||
}
|
||||
|
||||
// No position information is known.
|
||||
switch val := val.(type) {
|
||||
case *ssa.MakeInterface:
|
||||
return getPos(val.X)
|
||||
case *ssa.Return:
|
||||
syntax := val.Parent().Syntax()
|
||||
if syntax != nil {
|
||||
// non-synthetic
|
||||
return syntax.End()
|
||||
}
|
||||
return token.NoPos
|
||||
case *ssa.FieldAddr:
|
||||
return getPos(val.X)
|
||||
case *ssa.IndexAddr:
|
||||
return getPos(val.X)
|
||||
case *ssa.Slice:
|
||||
return getPos(val.X)
|
||||
case *ssa.Store:
|
||||
return getPos(val.Addr)
|
||||
case *ssa.Extract:
|
||||
return getPos(val.Tuple)
|
||||
default:
|
||||
// This is reachable, for example with *ssa.Const, *ssa.If, and
|
||||
// *ssa.Jump. They might be implemented in some way in the future.
|
||||
return token.NoPos
|
||||
}
|
||||
}
|
||||
|
||||
// createInstruction builds the LLVM IR equivalent instructions for the
|
||||
// particular Go SSA instruction.
|
||||
func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||
if b.Debug {
|
||||
pos := b.program.Fset.Position(instr.Pos())
|
||||
pos := b.program.Fset.Position(getPos(instr))
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestCompiler(t *testing.T) {
|
||||
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
||||
}
|
||||
|
||||
target, err := compileopts.LoadTarget("i686--linux")
|
||||
target, err := compileopts.LoadTarget("wasm")
|
||||
if err != nil {
|
||||
t.Fatal("failed to load target:", err)
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, p
|
||||
} else {
|
||||
// The stack size is fixed at compile time. By emitting it here as a
|
||||
// constant, it can be optimized.
|
||||
if b.Scheduler == "tasks" && b.DefaultStackSize == 0 {
|
||||
b.addError(pos, "default stack size for goroutines is not set")
|
||||
}
|
||||
stackSize = llvm.ConstInt(b.uintptrType, b.DefaultStackSize, false)
|
||||
}
|
||||
case "coroutines":
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'basic.go'
|
||||
source_filename = "basic.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'float.go'
|
||||
source_filename = "float.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; 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"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
%runtime.funcValueWithSignature = type { i32, i8* }
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'interface.go'
|
||||
source_filename = "interface.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID* }
|
||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'pointer.go'
|
||||
source_filename = "pointer.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'slice.go'
|
||||
source_filename = "slice.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'string.go'
|
||||
source_filename = "string.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
%runtime._string = type { i8*, i32 }
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.18.0-dev"
|
||||
const Version = "0.18.0"
|
||||
|
||||
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
||||
// If the goroot cannot be determined, (0, 0) is returned.
|
||||
|
||||
+22
-6
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -930,16 +931,31 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
||||
result = r.builder.CreateBitCast(operands[0], inst.llvmInst.Type(), inst.name)
|
||||
case llvm.ExtractValue:
|
||||
indices := inst.llvmInst.Indices()
|
||||
if len(indices) != 1 {
|
||||
panic("expected exactly one index")
|
||||
// Note: the Go LLVM API doesn't support multiple indices, so simulate
|
||||
// this operation with some extra extractvalue instructions. Hopefully
|
||||
// this is optimized to a single instruction.
|
||||
agg := operands[0]
|
||||
for i := 0; i < len(indices)-1; i++ {
|
||||
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg")
|
||||
}
|
||||
result = r.builder.CreateExtractValue(operands[0], int(indices[0]), inst.name)
|
||||
result = r.builder.CreateExtractValue(agg, int(indices[len(indices)-1]), inst.name)
|
||||
case llvm.InsertValue:
|
||||
indices := inst.llvmInst.Indices()
|
||||
if len(indices) != 1 {
|
||||
panic("expected exactly one index")
|
||||
// Similar to extractvalue, we're working around a limitation in the Go
|
||||
// LLVM API here by splitting the insertvalue into multiple instructions
|
||||
// if there is more than one operand.
|
||||
agg := operands[0]
|
||||
aggregates := []llvm.Value{agg}
|
||||
for i := 0; i < len(indices)-1; i++ {
|
||||
agg = r.builder.CreateExtractValue(agg, int(indices[i]), inst.name+".agg"+strconv.Itoa(i))
|
||||
aggregates = append(aggregates, agg)
|
||||
}
|
||||
result = r.builder.CreateInsertValue(operands[0], operands[1], int(indices[0]), inst.name)
|
||||
result = operands[1]
|
||||
for i := len(indices) - 1; i >= 0; i-- {
|
||||
agg := aggregates[i]
|
||||
result = r.builder.CreateInsertValue(agg, result, int(indices[i]), inst.name+".insertvalue"+strconv.Itoa(i))
|
||||
}
|
||||
|
||||
case llvm.Add:
|
||||
result = r.builder.CreateAdd(operands[0], operands[1], inst.name)
|
||||
case llvm.Sub:
|
||||
|
||||
Vendored
+10
@@ -8,6 +8,7 @@ target triple = "x86_64--linux"
|
||||
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
|
||||
@main.exposedValue1 = global i16 0
|
||||
@main.exposedValue2 = global i16 0
|
||||
@main.insertedValue = global {i8, i32, {float, {i64, i16}}} zeroinitializer
|
||||
|
||||
declare void @runtime.printint64(i64) unnamed_addr
|
||||
|
||||
@@ -71,6 +72,13 @@ entry:
|
||||
call void @runtime.printint64(i64 %switch1)
|
||||
call void @runtime.printint64(i64 %switch2)
|
||||
|
||||
; Test extractvalue/insertvalue with multiple operands.
|
||||
%agg = call {i8, i32, {float, {i64, i16}}} @nestedStruct()
|
||||
%elt = extractvalue {i8, i32, {float, {i64, i16}}} %agg, 2, 1, 0
|
||||
call void @runtime.printint64(i64 %elt)
|
||||
%agg2 = insertvalue {i8, i32, {float, {i64, i16}}} %agg, i64 5, 2, 1, 0
|
||||
store {i8, i32, {float, {i64, i16}}} %agg2, {i8, i32, {float, {i64, i16}}}* @main.insertedValue
|
||||
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -112,3 +120,5 @@ two:
|
||||
otherwise:
|
||||
ret i64 -1
|
||||
}
|
||||
|
||||
declare {i8, i32, {float, {i64, i16}}} @nestedStruct()
|
||||
|
||||
Vendored
+14
@@ -7,6 +7,7 @@ target triple = "x86_64--linux"
|
||||
@main.exportedValue = global [1 x i16*] [i16* @main.exposedValue1]
|
||||
@main.exposedValue1 = global i16 0
|
||||
@main.exposedValue2 = local_unnamed_addr global i16 0
|
||||
@main.insertedValue = local_unnamed_addr global { i8, i32, { float, { i64, i16 } } } zeroinitializer
|
||||
|
||||
declare void @runtime.printint64(i64) unnamed_addr
|
||||
|
||||
@@ -27,6 +28,17 @@ entry:
|
||||
store i16 7, i16* @main.exposedValue2
|
||||
call void @runtime.printint64(i64 6)
|
||||
call void @runtime.printint64(i64 -1)
|
||||
%agg = call { i8, i32, { float, { i64, i16 } } } @nestedStruct()
|
||||
%elt.agg = extractvalue { i8, i32, { float, { i64, i16 } } } %agg, 2
|
||||
%elt.agg1 = extractvalue { float, { i64, i16 } } %elt.agg, 1
|
||||
%elt = extractvalue { i64, i16 } %elt.agg1, 0
|
||||
call void @runtime.printint64(i64 %elt)
|
||||
%agg2.agg0 = extractvalue { i8, i32, { float, { i64, i16 } } } %agg, 2
|
||||
%agg2.agg1 = extractvalue { float, { i64, i16 } } %agg2.agg0, 1
|
||||
%agg2.insertvalue2 = insertvalue { i64, i16 } %agg2.agg1, i64 5, 0
|
||||
%agg2.insertvalue1 = insertvalue { float, { i64, i16 } } %agg2.agg0, { i64, i16 } %agg2.insertvalue2, 1
|
||||
%agg2.insertvalue0 = insertvalue { i8, i32, { float, { i64, i16 } } } %agg, { float, { i64, i16 } } %agg2.insertvalue1, 2
|
||||
store { i8, i32, { float, { i64, i16 } } } %agg2.insertvalue0, { i8, i32, { float, { i64, i16 } } }* @main.insertedValue
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -67,3 +79,5 @@ two: ; preds = %entry
|
||||
otherwise: ; preds = %entry
|
||||
ret i64 -1
|
||||
}
|
||||
|
||||
declare { i8, i32, { float, { i64, i16 } } } @nestedStruct() local_unnamed_addr
|
||||
|
||||
@@ -23,3 +23,13 @@ type Error struct {
|
||||
func (e Error) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
// Error returned when loading a *Program for a test binary but no test files
|
||||
// are present.
|
||||
type NoTestFilesError struct {
|
||||
ImportPath string
|
||||
}
|
||||
|
||||
func (e NoTestFilesError) Error() string {
|
||||
return "no test files"
|
||||
}
|
||||
|
||||
@@ -210,6 +210,12 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
p.Packages[pkg.ImportPath] = pkg
|
||||
}
|
||||
|
||||
if config.TestConfig.CompileTestBinary && !strings.HasSuffix(p.sorted[len(p.sorted)-1].ImportPath, ".test") {
|
||||
// Trying to compile a test binary but there are no test files in this
|
||||
// package.
|
||||
return p, NoTestFilesError{p.sorted[len(p.sorted)-1].ImportPath}
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -136,15 +136,17 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
|
||||
})
|
||||
}
|
||||
|
||||
// Test runs the tests in the given package.
|
||||
func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, outpath string) error {
|
||||
// Test runs the tests in the given package. Returns whether the test passed and
|
||||
// possibly an error if the test failed to run.
|
||||
func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, outpath string) (bool, error) {
|
||||
options.TestConfig.CompileTestBinary = true
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
return err
|
||||
return false, err
|
||||
}
|
||||
|
||||
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||
var passed bool
|
||||
err = builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||
if testCompileOnly || outpath != "" {
|
||||
// Write test binary to the specified file name.
|
||||
if outpath == "" {
|
||||
@@ -158,48 +160,78 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
|
||||
// Do not run the test.
|
||||
return nil
|
||||
}
|
||||
if len(config.Target.Emulator) == 0 {
|
||||
// Run directly.
|
||||
cmd := executeCommand(config.Options, result.Binary)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = result.MainDir
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
// Propagate the exit code
|
||||
if err, ok := err.(*exec.ExitError); ok {
|
||||
os.Exit(err.ExitCode())
|
||||
}
|
||||
return &commandError{"failed to run compiled binary", result.Binary, err}
|
||||
}
|
||||
return nil
|
||||
|
||||
// Run the test.
|
||||
start := time.Now()
|
||||
var err error
|
||||
passed, err = runPackageTest(config, result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
duration := time.Since(start)
|
||||
|
||||
// Print the result.
|
||||
importPath := strings.TrimSuffix(result.ImportPath, ".test")
|
||||
if passed {
|
||||
fmt.Printf("ok \t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||
} else {
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], result.Binary)
|
||||
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
buf := &bytes.Buffer{}
|
||||
w := io.MultiWriter(os.Stdout, buf)
|
||||
cmd.Stdout = w
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
if err, ok := err.(*exec.ExitError); !ok || !err.Exited() {
|
||||
// Workaround for QEMU which always exits with an error.
|
||||
return &commandError{"failed to run emulator with", result.Binary, err}
|
||||
}
|
||||
fmt.Printf("FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err, ok := err.(loader.NoTestFilesError); ok {
|
||||
fmt.Printf("? \t%s\t[no test files]\n", err.ImportPath)
|
||||
// Pretend the test passed - it at least didn't fail.
|
||||
return true, nil
|
||||
}
|
||||
return passed, err
|
||||
}
|
||||
|
||||
// runPackageTest runs a test binary that was previously built. The return
|
||||
// values are whether the test passed and any errors encountered while trying to
|
||||
// run the binary.
|
||||
func runPackageTest(config *compileopts.Config, result builder.BuildResult) (bool, error) {
|
||||
if len(config.Target.Emulator) == 0 {
|
||||
// Run directly.
|
||||
cmd := executeCommand(config.Options, result.Binary)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = result.MainDir
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
if _, ok := err.(*exec.ExitError); ok {
|
||||
// Binary exited with a non-zero exit code, which means the test
|
||||
// failed.
|
||||
return false, nil
|
||||
}
|
||||
testOutput := string(buf.Bytes())
|
||||
if testOutput == "PASS\n" || strings.HasSuffix(testOutput, "\nPASS\n") {
|
||||
// Test passed.
|
||||
return nil
|
||||
} else {
|
||||
// Test failed, either by ending with the word "FAIL" or with a
|
||||
// panic of some sort.
|
||||
os.Exit(1)
|
||||
return nil // unreachable
|
||||
return false, &commandError{"failed to run compiled binary", result.Binary, err}
|
||||
}
|
||||
return true, nil
|
||||
} else {
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], result.Binary)
|
||||
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
buf := &bytes.Buffer{}
|
||||
w := io.MultiWriter(os.Stdout, buf)
|
||||
cmd.Stdout = w
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
if err, ok := err.(*exec.ExitError); !ok || !err.Exited() {
|
||||
// Workaround for QEMU which always exits with an error.
|
||||
return false, &commandError{"failed to run emulator with", result.Binary, err}
|
||||
}
|
||||
}
|
||||
})
|
||||
testOutput := string(buf.Bytes())
|
||||
if testOutput == "PASS\n" || strings.HasSuffix(testOutput, "\nPASS\n") {
|
||||
// Test passed.
|
||||
return true, nil
|
||||
} else {
|
||||
// Test failed, either by ending with the word "FAIL" or with a
|
||||
// panic of some sort.
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flash builds and flashes the built binary to the given serial port.
|
||||
@@ -682,7 +714,9 @@ func getDefaultPort(portCandidates []string) (port string, err error) {
|
||||
}
|
||||
|
||||
for _, p := range portsList {
|
||||
ports = append(ports, p.Name)
|
||||
if p.IsUSB {
|
||||
ports = append(ports, p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if ports == nil || len(ports) == 0 {
|
||||
@@ -905,11 +939,13 @@ func main() {
|
||||
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
|
||||
printCommands := flag.Bool("x", false, "Print commands")
|
||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
||||
ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)")
|
||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||
port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)")
|
||||
programmer := flag.String("programmer", "", "which hardware programmer to use")
|
||||
ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags")
|
||||
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic")
|
||||
llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable")
|
||||
|
||||
var flagJSON, flagDeps *bool
|
||||
if command == "help" || command == "list" {
|
||||
@@ -943,6 +979,7 @@ func main() {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var printAllocs *regexp.Regexp
|
||||
if *printAllocsString != "" {
|
||||
printAllocs, err = regexp.Compile(*printAllocsString)
|
||||
@@ -951,24 +988,32 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var ocdCommands []string
|
||||
if *ocdCommandsString != "" {
|
||||
ocdCommands = strings.Split(*ocdCommandsString, ",")
|
||||
}
|
||||
|
||||
options := &compileopts.Options{
|
||||
Target: *target,
|
||||
Opt: *opt,
|
||||
GC: *gc,
|
||||
PanicStrategy: *panicStrategy,
|
||||
Scheduler: *scheduler,
|
||||
PrintIR: *printIR,
|
||||
DumpSSA: *dumpSSA,
|
||||
VerifyIR: *verifyIR,
|
||||
Debug: !*nodebug,
|
||||
PrintSizes: *printSize,
|
||||
PrintStacks: *printStacks,
|
||||
PrintAllocs: printAllocs,
|
||||
PrintCommands: *printCommands,
|
||||
Tags: *tags,
|
||||
GlobalValues: globalVarValues,
|
||||
WasmAbi: *wasmAbi,
|
||||
Programmer: *programmer,
|
||||
Target: *target,
|
||||
Opt: *opt,
|
||||
GC: *gc,
|
||||
PanicStrategy: *panicStrategy,
|
||||
Scheduler: *scheduler,
|
||||
PrintIR: *printIR,
|
||||
DumpSSA: *dumpSSA,
|
||||
VerifyIR: *verifyIR,
|
||||
Debug: !*nodebug,
|
||||
PrintSizes: *printSize,
|
||||
PrintStacks: *printStacks,
|
||||
PrintAllocs: printAllocs,
|
||||
PrintCommands: *printCommands,
|
||||
Tags: *tags,
|
||||
GlobalValues: globalVarValues,
|
||||
WasmAbi: *wasmAbi,
|
||||
Programmer: *programmer,
|
||||
OpenOCDCommands: ocdCommands,
|
||||
LLVMFeatures: *llvmFeatures,
|
||||
}
|
||||
|
||||
os.Setenv("CC", "clang -target="+*target)
|
||||
@@ -1061,16 +1106,26 @@ func main() {
|
||||
err := Run(pkgName, options)
|
||||
handleCompilerError(err)
|
||||
case "test":
|
||||
pkgName := "."
|
||||
if flag.NArg() == 1 {
|
||||
pkgName = filepath.ToSlash(flag.Arg(0))
|
||||
} else if flag.NArg() > 1 {
|
||||
fmt.Fprintln(os.Stderr, "test only accepts a single positional argument: package name, but multiple were specified")
|
||||
usage()
|
||||
var pkgNames []string
|
||||
for i := 0; i < flag.NArg(); i++ {
|
||||
pkgNames = append(pkgNames, filepath.ToSlash(flag.Arg(i)))
|
||||
}
|
||||
if len(pkgNames) == 0 {
|
||||
pkgNames = []string{"."}
|
||||
}
|
||||
allTestsPassed := true
|
||||
for _, pkgName := range pkgNames {
|
||||
// TODO: parallelize building the test binaries
|
||||
passed, err := Test(pkgName, options, *testCompileOnlyFlag, outpath)
|
||||
handleCompilerError(err)
|
||||
if !passed {
|
||||
allTestsPassed = false
|
||||
}
|
||||
}
|
||||
if !allTestsPassed {
|
||||
fmt.Println("FAIL")
|
||||
os.Exit(1)
|
||||
}
|
||||
err := Test(pkgName, options, *testCompileOnlyFlag, outpath)
|
||||
handleCompilerError(err)
|
||||
case "targets":
|
||||
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
|
||||
entries, err := ioutil.ReadDir(dir)
|
||||
|
||||
+15
-6
@@ -26,12 +26,21 @@ type SCB_Type struct {
|
||||
SHPR2 volatile.Register32 // 0xD1C: System Handler Priority Register 2
|
||||
SHPR3 volatile.Register32 // 0xD20: System Handler Priority Register 3
|
||||
// the following are only applicable for Cortex-M3/M33/M4/M7
|
||||
SHCSR volatile.Register32 // 0xD24: System Handler Control and State Register
|
||||
CFSR volatile.Register32 // 0xD28: Configurable Fault Status Register
|
||||
HFSR volatile.Register32 // 0xD2C: HardFault Status Register
|
||||
DFSR volatile.Register32 // 0xD30: Debug Fault Status Register
|
||||
MMFAR volatile.Register32 // 0xD34: MemManage Fault Address Register
|
||||
BFAR volatile.Register32 // 0xD38: BusFault Address Register
|
||||
SHCSR volatile.Register32 // 0xD24: System Handler Control and State Register
|
||||
CFSR volatile.Register32 // 0xD28: Configurable Fault Status Register
|
||||
HFSR volatile.Register32 // 0xD2C: HardFault Status Register
|
||||
DFSR volatile.Register32 // 0xD30: Debug Fault Status Register
|
||||
MMFAR volatile.Register32 // 0xD34: MemManage Fault Address Register
|
||||
BFAR volatile.Register32 // 0xD38: BusFault Address Register
|
||||
AFSR volatile.Register32 // 0xD3C: Auxiliary Fault Status Register
|
||||
PFR [2]volatile.Register32 // 0xD40: Processor Feature Register
|
||||
DFR volatile.Register32 // 0xD48: Debug Feature Register
|
||||
ADR volatile.Register32 // 0xD4C: Auxiliary Feature Register
|
||||
MMFR [4]volatile.Register32 // 0xD50: Memory Model Feature Register
|
||||
ISAR [5]volatile.Register32 // 0xD60: Instruction Set Attributes Register
|
||||
_ [5]uint32 // reserved
|
||||
CPACR volatile.Register32 // 0xD88: Coprocessor Access Control Register
|
||||
|
||||
}
|
||||
|
||||
var SCB = (*SCB_Type)(unsafe.Pointer(uintptr(SCB_BASE)))
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// +build feather_m4_can
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// power on the CAN Transceiver
|
||||
// https://learn.adafruit.com/adafruit-feather-m4-can-express/pinouts#can-bus-3078990-8
|
||||
boost_en := machine.BOOST_EN
|
||||
boost_en.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
boost_en.High()
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
can1 := machine.CAN1
|
||||
can1.Configure(machine.CANConfig{
|
||||
TransferRate: machine.CANTransferRate500kbps,
|
||||
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||
Rx: machine.CAN1_RX,
|
||||
Tx: machine.CAN1_TX,
|
||||
Standby: machine.CAN1_STANDBY,
|
||||
})
|
||||
|
||||
can0 := machine.CAN0
|
||||
can0.Configure(machine.CANConfig{
|
||||
TransferRate: machine.CANTransferRate500kbps,
|
||||
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||
Rx: machine.CAN0_RX,
|
||||
Tx: machine.CAN0_TX,
|
||||
Standby: machine.NoPin,
|
||||
})
|
||||
|
||||
rxMsg := machine.CANRxBufferElement{}
|
||||
|
||||
for {
|
||||
can1.Tx(0x123, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, false, false)
|
||||
can1.Tx(0x789, []byte{0x02, 0x24, 0x46, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, true, false)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
sz0 := can0.RxFifoSize()
|
||||
if sz0 > 0 {
|
||||
fmt.Printf("CAN0 %d\r\n", sz0)
|
||||
for i := 0; i < sz0; i++ {
|
||||
can0.RxRaw(&rxMsg)
|
||||
fmt.Printf("-> %08X %X %#v\r\n", rxMsg.ID, rxMsg.DLC, rxMsg.Data())
|
||||
}
|
||||
}
|
||||
|
||||
sz1 := can1.RxFifoSize()
|
||||
if sz1 > 0 {
|
||||
fmt.Printf("CAN1 %d\r\n", sz1)
|
||||
for i := 0; i < sz1; i++ {
|
||||
can1.RxRaw(&rxMsg)
|
||||
fmt.Printf("-> %08X %X %#v\r\n", rxMsg.ID, rxMsg.DLC, rxMsg.Data())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// +build feather_m4_can
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// power on the CAN Transceiver
|
||||
// https://learn.adafruit.com/adafruit-feather-m4-can-express/pinouts#can-bus-3078990-8
|
||||
boost_en := machine.BOOST_EN
|
||||
boost_en.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
boost_en.High()
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
type canMsg struct {
|
||||
ch byte
|
||||
id uint32
|
||||
dlc byte
|
||||
data []byte
|
||||
}
|
||||
|
||||
func main() {
|
||||
ch := make(chan canMsg, 10)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case m := <-ch:
|
||||
fmt.Printf("%d %03X %X ", m.ch, m.id, m.dlc)
|
||||
for _, d := range m.data {
|
||||
fmt.Printf("%02X ", d)
|
||||
}
|
||||
fmt.Printf("\r\n")
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
can1 := machine.CAN1
|
||||
can1.Configure(machine.CANConfig{
|
||||
TransferRate: machine.CANTransferRate500kbps,
|
||||
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||
Rx: machine.CAN1_RX,
|
||||
Tx: machine.CAN1_TX,
|
||||
Standby: machine.CAN1_STANDBY,
|
||||
})
|
||||
// RF0NE : Rx FIFO 0 New Message Interrupt Enable
|
||||
can1.SetInterrupt(sam.CAN_IE_RF0NE, func(*machine.CAN) {
|
||||
rxMsg := machine.CANRxBufferElement{}
|
||||
can1.RxRaw(&rxMsg)
|
||||
m := canMsg{ch: 1, id: rxMsg.ID, dlc: rxMsg.DLC, data: rxMsg.Data()}
|
||||
select {
|
||||
case ch <- m:
|
||||
}
|
||||
})
|
||||
|
||||
can0 := machine.CAN0
|
||||
can0.Configure(machine.CANConfig{
|
||||
TransferRate: machine.CANTransferRate500kbps,
|
||||
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||
Rx: machine.CAN0_RX,
|
||||
Tx: machine.CAN0_TX,
|
||||
Standby: machine.NoPin,
|
||||
})
|
||||
// RF0NE : Rx FIFO 0 New Message Interrupt Enable
|
||||
can0.SetInterrupt(sam.CAN_IE_RF0NE, func(*machine.CAN) {
|
||||
rxMsg := machine.CANRxBufferElement{}
|
||||
can0.RxRaw(&rxMsg)
|
||||
m := canMsg{ch: 2, id: rxMsg.ID, dlc: rxMsg.DLC, data: rxMsg.Data()}
|
||||
select {
|
||||
case ch <- m:
|
||||
}
|
||||
})
|
||||
|
||||
for {
|
||||
can0.Tx(0x123, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, false, false)
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
can1.Tx(0x456, []byte{0xAA, 0xBB, 0xCC}, false, false)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
.section .text.tinygo_startTask
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
tinygo_startTask:
|
||||
.cfi_startproc
|
||||
// Small assembly stub for starting a goroutine. This is already run on the
|
||||
// new stack, with the callee-saved registers already loaded.
|
||||
// Most importantly, EBX contain the pc of the to-be-started function and
|
||||
// ESI contain the only argument it is given. Multiple arguments are packed
|
||||
// into one by storing them in a new allocation.
|
||||
|
||||
// Indicate to the unwinder that there is nothing to unwind, this is the
|
||||
// root frame. It avoids bogus extra frames in GDB.
|
||||
.cfi_undefined eip
|
||||
|
||||
// Set the first argument of the goroutine start wrapper, which contains all
|
||||
// the arguments.
|
||||
pushl %esi
|
||||
|
||||
// Branch to the "goroutine start" function.
|
||||
calll *%ebx
|
||||
|
||||
// Rebalance the stack (to undo the above push).
|
||||
addl $4, %esp
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
jmp tinygo_pause
|
||||
.cfi_endproc
|
||||
|
||||
.global tinygo_swapTask
|
||||
.type tinygo_swapTask, %function
|
||||
tinygo_swapTask:
|
||||
// This function gets the following parameters:
|
||||
movl 4(%esp), %eax // newStack uintptr
|
||||
movl 8(%esp), %ecx // oldStack *uintptr
|
||||
// More information on the calling convention:
|
||||
// https://wiki.osdev.org/System_V_ABI#i386
|
||||
|
||||
// Save all callee-saved registers:
|
||||
pushl %ebp
|
||||
pushl %edi
|
||||
pushl %esi
|
||||
pushl %ebx
|
||||
|
||||
// Save the current stack pointer in oldStack.
|
||||
movl %esp, (%ecx)
|
||||
|
||||
// Switch to the new stack pointer.
|
||||
movl %eax, %esp
|
||||
|
||||
// Load saved register from the new stack.
|
||||
popl %ebx
|
||||
popl %esi
|
||||
popl %edi
|
||||
popl %ebp
|
||||
|
||||
// Return into the new task, as if tinygo_swapTask was a regular call.
|
||||
ret
|
||||
@@ -0,0 +1,59 @@
|
||||
// +build scheduler.tasks,386
|
||||
|
||||
package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_386.S that relies on the exact
|
||||
// layout of this struct.
|
||||
type calleeSavedRegs struct {
|
||||
ebx uintptr
|
||||
esi uintptr
|
||||
edi uintptr
|
||||
ebp uintptr
|
||||
|
||||
pc uintptr
|
||||
}
|
||||
|
||||
// archInit runs architecture-specific setup for the goroutine startup.
|
||||
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
// Store the initial sp for the startTask function (implemented in assembly).
|
||||
s.sp = uintptr(unsafe.Pointer(r))
|
||||
|
||||
// Initialize the registers.
|
||||
// These will be popped off of the stack on the first resume of the goroutine.
|
||||
|
||||
// Start the function at tinygo_startTask (defined in
|
||||
// src/internal/task/task_stack_386.S). This assembly code calls a function
|
||||
// (passed in EBX) with a single argument (passed in ESI). After the
|
||||
// function returns, it calls Pause().
|
||||
r.pc = uintptr(unsafe.Pointer(&startTask))
|
||||
|
||||
// Pass the function to call in EBX.
|
||||
// This function is a compiler-generated wrapper which loads arguments out
|
||||
// of a struct pointer. See createGoroutineStartWrapper (defined in
|
||||
// compiler/goroutine.go) for more information.
|
||||
r.ebx = fn
|
||||
|
||||
// Pass the pointer to the arguments struct in ESI.
|
||||
r.esi = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
}
|
||||
|
||||
// SystemStack returns the system stack pointer when called from a task stack.
|
||||
// When called from the system stack, it returns 0.
|
||||
func SystemStack() uintptr {
|
||||
return systemStack
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifdef __MACH__ // Darwin
|
||||
.global _tinygo_startTask
|
||||
_tinygo_startTask:
|
||||
#else // Linux etc
|
||||
.section .text.tinygo_startTask
|
||||
.global tinygo_startTask
|
||||
tinygo_startTask:
|
||||
#endif
|
||||
.cfi_startproc
|
||||
// Small assembly stub for starting a goroutine. This is already run on the
|
||||
// new stack, with the callee-saved registers already loaded.
|
||||
// Most importantly, r12 contain the pc of the to-be-started function and
|
||||
// r13 contain the only argument it is given. Multiple arguments are packed
|
||||
// into one by storing them in a new allocation.
|
||||
|
||||
// Indicate to the unwinder that there is nothing to unwind, this is the
|
||||
// root frame. It avoids bogus extra frames in GDB like here:
|
||||
// #10 0x00000000004277b6 in <goroutine wrapper> () at [...]
|
||||
// #11 0x00000000004278f3 in tinygo_startTask () at [...]
|
||||
// #12 0x0000000000002030 in ?? ()
|
||||
// #13 0x0000000000000071 in ?? ()
|
||||
.cfi_undefined rip
|
||||
|
||||
// Set the first argument of the goroutine start wrapper, which contains all
|
||||
// the arguments.
|
||||
movq %r13, %rdi
|
||||
|
||||
// Branch to the "goroutine start" function.
|
||||
callq *%r12
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
#ifdef __MACH__
|
||||
jmp _tinygo_pause
|
||||
#else
|
||||
jmp tinygo_pause
|
||||
#endif
|
||||
.cfi_endproc
|
||||
|
||||
#ifdef __MACH__ // Darwin
|
||||
.global _tinygo_swapTask
|
||||
_tinygo_swapTask:
|
||||
#else // Linux etc
|
||||
.global tinygo_swapTask
|
||||
.section .text.tinygo_swapTask
|
||||
tinygo_swapTask:
|
||||
#endif
|
||||
// This function gets the following parameters:
|
||||
// %rdi = newStack uintptr
|
||||
// %rsi = oldStack *uintptr
|
||||
|
||||
// Save all callee-saved registers:
|
||||
pushq %r15
|
||||
pushq %r14
|
||||
pushq %r13
|
||||
pushq %r12
|
||||
pushq %rbp
|
||||
pushq %rbx
|
||||
|
||||
// Save the current stack pointer in oldStack.
|
||||
movq %rsp, (%rsi)
|
||||
|
||||
// Switch to the new stack pointer.
|
||||
movq %rdi, %rsp
|
||||
|
||||
// Load saved register from the new stack.
|
||||
popq %rbx
|
||||
popq %rbp
|
||||
popq %r12
|
||||
popq %r13
|
||||
popq %r14
|
||||
popq %r15
|
||||
|
||||
// Return into the new task, as if tinygo_swapTask was a regular call.
|
||||
ret
|
||||
@@ -0,0 +1,61 @@
|
||||
// +build scheduler.tasks,amd64
|
||||
|
||||
package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_amd64.S that relies on the exact
|
||||
// layout of this struct.
|
||||
type calleeSavedRegs struct {
|
||||
rbx uintptr
|
||||
rbp uintptr
|
||||
r12 uintptr
|
||||
r13 uintptr
|
||||
r14 uintptr
|
||||
r15 uintptr
|
||||
|
||||
pc uintptr
|
||||
}
|
||||
|
||||
// archInit runs architecture-specific setup for the goroutine startup.
|
||||
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
// Store the initial sp for the startTask function (implemented in assembly).
|
||||
s.sp = uintptr(unsafe.Pointer(r))
|
||||
|
||||
// Initialize the registers.
|
||||
// These will be popped off of the stack on the first resume of the goroutine.
|
||||
|
||||
// Start the function at tinygo_startTask (defined in
|
||||
// src/internal/task/task_stack_amd64.S). This assembly code calls a
|
||||
// function (passed in r12) with a single argument (passed in r13). After
|
||||
// the function returns, it calls Pause().
|
||||
r.pc = uintptr(unsafe.Pointer(&startTask))
|
||||
|
||||
// Pass the function to call in r12.
|
||||
// This function is a compiler-generated wrapper which loads arguments out
|
||||
// of a struct pointer. See createGoroutineStartWrapper (defined in
|
||||
// compiler/goroutine.go) for more information.
|
||||
r.r12 = fn
|
||||
|
||||
// Pass the pointer to the arguments struct in r13.
|
||||
r.r13 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
}
|
||||
|
||||
// SystemStack returns the system stack pointer when called from a task stack.
|
||||
// When called from the system stack, it returns 0.
|
||||
func SystemStack() uintptr {
|
||||
return systemStack
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Only generate .debug_frame, don't generate .eh_frame.
|
||||
.cfi_sections .debug_frame
|
||||
|
||||
.section .text.tinygo_startTask
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
tinygo_startTask:
|
||||
.cfi_startproc
|
||||
// Small assembly stub for starting a goroutine. This is already run on the
|
||||
// new stack, with the callee-saved registers already loaded.
|
||||
// Most importantly, r4 contains the pc of the to-be-started function and r5
|
||||
// contains the only argument it is given. Multiple arguments are packed
|
||||
// into one by storing them in a new allocation.
|
||||
|
||||
// Indicate to the unwinder that there is nothing to unwind, this is the
|
||||
// root frame. It avoids the following (bogus) error message in GDB:
|
||||
// Backtrace stopped: previous frame identical to this frame (corrupt stack?)
|
||||
.cfi_undefined lr
|
||||
|
||||
// Set the first argument of the goroutine start wrapper, which contains all
|
||||
// the arguments.
|
||||
mov r0, r5
|
||||
|
||||
// Branch to the "goroutine start" function. By using blx instead of bx,
|
||||
// we'll return here instead of tail calling.
|
||||
blx r4
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
bl tinygo_pause
|
||||
.cfi_endproc
|
||||
.size tinygo_startTask, .-tinygo_startTask
|
||||
|
||||
.global tinygo_swapTask
|
||||
.type tinygo_swapTask, %function
|
||||
tinygo_swapTask:
|
||||
// This function gets the following parameters:
|
||||
// r0 = newStack uintptr
|
||||
// r1 = oldStack *uintptr
|
||||
|
||||
// Save all callee-saved registers:
|
||||
push {r4-r11, lr}
|
||||
|
||||
// Save the current stack pointer in oldStack.
|
||||
str sp, [r1]
|
||||
|
||||
// Switch to the new stack pointer.
|
||||
mov sp, r0
|
||||
|
||||
// Load state from new task and branch to the previous position in the
|
||||
// program.
|
||||
pop {r4-r11, pc}
|
||||
@@ -0,0 +1,61 @@
|
||||
// +build scheduler.tasks,arm,!cortexm,!avr,!xtensa
|
||||
|
||||
package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_arm.S that relies on the exact
|
||||
// layout of this struct.
|
||||
type calleeSavedRegs struct {
|
||||
r4 uintptr
|
||||
r5 uintptr
|
||||
r6 uintptr
|
||||
r7 uintptr
|
||||
r8 uintptr
|
||||
r9 uintptr
|
||||
r10 uintptr
|
||||
r11 uintptr
|
||||
|
||||
pc uintptr
|
||||
}
|
||||
|
||||
// archInit runs architecture-specific setup for the goroutine startup.
|
||||
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
// Store the initial sp for the startTask function (implemented in assembly).
|
||||
s.sp = uintptr(unsafe.Pointer(r))
|
||||
|
||||
// Initialize the registers.
|
||||
// These will be popped off of the stack on the first resume of the goroutine.
|
||||
|
||||
// Start the function at tinygo_startTask (defined in src/internal/task/task_stack_arm.S).
|
||||
// This assembly code calls a function (passed in r4) with a single argument
|
||||
// (passed in r5). After the function returns, it calls Pause().
|
||||
r.pc = uintptr(unsafe.Pointer(&startTask))
|
||||
|
||||
// Pass the function to call in r4.
|
||||
// This function is a compiler-generated wrapper which loads arguments out of a struct pointer.
|
||||
// See createGoroutineStartWrapper (defined in compiler/goroutine.go) for more information.
|
||||
r.r4 = fn
|
||||
|
||||
// Pass the pointer to the arguments struct in r5.
|
||||
r.r5 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
}
|
||||
|
||||
// SystemStack returns the system stack pointer when called from a task stack.
|
||||
// When called from the system stack, it returns 0.
|
||||
func SystemStack() uintptr {
|
||||
return systemStack
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
.section .text.tinygo_startTask
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
tinygo_startTask:
|
||||
.cfi_startproc
|
||||
// Small assembly stub for starting a goroutine. This is already run on the
|
||||
// new stack, with the callee-saved registers already loaded.
|
||||
// Most importantly, x19 contains the pc of the to-be-started function and
|
||||
// x20 contains the only argument it is given. Multiple arguments are packed
|
||||
// into one by storing them in a new allocation.
|
||||
|
||||
// Indicate to the unwinder that there is nothing to unwind, this is the
|
||||
// root frame. It avoids the following (bogus) error message in GDB:
|
||||
// Backtrace stopped: previous frame identical to this frame (corrupt stack?)
|
||||
.cfi_undefined lr
|
||||
|
||||
// Set the first argument of the goroutine start wrapper, which contains all
|
||||
// the arguments.
|
||||
mov x0, x20
|
||||
|
||||
// Branch to the "goroutine start" function. By using blx instead of bx,
|
||||
// we'll return here instead of tail calling.
|
||||
blr x19
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
b tinygo_pause
|
||||
.cfi_endproc
|
||||
.size tinygo_startTask, .-tinygo_startTask
|
||||
|
||||
.global tinygo_swapTask
|
||||
.type tinygo_swapTask, %function
|
||||
tinygo_swapTask:
|
||||
// This function gets the following parameters:
|
||||
// x0 = newStack uintptr
|
||||
// x1 = oldStack *uintptr
|
||||
|
||||
// Save all callee-saved registers:
|
||||
stp x19, x20, [sp, #-96]!
|
||||
stp x21, x22, [sp, #16]
|
||||
stp x23, x24, [sp, #32]
|
||||
stp x25, x26, [sp, #48]
|
||||
stp x27, x28, [sp, #64]
|
||||
stp x29, x30, [sp, #80]
|
||||
|
||||
// Save the current stack pointer in oldStack.
|
||||
mov x8, sp
|
||||
str x8, [x1]
|
||||
|
||||
// Switch to the new stack pointer.
|
||||
mov sp, x0
|
||||
|
||||
// Restore stack state and return.
|
||||
ldp x29, x30, [sp, #80]
|
||||
ldp x27, x28, [sp, #64]
|
||||
ldp x25, x26, [sp, #48]
|
||||
ldp x23, x24, [sp, #32]
|
||||
ldp x21, x22, [sp, #16]
|
||||
ldp x19, x20, [sp], #96
|
||||
ret
|
||||
@@ -0,0 +1,64 @@
|
||||
// +build scheduler.tasks,arm64
|
||||
|
||||
package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_arm64.S that relies on the exact
|
||||
// layout of this struct.
|
||||
type calleeSavedRegs struct {
|
||||
x19 uintptr
|
||||
x20 uintptr
|
||||
x21 uintptr
|
||||
x22 uintptr
|
||||
x23 uintptr
|
||||
x24 uintptr
|
||||
x25 uintptr
|
||||
x26 uintptr
|
||||
x27 uintptr
|
||||
x28 uintptr
|
||||
x29 uintptr
|
||||
|
||||
pc uintptr // aka x30 aka LR
|
||||
}
|
||||
|
||||
// archInit runs architecture-specific setup for the goroutine startup.
|
||||
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
// Store the initial sp for the startTask function (implemented in assembly).
|
||||
s.sp = uintptr(unsafe.Pointer(r))
|
||||
|
||||
// Initialize the registers.
|
||||
// These will be popped off of the stack on the first resume of the goroutine.
|
||||
|
||||
// Start the function at tinygo_startTask (defined in src/internal/task/task_stack_arm64.S).
|
||||
// This assembly code calls a function (passed in x19) with a single argument
|
||||
// (passed in x20). After the function returns, it calls Pause().
|
||||
r.pc = uintptr(unsafe.Pointer(&startTask))
|
||||
|
||||
// Pass the function to call in x19.
|
||||
// This function is a compiler-generated wrapper which loads arguments out of a struct pointer.
|
||||
// See createGoroutineStartWrapper (defined in compiler/goroutine.go) for more information.
|
||||
r.x19 = fn
|
||||
|
||||
// Pass the pointer to the arguments struct in x20.
|
||||
r.x20 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
}
|
||||
|
||||
// SystemStack returns the system stack pointer when called from a task stack.
|
||||
// When called from the system stack, it returns 0.
|
||||
func SystemStack() uintptr {
|
||||
return systemStack
|
||||
}
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
package task
|
||||
|
||||
// Note that this is almost the same as task_stack_arm.go, but it uses the MSP
|
||||
// register to store the system stack pointer instead of a global variable. The
|
||||
// big advantage of this is that interrupts always execute with MSP (and not
|
||||
// PSP, which is used for goroutines) so that goroutines do not need extra stack
|
||||
// space for interrupts.
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"unsafe"
|
||||
|
||||
@@ -328,3 +328,14 @@ var (
|
||||
SERCOM: 6,
|
||||
}
|
||||
)
|
||||
|
||||
// CAN on the SAM E54 Xplained Pro
|
||||
var (
|
||||
CAN0 = CAN{
|
||||
Bus: sam.CAN0,
|
||||
}
|
||||
|
||||
CAN1 = CAN{
|
||||
Bus: sam.CAN1,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -125,7 +125,7 @@ func init() {
|
||||
D7.High()
|
||||
}
|
||||
|
||||
// I2C on the Feather M4.
|
||||
// I2C on the Feather M4 CAN.
|
||||
var (
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
@@ -133,10 +133,21 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the Feather M4.
|
||||
// SPI on the Feather M4 CAN.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// CAN on the Feather M4 CAN.
|
||||
var (
|
||||
CAN0 = CAN{
|
||||
Bus: sam.CAN0,
|
||||
}
|
||||
|
||||
CAN1 = CAN{
|
||||
Bus: sam.CAN1,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -94,8 +94,8 @@ const (
|
||||
// I2C on the QT Py M0.
|
||||
var (
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
Bus: sam.SERCOM1_I2CM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ var (
|
||||
ErrNoPinChangeChannel = errors.New("machine: no channel available for pin interrupt")
|
||||
)
|
||||
|
||||
// PinMode sets the direction and pull mode of the pin. For example, PinOutput
|
||||
// sets the pin as an output and PinInputPullup sets the pin as an input with a
|
||||
// pull-up.
|
||||
type PinMode uint8
|
||||
|
||||
type PinConfig struct {
|
||||
Mode PinMode
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device"
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"errors"
|
||||
@@ -17,8 +16,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinAnalog PinMode = 1
|
||||
PinSERCOM PinMode = 2
|
||||
@@ -433,7 +430,18 @@ func (a ADC) Get() uint16 {
|
||||
sam.ADC.CTRLA.ClearBits(sam.ADC_CTRLA_ENABLE)
|
||||
waitADCSync()
|
||||
|
||||
return uint16(val) << 4 // scales from 12 to 16-bit result
|
||||
// scales to 16-bit result
|
||||
switch (sam.ADC.CTRLB.Get() & sam.ADC_CTRLB_RESSEL_Msk) >> sam.ADC_CTRLB_RESSEL_Pos {
|
||||
case sam.ADC_CTRLB_RESSEL_8BIT:
|
||||
val = val << 8
|
||||
case sam.ADC_CTRLB_RESSEL_10BIT:
|
||||
val = val << 6
|
||||
case sam.ADC_CTRLB_RESSEL_16BIT:
|
||||
val = val << 4
|
||||
case sam.ADC_CTRLB_RESSEL_12BIT:
|
||||
val = val << 4
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (a ADC) getADCChannel() uint8 {
|
||||
@@ -1290,43 +1298,21 @@ var (
|
||||
// spi.Tx(nil, rx)
|
||||
//
|
||||
func (spi SPI) Tx(w, r []byte) error {
|
||||
if spi.Bus.BAUD.Get() == 0x00 {
|
||||
// When the SPI Freq is 24MHz, special processing is performed to improve the speed.
|
||||
switch {
|
||||
case w == nil:
|
||||
// read only, so write zero and read a result.
|
||||
spi.rx(r)
|
||||
case r == nil:
|
||||
// write only
|
||||
spi.tx(w)
|
||||
|
||||
switch {
|
||||
case w == nil:
|
||||
// read only, so write zero and read a result.
|
||||
spi.rx(r)
|
||||
case r == nil:
|
||||
// write only
|
||||
spi.tx24mhz(w)
|
||||
|
||||
default:
|
||||
// write/read
|
||||
if len(w) != len(r) {
|
||||
return ErrTxInvalidSliceSize
|
||||
}
|
||||
|
||||
spi.txrx24mhz(w, r)
|
||||
default:
|
||||
// write/read
|
||||
if len(w) != len(r) {
|
||||
return ErrTxInvalidSliceSize
|
||||
}
|
||||
|
||||
} else {
|
||||
switch {
|
||||
case w == nil:
|
||||
// read only, so write zero and read a result.
|
||||
spi.rx(r)
|
||||
case r == nil:
|
||||
// write only
|
||||
spi.tx(w)
|
||||
|
||||
default:
|
||||
// write/read
|
||||
if len(w) != len(r) {
|
||||
return ErrTxInvalidSliceSize
|
||||
}
|
||||
|
||||
spi.txrx(w, r)
|
||||
}
|
||||
spi.txrx(w, r)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -1379,48 +1365,6 @@ func (spi SPI) txrx(tx, rx []byte) {
|
||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
|
||||
// tx24mhz is a special tx/rx function for CPU Clock 48 Mhz and SPI Freq 24 Mhz
|
||||
func (spi SPI) tx24mhz(tx []byte) {
|
||||
spi.Bus.DATA.Set(uint32(tx[0]))
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
|
||||
for i := 1; i < len(tx); i++ {
|
||||
spi.Bus.DATA.Set(uint32(tx[i]))
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
spi.Bus.DATA.Get()
|
||||
}
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||
}
|
||||
spi.Bus.DATA.Get()
|
||||
}
|
||||
|
||||
// txrx24mhz is a special tx/rx function for CPU Clock 48 Mhz and SPI Freq 24 Mhz
|
||||
func (spi SPI) txrx24mhz(tx, rx []byte) {
|
||||
spi.Bus.DATA.Set(uint32(tx[0]))
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
|
||||
for i := 1; i < len(rx); i++ {
|
||||
spi.Bus.DATA.Set(uint32(tx[i]))
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
rx[i-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||
}
|
||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
|
||||
// TCC is one timer/counter peripheral, which consists of a counter and multiple
|
||||
// output channels (that can be connected to actual pins). You can set the
|
||||
// frequency using SetPeriod, but only for all the channels in this TCC
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build sam,atsamd51
|
||||
// +build sam,atsamd51 sam,atsame5x
|
||||
|
||||
// Peripheral abstraction layer for the atsamd51.
|
||||
//
|
||||
@@ -20,8 +20,6 @@ func CPUFrequency() uint32 {
|
||||
return 120000000
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinAnalog PinMode = 1
|
||||
PinSERCOM PinMode = 2
|
||||
@@ -44,6 +42,9 @@ const (
|
||||
PinTCCF PinMode = PinTimerAlt
|
||||
PinTCCG PinMode = PinTCCPDEC
|
||||
PinInputPulldown PinMode = 18
|
||||
PinCAN PinMode = 19
|
||||
PinCAN0 PinMode = PinSDHC
|
||||
PinCAN1 PinMode = PinCom
|
||||
)
|
||||
|
||||
type PinChange uint8
|
||||
@@ -627,6 +628,18 @@ func (p Pin) Configure(config PinConfig) {
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN | sam.PORT_GROUP_PINCFG_DRVSTR)
|
||||
case PinSDHC:
|
||||
if p&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
|
||||
p.setPMux(val | (uint8(PinSDHC) << sam.PORT_GROUP_PMUX_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
|
||||
p.setPMux(val | (uint8(PinSDHC) << sam.PORT_GROUP_PMUX_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -848,7 +861,18 @@ func (a ADC) Get() uint16 {
|
||||
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
return uint16(val) << 4 // scales from 12 to 16-bit result
|
||||
// scales to 16-bit result
|
||||
switch (bus.CTRLB.Get() & sam.ADC_CTRLB_RESSEL_Msk) >> sam.ADC_CTRLB_RESSEL_Pos {
|
||||
case sam.ADC_CTRLB_RESSEL_8BIT:
|
||||
val = val << 8
|
||||
case sam.ADC_CTRLB_RESSEL_10BIT:
|
||||
val = val << 6
|
||||
case sam.ADC_CTRLB_RESSEL_16BIT:
|
||||
val = val << 4
|
||||
case sam.ADC_CTRLB_RESSEL_12BIT:
|
||||
val = val << 4
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (a ADC) getADCBus() *sam.ADC_Type {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,468 @@
|
||||
// +build sam,atsame51 sam,atsame54
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
CANRxFifoSize = 16
|
||||
CANTxFifoSize = 16
|
||||
CANEvFifoSize = 16
|
||||
)
|
||||
|
||||
// Message RAM can only be located in the first 64 KB area of the system RAM.
|
||||
// TODO: when the go:section pragma is merged, add the section configuration
|
||||
|
||||
//go:align 4
|
||||
var CANRxFifo [2][(8 + 64) * CANRxFifoSize]byte
|
||||
|
||||
//go:align 4
|
||||
var CANTxFifo [2][(8 + 64) * CANTxFifoSize]byte
|
||||
|
||||
//go:align 4
|
||||
var CANEvFifo [2][(8) * CANEvFifoSize]byte
|
||||
|
||||
type CAN struct {
|
||||
Bus *sam.CAN_Type
|
||||
}
|
||||
|
||||
type CANTransferRate uint32
|
||||
|
||||
// CAN transfer rates for CANConfig
|
||||
const (
|
||||
CANTransferRate125kbps CANTransferRate = 125000
|
||||
CANTransferRate250kbps CANTransferRate = 250000
|
||||
CANTransferRate500kbps CANTransferRate = 500000
|
||||
CANTransferRate1000kbps CANTransferRate = 1000000
|
||||
CANTransferRate2000kbps CANTransferRate = 2000000
|
||||
CANTransferRate4000kbps CANTransferRate = 4000000
|
||||
)
|
||||
|
||||
// CANConfig holds CAN configuration parameters. Tx and Rx need to be
|
||||
// specified with some pins. When the Standby Pin is specified, configure it
|
||||
// as an output pin and output Low in Configure(). If this operation is not
|
||||
// necessary, specify NoPin.
|
||||
type CANConfig struct {
|
||||
TransferRate CANTransferRate
|
||||
TransferRateFD CANTransferRate
|
||||
Tx Pin
|
||||
Rx Pin
|
||||
Standby Pin
|
||||
}
|
||||
|
||||
var (
|
||||
errCANInvalidTransferRate = errors.New("CAN: invalid TransferRate")
|
||||
errCANInvalidTransferRateFD = errors.New("CAN: invalid TransferRateFD")
|
||||
)
|
||||
|
||||
// Configure this CAN peripheral with the given configuration.
|
||||
func (can *CAN) Configure(config CANConfig) error {
|
||||
if config.Standby != NoPin {
|
||||
config.Standby.Configure(PinConfig{Mode: PinOutput})
|
||||
config.Standby.Low()
|
||||
}
|
||||
|
||||
mode := PinCAN0
|
||||
if can.instance() == 1 {
|
||||
mode = PinCAN1
|
||||
}
|
||||
|
||||
config.Rx.Configure(PinConfig{Mode: mode})
|
||||
config.Tx.Configure(PinConfig{Mode: mode})
|
||||
|
||||
can.Bus.CCCR.SetBits(sam.CAN_CCCR_INIT)
|
||||
for !can.Bus.CCCR.HasBits(sam.CAN_CCCR_INIT) {
|
||||
}
|
||||
|
||||
can.Bus.CCCR.SetBits(sam.CAN_CCCR_CCE)
|
||||
|
||||
can.Bus.CCCR.SetBits(sam.CAN_CCCR_BRSE | sam.CAN_CCCR_FDOE)
|
||||
can.Bus.MRCFG.Set(sam.CAN_MRCFG_QOS_MEDIUM)
|
||||
// base clock == 48 MHz
|
||||
if config.TransferRate == 0 {
|
||||
config.TransferRate = CANTransferRate500kbps
|
||||
}
|
||||
brp := uint32(6)
|
||||
switch config.TransferRate {
|
||||
case CANTransferRate125kbps:
|
||||
brp = 32
|
||||
case CANTransferRate250kbps:
|
||||
brp = 16
|
||||
case CANTransferRate500kbps:
|
||||
brp = 8
|
||||
case CANTransferRate1000kbps:
|
||||
brp = 4
|
||||
default:
|
||||
return errCANInvalidTransferRate
|
||||
}
|
||||
can.Bus.NBTP.Set(8<<sam.CAN_NBTP_NTSEG1_Pos | (brp-1)<<sam.CAN_NBTP_NBRP_Pos |
|
||||
1<<sam.CAN_NBTP_NTSEG2_Pos | 3<<sam.CAN_NBTP_NSJW_Pos)
|
||||
|
||||
if config.TransferRateFD == 0 {
|
||||
config.TransferRateFD = CANTransferRate1000kbps
|
||||
}
|
||||
if config.TransferRateFD < config.TransferRate {
|
||||
return errCANInvalidTransferRateFD
|
||||
}
|
||||
brp = uint32(2)
|
||||
switch config.TransferRateFD {
|
||||
case CANTransferRate125kbps:
|
||||
brp = 32
|
||||
case CANTransferRate250kbps:
|
||||
brp = 16
|
||||
case CANTransferRate500kbps:
|
||||
brp = 8
|
||||
case CANTransferRate1000kbps:
|
||||
brp = 4
|
||||
case CANTransferRate2000kbps:
|
||||
brp = 2
|
||||
case CANTransferRate4000kbps:
|
||||
brp = 1
|
||||
default:
|
||||
return errCANInvalidTransferRateFD
|
||||
}
|
||||
can.Bus.DBTP.Set((brp-1)<<sam.CAN_DBTP_DBRP_Pos | 8<<sam.CAN_DBTP_DTSEG1_Pos |
|
||||
1<<sam.CAN_DBTP_DTSEG2_Pos | 3<<sam.CAN_DBTP_DSJW_Pos)
|
||||
|
||||
can.Bus.RXF0C.Set(sam.CAN_RXF0C_F0OM | CANRxFifoSize<<sam.CAN_RXF0C_F0S_Pos | uint32(uintptr(unsafe.Pointer(&CANRxFifo[can.instance()][0])))&0xFFFF)
|
||||
can.Bus.RXESC.Set(sam.CAN_RXESC_F0DS_DATA64)
|
||||
can.Bus.TXESC.Set(sam.CAN_TXESC_TBDS_DATA64)
|
||||
can.Bus.TXBC.Set(CANTxFifoSize<<sam.CAN_TXBC_TFQS_Pos | 0<<sam.CAN_TXBC_NDTB_Pos | uint32(uintptr(unsafe.Pointer(&CANTxFifo[can.instance()][0])))&0xFFFF)
|
||||
can.Bus.TXEFC.Set(CANEvFifoSize<<sam.CAN_TXEFC_EFS_Pos | uint32(uintptr(unsafe.Pointer(&CANEvFifo[can.instance()][0])))&0xFFFF)
|
||||
|
||||
can.Bus.TSCC.Set(sam.CAN_TSCC_TSS_INC)
|
||||
|
||||
can.Bus.GFC.Set(0<<sam.CAN_GFC_ANFS_Pos | 0<<sam.CAN_GFC_ANFE_Pos)
|
||||
|
||||
can.Bus.SIDFC.Set(0 << sam.CAN_SIDFC_LSS_Pos)
|
||||
can.Bus.XIDFC.Set(0 << sam.CAN_SIDFC_LSS_Pos)
|
||||
|
||||
can.Bus.XIDAM.Set(0x1FFFFFFF << sam.CAN_XIDAM_EIDM_Pos)
|
||||
|
||||
can.Bus.ILE.SetBits(sam.CAN_ILE_EINT0)
|
||||
|
||||
can.Bus.CCCR.ClearBits(sam.CAN_CCCR_CCE)
|
||||
can.Bus.CCCR.ClearBits(sam.CAN_CCCR_INIT)
|
||||
for can.Bus.CCCR.HasBits(sam.CAN_CCCR_INIT) {
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Callbacks to be called for CAN.SetInterrupt(). Wre're using the magic
|
||||
// constant 2 and 32 here beacuse th SAM E51/E54 has 2 CAN and 32 interrupt
|
||||
// sources.
|
||||
var (
|
||||
canInstances [2]*CAN
|
||||
canCallbacks [2][32]func(*CAN)
|
||||
)
|
||||
|
||||
// SetInterrupt sets an interrupt to be executed when a particular CAN state.
|
||||
//
|
||||
// This call will replace a previously set callback. You can pass a nil func
|
||||
// to unset the CAN interrupt. If you do so, the change parameter is ignored
|
||||
// and can be set to any value (such as 0).
|
||||
func (can *CAN) SetInterrupt(ie uint32, callback func(*CAN)) error {
|
||||
if callback == nil {
|
||||
// Disable this CAN interrupt
|
||||
can.Bus.IE.ClearBits(ie)
|
||||
return nil
|
||||
}
|
||||
can.Bus.IE.SetBits(ie)
|
||||
|
||||
idx := 0
|
||||
switch can.Bus {
|
||||
case sam.CAN0:
|
||||
canInstances[0] = can
|
||||
case sam.CAN1:
|
||||
canInstances[1] = can
|
||||
idx = 1
|
||||
}
|
||||
|
||||
for i := uint(0); i < 32; i++ {
|
||||
if ie&(1<<i) != 0 {
|
||||
canCallbacks[idx][i] = callback
|
||||
}
|
||||
}
|
||||
|
||||
switch can.Bus {
|
||||
case sam.CAN0:
|
||||
interrupt.New(sam.IRQ_CAN0, func(interrupt.Interrupt) {
|
||||
ir := sam.CAN0.IR.Get()
|
||||
sam.CAN0.IR.Set(ir) // clear interrupt
|
||||
for i := uint(0); i < 32; i++ {
|
||||
if ir&(1<<i) != 0 && canCallbacks[0][i] != nil {
|
||||
canCallbacks[0][i](canInstances[0])
|
||||
}
|
||||
}
|
||||
}).Enable()
|
||||
case sam.CAN1:
|
||||
interrupt.New(sam.IRQ_CAN1, func(interrupt.Interrupt) {
|
||||
ir := sam.CAN1.IR.Get()
|
||||
sam.CAN1.IR.Set(ir) // clear interrupt
|
||||
for i := uint(0); i < 32; i++ {
|
||||
if ir&(1<<i) != 0 && canCallbacks[1][i] != nil {
|
||||
canCallbacks[1][i](canInstances[1])
|
||||
}
|
||||
}
|
||||
}).Enable()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TxFifoIsFull returns whether TxFifo is full or not.
|
||||
func (can *CAN) TxFifoIsFull() bool {
|
||||
return (can.Bus.TXFQS.Get() & sam.CAN_TXFQS_TFQF_Msk) == sam.CAN_TXFQS_TFQF_Msk
|
||||
}
|
||||
|
||||
// TxRaw sends a CAN Frame according to CANTxBufferElement.
|
||||
func (can *CAN) TxRaw(e *CANTxBufferElement) {
|
||||
putIndex := (can.Bus.TXFQS.Get() & sam.CAN_TXFQS_TFQPI_Msk) >> sam.CAN_TXFQS_TFQPI_Pos
|
||||
|
||||
f := CANTxFifo[can.instance()][putIndex*(8+64) : (putIndex+1)*(8+64)]
|
||||
id := e.ID
|
||||
if !e.XTD {
|
||||
// standard identifier is stored into ID[28:18]
|
||||
id <<= 18
|
||||
}
|
||||
|
||||
f[3] = byte(id >> 24)
|
||||
if e.ESI {
|
||||
f[3] |= 0x80
|
||||
}
|
||||
if e.XTD {
|
||||
f[3] |= 0x40
|
||||
}
|
||||
if e.RTR {
|
||||
f[3] |= 0x20
|
||||
}
|
||||
f[2] = byte(id >> 16)
|
||||
f[1] = byte(id >> 8)
|
||||
f[0] = byte(0)
|
||||
f[7] = e.MM
|
||||
f[6] = e.DLC
|
||||
if e.EFC {
|
||||
f[6] |= 0x80
|
||||
}
|
||||
if e.FDF {
|
||||
f[6] |= 0x20
|
||||
}
|
||||
if e.BRS {
|
||||
f[6] |= 0x10
|
||||
}
|
||||
f[5] = 0x00 // reserved
|
||||
f[4] = 0x00 // reserved
|
||||
|
||||
length := CANDlcToLength(e.DLC, e.FDF)
|
||||
for i := byte(0); i < length; i++ {
|
||||
f[8+i] = e.DB[i]
|
||||
}
|
||||
|
||||
can.Bus.TXBAR.SetBits(1 << putIndex)
|
||||
}
|
||||
|
||||
// The Tx transmits CAN frames. It is easier to use than TxRaw, but not as
|
||||
// flexible.
|
||||
func (can *CAN) Tx(id uint32, data []byte, isFD, isExtendedID bool) {
|
||||
length := byte(len(data))
|
||||
dlc := CANLengthToDlc(length, true)
|
||||
|
||||
e := CANTxBufferElement{
|
||||
ESI: false,
|
||||
XTD: isExtendedID,
|
||||
RTR: false,
|
||||
ID: id,
|
||||
MM: 0x00,
|
||||
EFC: true,
|
||||
FDF: isFD,
|
||||
BRS: isFD,
|
||||
DLC: dlc,
|
||||
}
|
||||
|
||||
if !isFD {
|
||||
if length > 8 {
|
||||
length = 8
|
||||
}
|
||||
}
|
||||
for i := byte(0); i < length; i++ {
|
||||
e.DB[i] = data[i]
|
||||
}
|
||||
|
||||
can.TxRaw(&e)
|
||||
}
|
||||
|
||||
// RxFifoSize returns the number of CAN Frames currently stored in the RXFifo.
|
||||
func (can *CAN) RxFifoSize() int {
|
||||
sz := (can.Bus.RXF0S.Get() & sam.CAN_RXF0S_F0FL_Msk) >> sam.CAN_RXF0S_F0FL_Pos
|
||||
return int(sz)
|
||||
}
|
||||
|
||||
// RxFifoIsFull returns whether RxFifo is full or not.
|
||||
func (can *CAN) RxFifoIsFull() bool {
|
||||
sz := (can.Bus.RXF0S.Get() & sam.CAN_RXF0S_F0FL_Msk) >> sam.CAN_RXF0S_F0FL_Pos
|
||||
return sz == CANRxFifoSize
|
||||
}
|
||||
|
||||
// RxFifoIsEmpty returns whether RxFifo is empty or not.
|
||||
func (can *CAN) RxFifoIsEmpty() bool {
|
||||
sz := (can.Bus.RXF0S.Get() & sam.CAN_RXF0S_F0FL_Msk) >> sam.CAN_RXF0S_F0FL_Pos
|
||||
return sz == 0
|
||||
}
|
||||
|
||||
// RxRaw copies the received CAN frame to CANRxBufferElement.
|
||||
func (can *CAN) RxRaw(e *CANRxBufferElement) {
|
||||
idx := (can.Bus.RXF0S.Get() & sam.CAN_RXF0S_F0GI_Msk) >> sam.CAN_RXF0S_F0GI_Pos
|
||||
f := CANRxFifo[can.instance()][idx*(8+64):]
|
||||
|
||||
e.ESI = false
|
||||
if (f[3] & 0x80) != 0x00 {
|
||||
e.ESI = true
|
||||
}
|
||||
|
||||
e.XTD = false
|
||||
if (f[3] & 0x40) != 0x00 {
|
||||
e.XTD = true
|
||||
}
|
||||
|
||||
e.RTR = false
|
||||
if (f[3] & 0x20) != 0x00 {
|
||||
e.RTR = true
|
||||
}
|
||||
|
||||
id := ((uint32(f[3]) << 24) + (uint32(f[2]) << 16) + (uint32(f[1]) << 8) + uint32(f[0])) & 0x1FFFFFFF
|
||||
if (f[3] & 0x20) == 0 {
|
||||
id >>= 18
|
||||
id &= 0x000007FF
|
||||
}
|
||||
e.ID = id
|
||||
|
||||
e.ANMF = false
|
||||
if (f[7] & 0x80) != 0x00 {
|
||||
e.ANMF = true
|
||||
}
|
||||
|
||||
e.FIDX = f[7] & 0x7F
|
||||
|
||||
e.FDF = false
|
||||
if (f[6] & 0x20) != 0x00 {
|
||||
e.FDF = true
|
||||
}
|
||||
|
||||
e.BRS = false
|
||||
if (f[6] & 0x10) != 0x00 {
|
||||
e.BRS = true
|
||||
}
|
||||
|
||||
e.DLC = f[6] & 0x0F
|
||||
|
||||
e.RXTS = (uint16(f[5]) << 8) + uint16(f[4])
|
||||
|
||||
for i := byte(0); i < CANDlcToLength(e.DLC, e.FDF); i++ {
|
||||
e.DB[i] = f[i+8]
|
||||
}
|
||||
|
||||
can.Bus.RXF0A.ReplaceBits(idx, sam.CAN_RXF0A_F0AI_Msk, sam.CAN_RXF0A_F0AI_Pos)
|
||||
}
|
||||
|
||||
// Rx receives a CAN frame. It is easier to use than RxRaw, but not as
|
||||
// flexible.
|
||||
func (can *CAN) Rx() (id uint32, dlc byte, data []byte, isFd, isExtendedID bool) {
|
||||
e := CANRxBufferElement{}
|
||||
can.RxRaw(&e)
|
||||
length := CANDlcToLength(e.DLC, e.FDF)
|
||||
return e.ID, length, e.DB[:length], e.FDF, e.XTD
|
||||
}
|
||||
|
||||
func (can *CAN) instance() byte {
|
||||
if can.Bus == sam.CAN0 {
|
||||
return 0
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// CANTxBufferElement is a struct that corresponds to the same5x' Tx Buffer
|
||||
// Element.
|
||||
type CANTxBufferElement struct {
|
||||
ESI bool
|
||||
XTD bool
|
||||
RTR bool
|
||||
ID uint32
|
||||
MM uint8
|
||||
EFC bool
|
||||
FDF bool
|
||||
BRS bool
|
||||
DLC uint8
|
||||
DB [64]uint8
|
||||
}
|
||||
|
||||
// CANRxBufferElement is a struct that corresponds to the same5x Rx Buffer and
|
||||
// FIFO Element.
|
||||
type CANRxBufferElement struct {
|
||||
ESI bool
|
||||
XTD bool
|
||||
RTR bool
|
||||
ID uint32
|
||||
ANMF bool
|
||||
FIDX uint8
|
||||
FDF bool
|
||||
BRS bool
|
||||
DLC uint8
|
||||
RXTS uint16
|
||||
DB [64]uint8
|
||||
}
|
||||
|
||||
// Data returns the received data as a slice of the size according to dlc.
|
||||
func (e CANRxBufferElement) Data() []byte {
|
||||
return e.DB[:CANDlcToLength(e.DLC, e.FDF)]
|
||||
}
|
||||
|
||||
// CANDlcToLength() converts a DLC value to its actual length.
|
||||
func CANDlcToLength(dlc byte, isFD bool) byte {
|
||||
length := dlc
|
||||
if dlc == 0x09 {
|
||||
length = 12
|
||||
} else if dlc == 0x0A {
|
||||
length = 16
|
||||
} else if dlc == 0x0B {
|
||||
length = 20
|
||||
} else if dlc == 0x0C {
|
||||
length = 24
|
||||
} else if dlc == 0x0D {
|
||||
length = 32
|
||||
} else if dlc == 0x0E {
|
||||
length = 48
|
||||
} else if dlc == 0x0F {
|
||||
length = 64
|
||||
}
|
||||
return length
|
||||
|
||||
}
|
||||
|
||||
// CANLengthToDlc() converts its actual length to a DLC value.
|
||||
func CANLengthToDlc(length byte, isFD bool) byte {
|
||||
dlc := length
|
||||
if length <= 0x08 {
|
||||
} else if length <= 12 {
|
||||
dlc = 0x09
|
||||
} else if length <= 16 {
|
||||
dlc = 0x0A
|
||||
} else if length <= 20 {
|
||||
dlc = 0x0B
|
||||
} else if length <= 24 {
|
||||
dlc = 0x0C
|
||||
} else if length <= 32 {
|
||||
dlc = 0x0D
|
||||
} else if length <= 48 {
|
||||
dlc = 0x0E
|
||||
} else if length <= 64 {
|
||||
dlc = 0x0F
|
||||
}
|
||||
return dlc
|
||||
}
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinInput PinMode = iota
|
||||
PinInputPullup
|
||||
|
||||
@@ -21,8 +21,6 @@ var (
|
||||
ErrInvalidSPIBus = errors.New("machine: invalid SPI bus")
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinOutput PinMode = iota
|
||||
PinInput
|
||||
|
||||
@@ -11,8 +11,6 @@ func CPUFrequency() uint32 {
|
||||
return 80000000 // 80MHz
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinOutput PinMode = iota
|
||||
PinInput
|
||||
|
||||
@@ -12,8 +12,6 @@ func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinInput PinMode = iota
|
||||
PinOutput
|
||||
|
||||
@@ -30,8 +30,6 @@ const (
|
||||
// Make it easier to directly write to I/O RAM.
|
||||
var ioram = (*[0x400]volatile.Register8)(unsafe.Pointer(uintptr(0x04000000)))
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
// Set has not been implemented.
|
||||
func (p Pin) Set(value bool) {
|
||||
// do nothing
|
||||
|
||||
@@ -10,8 +10,6 @@ var (
|
||||
UART0 = UART{0}
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinInput PinMode = iota
|
||||
PinOutput
|
||||
|
||||
@@ -14,7 +14,6 @@ func CPUFrequency() uint32 {
|
||||
return 390000000
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
type fpioaPullMode uint8
|
||||
type PinChange uint8
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@ func CPUFrequency() uint32 {
|
||||
return 600000000
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
// GPIO
|
||||
PinInput PinMode = iota
|
||||
|
||||
@@ -13,8 +13,6 @@ var (
|
||||
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinInput PinMode = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos)
|
||||
PinInputPullup PinMode = PinInput | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos)
|
||||
|
||||
@@ -37,8 +37,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
PinInput PinMode = iota
|
||||
PinInputPullUp
|
||||
|
||||
@@ -17,8 +17,6 @@ const (
|
||||
portJ
|
||||
)
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
// Peripheral operations sequence:
|
||||
// 1. Enable the clock to the alternate function.
|
||||
// 2. Enable clock to corresponding GPIO
|
||||
|
||||
@@ -204,15 +204,16 @@ func (spi SPI) configurePins(config SPIConfig) {
|
||||
// There are 2 I2C interfaces on the STM32F103xx.
|
||||
// Since the first interface is named I2C1, both I2C0 and I2C1 refer to I2C1.
|
||||
// TODO: implement I2C2.
|
||||
var (
|
||||
I2C1 = (*I2C)(unsafe.Pointer(stm32.I2C1))
|
||||
I2C0 = I2C1
|
||||
)
|
||||
|
||||
type I2C struct {
|
||||
Bus *stm32.I2C_Type
|
||||
}
|
||||
|
||||
var (
|
||||
I2C1 = &I2C{Bus: stm32.I2C1}
|
||||
I2C0 = I2C1
|
||||
)
|
||||
|
||||
func (i2c *I2C) configurePins(config I2CConfig) {
|
||||
if config.SDA == PB9 {
|
||||
// use alternate I2C1 pins PB8/PB9 via AFIO mapping
|
||||
|
||||
@@ -14,6 +14,9 @@ const TargetBits = 32
|
||||
//go:extern __heap_base
|
||||
var heapStartSymbol [0]byte
|
||||
|
||||
//go:extern __global_base
|
||||
var globalsStartSymbol [0]byte
|
||||
|
||||
//export llvm.wasm.memory.size.i32
|
||||
func wasm_memory_size(index int32) int32
|
||||
|
||||
@@ -21,8 +24,10 @@ func wasm_memory_size(index int32) int32
|
||||
func wasm_memory_grow(index int32, delta int32) int32
|
||||
|
||||
var (
|
||||
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
|
||||
heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
|
||||
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
|
||||
heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
|
||||
globalsStart = uintptr(unsafe.Pointer(&globalsStartSymbol))
|
||||
globalsEnd = uintptr(unsafe.Pointer(&heapStartSymbol))
|
||||
)
|
||||
|
||||
const wasmPageSize = 64 * 1024
|
||||
|
||||
@@ -401,7 +401,7 @@ func markRoots(start, end uintptr) {
|
||||
}
|
||||
}
|
||||
|
||||
for addr := start; addr != end; addr += unsafe.Alignof(addr) {
|
||||
for addr := start; addr < end; addr += unsafe.Alignof(addr) {
|
||||
root := *(*uintptr)(unsafe.Pointer(addr))
|
||||
markRoot(addr, root)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// +build gc.conservative gc.extalloc
|
||||
// +build baremetal
|
||||
// +build baremetal wasm
|
||||
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// +build gc.conservative gc.extalloc
|
||||
// +build !baremetal
|
||||
// +build !baremetal,!wasm
|
||||
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -3,3 +3,11 @@
|
||||
package runtime
|
||||
|
||||
const GOOS = "darwin"
|
||||
|
||||
const (
|
||||
// See https://github.com/golang/go/blob/master/src/syscall/zerrors_darwin_amd64.go
|
||||
flag_PROT_READ = 0x1
|
||||
flag_PROT_WRITE = 0x2
|
||||
flag_MAP_PRIVATE = 0x2
|
||||
flag_MAP_ANONYMOUS = 0x1000 // MAP_ANON
|
||||
)
|
||||
|
||||
@@ -3,3 +3,11 @@
|
||||
package runtime
|
||||
|
||||
const GOOS = "linux"
|
||||
|
||||
const (
|
||||
// See https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/mman-common.h
|
||||
flag_PROT_READ = 0x1
|
||||
flag_PROT_WRITE = 0x2
|
||||
flag_MAP_PRIVATE = 0x2
|
||||
flag_MAP_ANONYMOUS = 0x20
|
||||
)
|
||||
|
||||
@@ -71,8 +71,6 @@ func ticks() timeUnit {
|
||||
return 0
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func sleepTicks(d timeUnit) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@@ -217,11 +217,19 @@ func initRTC() {
|
||||
waitForSync()
|
||||
|
||||
rtcInterrupt := interrupt.New(sam.IRQ_RTC, func(intr interrupt.Interrupt) {
|
||||
// disable IRQ for CMP0 compare
|
||||
sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
|
||||
timerWakeup.Set(1)
|
||||
flags := sam.RTC_MODE0.INTFLAG.Get()
|
||||
if flags&sam.RTC_MODE0_INTENSET_CMP0 != 0 {
|
||||
// The timer (for a sleep) has expired.
|
||||
timerWakeup.Set(1)
|
||||
}
|
||||
if flags&sam.RTC_MODE0_INTENSET_OVF != 0 {
|
||||
// The 32-bit RTC timer has overflowed.
|
||||
rtcOverflows.Set(rtcOverflows.Get() + 1)
|
||||
}
|
||||
// Mark this interrupt has handled for CMP0 and OVF.
|
||||
sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0 | sam.RTC_MODE0_INTENSET_OVF)
|
||||
})
|
||||
sam.RTC_MODE0.INTENSET.Set(sam.RTC_MODE0_INTENSET_OVF)
|
||||
rtcInterrupt.SetPriority(0xc0)
|
||||
rtcInterrupt.Enable()
|
||||
}
|
||||
@@ -231,15 +239,10 @@ func waitForSync() {
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
timestamp timeUnit // ticks since boottime
|
||||
timerLastCounter uint64
|
||||
)
|
||||
var rtcOverflows volatile.Register32 // number of times the RTC wrapped around
|
||||
|
||||
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
|
||||
@@ -259,7 +262,6 @@ func nanosecondsToTicks(ns int64) timeUnit {
|
||||
// sleepTicks should sleep for d number of microseconds.
|
||||
func sleepTicks(d timeUnit) {
|
||||
for d != 0 {
|
||||
ticks() // update timestamp
|
||||
ticks := uint32(d)
|
||||
if !timerSleep(ticks) {
|
||||
// Bail out early to handle a non-time interrupt.
|
||||
@@ -269,17 +271,37 @@ func sleepTicks(d timeUnit) {
|
||||
}
|
||||
}
|
||||
|
||||
// ticks returns number of microseconds since start.
|
||||
// ticks returns the elapsed time since reset.
|
||||
func ticks() timeUnit {
|
||||
// For some ways of capturing the time atomically, see this thread:
|
||||
// https://www.eevblog.com/forum/microcontrollers/correct-timing-by-timer-overflow-count/msg749617/#msg749617
|
||||
// Here, instead of re-reading the counter register if an overflow has been
|
||||
// detected, we simply try again because that results in smaller code.
|
||||
for {
|
||||
mask := interrupt.Disable()
|
||||
counter := readRTC()
|
||||
overflows := rtcOverflows.Get()
|
||||
hasOverflow := sam.RTC_MODE0.INTFLAG.Get()&sam.RTC_MODE0_INTENSET_OVF != 0
|
||||
interrupt.Restore(mask)
|
||||
|
||||
if hasOverflow {
|
||||
// There was an overflow while trying to capture the timer.
|
||||
// Try again.
|
||||
continue
|
||||
}
|
||||
|
||||
// This is a 32-bit timer, so the number of timer overflows forms the
|
||||
// upper 32 bits of this timer.
|
||||
return timeUnit(overflows)<<32 + timeUnit(counter)
|
||||
}
|
||||
}
|
||||
|
||||
func readRTC() uint32 {
|
||||
// request read of count
|
||||
sam.RTC_MODE0.READREQ.Set(sam.RTC_MODE0_READREQ_RREQ)
|
||||
waitForSync()
|
||||
|
||||
rtcCounter := uint64(sam.RTC_MODE0.COUNT.Get()) // each counter tick == 30.5us
|
||||
offset := (rtcCounter - timerLastCounter) // change since last measurement
|
||||
timerLastCounter = rtcCounter
|
||||
timestamp += timeUnit(offset)
|
||||
return timestamp
|
||||
return sam.RTC_MODE0.COUNT.Get()
|
||||
}
|
||||
|
||||
// ticks are in microseconds
|
||||
@@ -305,7 +327,7 @@ func timerSleep(ticks uint32) bool {
|
||||
waitForSync()
|
||||
|
||||
// enable IRQ for CMP0 compare
|
||||
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
sam.RTC_MODE0.INTENSET.Set(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
|
||||
wait:
|
||||
waitForEvents()
|
||||
@@ -315,7 +337,7 @@ wait:
|
||||
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)
|
||||
sam.RTC_MODE0.INTENCLR.Set(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
return false
|
||||
} else {
|
||||
// This is running without a scheduler.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build sam,atsamd51
|
||||
// +build sam,atsamd51 sam,atsame5x
|
||||
|
||||
package runtime
|
||||
|
||||
@@ -16,6 +16,7 @@ func postinit() {}
|
||||
|
||||
//export Reset_Handler
|
||||
func main() {
|
||||
arm.SCB.CPACR.Set(0) // disable FPU if it is enabled
|
||||
preinit()
|
||||
run()
|
||||
abort()
|
||||
@@ -205,11 +206,19 @@ func initRTC() {
|
||||
}
|
||||
|
||||
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)
|
||||
flags := sam.RTC_MODE0.INTFLAG.Get()
|
||||
if flags&sam.RTC_MODE0_INTENSET_CMP0 != 0 {
|
||||
// The timer (for a sleep) has expired.
|
||||
timerWakeup.Set(1)
|
||||
}
|
||||
if flags&sam.RTC_MODE0_INTENSET_OVF != 0 {
|
||||
// The 32-bit RTC timer has overflowed.
|
||||
rtcOverflows.Set(rtcOverflows.Get() + 1)
|
||||
}
|
||||
// Mark this interrupt has handled for CMP0 and OVF.
|
||||
sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0 | sam.RTC_MODE0_INTENSET_OVF)
|
||||
})
|
||||
sam.RTC_MODE0.INTENSET.Set(sam.RTC_MODE0_INTENSET_OVF)
|
||||
irq.SetPriority(0xc0)
|
||||
irq.Enable()
|
||||
}
|
||||
@@ -219,15 +228,10 @@ func waitForSync() {
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
timestamp timeUnit // ticks since boottime
|
||||
timerLastCounter uint64
|
||||
)
|
||||
var rtcOverflows volatile.Register32 // number of times the RTC wrapped around
|
||||
|
||||
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
|
||||
@@ -247,7 +251,6 @@ func nanosecondsToTicks(ns int64) timeUnit {
|
||||
// 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
|
||||
@@ -256,15 +259,34 @@ func sleepTicks(d timeUnit) {
|
||||
}
|
||||
}
|
||||
|
||||
// ticks returns number of microseconds since start.
|
||||
// ticks returns the elapsed time since reset.
|
||||
func ticks() timeUnit {
|
||||
waitForSync()
|
||||
// For some ways of capturing the time atomically, see this thread:
|
||||
// https://www.eevblog.com/forum/microcontrollers/correct-timing-by-timer-overflow-count/msg749617/#msg749617
|
||||
// Here, instead of re-reading the counter register if an overflow has been
|
||||
// detected, we simply try again because that results in smaller code.
|
||||
for {
|
||||
mask := interrupt.Disable()
|
||||
counter := readRTC()
|
||||
overflows := rtcOverflows.Get()
|
||||
hasOverflow := sam.RTC_MODE0.INTFLAG.Get()&sam.RTC_MODE0_INTENSET_OVF != 0
|
||||
interrupt.Restore(mask)
|
||||
|
||||
rtcCounter := uint64(sam.RTC_MODE0.COUNT.Get())
|
||||
offset := (rtcCounter - timerLastCounter) // change since last measurement
|
||||
timerLastCounter = rtcCounter
|
||||
timestamp += timeUnit(offset)
|
||||
return timestamp
|
||||
if hasOverflow {
|
||||
// There was an overflow while trying to capture the timer.
|
||||
// Try again.
|
||||
continue
|
||||
}
|
||||
|
||||
// This is a 32-bit timer, so the number of timer overflows forms the
|
||||
// upper 32 bits of this timer.
|
||||
return timeUnit(overflows)<<32 + timeUnit(counter)
|
||||
}
|
||||
}
|
||||
|
||||
func readRTC() uint32 {
|
||||
waitForSync()
|
||||
return sam.RTC_MODE0.COUNT.Get()
|
||||
}
|
||||
|
||||
// ticks are in microseconds
|
||||
@@ -289,7 +311,7 @@ func timerSleep(ticks uint32) bool {
|
||||
sam.RTC_MODE0.COMP[0].Set(uint32(cnt) + ticks)
|
||||
|
||||
// enable IRQ for CMP0 compare
|
||||
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
sam.RTC_MODE0.INTENSET.Set(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
|
||||
wait:
|
||||
waitForEvents()
|
||||
@@ -299,7 +321,7 @@ wait:
|
||||
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)
|
||||
sam.RTC_MODE0.INTENCLR.Set(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
return false
|
||||
} else {
|
||||
// This is running without a scheduler.
|
||||
|
||||
@@ -1,338 +0,0 @@
|
||||
// +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")
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// +build sam,atsame51 sam,atsame54
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initCANClock()
|
||||
}
|
||||
|
||||
func initCANClock() {
|
||||
// Turn on clocks for CAN0/CAN1.
|
||||
sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_CAN0_)
|
||||
sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_CAN1_)
|
||||
|
||||
// Put Generic Clock Generator 1 as source for USB
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_CAN0].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_CAN1].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
@@ -58,8 +58,6 @@ func init() {
|
||||
initUART()
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
const tickNanos = 1024 * 16384 // roughly 16ms in nanoseconds
|
||||
|
||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||
|
||||
@@ -27,8 +27,6 @@ func main() {
|
||||
abort()
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||
return int64(ticks)
|
||||
}
|
||||
|
||||
@@ -97,8 +97,6 @@ func ticks() timeUnit {
|
||||
return timeUnit(uint64(esp.TIMG0.T0LO.Get()) | uint64(esp.TIMG0.T0HI.Get())<<32)
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func nanosecondsToTicks(ns int64) timeUnit {
|
||||
// Calculate the number of ticks from the number of nanoseconds. At a 80MHz
|
||||
// APB clock, that's 25 nanoseconds per tick with a timer prescaler of 2:
|
||||
|
||||
@@ -89,8 +89,6 @@ func ticks() timeUnit {
|
||||
return currentTime
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
const tickNanos = 3200 // time.Second / (80MHz / 256)
|
||||
|
||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||
|
||||
@@ -100,8 +100,6 @@ func putchar(c byte) {
|
||||
machine.UART0.WriteByte(c)
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
var timerWakeup volatile.Register8
|
||||
|
||||
func ticks() timeUnit {
|
||||
|
||||
@@ -112,8 +112,6 @@ func putchar(c byte) {
|
||||
machine.UART0.WriteByte(c)
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
var timerWakeup volatile.Register8
|
||||
|
||||
func ticks() timeUnit {
|
||||
|
||||
@@ -10,8 +10,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
//go:extern _svectors
|
||||
var _svectors [0]byte
|
||||
|
||||
@@ -102,11 +100,6 @@ func initSystem() {
|
||||
|
||||
func initPeripherals() {
|
||||
|
||||
// enable FPU - set CP10, CP11 full access
|
||||
nxp.SystemControl.CPACR.SetBits(
|
||||
((nxp.SCB_CPACR_CP10_CP10_3 << nxp.SCB_CPACR_CP10_Pos) & nxp.SCB_CPACR_CP10_Msk) |
|
||||
((nxp.SCB_CPACR_CP11_CP11_3 << nxp.SCB_CPACR_CP11_Pos) & nxp.SCB_CPACR_CP11_Msk))
|
||||
|
||||
enableTimerClocks() // activate GPT/PIT clock gates
|
||||
initSysTick() // enable SysTick
|
||||
initRTC() // enable real-time clock
|
||||
|
||||
@@ -6,8 +6,6 @@ import "unsafe"
|
||||
|
||||
type timeUnit int64
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
const (
|
||||
// Handles
|
||||
infoTypeTotalMemorySize = 6 // Total amount of memory available for process.
|
||||
|
||||
+38
-19
@@ -3,6 +3,7 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/nrf"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
@@ -18,6 +19,9 @@ func postinit() {}
|
||||
|
||||
//export Reset_Handler
|
||||
func main() {
|
||||
if nrf.FPUPresent {
|
||||
arm.SCB.CPACR.Set(0) // disable FPU if it is enabled
|
||||
}
|
||||
systemInit()
|
||||
preinit()
|
||||
run()
|
||||
@@ -43,10 +47,18 @@ func initLFCLK() {
|
||||
func initRTC() {
|
||||
nrf.RTC1.TASKS_START.Set(1)
|
||||
intr := interrupt.New(nrf.IRQ_RTC1, func(intr interrupt.Interrupt) {
|
||||
nrf.RTC1.INTENCLR.Set(nrf.RTC_INTENSET_COMPARE0)
|
||||
nrf.RTC1.EVENTS_COMPARE[0].Set(0)
|
||||
rtc_wakeup.Set(1)
|
||||
if nrf.RTC1.EVENTS_COMPARE[0].Get() != 0 {
|
||||
nrf.RTC1.EVENTS_COMPARE[0].Set(0)
|
||||
nrf.RTC1.INTENCLR.Set(nrf.RTC_INTENSET_COMPARE0)
|
||||
nrf.RTC1.EVENTS_COMPARE[0].Set(0)
|
||||
rtc_wakeup.Set(1)
|
||||
}
|
||||
if nrf.RTC1.EVENTS_OVRFLW.Get() != 0 {
|
||||
nrf.RTC1.EVENTS_OVRFLW.Set(0)
|
||||
rtcOverflows.Set(rtcOverflows.Get() + 1)
|
||||
}
|
||||
})
|
||||
nrf.RTC1.INTENSET.Set(nrf.RTC_INTENSET_OVRFLW)
|
||||
intr.SetPriority(0xc0) // low priority
|
||||
intr.Enable()
|
||||
}
|
||||
@@ -55,21 +67,15 @@ func putchar(c byte) {
|
||||
machine.UART0.WriteByte(c)
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func sleepTicks(d timeUnit) {
|
||||
for d != 0 {
|
||||
ticks() // update timestamp
|
||||
ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side)
|
||||
rtc_sleep(ticks)
|
||||
d -= timeUnit(ticks)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
timestamp timeUnit // nanoseconds since boottime
|
||||
rtcLastCounter uint32 // 24 bits ticks
|
||||
)
|
||||
var rtcOverflows volatile.Register32 // number of times the RTC wrapped around
|
||||
|
||||
// ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds.
|
||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||
@@ -88,16 +94,29 @@ func nanosecondsToTicks(ns int64) timeUnit {
|
||||
}
|
||||
|
||||
// Monotonically increasing numer of ticks since start.
|
||||
//
|
||||
// Note: very long pauses between measurements (more than 8 minutes) may
|
||||
// overflow the counter, leading to incorrect results. This might be fixed by
|
||||
// handling the overflow event.
|
||||
func ticks() timeUnit {
|
||||
rtcCounter := uint32(nrf.RTC1.COUNTER.Get())
|
||||
offset := (rtcCounter - rtcLastCounter) & 0xffffff // change since last measurement
|
||||
rtcLastCounter = rtcCounter
|
||||
timestamp += timeUnit(offset)
|
||||
return timestamp
|
||||
// For some ways of capturing the time atomically, see this thread:
|
||||
// https://www.eevblog.com/forum/microcontrollers/correct-timing-by-timer-overflow-count/msg749617/#msg749617
|
||||
// Here, instead of re-reading the counter register if an overflow has been
|
||||
// detected, we simply try again because that results in (slightly) smaller
|
||||
// code and is perhaps easier to prove correct.
|
||||
for {
|
||||
mask := interrupt.Disable()
|
||||
counter := uint32(nrf.RTC1.COUNTER.Get())
|
||||
overflows := rtcOverflows.Get()
|
||||
hasOverflow := nrf.RTC1.EVENTS_OVRFLW.Get() != 0
|
||||
interrupt.Restore(mask)
|
||||
|
||||
if hasOverflow {
|
||||
// There was an overflow. Try again.
|
||||
continue
|
||||
}
|
||||
|
||||
// The counter is 24 bits in size, so the number of overflows form the
|
||||
// upper 32 bits (together 56 bits, which covers 71493 years at
|
||||
// 32768kHz: I'd argue good enough for most purposes).
|
||||
return timeUnit(overflows)<<24 + timeUnit(counter)
|
||||
}
|
||||
}
|
||||
|
||||
var rtc_wakeup volatile.Register8
|
||||
|
||||
@@ -24,10 +24,10 @@ func waitForEvents() {
|
||||
if enabled != 0 {
|
||||
// Now pick the appropriate SVCall number. Hopefully they won't change
|
||||
// in the future with a different SoftDevice version.
|
||||
if nrf.DEVICE == "nrf51" {
|
||||
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" || nrf.DEVICE == "nrf52833" {
|
||||
} 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 {
|
||||
|
||||
@@ -75,7 +75,6 @@ func initSystem() {
|
||||
nxp.SIM.SCGC3.Set(nxp.SIM_SCGC3_ADC1 | nxp.SIM_SCGC3_FTM2 | nxp.SIM_SCGC3_FTM3)
|
||||
nxp.SIM.SCGC5.Set(0x00043F82) // clocks active to all GPIO
|
||||
nxp.SIM.SCGC6.Set(nxp.SIM_SCGC6_RTC | nxp.SIM_SCGC6_FTM0 | nxp.SIM_SCGC6_FTM1 | nxp.SIM_SCGC6_ADC0 | nxp.SIM_SCGC6_FTF)
|
||||
nxp.SystemControl.CPACR.Set(0x00F00000)
|
||||
nxp.LMEM.PCCCR.Set(0x85000003)
|
||||
|
||||
// release I/O pins hold, if we woke up from VLLS mode
|
||||
@@ -233,9 +232,6 @@ func putchar(c byte) {
|
||||
machine.PutcharUART(&machine.UART0, c)
|
||||
}
|
||||
|
||||
// ???
|
||||
const asyncScheduler = false
|
||||
|
||||
func abort() {
|
||||
println("!!! ABORT !!!")
|
||||
|
||||
|
||||
@@ -24,8 +24,6 @@ const (
|
||||
|
||||
type arrtype = uint32
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func init() {
|
||||
initCLK()
|
||||
|
||||
|
||||
@@ -80,8 +80,6 @@ const (
|
||||
|
||||
type arrtype = uint32
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func init() {
|
||||
initOSC() // configure oscillators
|
||||
initCLK()
|
||||
|
||||
@@ -42,8 +42,6 @@ const (
|
||||
|
||||
type arrtype = uint32
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func init() {
|
||||
initCLK()
|
||||
|
||||
|
||||
@@ -41,8 +41,6 @@ const (
|
||||
|
||||
type arrtype = uint32
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func init() {
|
||||
initCLK()
|
||||
|
||||
|
||||
@@ -13,8 +13,6 @@ const (
|
||||
|
||||
type arrtype = uint16
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func putchar(c byte) {
|
||||
machine.UART0.WriteByte(c)
|
||||
}
|
||||
|
||||
@@ -66,8 +66,6 @@ const (
|
||||
|
||||
type arrtype = uint32
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func init() {
|
||||
initCLK()
|
||||
|
||||
|
||||
@@ -42,8 +42,6 @@ const (
|
||||
|
||||
type arrtype = uint32
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func init() {
|
||||
initCLK()
|
||||
|
||||
|
||||
@@ -24,8 +24,6 @@ func main() {
|
||||
abort()
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||
return int64(ticks)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ func usleep(usec uint) int
|
||||
//export malloc
|
||||
func malloc(size uintptr) unsafe.Pointer
|
||||
|
||||
//export mmap
|
||||
func mmap(addr unsafe.Pointer, length, prot, flags, fd int, offset int) unsafe.Pointer
|
||||
|
||||
//export abort
|
||||
func abort()
|
||||
|
||||
@@ -55,18 +58,16 @@ func main(argc int32, argv *unsafe.Pointer) int {
|
||||
cap uintptr
|
||||
})(unsafe.Pointer(&args))
|
||||
argsSlice.ptr = malloc(uintptr(argc) * (unsafe.Sizeof(uintptr(0))) * 3)
|
||||
argsSlice.len = 0
|
||||
argsSlice.len = uintptr(argc)
|
||||
argsSlice.cap = uintptr(argc)
|
||||
|
||||
// Initialize command line parameters.
|
||||
for *argv != nil {
|
||||
for i := 0; i < int(argc); i++ {
|
||||
// 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)))
|
||||
arg := (*_string)(unsafe.Pointer(&args[i]))
|
||||
arg.length = length
|
||||
arg.ptr = (*byte)(*argv)
|
||||
// This is the Go equivalent of "argc++" in C.
|
||||
argv = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + unsafe.Sizeof(argv)))
|
||||
}
|
||||
@@ -122,8 +123,6 @@ func putchar(c byte) {
|
||||
_putchar(int(c))
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||
// The OS API works in nanoseconds so no conversion necessary.
|
||||
return int64(ticks)
|
||||
|
||||
@@ -5,20 +5,33 @@
|
||||
|
||||
package runtime
|
||||
|
||||
const heapSize = 1 * 1024 * 1024 // 1MB to start
|
||||
var heapSize uintptr = 128 * 1024 // small amount to start
|
||||
const heapMaxSize = 1 * 1024 * 1024 * 1024 // 1GB for the entire heap
|
||||
|
||||
var heapStart, heapEnd uintptr
|
||||
|
||||
func preinit() {
|
||||
heapStart = uintptr(malloc(heapSize))
|
||||
// Allocate a large chunk of virtual memory. Because it is virtual, it won't
|
||||
// really be allocated in RAM. Memory will only be allocated when it is
|
||||
// first touched.
|
||||
addr := mmap(nil, heapMaxSize, flag_PROT_READ|flag_PROT_WRITE, flag_MAP_PRIVATE|flag_MAP_ANONYMOUS, -1, 0)
|
||||
heapStart = uintptr(addr)
|
||||
heapEnd = heapStart + heapSize
|
||||
}
|
||||
|
||||
// growHeap tries to grow the heap size. It returns true if it succeeds, false
|
||||
// otherwise.
|
||||
func growHeap() bool {
|
||||
// At the moment, this is not possible. However it shouldn't be too
|
||||
// difficult (at least on Linux) to allocate a large amount of virtual
|
||||
// memory at startup that is then slowly used.
|
||||
return false
|
||||
if heapSize == heapMaxSize {
|
||||
// Already at the max. If we run out of memory, we should consider
|
||||
// increasing heapMaxSize on 64-bit systems.
|
||||
return false
|
||||
}
|
||||
// Grow the heap size used by the program.
|
||||
heapSize = (heapSize * 4 / 3) &^ 4095 // grow by around 33%
|
||||
if heapSize > heapMaxSize {
|
||||
heapSize = heapMaxSize
|
||||
}
|
||||
setHeapEnd(heapStart + heapSize)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -40,8 +40,6 @@ func go_scheduler() {
|
||||
scheduler()
|
||||
}
|
||||
|
||||
const asyncScheduler = true
|
||||
|
||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||
// The JavaScript API works in float64 milliseconds, so convert to
|
||||
// nanoseconds first before converting to a timeUnit (which is a float64),
|
||||
|
||||
@@ -30,6 +30,9 @@ func init() {
|
||||
// these args (argv).
|
||||
var argc, argv_buf_size uint32
|
||||
args_sizes_get(&argc, &argv_buf_size)
|
||||
if argc == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Obtain the command line arguments
|
||||
argsSlice := make([]unsafe.Pointer, argc)
|
||||
@@ -56,10 +59,7 @@ func nanosecondsToTicks(ns int64) timeUnit {
|
||||
return timeUnit(ns)
|
||||
}
|
||||
|
||||
const (
|
||||
asyncScheduler = false
|
||||
timePrecisionNanoseconds = 1000 // TODO: how can we determine the appropriate `precision`?
|
||||
)
|
||||
const timePrecisionNanoseconds = 1000 // TODO: how can we determine the appropriate `precision`?
|
||||
|
||||
var (
|
||||
sleepTicksSubscription = __wasi_subscription_t{
|
||||
|
||||
@@ -20,6 +20,10 @@ import (
|
||||
|
||||
const schedulerDebug = false
|
||||
|
||||
// On JavaScript, we can't do a blocking sleep. Instead we have to return and
|
||||
// queue a new scheduler invocation using setTimeout.
|
||||
const asyncScheduler = GOOS == "js"
|
||||
|
||||
var schedulerDone bool
|
||||
|
||||
// Queues used by the scheduler.
|
||||
@@ -138,6 +142,7 @@ func scheduler() {
|
||||
if t == nil {
|
||||
if sleepQueue == nil {
|
||||
if asyncScheduler {
|
||||
// JavaScript is treated specially, see below.
|
||||
return
|
||||
}
|
||||
waitForEvents()
|
||||
@@ -154,7 +159,8 @@ func scheduler() {
|
||||
if asyncScheduler {
|
||||
// The sleepTicks function above only sets a timeout at which
|
||||
// point the scheduler will be called again. It does not really
|
||||
// sleep.
|
||||
// sleep. So instead of sleeping, we return and expect to be
|
||||
// called again.
|
||||
break
|
||||
}
|
||||
continue
|
||||
|
||||
@@ -201,6 +201,10 @@ type M struct {
|
||||
|
||||
// Run the test suite.
|
||||
func (m *M) Run() int {
|
||||
if len(m.Tests) == 0 {
|
||||
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
|
||||
}
|
||||
|
||||
failures := 0
|
||||
for _, test := range m.Tests {
|
||||
t := &T{
|
||||
@@ -226,7 +230,6 @@ func (m *M) Run() int {
|
||||
}
|
||||
|
||||
if failures > 0 {
|
||||
fmt.Printf("exit status %d\n", failures)
|
||||
fmt.Println("FAIL")
|
||||
} else {
|
||||
fmt.Println("PASS")
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"inherits": ["arduino-nano"],
|
||||
"flash-command": "avrdude -c arduino -p atmega328p -b 115200 -P {port} -U flash:w:{hex}:i"
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
"inherits": ["atsame54p20a"],
|
||||
"build-tags": ["atsame54_xpro"],
|
||||
"flash-method": "openocd",
|
||||
"openocd-interface": "cmsis-dap"
|
||||
"openocd-interface": "cmsis-dap",
|
||||
"default-stack-size": 4096
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"inherits": ["cortex-m4"],
|
||||
"build-tags": ["sam", "atsame5x", "atsame54p20", "atsame54p20a"],
|
||||
"build-tags": ["sam", "atsame5x", "atsame54", "atsame54p20", "atsame54p20a"],
|
||||
"linkerscript": "targets/atsame5xx20-no-bootloader.ld",
|
||||
"extra-files": [
|
||||
"src/device/sam/atsame54p20a.s"
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
"flash-1200-bps-reset": "true",
|
||||
"flash-method": "msd",
|
||||
"msd-volume-name": "FTHRCANBOOT",
|
||||
"msd-firmware-name": "firmware.uf2"
|
||||
"msd-firmware-name": "firmware.uf2",
|
||||
"default-stack-size": 4096
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user