mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-03 02:27:48 +00:00
Merge remote-tracking branch 'origin/dev' into feature/usb-common
This commit is contained in:
+7
-21
@@ -286,8 +286,8 @@ commands:
|
||||
- run:
|
||||
name: "Install dependencies"
|
||||
command: |
|
||||
curl https://dl.google.com/go/go1.16.darwin-amd64.tar.gz -o go1.16.darwin-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xzf go1.16.darwin-amd64.tar.gz
|
||||
curl https://dl.google.com/go/go1.17.darwin-amd64.tar.gz -o go1.17.darwin-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xzf go1.17.darwin-amd64.tar.gz
|
||||
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
|
||||
- install-xtensa-toolchain:
|
||||
@@ -365,24 +365,12 @@ commands:
|
||||
- /go/pkg/mod
|
||||
|
||||
jobs:
|
||||
test-llvm10-go113:
|
||||
docker:
|
||||
- image: circleci/golang:1.13-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "10"
|
||||
test-llvm10-go114:
|
||||
docker:
|
||||
- image: circleci/golang:1.14-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "10"
|
||||
test-llvm11-go115:
|
||||
test-llvm10-go115:
|
||||
docker:
|
||||
- image: circleci/golang:1.15-buster
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "11"
|
||||
llvm: "10"
|
||||
test-llvm11-go116:
|
||||
docker:
|
||||
- image: circleci/golang:1.16-buster
|
||||
@@ -391,12 +379,12 @@ jobs:
|
||||
llvm: "11"
|
||||
assert-test-linux:
|
||||
docker:
|
||||
- image: circleci/golang:1.14-stretch
|
||||
- image: circleci/golang:1.17-stretch
|
||||
steps:
|
||||
- assert-test-linux
|
||||
build-linux:
|
||||
docker:
|
||||
- image: circleci/golang:1.14-stretch
|
||||
- image: circleci/golang:1.17-stretch
|
||||
steps:
|
||||
- build-linux
|
||||
build-macos:
|
||||
@@ -410,9 +398,7 @@ jobs:
|
||||
workflows:
|
||||
test-all:
|
||||
jobs:
|
||||
- test-llvm10-go113
|
||||
- test-llvm10-go114
|
||||
- test-llvm11-go115
|
||||
- test-llvm10-go115
|
||||
- test-llvm11-go116
|
||||
- build-linux
|
||||
- build-macos
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
build/
|
||||
llvm-*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: CI for tinygo-dev docker container
|
||||
on:
|
||||
push:
|
||||
branches: [ dev ]
|
||||
branches: [ dev, fix-docker-llvm-build ]
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ different guide:
|
||||
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
||||
build tools to be built. Go is of course necessary to build TinyGo itself.
|
||||
|
||||
* Go (1.13+)
|
||||
* Go (1.15+)
|
||||
* Standard build tools (gcc/clang)
|
||||
* git
|
||||
* CMake
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
# TinyGo base stage installs the most recent Go 1.16.x, LLVM 11 and the TinyGo compiler itself.
|
||||
FROM golang:1.16 AS tinygo-base
|
||||
# TinyGo base stage installs the most recent Go 1.17.x, LLVM 11 and the TinyGo compiler itself.
|
||||
FROM golang:1.17 AS tinygo-base
|
||||
|
||||
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
|
||||
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main" >> /etc/apt/sources.list && \
|
||||
echo "deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-11 main" >> /etc/apt/sources.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y llvm-11-dev libclang-11-dev lld-11 git
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ endif
|
||||
clean:
|
||||
@rm -rf build
|
||||
|
||||
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/internal/reflectlite transform
|
||||
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/testing src/internal/reflectlite transform
|
||||
fmt:
|
||||
@gofmt -l -w $(FMT_PATHS)
|
||||
fmt-check:
|
||||
@@ -190,6 +190,10 @@ TEST_PACKAGES = \
|
||||
container/list \
|
||||
container/ring \
|
||||
crypto/des \
|
||||
crypto/md5 \
|
||||
crypto/sha1 \
|
||||
crypto/sha256 \
|
||||
crypto/sha512 \
|
||||
encoding \
|
||||
encoding/ascii85 \
|
||||
encoding/base32 \
|
||||
@@ -199,6 +203,7 @@ TEST_PACKAGES = \
|
||||
hash/crc64 \
|
||||
math \
|
||||
math/cmplx \
|
||||
testing \
|
||||
text/scanner \
|
||||
unicode/utf8 \
|
||||
|
||||
@@ -358,6 +363,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-nano33 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-mkrwifi1010 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nano-33-ble examples/blinky1
|
||||
@@ -444,6 +451,9 @@ endif
|
||||
@$(MD5SUM) test.nro
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(OS),Windows_NT)
|
||||
$(TINYGO) build -o test.elf -gc=leaking -scheduler=none examples/serial
|
||||
endif
|
||||
|
||||
|
||||
wasmtest:
|
||||
|
||||
@@ -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 68 microcontroller boards are currently supported:
|
||||
The following 70 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)
|
||||
@@ -69,6 +69,7 @@ The following 68 microcontroller boards are currently supported:
|
||||
* [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 MKR WiFi 1010](https://store.arduino.cc/usa/mkr-wifi-1010)
|
||||
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
|
||||
* [Arduino Nano 33 BLE](https://store.arduino.cc/nano-33-ble)
|
||||
* [Arduino Nano 33 BLE Sense](https://store.arduino.cc/nano-33-ble-sense)
|
||||
@@ -101,6 +102,7 @@ The following 68 microcontroller boards are currently supported:
|
||||
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
||||
* [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html)
|
||||
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
|
||||
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
|
||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
||||
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ jobs:
|
||||
steps:
|
||||
- task: GoTool@0
|
||||
inputs:
|
||||
version: '1.16'
|
||||
version: '1.17'
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
- task: Cache@2
|
||||
|
||||
@@ -155,6 +155,10 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
||||
|
||||
// Write dependencies file.
|
||||
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
buf, err = json.MarshalIndent(dependencySlice, "", "\t")
|
||||
if err != nil {
|
||||
panic(err) // shouldn't happen
|
||||
|
||||
+2
-2
@@ -33,8 +33,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||
}
|
||||
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)
|
||||
if major != 1 || minor < 15 || minor > 17 {
|
||||
return nil, fmt.Errorf("requires go version 1.15 through 1.17, got go%d.%d", major, minor)
|
||||
}
|
||||
|
||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
|
||||
@@ -297,6 +297,9 @@ func (c *Config) Programmer() (method, openocdInterface string) {
|
||||
case "openocd", "msd", "command":
|
||||
// The -programmer flag only specifies the flash method.
|
||||
return c.Options.Programmer, c.Target.OpenOCDInterface
|
||||
case "bmp":
|
||||
// The -programmer flag only specifies the flash method.
|
||||
return c.Options.Programmer, ""
|
||||
default:
|
||||
// The -programmer flag specifies something else, assume it specifies
|
||||
// the OpenOCD interface name.
|
||||
|
||||
@@ -253,6 +253,9 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
GDB: []string{"gdb"},
|
||||
PortReset: "false",
|
||||
}
|
||||
if goarch == "386" {
|
||||
spec.CPU = "pentium4"
|
||||
}
|
||||
if goos == "darwin" {
|
||||
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
||||
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package compiler
|
||||
|
||||
// This file defines alias functions for functions that are normally defined in
|
||||
// Go assembly.
|
||||
//
|
||||
// The Go toolchain defines many performance critical functions in assembly
|
||||
// instead of plain Go. This is a problem for TinyGo as it currently (as of
|
||||
// august 2021) is not able to compile these assembly files and even if it
|
||||
// could, it would not be able to make use of them for many targets that are
|
||||
// supported by TinyGo (baremetal RISC-V, AVR, etc). Therefore, many of these
|
||||
// functions are aliased to their generic Go implementation.
|
||||
// This results in slower than possible implementations, but at least they are
|
||||
// usable.
|
||||
|
||||
import "tinygo.org/x/go-llvm"
|
||||
|
||||
var stdlibAliases = map[string]string{
|
||||
// crypto packages
|
||||
"crypto/md5.block": "crypto/md5.blockGeneric",
|
||||
"crypto/sha1.block": "crypto/sha1.blockGeneric",
|
||||
"crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric",
|
||||
"crypto/sha256.block": "crypto/sha256.blockGeneric",
|
||||
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
|
||||
|
||||
// math package
|
||||
"math.Asin": "math.asin",
|
||||
"math.Asinh": "math.asinh",
|
||||
"math.Acos": "math.acos",
|
||||
"math.Acosh": "math.acosh",
|
||||
"math.Atan": "math.atan",
|
||||
"math.Atanh": "math.atanh",
|
||||
"math.Atan2": "math.atan2",
|
||||
"math.Cbrt": "math.cbrt",
|
||||
"math.Ceil": "math.ceil",
|
||||
"math.archCeil": "math.ceil",
|
||||
"math.Cos": "math.cos",
|
||||
"math.Cosh": "math.cosh",
|
||||
"math.Erf": "math.erf",
|
||||
"math.Erfc": "math.erfc",
|
||||
"math.Exp": "math.exp",
|
||||
"math.archExp": "math.exp",
|
||||
"math.Expm1": "math.expm1",
|
||||
"math.Exp2": "math.exp2",
|
||||
"math.archExp2": "math.exp2",
|
||||
"math.Floor": "math.floor",
|
||||
"math.archFloor": "math.floor",
|
||||
"math.Frexp": "math.frexp",
|
||||
"math.Hypot": "math.hypot",
|
||||
"math.archHypot": "math.hypot",
|
||||
"math.Ldexp": "math.ldexp",
|
||||
"math.Log": "math.log",
|
||||
"math.archLog": "math.log",
|
||||
"math.Log1p": "math.log1p",
|
||||
"math.Log10": "math.log10",
|
||||
"math.Log2": "math.log2",
|
||||
"math.Max": "math.max",
|
||||
"math.archMax": "math.max",
|
||||
"math.Min": "math.min",
|
||||
"math.archMin": "math.min",
|
||||
"math.Mod": "math.mod",
|
||||
"math.Modf": "math.modf",
|
||||
"math.archModf": "math.modf",
|
||||
"math.Pow": "math.pow",
|
||||
"math.Remainder": "math.remainder",
|
||||
"math.Sin": "math.sin",
|
||||
"math.Sinh": "math.sinh",
|
||||
"math.Sqrt": "math.sqrt",
|
||||
"math.archSqrt": "math.sqrt",
|
||||
"math.Tan": "math.tan",
|
||||
"math.Tanh": "math.tanh",
|
||||
"math.Trunc": "math.trunc",
|
||||
"math.archTrunc": "math.trunc",
|
||||
}
|
||||
|
||||
// createAlias implements the function (in the builder) as a call to the alias
|
||||
// function.
|
||||
func (b *builder) createAlias(alias llvm.Value) {
|
||||
if b.Debug {
|
||||
if b.fn.Syntax() != nil {
|
||||
// Create debug info file if present.
|
||||
b.difunc = b.attachDebugInfo(b.fn)
|
||||
}
|
||||
pos := b.program.Fset.Position(b.fn.Pos())
|
||||
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||
}
|
||||
entryBlock := llvm.AddBasicBlock(b.llvmFn, "entry")
|
||||
b.SetInsertPointAtEnd(entryBlock)
|
||||
if b.llvmFn.Type() != alias.Type() {
|
||||
b.addError(b.fn.Pos(), "alias function should have the same type as aliasee "+alias.Name())
|
||||
b.CreateUnreachable()
|
||||
return
|
||||
}
|
||||
result := b.CreateCall(alias, b.llvmFn.Params(), "")
|
||||
if result.Type().TypeKind() == llvm.VoidTypeKind {
|
||||
b.CreateRetVoid()
|
||||
} else {
|
||||
b.CreateRet(result)
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,55 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo
|
||||
b.createRuntimeAssert(outOfBounds, "slice", "slicePanic")
|
||||
}
|
||||
|
||||
// createSliceToArrayPointerCheck adds a check for slice-to-array pointer
|
||||
// conversions. This conversion was added in Go 1.17. For details, see:
|
||||
// https://tip.golang.org/ref/spec#Conversions_from_slice_to_array_pointer
|
||||
func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen int64) {
|
||||
// From the spec:
|
||||
// > If the length of the slice is less than the length of the array, a
|
||||
// > run-time panic occurs.
|
||||
arrayLenValue := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
|
||||
isLess := b.CreateICmp(llvm.IntULT, sliceLen, arrayLenValue, "")
|
||||
b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic")
|
||||
}
|
||||
|
||||
// createUnsafeSliceCheck inserts a runtime check used for unsafe.Slice. This
|
||||
// function must panic if the ptr/len parameters are invalid.
|
||||
func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Basic) {
|
||||
// From the documentation of unsafe.Slice:
|
||||
// > At run time, if len is negative, or if ptr is nil and len is not
|
||||
// > zero, a run-time panic occurs.
|
||||
// However, in practice, it is also necessary to check that the length is
|
||||
// not too big that a GEP wouldn't be possible without wrapping the pointer.
|
||||
// These two checks (non-negative and not too big) can be merged into one
|
||||
// using an unsiged greater than.
|
||||
|
||||
// Make sure the len value is at least as big as a uintptr.
|
||||
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
||||
if lenType.Info()&types.IsUnsigned != 0 {
|
||||
len = b.CreateZExt(len, b.uintptrType, "")
|
||||
} else {
|
||||
len = b.CreateSExt(len, b.uintptrType, "")
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the maximum slice size, and therefore the maximum value of the
|
||||
// len parameter.
|
||||
maxSize := b.maxSliceSize(ptr.Type().ElementType())
|
||||
maxSizeValue := llvm.ConstInt(len.Type(), maxSize, false)
|
||||
|
||||
// Do the check. By using unsigned greater than for the length check, signed
|
||||
// negative values are also checked (which are very large numbers when
|
||||
// interpreted as signed values).
|
||||
zero := llvm.ConstInt(len.Type(), 0, false)
|
||||
lenOutOfBounds := b.CreateICmp(llvm.IntUGT, len, maxSizeValue, "")
|
||||
ptrIsNil := b.CreateICmp(llvm.IntEQ, ptr, llvm.ConstNull(ptr.Type()), "")
|
||||
lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "")
|
||||
assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
|
||||
assert = b.CreateOr(assert, lenOutOfBounds, "")
|
||||
b.createRuntimeAssert(assert, "unsafe.Slice", "unsafeSlicePanic")
|
||||
}
|
||||
|
||||
// createChanBoundsCheck creates a bounds check before creating a new channel to
|
||||
// check that the value is not too big for runtime.chanMake.
|
||||
func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) {
|
||||
|
||||
+120
-15
@@ -23,7 +23,7 @@ import (
|
||||
// Version of the compiler pacakge. Must be incremented each time the compiler
|
||||
// package changes in a way that affects the generated LLVM module.
|
||||
// This version is independent of the TinyGo version number.
|
||||
const Version = 12 // last change: implement syscall.rawSyscallNoError
|
||||
const Version = 18 // last change: fix duplicated named structs
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargets()
|
||||
@@ -74,6 +74,7 @@ type compilerContext struct {
|
||||
cu llvm.Metadata
|
||||
difiles map[string]llvm.Metadata
|
||||
ditypes map[types.Type]llvm.Metadata
|
||||
llvmTypes map[types.Type]llvm.Type
|
||||
machine llvm.TargetMachine
|
||||
targetData llvm.TargetData
|
||||
intType llvm.Type
|
||||
@@ -94,6 +95,7 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
|
||||
DumpSSA: dumpSSA,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
ditypes: make(map[types.Type]llvm.Metadata),
|
||||
llvmTypes: make(map[types.Type]llvm.Type),
|
||||
machine: machine,
|
||||
targetData: machine.CreateTargetData(),
|
||||
astComments: map[string]*ast.CommentGroup{},
|
||||
@@ -315,10 +317,23 @@ func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
|
||||
return c.getLLVMType(typ)
|
||||
}
|
||||
|
||||
// getLLVMType creates and returns a LLVM type for a Go type. In the case of
|
||||
// named struct types (or Go types implemented as named LLVM structs such as
|
||||
// strings) it also creates it first if necessary.
|
||||
// getLLVMType returns a LLVM type for a Go type. It doesn't recreate already
|
||||
// created types. This is somewhat important for performance, but especially
|
||||
// important for named struct types (which should only be created once).
|
||||
func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
||||
// Try to load the LLVM type from the cache.
|
||||
if t, ok := c.llvmTypes[goType]; ok {
|
||||
return t
|
||||
}
|
||||
// Not already created, so adding this type to the cache.
|
||||
llvmType := c.makeLLVMType(goType)
|
||||
c.llvmTypes[goType] = llvmType
|
||||
return llvmType
|
||||
}
|
||||
|
||||
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
|
||||
// getLLVMType instead.
|
||||
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
||||
switch typ := goType.(type) {
|
||||
case *types.Array:
|
||||
elemType := c.getLLVMType(typ.Elem())
|
||||
@@ -367,12 +382,10 @@ func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
||||
// LLVM. This is because it is otherwise impossible to create
|
||||
// self-referencing types such as linked lists.
|
||||
llvmName := typ.Obj().Pkg().Path() + "." + typ.Obj().Name()
|
||||
llvmType := c.mod.GetTypeByName(llvmName)
|
||||
if llvmType.IsNil() {
|
||||
llvmType = c.ctx.StructCreateNamed(llvmName)
|
||||
underlying := c.getLLVMType(st)
|
||||
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
||||
}
|
||||
llvmType := c.ctx.StructCreateNamed(llvmName)
|
||||
c.llvmTypes[goType] = llvmType // avoid infinite recursion
|
||||
underlying := c.getLLVMType(st)
|
||||
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
||||
return llvmType
|
||||
}
|
||||
return c.getLLVMType(typ.Underlying())
|
||||
@@ -768,6 +781,29 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add forwarding functions for functions that would otherwise be
|
||||
// implemented in assembly.
|
||||
for _, name := range members {
|
||||
member := pkg.Members[name]
|
||||
switch member := member.(type) {
|
||||
case *ssa.Function:
|
||||
if member.Blocks != nil {
|
||||
continue // external function
|
||||
}
|
||||
info := c.getFunctionInfo(member)
|
||||
if aliasName, ok := stdlibAliases[info.linkName]; ok {
|
||||
alias := c.mod.NamedFunction(aliasName)
|
||||
if alias.IsNil() {
|
||||
// Shouldn't happen, but perhaps best to just ignore.
|
||||
// The error will be a link error, if there is an error.
|
||||
continue
|
||||
}
|
||||
b := newBuilder(c, irbuilder, member)
|
||||
b.createAlias(alias)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// createFunction builds the LLVM IR implementation for this function. The
|
||||
@@ -1270,6 +1306,38 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
||||
case "ssa:wrapnilchk":
|
||||
// TODO: do an actual nil check?
|
||||
return argValues[0], nil
|
||||
|
||||
// Builtins from the unsafe package.
|
||||
case "Add": // unsafe.Add
|
||||
// This is basically just a GEP operation.
|
||||
// Note: the pointer is always of type *i8.
|
||||
ptr := argValues[0]
|
||||
len := argValues[1]
|
||||
return b.CreateGEP(ptr, []llvm.Value{len}, ""), nil
|
||||
case "Slice": // unsafe.Slice
|
||||
// This creates a slice from a pointer and a length.
|
||||
// Note that the exception mentioned in the documentation (if the
|
||||
// pointer and length are nil, the slice is also nil) is trivially
|
||||
// already the case.
|
||||
ptr := argValues[0]
|
||||
len := argValues[1]
|
||||
slice := llvm.Undef(b.ctx.StructType([]llvm.Type{
|
||||
ptr.Type(),
|
||||
b.uintptrType,
|
||||
b.uintptrType,
|
||||
}, false))
|
||||
b.createUnsafeSliceCheck(ptr, len, argTypes[1].Underlying().(*types.Basic))
|
||||
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
||||
// Too small, zero-extend len.
|
||||
len = b.CreateZExt(len, b.uintptrType, "")
|
||||
} else if len.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() {
|
||||
// Too big, truncate len.
|
||||
len = b.CreateTrunc(len, b.uintptrType, "")
|
||||
}
|
||||
slice = b.CreateInsertValue(slice, ptr, 0, "")
|
||||
slice = b.CreateInsertValue(slice, len, 1, "")
|
||||
slice = b.CreateInsertValue(slice, len, 2, "")
|
||||
return slice, nil
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
|
||||
}
|
||||
@@ -1298,6 +1366,11 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
return b.createMemoryCopyCall(fn, instr.Args)
|
||||
case name == "runtime.memzero":
|
||||
return b.createMemoryZeroCall(instr.Args)
|
||||
case name == "math.Ceil" || name == "math.Floor" || name == "math.Sqrt" || name == "math.Trunc":
|
||||
result, ok := b.createMathOp(instr)
|
||||
if ok {
|
||||
return result, nil
|
||||
}
|
||||
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
|
||||
return b.createInlineAsm(instr.Args)
|
||||
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
|
||||
@@ -1411,6 +1484,28 @@ func (b *builder) getValue(expr ssa.Value) llvm.Value {
|
||||
}
|
||||
}
|
||||
|
||||
// maxSliceSize determines the maximum size a slice of the given element type
|
||||
// can be.
|
||||
func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
|
||||
// Calculate ^uintptr(0), which is the max value that fits in uintptr.
|
||||
maxPointerValue := llvm.ConstNot(llvm.ConstInt(c.uintptrType, 0, false)).ZExtValue()
|
||||
// Calculate (^uint(0))/2, which is the max value that fits in an int.
|
||||
maxIntegerValue := llvm.ConstNot(llvm.ConstInt(c.intType, 0, false)).ZExtValue() / 2
|
||||
|
||||
// Determine the maximum allowed size for a slice. The biggest possible
|
||||
// pointer (starting from 0) would be maxPointerValue*sizeof(elementType) so
|
||||
// divide by the element type to get the real maximum size.
|
||||
maxSize := maxPointerValue / c.targetData.TypeAllocSize(elementType)
|
||||
|
||||
// len(slice) is an int. Make sure the length remains small enough to fit in
|
||||
// an int.
|
||||
if maxSize > maxIntegerValue {
|
||||
maxSize = maxIntegerValue
|
||||
}
|
||||
|
||||
return maxSize
|
||||
}
|
||||
|
||||
// createExpr translates a Go SSA expression to LLVM IR. This can be zero, one,
|
||||
// or multiple LLVM IR instructions and/or runtime calls.
|
||||
func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
@@ -1624,10 +1719,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
elemSize := b.targetData.TypeAllocSize(llvmElemType)
|
||||
elemSizeValue := llvm.ConstInt(b.uintptrType, elemSize, false)
|
||||
|
||||
// Calculate (^uintptr(0)) >> 1, which is the max value that fits in
|
||||
// uintptr if uintptr were signed.
|
||||
maxSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)), llvm.ConstInt(b.uintptrType, 1, false))
|
||||
if elemSize > maxSize.ZExtValue() {
|
||||
maxSize := b.maxSliceSize(llvmElemType)
|
||||
if elemSize > maxSize {
|
||||
// This seems to be checked by the typechecker already, but let's
|
||||
// check it again just to be sure.
|
||||
return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("slice element type is too big (%v bytes)", elemSize))
|
||||
@@ -1636,7 +1729,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
// Bounds checking.
|
||||
lenType := expr.Len.Type().Underlying().(*types.Basic)
|
||||
capType := expr.Cap.Type().Underlying().(*types.Basic)
|
||||
b.createSliceBoundsCheck(maxSize, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
|
||||
maxSizeValue := llvm.ConstInt(b.uintptrType, maxSize, false)
|
||||
b.createSliceBoundsCheck(maxSizeValue, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
|
||||
|
||||
// Allocate the backing array.
|
||||
sliceCapCast, err := b.createConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
|
||||
@@ -1879,6 +1973,17 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(expr.Pos(), "unknown slice type: "+typ.String())
|
||||
}
|
||||
case *ssa.SliceToArrayPointer:
|
||||
// Conversion from a slice to an array pointer, as the name clearly
|
||||
// says. This requires a runtime check to make sure the slice is at
|
||||
// least as big as the array.
|
||||
slice := b.getValue(expr.X)
|
||||
sliceLen := b.CreateExtractValue(slice, 1, "")
|
||||
arrayLen := expr.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
|
||||
b.createSliceToArrayPointerCheck(sliceLen, arrayLen)
|
||||
ptr := b.CreateExtractValue(slice, 0, "")
|
||||
ptr = b.CreateBitCast(ptr, b.getLLVMType(expr.Type()), "")
|
||||
return ptr, nil
|
||||
case *ssa.TypeAssert:
|
||||
return b.createTypeAssert(expr), nil
|
||||
case *ssa.UnOp:
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
@@ -16,6 +17,11 @@ import (
|
||||
// Pass -update to go test to update the output of the test files.
|
||||
var flagUpdate = flag.Bool("update", false, "update tests based on test output")
|
||||
|
||||
type testCase struct {
|
||||
file string
|
||||
target string
|
||||
}
|
||||
|
||||
// Basic tests for the compiler. Build some Go files and compare the output with
|
||||
// the expected LLVM IR for regression testing.
|
||||
func TestCompiler(t *testing.T) {
|
||||
@@ -34,10 +40,7 @@ func TestCompiler(t *testing.T) {
|
||||
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
file string
|
||||
target string
|
||||
}{
|
||||
tests := []testCase{
|
||||
{"basic.go", ""},
|
||||
{"pointer.go", ""},
|
||||
{"slice.go", ""},
|
||||
@@ -48,6 +51,16 @@ func TestCompiler(t *testing.T) {
|
||||
{"pragma.go", ""},
|
||||
{"goroutine.go", "wasm"},
|
||||
{"goroutine.go", "cortex-m-qemu"},
|
||||
{"intrinsics.go", "cortex-m-qemu"},
|
||||
{"intrinsics.go", "wasm"},
|
||||
}
|
||||
|
||||
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||
if err != nil {
|
||||
t.Fatal("could not read Go version:", err)
|
||||
}
|
||||
if minor >= 17 {
|
||||
tests = append(tests, testCase{"go1.17.go", ""})
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
|
||||
@@ -48,3 +48,57 @@ func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
|
||||
b.CreateCall(llvmFn, params, "")
|
||||
return llvm.Value{}, nil
|
||||
}
|
||||
|
||||
var mathToLLVMMapping = map[string]string{
|
||||
"math.Sqrt": "llvm.sqrt.f64",
|
||||
"math.Floor": "llvm.floor.f64",
|
||||
"math.Ceil": "llvm.ceil.f64",
|
||||
"math.Trunc": "llvm.trunc.f64",
|
||||
}
|
||||
|
||||
// createMathOp tries to lower the given call as a LLVM math intrinsic, if
|
||||
// possible. It returns the call result if possible, and a boolean whether it
|
||||
// succeeded. If it doesn't succeed, the architecture doesn't support the given
|
||||
// intrinsic.
|
||||
func (b *builder) createMathOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||
// Check whether this intrinsic is supported on the given GOARCH.
|
||||
// If it is unsupported, this can have two reasons:
|
||||
//
|
||||
// 1. LLVM can expand the intrinsic inline (using float instructions), but
|
||||
// the result doesn't pass the tests of the math package.
|
||||
// 2. LLVM cannot expand the intrinsic inline, will therefore lower it as a
|
||||
// libm function call, but the libm function call also fails the math
|
||||
// package tests.
|
||||
//
|
||||
// Whatever the implementation, it must pass the tests in the math package
|
||||
// so unfortunately only the below intrinsic+architecture combinations are
|
||||
// supported.
|
||||
name := call.StaticCallee().RelString(nil)
|
||||
switch name {
|
||||
case "math.Ceil", "math.Floor", "math.Trunc":
|
||||
if b.GOARCH != "wasm" && b.GOARCH != "arm64" {
|
||||
return llvm.Value{}, false
|
||||
}
|
||||
case "math.Sqrt":
|
||||
if b.GOARCH != "wasm" && b.GOARCH != "amd64" && b.GOARCH != "386" {
|
||||
return llvm.Value{}, false
|
||||
}
|
||||
default:
|
||||
return llvm.Value{}, false // only the above functions are supported.
|
||||
}
|
||||
|
||||
llvmFn := b.mod.NamedFunction(mathToLLVMMapping[name])
|
||||
if llvmFn.IsNil() {
|
||||
// The intrinsic doesn't exist yet, so declare it.
|
||||
// At the moment, all supported intrinsics have the form "double
|
||||
// foo(double %x)" so we can hardcode the signature here.
|
||||
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
||||
llvmFn = llvm.AddFunction(b.mod, mathToLLVMMapping[name], llvmType)
|
||||
}
|
||||
// Create a call to the intrinsic.
|
||||
args := make([]llvm.Value, len(call.Args))
|
||||
for i, arg := range call.Args {
|
||||
args[i] = b.getValue(arg)
|
||||
}
|
||||
return b.CreateCall(llvmFn, args, ""), true
|
||||
}
|
||||
|
||||
Vendored
+15
@@ -55,3 +55,18 @@ func complexMul(x, y complex64) complex64 {
|
||||
}
|
||||
|
||||
// TODO: complexDiv (requires runtime call)
|
||||
|
||||
// A type 'kv' also exists in function foo. Test that these two types don't
|
||||
// conflict with each other.
|
||||
type kv struct {
|
||||
v float32
|
||||
}
|
||||
|
||||
func foo(a *kv) {
|
||||
// Define a new 'kv' type.
|
||||
type kv struct {
|
||||
v byte
|
||||
}
|
||||
// Use this type.
|
||||
func(b *kv) {}(nil)
|
||||
}
|
||||
|
||||
Vendored
+14
@@ -3,6 +3,9 @@ source_filename = "basic.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
%main.kv = type { float }
|
||||
%main.kv.0 = type { i8 }
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
@@ -98,3 +101,14 @@ entry:
|
||||
%7 = insertvalue { float, float } %6, float %5, 1
|
||||
ret { float, float } %7
|
||||
}
|
||||
|
||||
define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
call void @"main.foo$1"(%main.kv.0* null, i8* undef, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
// Test changes to the language introduced in Go 1.17.
|
||||
// For details, see: https://tip.golang.org/doc/go1.17#language
|
||||
// These tests should be merged into the regular slice tests once Go 1.17 is the
|
||||
// minimun Go version for TinyGo.
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func Add32(p unsafe.Pointer, len int) unsafe.Pointer {
|
||||
return unsafe.Add(p, len)
|
||||
}
|
||||
|
||||
func Add64(p unsafe.Pointer, len int64) unsafe.Pointer {
|
||||
return unsafe.Add(p, len)
|
||||
}
|
||||
|
||||
func SliceToArray(s []int) *[4]int {
|
||||
return (*[4]int)(s)
|
||||
}
|
||||
|
||||
func SliceToArrayConst() *[4]int {
|
||||
s := make([]int, 6)
|
||||
return (*[4]int)(s)
|
||||
}
|
||||
|
||||
func SliceInt(ptr *int, len int) []int {
|
||||
return unsafe.Slice(ptr, len)
|
||||
}
|
||||
|
||||
func SliceUint16(ptr *byte, len uint16) []byte {
|
||||
return unsafe.Slice(ptr, len)
|
||||
}
|
||||
|
||||
func SliceUint64(ptr *int, len uint64) []int {
|
||||
return unsafe.Slice(ptr, len)
|
||||
}
|
||||
|
||||
func SliceInt64(ptr *int, len int64) []int {
|
||||
return unsafe.Slice(ptr, len)
|
||||
}
|
||||
Vendored
+136
@@ -0,0 +1,136 @@
|
||||
; ModuleID = 'go1.17.go'
|
||||
source_filename = "go1.17.go"
|
||||
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*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = getelementptr i8, i8* %p, i32 %len
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = trunc i64 %len to i32
|
||||
%1 = getelementptr i8, i8* %p, i32 %0
|
||||
ret i8* %1
|
||||
}
|
||||
|
||||
define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = icmp ult i32 %s.len, 4
|
||||
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
|
||||
|
||||
slicetoarray.throw: ; preds = %entry
|
||||
call void @runtime.sliceToArrayPointerPanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
slicetoarray.next: ; preds = %entry
|
||||
%1 = bitcast i32* %s.data to [4 x i32]*
|
||||
ret [4 x i32]* %1
|
||||
}
|
||||
|
||||
declare void @runtime.sliceToArrayPointerPanic(i8*, i8*)
|
||||
|
||||
define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%makeslice = call i8* @runtime.alloc(i32 24, i8* undef, i8* null)
|
||||
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
|
||||
|
||||
slicetoarray.throw: ; preds = %entry
|
||||
unreachable
|
||||
|
||||
slicetoarray.next: ; preds = %entry
|
||||
%0 = bitcast i8* %makeslice to [4 x i32]*
|
||||
ret [4 x i32]* %0
|
||||
}
|
||||
|
||||
define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = icmp ugt i32 %len, 1073741823
|
||||
%1 = icmp eq i32* %ptr, null
|
||||
%2 = icmp ne i32 %len, 0
|
||||
%3 = and i1 %1, %2
|
||||
%4 = or i1 %3, %0
|
||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
unsafe.Slice.next: ; preds = %entry
|
||||
%5 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||
%6 = insertvalue { i32*, i32, i32 } %5, i32 %len, 1
|
||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %len, 2
|
||||
ret { i32*, i32, i32 } %7
|
||||
}
|
||||
|
||||
declare void @runtime.unsafeSlicePanic(i8*, i8*)
|
||||
|
||||
define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = icmp eq i8* %ptr, null
|
||||
%1 = icmp ne i16 %len, 0
|
||||
%2 = and i1 %0, %1
|
||||
br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
unsafe.Slice.next: ; preds = %entry
|
||||
%3 = zext i16 %len to i32
|
||||
%4 = insertvalue { i8*, i32, i32 } undef, i8* %ptr, 0
|
||||
%5 = insertvalue { i8*, i32, i32 } %4, i32 %3, 1
|
||||
%6 = insertvalue { i8*, i32, i32 } %5, i32 %3, 2
|
||||
ret { i8*, i32, i32 } %6
|
||||
}
|
||||
|
||||
define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = icmp ugt i64 %len, 1073741823
|
||||
%1 = icmp eq i32* %ptr, null
|
||||
%2 = icmp ne i64 %len, 0
|
||||
%3 = and i1 %1, %2
|
||||
%4 = or i1 %3, %0
|
||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
unsafe.Slice.next: ; preds = %entry
|
||||
%5 = trunc i64 %len to i32
|
||||
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
||||
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
||||
ret { i32*, i32, i32 } %8
|
||||
}
|
||||
|
||||
define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = icmp ugt i64 %len, 1073741823
|
||||
%1 = icmp eq i32* %ptr, null
|
||||
%2 = icmp ne i64 %len, 0
|
||||
%3 = and i1 %1, %2
|
||||
%4 = or i1 %3, %0
|
||||
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
unsafe.Slice.next: ; preds = %entry
|
||||
%5 = trunc i64 %len to i32
|
||||
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
||||
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
||||
ret { i32*, i32, i32 } %8
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
; ModuleID = 'intrinsics.go'
|
||||
source_filename = "intrinsics.go"
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv7m-none-eabi"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden double @main.mySqrt(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call double @math.Sqrt(double %x, i8* undef, i8* undef)
|
||||
ret double %0
|
||||
}
|
||||
|
||||
declare double @math.Sqrt(double, i8*, i8*)
|
||||
|
||||
define hidden double @main.myTrunc(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call double @math.Trunc(double %x, i8* undef, i8* undef)
|
||||
ret double %0
|
||||
}
|
||||
|
||||
declare double @math.Trunc(double, i8*, i8*)
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
; ModuleID = 'intrinsics.go'
|
||||
source_filename = "intrinsics.go"
|
||||
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*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden double @main.mySqrt(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call double @llvm.sqrt.f64(double %x)
|
||||
ret double %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind readnone speculatable willreturn
|
||||
declare double @llvm.sqrt.f64(double) #0
|
||||
|
||||
define hidden double @main.myTrunc(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call double @llvm.trunc.f64(double %x)
|
||||
ret double %0
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind readnone speculatable willreturn
|
||||
declare double @llvm.trunc.f64(double) #0
|
||||
|
||||
attributes #0 = { nounwind readnone speculatable willreturn }
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
// Test how intrinsics are lowered: either as regular calls to the math
|
||||
// functions or as LLVM builtins (such as llvm.sqrt.f64).
|
||||
|
||||
import "math"
|
||||
|
||||
func mySqrt(x float64) float64 {
|
||||
return math.Sqrt(x)
|
||||
}
|
||||
|
||||
func myTrunc(x float64) float64 {
|
||||
return math.Trunc(x)
|
||||
}
|
||||
Vendored
+18
@@ -23,3 +23,21 @@ func sliceAppendSlice(ints, added []int) []int {
|
||||
func sliceCopy(dst, src []int) int {
|
||||
return copy(dst, src)
|
||||
}
|
||||
|
||||
// Test bounds checking in *ssa.MakeSlice instruction.
|
||||
|
||||
func makeByteSlice(len int) []byte {
|
||||
return make([]byte, len)
|
||||
}
|
||||
|
||||
func makeInt16Slice(len int) []int16 {
|
||||
return make([]int16, len)
|
||||
}
|
||||
|
||||
func makeArraySlice(len int) [][3]byte {
|
||||
return make([][3]byte, len) // slice with element size of 3
|
||||
}
|
||||
|
||||
func makeInt32Slice(len int) []int32 {
|
||||
return make([]int32, len)
|
||||
}
|
||||
|
||||
Vendored
+76
@@ -86,3 +86,79 @@ entry:
|
||||
}
|
||||
|
||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||
|
||||
define hidden { i8*, i32, i32 } @main.makeByteSlice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%slice.maxcap = icmp slt i32 %len, 0
|
||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* undef, i8* null)
|
||||
%0 = insertvalue { i8*, i32, i32 } undef, i8* %makeslice.buf, 0
|
||||
%1 = insertvalue { i8*, i32, i32 } %0, i32 %len, 1
|
||||
%2 = insertvalue { i8*, i32, i32 } %1, i32 %len, 2
|
||||
ret { i8*, i32, i32 } %2
|
||||
}
|
||||
|
||||
declare void @runtime.slicePanic(i8*, i8*)
|
||||
|
||||
define hidden { i16*, i32, i32 } @main.makeInt16Slice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%slice.maxcap = icmp slt i32 %len, 0
|
||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%makeslice.cap = shl i32 %len, 1
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* undef, i8* null)
|
||||
%makeslice.array = bitcast i8* %makeslice.buf to i16*
|
||||
%0 = insertvalue { i16*, i32, i32 } undef, i16* %makeslice.array, 0
|
||||
%1 = insertvalue { i16*, i32, i32 } %0, i32 %len, 1
|
||||
%2 = insertvalue { i16*, i32, i32 } %1, i32 %len, 2
|
||||
ret { i16*, i32, i32 } %2
|
||||
}
|
||||
|
||||
define hidden { [3 x i8]*, i32, i32 } @main.makeArraySlice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%slice.maxcap = icmp ugt i32 %len, 1431655765
|
||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%makeslice.cap = mul i32 %len, 3
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* undef, i8* null)
|
||||
%makeslice.array = bitcast i8* %makeslice.buf to [3 x i8]*
|
||||
%0 = insertvalue { [3 x i8]*, i32, i32 } undef, [3 x i8]* %makeslice.array, 0
|
||||
%1 = insertvalue { [3 x i8]*, i32, i32 } %0, i32 %len, 1
|
||||
%2 = insertvalue { [3 x i8]*, i32, i32 } %1, i32 %len, 2
|
||||
ret { [3 x i8]*, i32, i32 } %2
|
||||
}
|
||||
|
||||
define hidden { i32*, i32, i32 } @main.makeInt32Slice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%slice.maxcap = icmp ugt i32 %len, 1073741823
|
||||
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%makeslice.cap = shl i32 %len, 2
|
||||
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* undef, i8* null)
|
||||
%makeslice.array = bitcast i8* %makeslice.buf to i32*
|
||||
%0 = insertvalue { i32*, i32, i32 } undef, i32* %makeslice.array, 0
|
||||
%1 = insertvalue { i32*, i32, i32 } %0, i32 %len, 1
|
||||
%2 = insertvalue { i32*, i32, i32 } %1, i32 %len, 2
|
||||
ret { i32*, i32, i32 } %2
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/tinygo-org/tinygo
|
||||
|
||||
go 1.13
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||
@@ -10,7 +10,7 @@ require (
|
||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
go.bug.st/serial v1.1.2
|
||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
|
||||
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
|
||||
)
|
||||
|
||||
@@ -30,6 +30,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.bug.st/serial v1.0.0 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k=
|
||||
go.bug.st/serial v1.0.0/go.mod h1:rpXPISGjuNjPTRTcMlxi9lN6LoIPxd1ixVjBd8aSk/Q=
|
||||
go.bug.st/serial v1.1.2 h1:6xDpbta8KJ+VLRTeM8ghhxXRMLE/Lr8h9iDKwydarAY=
|
||||
@@ -38,23 +40,32 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 h1:0sfSpGSa544Fwnbot3Oxq/U6SXqjty6Jy/3wRhVS7ig=
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9 h1:nvvuMxmx1q0gfRki3T0hjG8EwAcVCs91oWAXvyt4zhI=
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
|
||||
|
||||
+4
-4
@@ -48,7 +48,10 @@ func GetGorootVersion(goroot string) (major, minor int, err error) {
|
||||
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
|
||||
// can have some variations (for beta releases, for example).
|
||||
func GorootVersionString(goroot string) (string, error) {
|
||||
if data, err := ioutil.ReadFile(filepath.Join(
|
||||
if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
||||
return string(data), nil
|
||||
|
||||
} else if data, err := ioutil.ReadFile(filepath.Join(
|
||||
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
|
||||
|
||||
r := regexp.MustCompile("const TheVersion = `(.*)`")
|
||||
@@ -59,9 +62,6 @@ func GorootVersionString(goroot string) (string, error) {
|
||||
|
||||
return string(matches[1]), nil
|
||||
|
||||
} else if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
||||
return string(data), nil
|
||||
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -590,18 +590,14 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// GetElementPtr does pointer arithmetic, changing the offset of the
|
||||
// pointer into the underlying object.
|
||||
var offset uint64
|
||||
var gepOperands []uint64
|
||||
for i := 2; i < len(operands); i += 2 {
|
||||
index := operands[i].Uint()
|
||||
elementSize := operands[i+1].Uint()
|
||||
if int64(elementSize) < 0 {
|
||||
// This is a struct field.
|
||||
// The field number is encoded by flipping all the bits.
|
||||
gepOperands = append(gepOperands, ^elementSize)
|
||||
offset += index
|
||||
} else {
|
||||
// This is a normal GEP, probably an array index.
|
||||
gepOperands = append(gepOperands, index)
|
||||
offset += elementSize * index
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -208,7 +208,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
|
||||
// with the TinyGo version. This is the case on some targets.
|
||||
func needsSyscallPackage(buildTags []string) bool {
|
||||
for _, tag := range buildTags {
|
||||
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" || tag == "wasi" {
|
||||
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" || tag == "tinygo.wasm" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
@@ -158,7 +159,7 @@ func Build(pkgName, outpath string, options *compileopts.Options) 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) {
|
||||
func Test(pkgName string, options *compileopts.Options, testCompileOnly, testVerbose bool, outpath string) (bool, error) {
|
||||
options.TestConfig.CompileTestBinary = true
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
@@ -184,7 +185,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
|
||||
// Run the test.
|
||||
start := time.Now()
|
||||
var err error
|
||||
passed, err = runPackageTest(config, result)
|
||||
passed, err = runPackageTest(config, result, testVerbose)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -210,10 +211,14 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
|
||||
// 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) {
|
||||
func runPackageTest(config *compileopts.Config, result builder.BuildResult, testVerbose bool) (bool, error) {
|
||||
if len(config.Target.Emulator) == 0 {
|
||||
// Run directly.
|
||||
cmd := executeCommand(config.Options, result.Binary)
|
||||
var flags []string
|
||||
if testVerbose {
|
||||
flags = append(flags, "-test.v")
|
||||
}
|
||||
cmd := executeCommand(config.Options, result.Binary, flags...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = result.MainDir
|
||||
@@ -229,6 +234,7 @@ func runPackageTest(config *compileopts.Config, result builder.BuildResult) (boo
|
||||
return true, nil
|
||||
} else {
|
||||
// Run in an emulator.
|
||||
// TODO: pass the -test.v flag if needed.
|
||||
args := append(config.Target.Emulator[1:], result.Binary)
|
||||
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||
buf := &bytes.Buffer{}
|
||||
@@ -288,6 +294,8 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
fileExt = filepath.Ext(config.Target.FlashFilename)
|
||||
case "openocd":
|
||||
fileExt = ".hex"
|
||||
case "bmp":
|
||||
fileExt = ".elf"
|
||||
case "native":
|
||||
return errors.New("unknown flash method \"native\" - did you miss a -target flag?")
|
||||
default:
|
||||
@@ -380,6 +388,25 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
return nil
|
||||
case "bmp":
|
||||
gdb, err := config.Target.LookupGDB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var bmpGDBPort string
|
||||
bmpGDBPort, _, err = getBMPPorts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args := []string{"-ex", "target extended-remote " + bmpGDBPort, "-ex", "monitor swdp_scan", "-ex", "attach 1", "-ex", "load", filepath.ToSlash(result.Binary)}
|
||||
cmd := executeCommand(config.Options, gdb, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return &commandError{"failed to flash", result.Binary, err}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unknown flash method: %s", flashMethod)
|
||||
}
|
||||
@@ -434,6 +461,13 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
switch gdbInterface {
|
||||
case "native":
|
||||
// Run GDB directly.
|
||||
case "bmp":
|
||||
var bmpGDBPort string
|
||||
bmpGDBPort, _, err = getBMPPorts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gdbCommands = append(gdbCommands, "target extended-remote "+bmpGDBPort, "monitor swdp_scan", "compare-sections", "attach 1", "load")
|
||||
case "openocd":
|
||||
gdbCommands = append(gdbCommands, "target extended-remote :3333", "monitor halt", "load", "monitor reset halt")
|
||||
|
||||
@@ -841,6 +875,35 @@ func getDefaultPort(portFlag string, usbInterfaces []string) (port string, err e
|
||||
return "", errors.New("port you specified '" + strings.Join(portCandidates, ",") + "' does not exist, available ports are " + strings.Join(ports, ", "))
|
||||
}
|
||||
|
||||
// getBMPPorts returns BlackMagicProbe's serial ports if any
|
||||
func getBMPPorts() (gdbPort, uartPort string, err error) {
|
||||
var portsList []*enumerator.PortDetails
|
||||
portsList, err = enumerator.GetDetailedPortsList()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
var ports []string
|
||||
for _, p := range portsList {
|
||||
if !p.IsUSB {
|
||||
continue
|
||||
}
|
||||
if p.VID != "" && p.PID != "" {
|
||||
vid, vidErr := strconv.ParseUint(p.VID, 16, 16)
|
||||
pid, pidErr := strconv.ParseUint(p.PID, 16, 16)
|
||||
if vidErr == nil && pidErr == nil && vid == 0x1d50 && pid == 0x6018 {
|
||||
ports = append(ports, p.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(ports) == 2 {
|
||||
return ports[0], ports[1], nil
|
||||
} else if len(ports) == 0 {
|
||||
return "", "", errors.New("no BMP detected")
|
||||
} else {
|
||||
return "", "", fmt.Errorf("expected 2 BMP serial ports, found %d - did you perhaps connect more than one BMP?", len(ports))
|
||||
}
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
||||
fmt.Fprintln(os.Stderr, "version:", goenv.Version)
|
||||
@@ -1027,6 +1090,7 @@ func main() {
|
||||
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")
|
||||
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
|
||||
|
||||
var flagJSON, flagDeps, flagTest *bool
|
||||
if command == "help" || command == "list" {
|
||||
@@ -1038,9 +1102,10 @@ func main() {
|
||||
if command == "help" || command == "build" || command == "build-library" || command == "test" {
|
||||
flag.StringVar(&outpath, "o", "", "output filename")
|
||||
}
|
||||
var testCompileOnlyFlag *bool
|
||||
var testCompileOnlyFlag, testVerboseFlag *bool
|
||||
if command == "help" || command == "test" {
|
||||
testCompileOnlyFlag = flag.Bool("c", false, "compile the test binary but do not run it")
|
||||
testVerboseFlag = flag.Bool("v", false, "verbose: print additional output")
|
||||
}
|
||||
|
||||
// Early command processing, before commands are interpreted by the Go flag
|
||||
@@ -1110,6 +1175,20 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if *cpuprofile != "" {
|
||||
f, err := os.Create(*cpuprofile)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "could not create CPU profile: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer f.Close()
|
||||
if err := pprof.StartCPUProfile(f); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "could not start CPU profile: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
switch command {
|
||||
case "build":
|
||||
if outpath == "" {
|
||||
@@ -1201,7 +1280,7 @@ func main() {
|
||||
allTestsPassed := true
|
||||
for _, pkgName := range pkgNames {
|
||||
// TODO: parallelize building the test binaries
|
||||
passed, err := Test(pkgName, options, *testCompileOnlyFlag, outpath)
|
||||
passed, err := Test(pkgName, options, *testCompileOnlyFlag, *testVerboseFlag, outpath)
|
||||
handleCompilerError(err)
|
||||
if !passed {
|
||||
allTestsPassed = false
|
||||
|
||||
+73
-49
@@ -51,9 +51,18 @@ func TestCompiler(t *testing.T) {
|
||||
"stdlib.go",
|
||||
"string.go",
|
||||
"structs.go",
|
||||
"testing.go",
|
||||
"zeroalloc.go",
|
||||
}
|
||||
|
||||
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||
if err != nil {
|
||||
t.Fatal("could not read version from GOROOT:", err)
|
||||
}
|
||||
if minor >= 17 {
|
||||
tests = append(tests, "go1.17.go")
|
||||
}
|
||||
|
||||
if *testTarget != "" {
|
||||
// This makes it possible to run one specific test (instead of all),
|
||||
// which is especially useful to quickly check whether some changes
|
||||
@@ -94,21 +103,9 @@ func TestCompiler(t *testing.T) {
|
||||
t.Run("ARM64Linux", func(t *testing.T) {
|
||||
runPlatTests("aarch64--linux-gnu", tests, t)
|
||||
})
|
||||
goVersion, err := goenv.GorootVersionString(goenv.Get("GOROOT"))
|
||||
if err != nil {
|
||||
t.Error("could not get Go version:", err)
|
||||
return
|
||||
}
|
||||
minorVersion := strings.Split(goVersion, ".")[1]
|
||||
if minorVersion != "13" {
|
||||
// WebAssembly tests fail on Go 1.13, so skip them there. Versions
|
||||
// below that are also not supported but still seem to pass, so
|
||||
// include them in the tests for now.
|
||||
t.Run("WebAssembly", func(t *testing.T) {
|
||||
runPlatTests("wasm", tests, t)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("WebAssembly", func(t *testing.T) {
|
||||
runPlatTests("wasm", tests, t)
|
||||
})
|
||||
t.Run("WASI", func(t *testing.T) {
|
||||
runPlatTests("wasi", tests, t)
|
||||
})
|
||||
@@ -162,14 +159,20 @@ func runPlatTests(target string, tests []string, t *testing.T) {
|
||||
runTest(name, target, t, nil, nil)
|
||||
})
|
||||
}
|
||||
if target == "wasi" || target == "" {
|
||||
t.Run("env.go", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest("env.go", target, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
|
||||
})
|
||||
if target == "" || target == "wasi" {
|
||||
t.Run("filesystem.go", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest("filesystem.go", target, t, nil, nil)
|
||||
})
|
||||
t.Run("env.go", func(t *testing.T) {
|
||||
}
|
||||
if target == "" || target == "wasi" || target == "wasm" {
|
||||
t.Run("rand.go", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest("env.go", target, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
|
||||
runTest("rand.go", target, t, nil, nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -216,16 +219,43 @@ func runTestWithConfig(name, target string, t *testing.T, options compileopts.Op
|
||||
}
|
||||
|
||||
// Create a temporary directory for test output files.
|
||||
tmpdir, err := ioutil.TempDir("", "tinygo-test")
|
||||
tmpdir := t.TempDir()
|
||||
|
||||
// Determine whether we're on a system that supports environment variables
|
||||
// and command line parameters (operating systems, WASI) or not (baremetal,
|
||||
// WebAssembly in the browser). If we're on a system without an environment,
|
||||
// we need to pass command line arguments and environment variables through
|
||||
// global variables (built into the binary directly) instead of the
|
||||
// conventional way.
|
||||
spec, err := compileopts.LoadTarget(target)
|
||||
if err != nil {
|
||||
t.Fatal("could not create temporary directory:", err)
|
||||
t.Fatal("failed to load target spec:", err)
|
||||
}
|
||||
defer func() {
|
||||
rerr := os.RemoveAll(tmpdir)
|
||||
if rerr != nil {
|
||||
t.Errorf("failed to remove temporary directory %q: %s", tmpdir, rerr.Error())
|
||||
needsEnvInVars := spec.GOOS == "js"
|
||||
for _, tag := range spec.BuildTags {
|
||||
if tag == "baremetal" {
|
||||
needsEnvInVars = true
|
||||
}
|
||||
}()
|
||||
}
|
||||
if needsEnvInVars {
|
||||
runtimeGlobals := make(map[string]string)
|
||||
if len(cmdArgs) != 0 {
|
||||
runtimeGlobals["osArgs"] = strings.Join(cmdArgs, "\x00")
|
||||
}
|
||||
if len(environmentVars) != 0 {
|
||||
runtimeGlobals["osEnv"] = strings.Join(environmentVars, "\x00")
|
||||
}
|
||||
if len(runtimeGlobals) != 0 {
|
||||
// This sets the global variables like they would be set with
|
||||
// `-ldflags="-X=runtime.osArgs=first\x00second`.
|
||||
// The runtime package has two variables (osArgs and osEnv) that are
|
||||
// both strings, from which the parameters and environment variables
|
||||
// are read.
|
||||
options.GlobalValues = map[string]map[string]string{
|
||||
"runtime": runtimeGlobals,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build the test binary.
|
||||
binary := filepath.Join(tmpdir, "test")
|
||||
@@ -236,37 +266,31 @@ func runTestWithConfig(name, target string, t *testing.T, options compileopts.Op
|
||||
return
|
||||
}
|
||||
|
||||
// Run the test.
|
||||
runComplete := make(chan struct{})
|
||||
// Create the test command, taking care of emulators etc.
|
||||
var cmd *exec.Cmd
|
||||
ranTooLong := false
|
||||
if target == "" {
|
||||
if len(spec.Emulator) == 0 {
|
||||
cmd = exec.Command(binary)
|
||||
cmd.Env = append(cmd.Env, environmentVars...)
|
||||
} else {
|
||||
args := append(spec.Emulator[1:], binary)
|
||||
cmd = exec.Command(spec.Emulator[0], args...)
|
||||
}
|
||||
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
||||
// Allow reading from the current directory.
|
||||
cmd.Args = append(cmd.Args, "--dir=.")
|
||||
for _, v := range environmentVars {
|
||||
cmd.Args = append(cmd.Args, "--env", v)
|
||||
}
|
||||
cmd.Args = append(cmd.Args, cmdArgs...)
|
||||
} else {
|
||||
spec, err := compileopts.LoadTarget(target)
|
||||
if err != nil {
|
||||
t.Fatal("failed to load target spec:", err)
|
||||
}
|
||||
if len(spec.Emulator) == 0 {
|
||||
cmd = exec.Command(binary)
|
||||
} else {
|
||||
args := append(spec.Emulator[1:], binary)
|
||||
cmd = exec.Command(spec.Emulator[0], args...)
|
||||
}
|
||||
|
||||
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
||||
// Allow reading from the current directory.
|
||||
cmd.Args = append(cmd.Args, "--dir=.")
|
||||
for _, v := range environmentVars {
|
||||
cmd.Args = append(cmd.Args, "--env", v)
|
||||
}
|
||||
cmd.Args = append(cmd.Args, cmdArgs...)
|
||||
} else {
|
||||
if !needsEnvInVars {
|
||||
cmd.Args = append(cmd.Args, cmdArgs...) // works on qemu-aarch64 etc
|
||||
cmd.Env = append(cmd.Env, environmentVars...)
|
||||
}
|
||||
}
|
||||
|
||||
// Run the test.
|
||||
runComplete := make(chan struct{})
|
||||
ranTooLong := false
|
||||
stdout := &bytes.Buffer{}
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// +build darwin freebsd tinygo.wasm
|
||||
|
||||
// This implementation of crypto/rand uses the arc4random_buf function
|
||||
// (available on both MacOS and WASI) to generate random numbers.
|
||||
//
|
||||
// Note: arc4random_buf (unlike what the name suggets) does not use the insecure
|
||||
// RC4 cipher. Instead, it uses a high-quality cipher, varying by the libc
|
||||
// implementation.
|
||||
|
||||
package rand
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func init() {
|
||||
Reader = &reader{}
|
||||
}
|
||||
|
||||
type reader struct {
|
||||
}
|
||||
|
||||
func (r *reader) Read(b []byte) (n int, err error) {
|
||||
if len(b) != 0 {
|
||||
libc_arc4random_buf(unsafe.Pointer(&b[0]), uint(len(b)))
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// void arc4random_buf(void *buf, size_t buflen);
|
||||
//export arc4random_buf
|
||||
func libc_arc4random_buf(buf unsafe.Pointer, buflen uint)
|
||||
@@ -1,38 +0,0 @@
|
||||
// +build darwin freebsd wasi
|
||||
|
||||
// This implementation of crypto/rand uses the getentropy system call (available
|
||||
// on both MacOS and WASI) to generate random numbers.
|
||||
|
||||
package rand
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var errReadFailed = errors.New("rand: could not read random bytes")
|
||||
|
||||
func init() {
|
||||
Reader = &reader{}
|
||||
}
|
||||
|
||||
type reader struct {
|
||||
}
|
||||
|
||||
func (r *reader) Read(b []byte) (n int, err error) {
|
||||
if len(b) != 0 {
|
||||
if len(b) > 256 {
|
||||
b = b[:256]
|
||||
}
|
||||
result := libc_getentropy(unsafe.Pointer(&b[0]), len(b))
|
||||
if result < 0 {
|
||||
// Maybe we should return a syscall.Errno here?
|
||||
return 0, errReadFailed
|
||||
}
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// int getentropy(void *buf, size_t buflen);
|
||||
//export getentropy
|
||||
func libc_getentropy(buf unsafe.Pointer, buflen int) int
|
||||
@@ -0,0 +1,11 @@
|
||||
// +build pico
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
pwm = machine.PWM4 // Pin 25 (LED on pico) corresponds to PWM4.
|
||||
pinA = machine.LED
|
||||
pinB = machine.GPIO24
|
||||
)
|
||||
@@ -16,6 +16,9 @@ type calleeSavedRegs struct {
|
||||
ebp uintptr
|
||||
|
||||
pc uintptr
|
||||
|
||||
// Pad this struct so that tasks start on a 16-byte aligned stack.
|
||||
_ [3]uintptr
|
||||
}
|
||||
|
||||
// archInit runs architecture-specific setup for the goroutine startup.
|
||||
|
||||
@@ -72,3 +72,8 @@ tinygo_swapTask:
|
||||
|
||||
// Return into the new task, as if tinygo_swapTask was a regular call.
|
||||
ret
|
||||
|
||||
#ifdef __MACH__ // Darwin
|
||||
// allow these symbols to stripped as dead code
|
||||
.subsections_via_symbols
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
// +build arduino_mkrwifi1010
|
||||
|
||||
// This contains the pin mappings for the Arduino MKR WiFi 1010 board.
|
||||
//
|
||||
// For more information, see: https://store.arduino.cc/usa/mkr-wifi-1010
|
||||
//
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0x07738135
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
RX0 Pin = PB23 // UART1 RX
|
||||
TX1 Pin = PB22 // UART1 TX
|
||||
|
||||
D0 Pin = PA22 // PWM available
|
||||
D1 Pin = PA23 // PWM available
|
||||
D2 Pin = PA10 // PWM available
|
||||
D3 Pin = PA11 // PWM available
|
||||
D4 Pin = PB10 // PWM available
|
||||
D5 Pin = PB11 // PWM available
|
||||
|
||||
D6 Pin = PA20 // PWM available
|
||||
D7 Pin = PA21 // PWM available
|
||||
D8 Pin = PA16 // PWM available
|
||||
D9 Pin = PA17
|
||||
D10 Pin = PA19 // PWM available
|
||||
D11 Pin = PA08 // SDA
|
||||
D12 Pin = PA09 // PWM available, SCL
|
||||
D13 Pin = PB23 // RX
|
||||
D14 Pin = PB22 // TX
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 Pin = PA02 // ADC0/AIN[0]
|
||||
A1 Pin = PB02 // AIN[10]
|
||||
A2 Pin = PB03 // AIN[11]
|
||||
A3 Pin = PA04 // AIN[04]
|
||||
A4 Pin = PA05 // AIN[05]
|
||||
A5 Pin = PA06 // AIN[06]
|
||||
A6 Pin = PA07 // AIN[07]
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D6
|
||||
)
|
||||
|
||||
// USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN Pin = PA24
|
||||
USBCDC_DP_PIN Pin = PA25
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN Pin = PB22
|
||||
UART_RX_PIN Pin = PB23
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN Pin = D11 // SDA
|
||||
SCL_PIN Pin = D12 // SCL
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = D9 // SCK: S1
|
||||
SPI0_SDO_PIN Pin = D8 // SDO: S1
|
||||
SPI0_SDI_PIN Pin = D10 // SDI: S1
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN Pin = PA10
|
||||
I2S_SD_PIN Pin = PA07
|
||||
I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino MKR WiFi 1010.
|
||||
)
|
||||
|
||||
// NINA-W102 Pins
|
||||
const (
|
||||
NINA_SDO Pin = PA12
|
||||
NINA_SDI Pin = PA13
|
||||
NINA_CS Pin = PA14
|
||||
NINA_SCK Pin = PA15
|
||||
NINA_GPIO0 Pin = PA27
|
||||
NINA_RESETN Pin = PB08
|
||||
NINA_ACK Pin = PA28
|
||||
NINA_TX Pin = PA22
|
||||
NINA_RX Pin = PA23
|
||||
)
|
||||
|
||||
// UART on the Arduino MKR WiFi 1010.
|
||||
var (
|
||||
UART1 = &_UART1
|
||||
_UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM5_USART,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5, _UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the Arduino MKR WiFi 1010.
|
||||
var (
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the Arduino MKR WiFi 1010.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPI,
|
||||
SERCOM: 1,
|
||||
}
|
||||
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM4_SPI,
|
||||
SERCOM: 4,
|
||||
}
|
||||
NINA_SPI = SPI1
|
||||
)
|
||||
|
||||
// I2S on the Arduino MKR WiFi 1010.
|
||||
var (
|
||||
I2S0 = I2S{Bus: sam.I2S}
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Arduino MKR WiFi 1010"
|
||||
usb_STRING_MANUFACTURER = "Arduino"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2341
|
||||
usb_PID uint16 = 0x8054
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultUART = UART1
|
||||
)
|
||||
@@ -9,6 +9,37 @@ const (
|
||||
xoscFreq = 12 // MHz
|
||||
)
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D4 = GPIO6
|
||||
D5 = GPIO7
|
||||
D6 = GPIO8
|
||||
D9 = GPIO9
|
||||
D10 = GPIO10
|
||||
D11 = GPIO11
|
||||
D12 = GPIO12
|
||||
D13 = GPIO13
|
||||
D24 = GPIO24
|
||||
D25 = GPIO25
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = GPIO26
|
||||
A1 = GPIO27
|
||||
A2 = GPIO28
|
||||
A3 = GPIO29
|
||||
)
|
||||
|
||||
// I2C Pins.
|
||||
const (
|
||||
I2C0_SDA_PIN = GPIO24
|
||||
I2C0_SCL_PIN = GPIO25
|
||||
|
||||
I2C1_SDA_PIN = GPIO2
|
||||
I2C1_SCL_PIN = GPIO3
|
||||
)
|
||||
|
||||
// SPI default pins
|
||||
const (
|
||||
// Default Serial Clock Bus 0 for SPI communications
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// +build mdbt50qrx
|
||||
|
||||
package machine
|
||||
|
||||
const HasLowFrequencyCrystal = false
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = P1_13 // LED1
|
||||
D1 = P1_11 // LED2 (not populated by default)
|
||||
D2 = P0_15 // Button
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D0
|
||||
)
|
||||
|
||||
// MDBT50Q-RX dongle does not have pins broken out for the peripherals below,
|
||||
// however the machine_nrf*.go implementations of I2C/SPI/etc expect the pin
|
||||
// constants to be defined, so we are defining them all as 0
|
||||
const (
|
||||
UART_TX_PIN = 0
|
||||
UART_RX_PIN = 0
|
||||
SDA_PIN = 0
|
||||
SCL_PIN = 0
|
||||
SPI0_SCK_PIN = 0
|
||||
SPI0_SDO_PIN = 0
|
||||
SPI0_SDI_PIN = 0
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Raytac MDBT50Q - RX"
|
||||
usb_STRING_MANUFACTURER = "Raytac Corporation"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x810B
|
||||
)
|
||||
@@ -49,8 +49,11 @@ const (
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN Pin = GPIO12
|
||||
SCL_PIN Pin = GPIO13
|
||||
I2C0_SDA_PIN Pin = GPIO12
|
||||
I2C0_SCL_PIN Pin = GPIO13
|
||||
|
||||
I2C1_SDA_PIN Pin = GPIO18
|
||||
I2C1_SCL_PIN Pin = GPIO19
|
||||
)
|
||||
|
||||
// SPI pins. SPI1 not available on Nano RP2040 Connect.
|
||||
@@ -65,6 +68,10 @@ const (
|
||||
SPI1_SDI_PIN Pin = GPIO22
|
||||
)
|
||||
|
||||
var (
|
||||
NINA_SPI = SPI1
|
||||
)
|
||||
|
||||
// NINA-W102 Pins
|
||||
const (
|
||||
NINA_SCK Pin = GPIO14
|
||||
@@ -73,7 +80,7 @@ const (
|
||||
|
||||
NINA_CS Pin = GPIO9
|
||||
NINA_ACK Pin = GPIO10
|
||||
NINA_GPIO0 Pin = GPIO0
|
||||
NINA_GPIO0 Pin = GPIO2
|
||||
NINA_RESETN Pin = GPIO3
|
||||
|
||||
NINA_TX Pin = GPIO9
|
||||
|
||||
@@ -38,6 +38,15 @@ const (
|
||||
xoscFreq = 12 // MHz
|
||||
)
|
||||
|
||||
// I2C Default pins on Raspberry Pico.
|
||||
const (
|
||||
I2C0_SDA_PIN = GP4
|
||||
I2C0_SCL_PIN = GP5
|
||||
|
||||
I2C1_SDA_PIN = GP2
|
||||
I2C1_SCL_PIN = GP3
|
||||
)
|
||||
|
||||
// SPI default pins
|
||||
const (
|
||||
// Default Serial Clock Bus 0 for SPI communications
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build atmega nrf sam stm32 fe310 k210
|
||||
// +build atmega nrf sam stm32 fe310 k210 rp2040
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -47,14 +47,27 @@ type pinFunc uint8
|
||||
// GPIO function selectors
|
||||
const (
|
||||
fnJTAG pinFunc = 0
|
||||
fnSPI pinFunc = 1
|
||||
fnSPI pinFunc = 1 // Connect one of the internal PL022 SPI peripherals to GPIO
|
||||
fnUART pinFunc = 2
|
||||
fnI2C pinFunc = 3
|
||||
fnPWM pinFunc = 4
|
||||
fnSIO pinFunc = 5
|
||||
fnPIO0 pinFunc = 6
|
||||
fnPIO1 pinFunc = 7
|
||||
// Connect a PWM slice to GPIO. There are eight PWM slices,
|
||||
// each with two outputchannels (A/B). The B pin can also be used as an input,
|
||||
// for frequency and duty cyclemeasurement
|
||||
fnPWM pinFunc = 4
|
||||
// Software control of GPIO, from the single-cycle IO (SIO) block.
|
||||
// The SIO function (F5)must be selected for the processors to drive a GPIO,
|
||||
// but the input is always connected,so software can check the state of GPIOs at any time.
|
||||
fnSIO pinFunc = 5
|
||||
// Connect one of the programmable IO blocks (PIO) to GPIO. PIO can implement a widevariety of interfaces,
|
||||
// and has its own internal pin mapping hardware, allowing flexibleplacement of digital interfaces on bank 0 GPIOs.
|
||||
// The PIO function (F6, F7) must beselected for PIO to drive a GPIO, but the input is always connected,
|
||||
// so the PIOs canalways see the state of all pins.
|
||||
fnPIO0, fnPIO1 pinFunc = 6, 7
|
||||
// General purpose clock inputs/outputs. Can be routed to a number of internal clock domains onRP2040,
|
||||
// e.g. Input: to provide a 1 Hz clock for the RTC, or can be connected to an internalfrequency counter.
|
||||
// e.g. Output: optional integer divide
|
||||
fnGPCK pinFunc = 8
|
||||
// USB power control signals to/from the internal USB controller
|
||||
fnUSB pinFunc = 9
|
||||
fnNULL pinFunc = 0x1f
|
||||
|
||||
@@ -68,6 +81,8 @@ const (
|
||||
PinInputPullup
|
||||
PinAnalog
|
||||
PinUART
|
||||
PinPWM
|
||||
PinI2C
|
||||
PinSPI
|
||||
)
|
||||
|
||||
@@ -91,7 +106,7 @@ func (p Pin) xor() {
|
||||
|
||||
// get returns the pin value
|
||||
func (p Pin) get() bool {
|
||||
return rp.SIO.GPIO_IN.HasBits(uint32(1) << p)
|
||||
return rp.SIO.GPIO_IN.HasBits(1 << p)
|
||||
}
|
||||
|
||||
func (p Pin) ioCtrl() *volatile.Register32 {
|
||||
@@ -117,6 +132,17 @@ func (p Pin) pulloff() {
|
||||
p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE)
|
||||
}
|
||||
|
||||
// setSlew sets pad slew rate control.
|
||||
// true sets to fast. false sets to slow.
|
||||
func (p Pin) setSlew(sr bool) {
|
||||
p.padCtrl().ReplaceBits(boolToBit(sr)<<rp.PADS_BANK0_GPIO0_SLEWFAST_Pos, rp.PADS_BANK0_GPIO0_SLEWFAST_Msk, 0)
|
||||
}
|
||||
|
||||
// setSchmitt enables or disables Schmitt trigger.
|
||||
func (p Pin) setSchmitt(trigger bool) {
|
||||
p.padCtrl().ReplaceBits(boolToBit(trigger)<<rp.PADS_BANK0_GPIO0_SCHMITT_Pos, rp.PADS_BANK0_GPIO0_SCHMITT_Msk, 0)
|
||||
}
|
||||
|
||||
// setFunc will set pin function to fn.
|
||||
func (p Pin) setFunc(fn pinFunc) {
|
||||
// Set input enable, Clear output disable
|
||||
@@ -156,6 +182,14 @@ func (p Pin) Configure(config PinConfig) {
|
||||
p.pulloff()
|
||||
case PinUART:
|
||||
p.setFunc(fnUART)
|
||||
case PinPWM:
|
||||
p.setFunc(fnPWM)
|
||||
case PinI2C:
|
||||
// IO config according to 4.3.1.3 of rp2040 datasheet.
|
||||
p.setFunc(fnI2C)
|
||||
p.pullup()
|
||||
p.setSchmitt(true)
|
||||
p.setSlew(false)
|
||||
case PinSPI:
|
||||
p.setFunc(fnSPI)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,461 @@
|
||||
// +build rp2040
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/rp"
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// I2C on the RP2040.
|
||||
var (
|
||||
I2C0 = &_I2C0
|
||||
_I2C0 = I2C{
|
||||
Bus: rp.I2C0,
|
||||
}
|
||||
I2C1 = &_I2C1
|
||||
_I2C1 = I2C{
|
||||
Bus: rp.I2C1,
|
||||
}
|
||||
)
|
||||
|
||||
// Features: Taken from datasheet.
|
||||
// Default master mode, with slave mode available (not simulataneously).
|
||||
// Default slave address of RP2040: 0x055
|
||||
// Supports 10-bit addressing in Master mode
|
||||
// 16-element transmit buffer
|
||||
// 16-element receive buffer
|
||||
// Can be driven from DMA
|
||||
// Can generate interrupts
|
||||
// Fast mode plus max transfer speed (1000kb/s)
|
||||
|
||||
// GPIO config
|
||||
// Each controller must connect its clock SCL and data SDA to one pair of GPIOs.
|
||||
// The I2C standard requires that drivers drivea signal low, or when not driven the signal will be pulled high.
|
||||
// This applies to SCL and SDA. The GPIO pads should beconfigured for:
|
||||
// Pull-up enabled
|
||||
// Slew rate limited
|
||||
// Schmitt trigger enabled
|
||||
// Note: There should also be external pull-ups on the board as the internal pad pull-ups may not be strong enough to pull upexternal circuits.
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
Frequency uint32
|
||||
// SDA/SCL Serial Data and clock pins. Refer to datasheet to see
|
||||
// which pins match the desired bus.
|
||||
SDA, SCL Pin
|
||||
}
|
||||
|
||||
type I2C struct {
|
||||
Bus *rp.I2C0_Type
|
||||
restartOnNext bool
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidI2CBaudrate = errors.New("invalid i2c baudrate")
|
||||
ErrInvalidTgtAddr = errors.New("invalid target i2c address not in 0..0x80 or is reserved")
|
||||
ErrI2CGeneric = errors.New("i2c error")
|
||||
ErrRP2040I2CDisable = errors.New("i2c rp2040 peripheral timeout in disable")
|
||||
)
|
||||
|
||||
// Tx performs a write and then a read transfer placing the result in
|
||||
// in r.
|
||||
//
|
||||
// Passing a nil value for w or r skips the transfer corresponding to write
|
||||
// or read, respectively.
|
||||
//
|
||||
// i2c.Tx(addr, nil, r)
|
||||
// Performs only a read transfer.
|
||||
//
|
||||
// i2c.Tx(addr, w, nil)
|
||||
// Performs only a write transfer.
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
// timeout in microseconds.
|
||||
const timeout = 40 * 1000 // 40ms is a reasonable time for a real-time system.
|
||||
if len(w) > 0 {
|
||||
if err := i2c.tx(uint8(addr), w, false, timeout); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(r) > 0 {
|
||||
if err := i2c.rx(uint8(addr), r, false, timeout); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Configure initializes i2c peripheral and configures I2C config's pins passed.
|
||||
// Here's a list of valid SDA and SCL GPIO pins on bus I2C0 of the rp2040:
|
||||
// SDA: 0, 4, 8, 12, 16, 20
|
||||
// SCL: 1, 5, 9, 13, 17, 21
|
||||
// Same as above for I2C1 bus:
|
||||
// SDA: 2, 6, 10, 14, 18, 26
|
||||
// SCL: 3, 7, 11, 15, 19, 27
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
if config.SCL == 0 {
|
||||
// If config pins are zero valued or clock pin is invalid then we set default values.
|
||||
switch i2c.Bus {
|
||||
case rp.I2C0:
|
||||
config.SCL = I2C0_SCL_PIN
|
||||
config.SDA = I2C0_SDA_PIN
|
||||
case rp.I2C1:
|
||||
config.SCL = I2C1_SCL_PIN
|
||||
config.SDA = I2C1_SDA_PIN
|
||||
}
|
||||
}
|
||||
config.SDA.Configure(PinConfig{PinI2C})
|
||||
config.SCL.Configure(PinConfig{PinI2C})
|
||||
return i2c.init(config)
|
||||
}
|
||||
|
||||
// SetBaudrate sets the I2C frequency. It has the side effect of also
|
||||
// enabling the I2C hardware if disabled beforehand.
|
||||
//go:inline
|
||||
func (i2c *I2C) SetBaudrate(br uint32) error {
|
||||
const freqin uint32 = 125 * MHz
|
||||
// Find smallest prescale value which puts o
|
||||
|
||||
// TODO there are some subtleties to I2C timing which we are completely ignoring here
|
||||
period := (freqin + br/2) / br
|
||||
lcnt := period * 3 / 5 // oof this one hurts
|
||||
hcnt := period - lcnt
|
||||
// Check for out-of-range divisors:
|
||||
if hcnt > rp.I2C0_IC_FS_SCL_HCNT_IC_FS_SCL_HCNT_Msk || hcnt < 8 || lcnt > rp.I2C0_IC_FS_SCL_LCNT_IC_FS_SCL_LCNT_Msk || lcnt < 8 {
|
||||
return ErrInvalidI2CBaudrate
|
||||
}
|
||||
|
||||
// Per I2C-bus specification a device in standard or fast mode must
|
||||
// internally provide a hold time of at least 300ns for the SDA signal to
|
||||
// bridge the undefined region of the falling edge of SCL. A smaller hold
|
||||
// time of 120ns is used for fast mode plus.
|
||||
|
||||
// sda_tx_hold_count = freq_in [cycles/s] * 300ns * (1s / 1e9ns)
|
||||
// Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint.
|
||||
// Add 1 to avoid division truncation.
|
||||
sdaTxHoldCnt := ((freqin * 3) / 10000000) + 1
|
||||
if br >= 1_000_000 {
|
||||
// sda_tx_hold_count = freq_in [cycles/s] * 120ns * (1s / 1e9ns)
|
||||
// Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint.
|
||||
// Add 1 to avoid division truncation.
|
||||
sdaTxHoldCnt = ((freqin * 3) / 25000000) + 1
|
||||
}
|
||||
|
||||
if sdaTxHoldCnt > lcnt-2 {
|
||||
return ErrInvalidI2CBaudrate
|
||||
}
|
||||
err := i2c.disable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Always use "fast" mode (<= 400 kHz, works fine for standard mode too)
|
||||
|
||||
i2c.Bus.IC_CON.ReplaceBits(rp.I2C0_IC_CON_SPEED_FAST<<rp.I2C0_IC_CON_SPEED_Pos, rp.I2C0_IC_CON_SPEED_Msk, 0)
|
||||
i2c.Bus.IC_FS_SCL_HCNT.Set(hcnt)
|
||||
i2c.Bus.IC_FS_SCL_LCNT.Set(lcnt)
|
||||
|
||||
i2c.Bus.IC_FS_SPKLEN.Set(u32max(1, lcnt/16))
|
||||
|
||||
i2c.Bus.IC_SDA_HOLD.ReplaceBits(sdaTxHoldCnt<<rp.I2C0_IC_SDA_HOLD_IC_SDA_TX_HOLD_Pos, rp.I2C0_IC_SDA_HOLD_IC_SDA_TX_HOLD_Msk, 0)
|
||||
i2c.enable()
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (i2c *I2C) enable() {
|
||||
i2c.Bus.IC_ENABLE.ReplaceBits(rp.I2C0_IC_ENABLE_ENABLE<<rp.I2C0_IC_ENABLE_ENABLE_Pos, rp.I2C0_IC_ENABLE_ENABLE_Msk, 0)
|
||||
}
|
||||
|
||||
// Implemented as per 4.3.10.3. Disabling DW_apb_i2c section.
|
||||
//go:inline
|
||||
func (i2c *I2C) disable() error {
|
||||
const MAX_T_POLL_COUNT = 64 // 64 us timeout corresponds to around 1000kb/s i2c transfer rate.
|
||||
deadline := ticks() + MAX_T_POLL_COUNT
|
||||
i2c.Bus.IC_ENABLE.Set(0)
|
||||
for i2c.Bus.IC_ENABLE_STATUS.Get()&1 != 0 {
|
||||
if ticks() > deadline {
|
||||
return ErrRP2040I2CDisable
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (i2c *I2C) init(config I2CConfig) error {
|
||||
i2c.reset()
|
||||
if err := i2c.disable(); err != nil {
|
||||
return err
|
||||
}
|
||||
i2c.restartOnNext = false
|
||||
// Configure as a fast-mode master with RepStart support, 7-bit addresses
|
||||
i2c.Bus.IC_CON.Set((rp.I2C0_IC_CON_SPEED_FAST << rp.I2C0_IC_CON_SPEED_Pos) |
|
||||
rp.I2C0_IC_CON_MASTER_MODE | rp.I2C0_IC_CON_IC_SLAVE_DISABLE |
|
||||
rp.I2C0_IC_CON_IC_RESTART_EN | rp.I2C0_IC_CON_TX_EMPTY_CTRL) // sets TX_EMPTY_CTRL to enable TX_EMPTY interrupt status
|
||||
|
||||
// Set FIFO watermarks to 1 to make things simpler. This is encoded by a register value of 0.
|
||||
i2c.Bus.IC_TX_TL.Set(0)
|
||||
i2c.Bus.IC_RX_TL.Set(0)
|
||||
|
||||
// Always enable the DREQ signalling -- harmless if DMA isn't listening
|
||||
i2c.Bus.IC_DMA_CR.Set(rp.I2C0_IC_DMA_CR_TDMAE | rp.I2C0_IC_DMA_CR_RDMAE)
|
||||
return i2c.SetBaudrate(config.Frequency)
|
||||
}
|
||||
|
||||
// reset sets I2C register RESET bits in the reset peripheral and then clears them.
|
||||
//go:inline
|
||||
func (i2c *I2C) reset() {
|
||||
resetVal := i2c.deinit()
|
||||
rp.RESETS.RESET.ClearBits(resetVal)
|
||||
// Wait until reset is done.
|
||||
for !rp.RESETS.RESET_DONE.HasBits(resetVal) {
|
||||
}
|
||||
}
|
||||
|
||||
// deinit sets reset bit for I2C. Must call reset to reenable I2C after deinit.
|
||||
//go:inline
|
||||
func (i2c *I2C) deinit() (resetVal uint32) {
|
||||
switch {
|
||||
case i2c.Bus == rp.I2C0:
|
||||
resetVal = rp.RESETS_RESET_I2C0
|
||||
case i2c.Bus == rp.I2C1:
|
||||
resetVal = rp.RESETS_RESET_I2C1
|
||||
}
|
||||
// Perform I2C reset.
|
||||
rp.RESETS.RESET.SetBits(resetVal)
|
||||
|
||||
return resetVal
|
||||
}
|
||||
|
||||
// tx is a primitive i2c blocking write to bus routine. timeout is time to wait
|
||||
// in microseconds since calling this function for write to finish.
|
||||
func (i2c *I2C) tx(addr uint8, tx []byte, nostop bool, timeout uint64) (err error) {
|
||||
deadline := ticks() + timeout
|
||||
if addr >= 0x80 || isReservedI2CAddr(addr) {
|
||||
return ErrInvalidTgtAddr
|
||||
}
|
||||
tlen := len(tx)
|
||||
// Quick return if possible.
|
||||
if tlen == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = i2c.disable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i2c.Bus.IC_TAR.Set(uint32(addr))
|
||||
i2c.enable()
|
||||
// If no timeout was passed timeoutCheck is false.
|
||||
abort := false
|
||||
var abortReason uint32
|
||||
byteCtr := 0
|
||||
for ; byteCtr < tlen; byteCtr++ {
|
||||
first := byteCtr == 0
|
||||
last := byteCtr == tlen-1
|
||||
i2c.Bus.IC_DATA_CMD.Set(
|
||||
(boolToBit(first && i2c.restartOnNext) << rp.I2C0_IC_DATA_CMD_RESTART_Pos) |
|
||||
(boolToBit(last && !nostop) << rp.I2C0_IC_DATA_CMD_STOP_Pos) |
|
||||
uint32(tx[byteCtr]))
|
||||
|
||||
// Wait until the transmission of the address/data from the internal
|
||||
// shift register has completed. For this to function correctly, the
|
||||
// TX_EMPTY_CTRL flag in IC_CON must be set. The TX_EMPTY_CTRL flag
|
||||
// was set in i2c_init.
|
||||
|
||||
// IC_RAW_INTR_STAT_TX_EMPTY: This bit is set to 1 when the transmit buffer is at or below
|
||||
// the threshold value set in the IC_TX_TL register and the
|
||||
// transmission of the address/data from the internal shift
|
||||
// register for the most recently popped command is
|
||||
// completed. It is automatically cleared by hardware when
|
||||
// the buffer level goes above the threshold. When
|
||||
// IC_ENABLE[0] is set to 0, the TX FIFO is flushed and held
|
||||
// in reset. There the TX FIFO looks like it has no data within
|
||||
// it, so this bit is set to 1, provided there is activity in the
|
||||
// master or slave state machines. When there is no longer
|
||||
// any activity, then with ic_en=0, this bit is set to 0.
|
||||
for !i2c.interrupted(rp.I2C0_IC_RAW_INTR_STAT_TX_EMPTY) {
|
||||
if ticks() > deadline {
|
||||
i2c.restartOnNext = nostop
|
||||
println(1)
|
||||
return errI2CWriteTimeout // If there was a timeout, don't attempt to do anything else.
|
||||
}
|
||||
}
|
||||
|
||||
abortReason = i2c.getAbortReason()
|
||||
if abortReason != 0 {
|
||||
i2c.clearAbortReason()
|
||||
abort = true
|
||||
}
|
||||
if abort || (last && !nostop) {
|
||||
// If the transaction was aborted or if it completed
|
||||
// successfully wait until the STOP condition has occured.
|
||||
|
||||
// TODO Could there be an abort while waiting for the STOP
|
||||
// condition here? If so, additional code would be needed here
|
||||
// to take care of the abort.
|
||||
for !i2c.interrupted(rp.I2C0_IC_RAW_INTR_STAT_STOP_DET) {
|
||||
if ticks() > deadline {
|
||||
println(2)
|
||||
i2c.restartOnNext = nostop
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
}
|
||||
i2c.Bus.IC_CLR_STOP_DET.Get()
|
||||
}
|
||||
}
|
||||
|
||||
// From Pico SDK: A lot of things could have just happened due to the ingenious and
|
||||
// creative design of I2C. Try to figure things out.
|
||||
if abort {
|
||||
switch {
|
||||
case abortReason == 0 || abortReason&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK != 0:
|
||||
// No reported errors - seems to happen if there is nothing connected to the bus.
|
||||
// Address byte not acknowledged
|
||||
err = ErrI2CGeneric
|
||||
case abortReason&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK != 0:
|
||||
// Address acknowledged, some data not acknowledged
|
||||
fallthrough
|
||||
default:
|
||||
// panic("unknown i2c abortReason:" + strconv.Itoa(abortReason)
|
||||
err = makeI2CBuffError(byteCtr)
|
||||
}
|
||||
}
|
||||
|
||||
// nostop means we are now at the end of a *message* but not the end of a *transfer*
|
||||
i2c.restartOnNext = nostop
|
||||
return err
|
||||
}
|
||||
|
||||
// rx is a primitive i2c blocking read routine. timeout is time to wait
|
||||
// in microseconds since calling this function for read to finish.
|
||||
func (i2c *I2C) rx(addr uint8, rx []byte, nostop bool, timeout uint64) (err error) {
|
||||
deadline := ticks() + timeout
|
||||
if addr >= 0x80 || isReservedI2CAddr(addr) {
|
||||
return ErrInvalidTgtAddr
|
||||
}
|
||||
rlen := len(rx)
|
||||
// Quick return if possible.
|
||||
if rlen == 0 {
|
||||
return nil
|
||||
}
|
||||
err = i2c.disable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i2c.Bus.IC_TAR.Set(uint32(addr))
|
||||
i2c.enable()
|
||||
// If no timeout was passed timeoutCheck is false.
|
||||
abort := false
|
||||
var abortReason uint32
|
||||
byteCtr := 0
|
||||
for ; byteCtr < rlen; byteCtr++ {
|
||||
first := byteCtr == 0
|
||||
last := byteCtr == rlen-1
|
||||
for i2c.writeAvailable() == 0 {
|
||||
}
|
||||
i2c.Bus.IC_DATA_CMD.Set(
|
||||
boolToBit(first && i2c.restartOnNext)<<rp.I2C0_IC_DATA_CMD_RESTART_Pos |
|
||||
boolToBit(last && !nostop)<<rp.I2C0_IC_DATA_CMD_STOP_Pos |
|
||||
rp.I2C0_IC_DATA_CMD_CMD) // -> 1 for read
|
||||
|
||||
for !abort && i2c.readAvailable() == 0 {
|
||||
abortReason = i2c.getAbortReason()
|
||||
i2c.clearAbortReason()
|
||||
if abortReason != 0 {
|
||||
abort = true
|
||||
}
|
||||
if ticks() > deadline {
|
||||
i2c.restartOnNext = nostop
|
||||
return errI2CReadTimeout // If there was a timeout, don't attempt to do anything else.
|
||||
}
|
||||
}
|
||||
if abort {
|
||||
break
|
||||
}
|
||||
rx[byteCtr] = uint8(i2c.Bus.IC_DATA_CMD.Get())
|
||||
}
|
||||
|
||||
if abort {
|
||||
switch {
|
||||
case abortReason == 0 || abortReason&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK != 0:
|
||||
// No reported errors - seems to happen if there is nothing connected to the bus.
|
||||
// Address byte not acknowledged
|
||||
err = ErrI2CGeneric
|
||||
default:
|
||||
// undefined abort sequence
|
||||
err = makeI2CBuffError(byteCtr)
|
||||
}
|
||||
}
|
||||
|
||||
i2c.restartOnNext = nostop
|
||||
return err
|
||||
}
|
||||
|
||||
// writeAvailable determines non-blocking write space available
|
||||
//go:inline
|
||||
func (i2c *I2C) writeAvailable() uint32 {
|
||||
return rp.I2C0_IC_COMP_PARAM_1_TX_BUFFER_DEPTH_Pos - i2c.Bus.IC_TXFLR.Get()
|
||||
}
|
||||
|
||||
// readAvailable determines number of bytes received
|
||||
//go:inline
|
||||
func (i2c *I2C) readAvailable() uint32 {
|
||||
return i2c.Bus.IC_RXFLR.Get()
|
||||
}
|
||||
|
||||
// Equivalent to IC_CLR_TX_ABRT.Get() (side effect clears ABORT_REASON)
|
||||
//go:inline
|
||||
func (i2c *I2C) clearAbortReason() {
|
||||
// Note clearing the abort flag also clears the reason, and
|
||||
// this instance of flag is clear-on-read! Note also the
|
||||
// IC_CLR_TX_ABRT register always reads as 0.
|
||||
i2c.Bus.IC_CLR_TX_ABRT.Get()
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (i2c *I2C) getAbortReason() uint32 {
|
||||
return i2c.Bus.IC_TX_ABRT_SOURCE.Get()
|
||||
}
|
||||
|
||||
// returns true if RAW_INTR_STAT bits in mask are all set. performs:
|
||||
// RAW_INTR_STAT & mask == mask
|
||||
//go:inline
|
||||
func (i2c *I2C) interrupted(mask uint32) bool {
|
||||
reg := i2c.Bus.IC_RAW_INTR_STAT.Get()
|
||||
return reg&mask == mask
|
||||
}
|
||||
|
||||
type i2cBuffError int
|
||||
|
||||
func (b i2cBuffError) Error() string {
|
||||
return "i2c err after addr ack at data " + strconv.Itoa(int(b))
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func makeI2CBuffError(idx int) error {
|
||||
return i2cBuffError(idx)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func boolToBit(a bool) uint32 {
|
||||
if a {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func u32max(a, b uint32) uint32 {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func isReservedI2CAddr(addr uint8) bool {
|
||||
return (addr&0x78) == 0 || (addr&0x78) == 0x78
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
// +build rp2040
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/rp"
|
||||
"errors"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrPeriodTooBig = errors.New("period outside valid range 1..4e9ns")
|
||||
)
|
||||
|
||||
const (
|
||||
maxPWMPins = 29
|
||||
)
|
||||
|
||||
// pwmGroup is one PWM peripheral, which consists of a counter and two output
|
||||
// channels. You can set the frequency using SetPeriod,
|
||||
// but only for all the channels in this PWM peripheral at once.
|
||||
//
|
||||
// div: integer value to reduce counting rate by. Must be greater than or equal to 1.
|
||||
//
|
||||
// cc: counter compare level. Contains 2 channel levels. The 16 LSBs are Channel A's level (Duty Cycle)
|
||||
// and the 16 MSBs are Channel B's level.
|
||||
//
|
||||
// top: Wrap. Highest number counter will reach before wrapping over. usually 0xffff.
|
||||
//
|
||||
// csr: Clock mode. PWM_CH0_CSR_DIVMODE_xxx registers have 4 possible modes, of which Free-running is used.
|
||||
// csr contains output polarity bit at PWM_CH0_CSR_x_INV where x is the channel.
|
||||
// csr contains phase correction bit at PWM_CH0_CSR_PH_CORRECT_Msk.
|
||||
// csr contains PWM enable bit at PWM_CH0_CSR_EN. If not enabled PWM will not be active.
|
||||
//
|
||||
// ctr: PWM counter value.
|
||||
type pwmGroup struct {
|
||||
CSR volatile.Register32
|
||||
DIV volatile.Register32
|
||||
CTR volatile.Register32
|
||||
CC volatile.Register32
|
||||
TOP volatile.Register32
|
||||
}
|
||||
|
||||
// Equivalent of
|
||||
// var pwmSlice []pwmGroup = (*[8]pwmGroup)(unsafe.Pointer(rp.PWM))[:]
|
||||
// return &pwmSlice[index]
|
||||
// 0x14 is the size of a pwmGroup.
|
||||
func getPWMGroup(index uintptr) *pwmGroup {
|
||||
return (*pwmGroup)(unsafe.Pointer(uintptr(unsafe.Pointer(rp.PWM)) + 0x14*index))
|
||||
}
|
||||
|
||||
// PWM peripherals available on RP2040. Each peripheral has 2 pins available for
|
||||
// a total of 16 available PWM outputs. Some pins may not be available on some boards.
|
||||
var (
|
||||
PWM0 = getPWMGroup(0)
|
||||
PWM1 = getPWMGroup(1)
|
||||
PWM2 = getPWMGroup(2)
|
||||
PWM3 = getPWMGroup(3)
|
||||
PWM4 = getPWMGroup(4)
|
||||
PWM5 = getPWMGroup(5)
|
||||
PWM6 = getPWMGroup(6)
|
||||
PWM7 = getPWMGroup(7)
|
||||
)
|
||||
|
||||
// Configure enables and configures this PWM.
|
||||
func (pwm *pwmGroup) Configure(config PWMConfig) error {
|
||||
return pwm.init(config, true)
|
||||
}
|
||||
|
||||
// Channel returns a PWM channel for the given pin. If pin does
|
||||
// not belong to PWM peripheral ErrInvalidOutputPin error is returned.
|
||||
// It also configures pin as PWM output.
|
||||
func (pwm *pwmGroup) Channel(pin Pin) (channel uint8, err error) {
|
||||
if pin > maxPWMPins || pwmGPIOToSlice(pin) != pwm.peripheral() {
|
||||
return 3, ErrInvalidOutputPin
|
||||
}
|
||||
pin.Configure(PinConfig{PinPWM})
|
||||
return pwmGPIOToChannel(pin), nil
|
||||
}
|
||||
|
||||
// Peripheral returns the RP2040 PWM peripheral which ranges from 0 to 7. Each
|
||||
// PWM peripheral has 2 channels, A and B which correspond to 0 and 1 in the program.
|
||||
// This number corresponds to the package's PWM0 throughout PWM7 handles
|
||||
func PWMPeripheral(pin Pin) (sliceNum uint8, err error) {
|
||||
if pin > maxPWMPins {
|
||||
return 0, ErrInvalidOutputPin
|
||||
}
|
||||
return pwmGPIOToSlice(pin), nil
|
||||
}
|
||||
|
||||
// returns the number of the pwm peripheral (0-7)
|
||||
func (pwm *pwmGroup) peripheral() uint8 {
|
||||
return uint8((uintptr(unsafe.Pointer(pwm)) - uintptr(unsafe.Pointer(rp.PWM))) / 0x14)
|
||||
}
|
||||
|
||||
// SetPeriod updates the period of this PWM peripheral.
|
||||
// To set a particular frequency, use the following formula:
|
||||
//
|
||||
// period = 1e9 / frequency
|
||||
//
|
||||
// If you use a period of 0, a period that works well for LEDs will be picked.
|
||||
//
|
||||
// SetPeriod will not change the prescaler, but also won't change the current
|
||||
// value in any of the channels. This means that you may need to update the
|
||||
// value for the particular channel.
|
||||
//
|
||||
// Note that you cannot pick any arbitrary period after the PWM peripheral has
|
||||
// been configured. If you want to switch between frequencies, pick the lowest
|
||||
// frequency (longest period) once when calling Configure and adjust the
|
||||
// frequency here as needed.
|
||||
func (p *pwmGroup) SetPeriod(period uint64) error {
|
||||
if period > 0xffff_ffff {
|
||||
return ErrPeriodTooBig
|
||||
}
|
||||
if period == 0 {
|
||||
period = 1e5
|
||||
}
|
||||
p.setPeriod(period)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Top returns the current counter top, for use in duty cycle calculation.
|
||||
//
|
||||
// The value returned here is hardware dependent. In general, it's best to treat
|
||||
// it as an opaque value that can be divided by some number and passed to Set
|
||||
// (see Set documentation for more information).
|
||||
func (p *pwmGroup) Top() uint32 {
|
||||
return p.getWrap()
|
||||
}
|
||||
|
||||
// Counter returns the current counter value of the timer in this PWM
|
||||
// peripheral. It may be useful for debugging.
|
||||
func (p *pwmGroup) Counter() uint32 {
|
||||
return (p.CTR.Get() & rp.PWM_CH0_CTR_CH0_CTR_Msk) >> rp.PWM_CH0_CTR_CH0_CTR_Pos
|
||||
}
|
||||
|
||||
// Period returns the used PWM period in nanoseconds. It might deviate slightly
|
||||
// from the configured period due to rounding.
|
||||
func (p *pwmGroup) Period() uint64 {
|
||||
periodPerCycle := getPeriod()
|
||||
top := p.getWrap()
|
||||
phc := p.getPhaseCorrect()
|
||||
Int, frac := p.getClockDiv()
|
||||
return uint64((Int + frac/16) * (top + 1) * (phc + 1) * periodPerCycle) // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
|
||||
}
|
||||
|
||||
// SetInverting sets whether to invert the output of this channel.
|
||||
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
||||
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
||||
// was placed at the output, meaning that the output would be 25% low and 75%
|
||||
// high with a duty cycle of 25%.
|
||||
func (p *pwmGroup) SetInverting(channel uint8, inverting bool) {
|
||||
channel &= 1
|
||||
p.setInverting(channel, inverting)
|
||||
}
|
||||
|
||||
// Set updates the channel value. This is used to control the channel duty
|
||||
// cycle, in other words the fraction of time the channel output is high (or low
|
||||
// when inverted). For example, to set it to a 25% duty cycle, use:
|
||||
//
|
||||
// pwm.Set(channel, pwm.Top() / 4)
|
||||
//
|
||||
// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
|
||||
// pwm.Top()) will set the output to high, assuming the output isn't inverted.
|
||||
func (p *pwmGroup) Set(channel uint8, value uint32) {
|
||||
val := uint16(value)
|
||||
channel &= 1
|
||||
p.setChanLevel(channel, val)
|
||||
}
|
||||
|
||||
// Get current level (last set by Set). Default value on initialization is 0.
|
||||
func (p *pwmGroup) Get(channel uint8) (value uint32) {
|
||||
channel &= 1
|
||||
return uint32(p.getChanLevel(channel))
|
||||
}
|
||||
|
||||
// SetTop sets TOP control register. Max value is 16bit (0xffff).
|
||||
func (p *pwmGroup) SetTop(top uint32) {
|
||||
p.setWrap(uint16(top))
|
||||
}
|
||||
|
||||
// Enable enables or disables PWM peripheral channels.
|
||||
func (p *pwmGroup) Enable(enable bool) {
|
||||
p.enable(enable)
|
||||
}
|
||||
|
||||
// IsEnabled returns true if peripheral is enabled.
|
||||
func (p *pwmGroup) IsEnabled() (enabled bool) {
|
||||
return (p.CSR.Get()&rp.PWM_CH0_CSR_EN_Msk)>>rp.PWM_CH0_CSR_EN_Pos != 0
|
||||
}
|
||||
|
||||
// Hardware Pulse Width Modulation (PWM) API
|
||||
//
|
||||
// The RP2040 PWM block has 8 identical slices. Each slice can drive two PWM output signals, or
|
||||
// measure the frequency or duty cycle of an input signal. This gives a total of up to 16 controllable
|
||||
// PWM outputs. All 30 GPIOs can be driven by the PWM block
|
||||
//
|
||||
// The PWM hardware functions by continuously comparing the input value to a free-running counter. This produces a
|
||||
// toggling output where the amount of time spent at the high output level is proportional to the input value. The fraction of
|
||||
// time spent at the high signal level is known as the duty cycle of the signal.
|
||||
//
|
||||
// The default behaviour of a PWM slice is to count upward until the wrap value (\ref pwm_config_set_wrap) is reached, and then
|
||||
// immediately wrap to 0. PWM slices also offer a phase-correct mode, where the counter starts to count downward after
|
||||
// reaching TOP, until it reaches 0 again.
|
||||
type pwms struct {
|
||||
slice pwmGroup
|
||||
hw *rp.PWM_Type
|
||||
}
|
||||
|
||||
// Handle to all pwm peripheral registers.
|
||||
var _PWM = pwms{
|
||||
hw: rp.PWM,
|
||||
}
|
||||
|
||||
// Initialise a PWM with settings from a configuration object.
|
||||
// If start is true then PWM starts on initialization.
|
||||
func (pwm *pwmGroup) init(config PWMConfig, start bool) error {
|
||||
// Not enable Phase correction
|
||||
pwm.setPhaseCorrect(false)
|
||||
|
||||
// Clock mode set by default to Free running
|
||||
pwm.setDivMode(rp.PWM_CH0_CSR_DIVMODE_DIV)
|
||||
|
||||
// Set Output polarity (false/false)
|
||||
pwm.setInverting(0, false)
|
||||
pwm.setInverting(1, false)
|
||||
|
||||
// Set wrap. The highest value the counter will reach before returning to zero, also known as TOP.
|
||||
pwm.setWrap(0xffff)
|
||||
// period is set after TOP (Wrap).
|
||||
err := pwm.SetPeriod(config.Period)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// period already set beforea
|
||||
// Reset counter and compare (pwm level set to zero)
|
||||
pwm.CTR.ReplaceBits(0, rp.PWM_CH0_CTR_CH0_CTR_Msk, 0) // PWM_CH0_CTR_RESET
|
||||
pwm.CC.Set(0) // PWM_CH0_CC_RESET
|
||||
|
||||
pwm.enable(start)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pwm *pwmGroup) setPhaseCorrect(correct bool) {
|
||||
pwm.CSR.ReplaceBits(boolToBit(correct)<<rp.PWM_CH0_CSR_PH_CORRECT_Pos, rp.PWM_CH0_CSR_PH_CORRECT_Msk, 0)
|
||||
}
|
||||
|
||||
// Takes any of the following:
|
||||
// rp.PWM_CH0_CSR_DIVMODE_DIV, rp.PWM_CH0_CSR_DIVMODE_FALL,
|
||||
// rp.PWM_CH0_CSR_DIVMODE_LEVEL, rp.PWM_CH0_CSR_DIVMODE_RISE
|
||||
func (pwm *pwmGroup) setDivMode(mode uint32) {
|
||||
pwm.CSR.ReplaceBits(mode<<rp.PWM_CH0_CSR_DIVMODE_Pos, rp.PWM_CH0_CSR_DIVMODE_Msk, 0)
|
||||
}
|
||||
|
||||
// setPeriod sets the pwm peripheral period (frequency). Calculates DIV_INT and sets it from following equation:
|
||||
// cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
|
||||
// where cycles is amount of clock cycles per PWM period.
|
||||
func (pwm *pwmGroup) setPeriod(period uint64) {
|
||||
targetPeriod := uint32(period)
|
||||
periodPerCycle := getPeriod()
|
||||
top := pwm.getWrap()
|
||||
phc := pwm.getPhaseCorrect()
|
||||
_, frac := pwm.getClockDiv()
|
||||
// clearing above expression:
|
||||
// DIV_INT = cycles / ( (TOP+1) * (CSRPHCorrect+1) ) - DIV_FRAC/16
|
||||
// where cycles must be converted to time:
|
||||
// target_period = cycles * period_per_cycle ==> cycles = target_period/period_per_cycle
|
||||
Int := targetPeriod/((1+phc)*periodPerCycle*(1+top)) - frac/16
|
||||
if Int > 0xff {
|
||||
Int = 0xff
|
||||
}
|
||||
pwm.setClockDiv(uint8(Int), 0)
|
||||
}
|
||||
|
||||
// Int is integer value to reduce counting rate by. Must be greater than or equal to 1. DIV_INT is bits 4:11 (8 bits).
|
||||
// frac's (DIV_FRAC) default value on reset is 0. Max value for frac is 15 (4 bits). This is known as a fixed-point
|
||||
// fractional number.
|
||||
//
|
||||
// cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
|
||||
func (pwm *pwmGroup) setClockDiv(Int, frac uint8) {
|
||||
pwm.DIV.ReplaceBits((uint32(frac)<<rp.PWM_CH0_DIV_FRAC_Pos)|
|
||||
u32max(uint32(Int), 1)<<rp.PWM_CH0_DIV_INT_Pos, rp.PWM_CH0_DIV_FRAC_Msk|rp.PWM_CH0_DIV_INT_Msk, 0)
|
||||
}
|
||||
|
||||
// Set the highest value the counter will reach before returning to 0. Also
|
||||
// known as TOP.
|
||||
//
|
||||
// The counter wrap value is double-buffered in hardware. This means that,
|
||||
// when the PWM is running, a write to the counter wrap value does not take
|
||||
// effect until after the next time the PWM slice wraps (or, in phase-correct
|
||||
// mode, the next time the slice reaches 0). If the PWM is not running, the
|
||||
// write is latched in immediately.
|
||||
func (pwm *pwmGroup) setWrap(wrap uint16) {
|
||||
pwm.TOP.ReplaceBits(uint32(wrap)<<rp.PWM_CH0_TOP_CH0_TOP_Pos, rp.PWM_CH0_TOP_CH0_TOP_Msk, 0)
|
||||
}
|
||||
|
||||
// enables/disables the PWM peripheral with rp.PWM_CH0_CSR_EN bit.
|
||||
func (pwm *pwmGroup) enable(enable bool) {
|
||||
pwm.CSR.ReplaceBits(boolToBit(enable)<<rp.PWM_CH0_CSR_EN_Pos, rp.PWM_CH0_CSR_EN_Msk, 0)
|
||||
}
|
||||
|
||||
func (pwm *pwmGroup) setInverting(channel uint8, invert bool) {
|
||||
var pos uint8
|
||||
var msk uint32
|
||||
switch channel {
|
||||
case 0:
|
||||
pos = rp.PWM_CH0_CSR_A_INV_Pos
|
||||
msk = rp.PWM_CH0_CSR_A_INV_Msk
|
||||
case 1:
|
||||
pos = rp.PWM_CH0_CSR_B_INV_Pos
|
||||
msk = rp.PWM_CH0_CSR_B_INV_Msk
|
||||
}
|
||||
pwm.CSR.ReplaceBits(boolToBit(invert)<<pos, msk, 0)
|
||||
}
|
||||
|
||||
// Set the current PWM counter compare value for one channel
|
||||
//
|
||||
// The counter compare register is double-buffered in hardware. This means
|
||||
// that, when the PWM is running, a write to the counter compare values does
|
||||
// not take effect until the next time the PWM slice wraps (or, in
|
||||
// phase-correct mode, the next time the slice reaches 0). If the PWM is not
|
||||
// running, the write is latched in immediately.
|
||||
// Channel is 0 for A, 1 for B.
|
||||
func (pwm *pwmGroup) setChanLevel(channel uint8, level uint16) {
|
||||
var pos uint8
|
||||
var mask uint32
|
||||
switch channel {
|
||||
case 0:
|
||||
pos = rp.PWM_CH0_CC_A_Pos
|
||||
mask = rp.PWM_CH0_CC_A_Msk
|
||||
case 1:
|
||||
pos = rp.PWM_CH0_CC_B_Pos
|
||||
mask = rp.PWM_CH0_CC_B_Msk
|
||||
}
|
||||
pwm.CC.ReplaceBits(uint32(level)<<pos, mask, 0)
|
||||
}
|
||||
|
||||
func (pwm *pwmGroup) getChanLevel(channel uint8) (level uint16) {
|
||||
var pos uint8
|
||||
var mask uint32
|
||||
switch channel {
|
||||
case 0:
|
||||
pos = rp.PWM_CH0_CC_A_Pos
|
||||
mask = rp.PWM_CH0_CC_A_Msk
|
||||
case 1:
|
||||
pos = rp.PWM_CH0_CC_B_Pos
|
||||
mask = rp.PWM_CH0_CC_B_Msk
|
||||
}
|
||||
|
||||
level = uint16((pwm.CC.Get() & mask) >> pos)
|
||||
return level
|
||||
}
|
||||
|
||||
func (pwm *pwmGroup) getWrap() (top uint32) {
|
||||
return (pwm.TOP.Get() & rp.PWM_CH0_TOP_CH0_TOP_Msk) >> rp.PWM_CH0_TOP_CH0_TOP_Pos
|
||||
}
|
||||
|
||||
func (pwm *pwmGroup) getPhaseCorrect() (phCorrect uint32) {
|
||||
return (pwm.CSR.Get() & rp.PWM_CH0_CSR_PH_CORRECT_Msk) >> rp.PWM_CH0_CSR_PH_CORRECT_Pos
|
||||
}
|
||||
|
||||
func (pwm *pwmGroup) getClockDiv() (Int, frac uint32) {
|
||||
div := pwm.DIV.Get()
|
||||
return (div & rp.PWM_CH0_DIV_INT_Msk) >> rp.PWM_CH0_DIV_INT_Pos, (div & rp.PWM_CH0_DIV_FRAC_Msk) >> rp.PWM_CH0_DIV_FRAC_Pos
|
||||
}
|
||||
|
||||
// pwmGPIOToSlice Determine the PWM channel that is attached to the specified GPIO.
|
||||
// gpio must be less than 30. Returns the PWM slice number that controls the specified GPIO.
|
||||
func pwmGPIOToSlice(gpio Pin) (slicenum uint8) {
|
||||
return (uint8(gpio) >> 1) & 7
|
||||
}
|
||||
|
||||
// Determine the PWM channel that is attached to the specified GPIO.
|
||||
// Each slice 0 to 7 has two channels, A and B.
|
||||
func pwmGPIOToChannel(gpio Pin) (channel uint8) {
|
||||
return uint8(gpio) & 1
|
||||
}
|
||||
|
||||
// Returns the period of a clock cycle for the raspberry pi pico in nanoseconds.
|
||||
func getPeriod() uint32 {
|
||||
const periodIn uint32 = 1e9 / (125 * MHz)
|
||||
return periodIn
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// +build !linux
|
||||
|
||||
package os
|
||||
|
||||
import "errors"
|
||||
|
||||
func Executable() (string, error) {
|
||||
return "", errors.New("Executable not implemented")
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// The following is copied from Go 1.17 official implementation.
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux
|
||||
|
||||
package os
|
||||
|
||||
func Executable() (string, error) {
|
||||
path, err := Readlink("/proc/self/exe")
|
||||
|
||||
// When the executable has been deleted then Readlink returns a
|
||||
// path appended with " (deleted)".
|
||||
return stringsTrimSuffix(path, " (deleted)"), err
|
||||
}
|
||||
|
||||
// stringsTrimSuffix is the same as strings.TrimSuffix.
|
||||
func stringsTrimSuffix(s, suffix string) string {
|
||||
if len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix {
|
||||
return s[:len(s)-len(suffix)]
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -24,6 +24,11 @@ func Mkdir(path string, perm FileMode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MkdirTemp is a stub, it will always return an error.
|
||||
func MkdirTemp(dir, pattern string) (string, error) {
|
||||
return "", &PathError{"mkdirtemp", dir, ErrNotImplemented}
|
||||
}
|
||||
|
||||
// Remove removes a file or (empty) directory. If the operation fails, it will
|
||||
// return an error of type *PathError.
|
||||
func Remove(path string) error {
|
||||
|
||||
@@ -706,6 +706,11 @@ type StructField struct {
|
||||
Offset uintptr
|
||||
}
|
||||
|
||||
// IsExported reports whether the field is exported.
|
||||
func (f StructField) IsExported() bool {
|
||||
return f.PkgPath == ""
|
||||
}
|
||||
|
||||
// rawStructField is the same as StructField but with the Type member replaced
|
||||
// with rawType. For internal use only. Avoiding this conversion to the Type
|
||||
// interface improves code size in many cases.
|
||||
|
||||
@@ -9,7 +9,7 @@ const TargetBits = 32
|
||||
|
||||
// Align on word boundary.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 3) &^ 3
|
||||
return (ptr + 15) &^ 15
|
||||
}
|
||||
|
||||
func getCurrentStackPointer() uintptr {
|
||||
|
||||
@@ -13,10 +13,11 @@ tinygo_scanCurrentStack:
|
||||
pushl %ebp
|
||||
|
||||
// Scan the stack.
|
||||
subl $8, %esp // adjust the stack before the call to maintain 16-byte alignment
|
||||
pushl %esp
|
||||
calll tinygo_scanstack
|
||||
|
||||
// Restore the stack pointer. Registers do not need to be restored as they
|
||||
// were only pushed to be discoverable by the GC.
|
||||
addl $20, %esp
|
||||
addl $28, %esp
|
||||
retl
|
||||
|
||||
@@ -27,3 +27,8 @@ _tinygo_scanCurrentStack:
|
||||
// were only pushed to be discoverable by the GC.
|
||||
addq $56, %rsp
|
||||
retq
|
||||
|
||||
#ifdef __MACH__ // Darwin
|
||||
// allow these symbols to stripped as dead code
|
||||
.subsections_via_symbols
|
||||
#endif
|
||||
|
||||
@@ -52,7 +52,8 @@ func SetFinalizer(obj interface{}, finalizer interface{}) {
|
||||
}
|
||||
|
||||
func initHeap() {
|
||||
// Nothing to initialize.
|
||||
// preinit() may have moved heapStart; reset heapptr
|
||||
heapptr = heapStart
|
||||
}
|
||||
|
||||
// setHeapEnd sets a new (larger) heapEnd pointer.
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
package runtime
|
||||
|
||||
// This file redirects math stubs to their fallback implementation.
|
||||
// TODO: use optimized versions if possible.
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
//go:linkname math_Asin math.Asin
|
||||
func math_Asin(x float64) float64 { return math_asin(x) }
|
||||
|
||||
//go:linkname math_asin math.asin
|
||||
func math_asin(x float64) float64
|
||||
|
||||
//go:linkname math_Asinh math.Asinh
|
||||
func math_Asinh(x float64) float64 { return math_asinh(x) }
|
||||
|
||||
//go:linkname math_asinh math.asinh
|
||||
func math_asinh(x float64) float64
|
||||
|
||||
//go:linkname math_Acos math.Acos
|
||||
func math_Acos(x float64) float64 { return math_acos(x) }
|
||||
|
||||
//go:linkname math_acos math.acos
|
||||
func math_acos(x float64) float64
|
||||
|
||||
//go:linkname math_Acosh math.Acosh
|
||||
func math_Acosh(x float64) float64 { return math_acosh(x) }
|
||||
|
||||
//go:linkname math_acosh math.acosh
|
||||
func math_acosh(x float64) float64
|
||||
|
||||
//go:linkname math_Atan math.Atan
|
||||
func math_Atan(x float64) float64 { return math_atan(x) }
|
||||
|
||||
//go:linkname math_atan math.atan
|
||||
func math_atan(x float64) float64
|
||||
|
||||
//go:linkname math_Atanh math.Atanh
|
||||
func math_Atanh(x float64) float64 { return math_atanh(x) }
|
||||
|
||||
//go:linkname math_atanh math.atanh
|
||||
func math_atanh(x float64) float64
|
||||
|
||||
//go:linkname math_Atan2 math.Atan2
|
||||
func math_Atan2(y, x float64) float64 { return math_atan2(y, x) }
|
||||
|
||||
//go:linkname math_atan2 math.atan2
|
||||
func math_atan2(y, x float64) float64
|
||||
|
||||
//go:linkname math_Cbrt math.Cbrt
|
||||
func math_Cbrt(x float64) float64 { return math_cbrt(x) }
|
||||
|
||||
//go:linkname math_cbrt math.cbrt
|
||||
func math_cbrt(x float64) float64
|
||||
|
||||
//go:linkname math_Ceil math.Ceil
|
||||
func math_Ceil(x float64) float64 {
|
||||
if GOARCH == "arm64" || GOARCH == "wasm" {
|
||||
return llvm_ceil(x)
|
||||
}
|
||||
return math_ceil(x)
|
||||
}
|
||||
|
||||
//export llvm.ceil.f64
|
||||
func llvm_ceil(x float64) float64
|
||||
|
||||
//go:linkname math_ceil math.ceil
|
||||
func math_ceil(x float64) float64
|
||||
|
||||
//go:linkname math_Cos math.Cos
|
||||
func math_Cos(x float64) float64 { return math_cos(x) }
|
||||
|
||||
//go:linkname math_cos math.cos
|
||||
func math_cos(x float64) float64
|
||||
|
||||
//go:linkname math_Cosh math.Cosh
|
||||
func math_Cosh(x float64) float64 { return math_cosh(x) }
|
||||
|
||||
//go:linkname math_cosh math.cosh
|
||||
func math_cosh(x float64) float64
|
||||
|
||||
//go:linkname math_Erf math.Erf
|
||||
func math_Erf(x float64) float64 { return math_erf(x) }
|
||||
|
||||
//go:linkname math_erf math.erf
|
||||
func math_erf(x float64) float64
|
||||
|
||||
//go:linkname math_Erfc math.Erfc
|
||||
func math_Erfc(x float64) float64 { return math_erfc(x) }
|
||||
|
||||
//go:linkname math_erfc math.erfc
|
||||
func math_erfc(x float64) float64
|
||||
|
||||
//go:linkname math_Exp math.Exp
|
||||
func math_Exp(x float64) float64 { return math_exp(x) }
|
||||
|
||||
//go:linkname math_exp math.exp
|
||||
func math_exp(x float64) float64
|
||||
|
||||
//go:linkname math_Expm1 math.Expm1
|
||||
func math_Expm1(x float64) float64 { return math_expm1(x) }
|
||||
|
||||
//go:linkname math_expm1 math.expm1
|
||||
func math_expm1(x float64) float64
|
||||
|
||||
//go:linkname math_Exp2 math.Exp2
|
||||
func math_Exp2(x float64) float64 { return math_exp2(x) }
|
||||
|
||||
//go:linkname math_exp2 math.exp2
|
||||
func math_exp2(x float64) float64
|
||||
|
||||
//go:linkname math_Floor math.Floor
|
||||
func math_Floor(x float64) float64 {
|
||||
if GOARCH == "arm64" || GOARCH == "wasm" {
|
||||
return llvm_floor(x)
|
||||
}
|
||||
return math_floor(x)
|
||||
}
|
||||
|
||||
//export llvm.floor.f64
|
||||
func llvm_floor(x float64) float64
|
||||
|
||||
//go:linkname math_floor math.floor
|
||||
func math_floor(x float64) float64
|
||||
|
||||
//go:linkname math_Frexp math.Frexp
|
||||
func math_Frexp(x float64) (float64, int) { return math_frexp(x) }
|
||||
|
||||
//go:linkname math_frexp math.frexp
|
||||
func math_frexp(x float64) (float64, int)
|
||||
|
||||
//go:linkname math_Hypot math.Hypot
|
||||
func math_Hypot(p, q float64) float64 { return math_hypot(p, q) }
|
||||
|
||||
//go:linkname math_hypot math.hypot
|
||||
func math_hypot(p, q float64) float64
|
||||
|
||||
//go:linkname math_Ldexp math.Ldexp
|
||||
func math_Ldexp(frac float64, exp int) float64 { return math_ldexp(frac, exp) }
|
||||
|
||||
//go:linkname math_ldexp math.ldexp
|
||||
func math_ldexp(frac float64, exp int) float64
|
||||
|
||||
//go:linkname math_Log math.Log
|
||||
func math_Log(x float64) float64 { return math_log(x) }
|
||||
|
||||
//go:linkname math_log math.log
|
||||
func math_log(x float64) float64
|
||||
|
||||
//go:linkname math_Log1p math.Log1p
|
||||
func math_Log1p(x float64) float64 { return math_log1p(x) }
|
||||
|
||||
//go:linkname math_log1p math.log1p
|
||||
func math_log1p(x float64) float64
|
||||
|
||||
//go:linkname math_Log10 math.Log10
|
||||
func math_Log10(x float64) float64 { return math_log10(x) }
|
||||
|
||||
//go:linkname math_log10 math.log10
|
||||
func math_log10(x float64) float64
|
||||
|
||||
//go:linkname math_Log2 math.Log2
|
||||
func math_Log2(x float64) float64 { return math_log2(x) }
|
||||
|
||||
//go:linkname math_log2 math.log2
|
||||
func math_log2(x float64) float64
|
||||
|
||||
//go:linkname math_Max math.Max
|
||||
func math_Max(x, y float64) float64 {
|
||||
if GOARCH == "arm64" || GOARCH == "wasm" {
|
||||
return llvm_maximum(x, y)
|
||||
}
|
||||
return math_max(x, y)
|
||||
}
|
||||
|
||||
//export llvm.maximum.f64
|
||||
func llvm_maximum(x, y float64) float64
|
||||
|
||||
//go:linkname math_max math.max
|
||||
func math_max(x, y float64) float64
|
||||
|
||||
//go:linkname math_Min math.Min
|
||||
func math_Min(x, y float64) float64 {
|
||||
if GOARCH == "arm64" || GOARCH == "wasm" {
|
||||
return llvm_minimum(x, y)
|
||||
}
|
||||
return math_min(x, y)
|
||||
}
|
||||
|
||||
//export llvm.minimum.f64
|
||||
func llvm_minimum(x, y float64) float64
|
||||
|
||||
//go:linkname math_min math.min
|
||||
func math_min(x, y float64) float64
|
||||
|
||||
//go:linkname math_Mod math.Mod
|
||||
func math_Mod(x, y float64) float64 { return math_mod(x, y) }
|
||||
|
||||
//go:linkname math_mod math.mod
|
||||
func math_mod(x, y float64) float64
|
||||
|
||||
//go:linkname math_Modf math.Modf
|
||||
func math_Modf(x float64) (float64, float64) { return math_modf(x) }
|
||||
|
||||
//go:linkname math_modf math.modf
|
||||
func math_modf(x float64) (float64, float64)
|
||||
|
||||
//go:linkname math_Pow math.Pow
|
||||
func math_Pow(x, y float64) float64 { return math_pow(x, y) }
|
||||
|
||||
//go:linkname math_pow math.pow
|
||||
func math_pow(x, y float64) float64
|
||||
|
||||
//go:linkname math_Remainder math.Remainder
|
||||
func math_Remainder(x, y float64) float64 { return math_remainder(x, y) }
|
||||
|
||||
//go:linkname math_remainder math.remainder
|
||||
func math_remainder(x, y float64) float64
|
||||
|
||||
//go:linkname math_Sin math.Sin
|
||||
func math_Sin(x float64) float64 { return math_sin(x) }
|
||||
|
||||
//go:linkname math_sin math.sin
|
||||
func math_sin(x float64) float64
|
||||
|
||||
//go:linkname math_Sinh math.Sinh
|
||||
func math_Sinh(x float64) float64 { return math_sinh(x) }
|
||||
|
||||
//go:linkname math_sinh math.sinh
|
||||
func math_sinh(x float64) float64
|
||||
|
||||
//go:linkname math_Sqrt math.Sqrt
|
||||
func math_Sqrt(x float64) float64 {
|
||||
if GOARCH == "x86" || GOARCH == "amd64" || GOARCH == "wasm" {
|
||||
return llvm_sqrt(x)
|
||||
}
|
||||
return math_sqrt(x)
|
||||
}
|
||||
|
||||
//export llvm.sqrt.f64
|
||||
func llvm_sqrt(x float64) float64
|
||||
|
||||
//go:linkname math_sqrt math.sqrt
|
||||
func math_sqrt(x float64) float64
|
||||
|
||||
//go:linkname math_Tan math.Tan
|
||||
func math_Tan(x float64) float64 { return math_tan(x) }
|
||||
|
||||
//go:linkname math_tan math.tan
|
||||
func math_tan(x float64) float64
|
||||
|
||||
//go:linkname math_Tanh math.Tanh
|
||||
func math_Tanh(x float64) float64 { return math_tanh(x) }
|
||||
|
||||
//go:linkname math_tanh math.tanh
|
||||
func math_tanh(x float64) float64
|
||||
|
||||
//go:linkname math_Trunc math.Trunc
|
||||
func math_Trunc(x float64) float64 {
|
||||
if GOARCH == "arm64" || GOARCH == "wasm" {
|
||||
return llvm_trunc(x)
|
||||
}
|
||||
return math_trunc(x)
|
||||
}
|
||||
|
||||
//export llvm.trunc.f64
|
||||
func llvm_trunc(x float64) float64
|
||||
|
||||
//go:linkname math_trunc math.trunc
|
||||
func math_trunc(x float64) float64
|
||||
@@ -0,0 +1,48 @@
|
||||
// +build baremetal js
|
||||
|
||||
package runtime
|
||||
|
||||
// This file is for non-hosted environments, that don't support command line
|
||||
// parameters or environment variables. To still be able to run certain tests,
|
||||
// command line parameters and environment variables can be passed to the binary
|
||||
// by setting the variables `runtime.osArgs` and `runtime.osEnv`, both of which
|
||||
// are strings separated by newlines.
|
||||
//
|
||||
// The primary use case is `tinygo test`, which takes some parameters (such as
|
||||
// -test.v).
|
||||
|
||||
var env []string
|
||||
|
||||
//go:linkname syscall_runtime_envs syscall.runtime_envs
|
||||
func syscall_runtime_envs() []string {
|
||||
return env
|
||||
}
|
||||
|
||||
var osArgs string
|
||||
var osEnv string
|
||||
|
||||
func init() {
|
||||
if osArgs != "" {
|
||||
s := osArgs
|
||||
start := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == 0 {
|
||||
args = append(args, s[start:i])
|
||||
start = i + 1
|
||||
}
|
||||
}
|
||||
args = append(args, s[start:])
|
||||
}
|
||||
|
||||
if osEnv != "" {
|
||||
s := osEnv
|
||||
start := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == 0 {
|
||||
env = append(env, s[start:i])
|
||||
start = i + 1
|
||||
}
|
||||
}
|
||||
env = append(env, s[start:])
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,18 @@ func slicePanic() {
|
||||
runtimePanic("slice out of range")
|
||||
}
|
||||
|
||||
// Panic when trying to convert a slice to an array pointer (Go 1.17+) and the
|
||||
// slice is shorter than the array.
|
||||
func sliceToArrayPointerPanic() {
|
||||
runtimePanic("slice smaller than array")
|
||||
}
|
||||
|
||||
// Panic when calling unsafe.Slice() (Go 1.17+) with a len that's too large
|
||||
// (which includes if the ptr is nil and len is nonzero).
|
||||
func unsafeSlicePanic() {
|
||||
runtimePanic("unsafe.Slice: len out of range")
|
||||
}
|
||||
|
||||
// Panic when trying to create a new channel that is too big.
|
||||
func chanMakePanic() {
|
||||
runtimePanic("new channel is too big")
|
||||
|
||||
@@ -15,11 +15,6 @@ func _start() {
|
||||
run()
|
||||
}
|
||||
|
||||
//go:linkname syscall_runtime_envs syscall.runtime_envs
|
||||
func syscall_runtime_envs() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
var handleEvent func()
|
||||
|
||||
//go:linkname setEventHandler syscall/js.setEventHandler
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build baremetal
|
||||
// +build baremetal js
|
||||
|
||||
package syscall
|
||||
|
||||
@@ -47,8 +47,24 @@ const (
|
||||
O_CLOEXEC = 0
|
||||
)
|
||||
|
||||
func runtime_envs() []string
|
||||
|
||||
func Getenv(key string) (value string, found bool) {
|
||||
return "", false // stub
|
||||
env := runtime_envs()
|
||||
for _, keyval := range env {
|
||||
// Split at '=' character.
|
||||
var k, v string
|
||||
for i := 0; i < len(keyval); i++ {
|
||||
if keyval[i] == '=' {
|
||||
k = keyval[:i]
|
||||
v = keyval[i+1:]
|
||||
}
|
||||
}
|
||||
if k == key {
|
||||
return v, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func Open(path string, mode int, perm uint32) (fd int, err error) {
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build baremetal nintendoswitch
|
||||
// +build baremetal nintendoswitch js
|
||||
|
||||
package syscall
|
||||
|
||||
+192
-12
@@ -6,29 +6,209 @@
|
||||
|
||||
package testing
|
||||
|
||||
// B is a type passed to Benchmark functions to manage benchmark timing and to
|
||||
// specify the number of iterations to run.
|
||||
//
|
||||
// TODO: Implement benchmarks. This struct allows test files containing
|
||||
// benchmarks to compile and run, but will not run the benchmarks themselves.
|
||||
type B struct {
|
||||
common
|
||||
N int
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
benchTime = benchTimeFlag{d: 1 * time.Second} // changed during test of testing package
|
||||
)
|
||||
|
||||
type benchTimeFlag struct {
|
||||
d time.Duration
|
||||
}
|
||||
|
||||
// B is a type passed to Benchmark functions to manage benchmark timing and to
|
||||
// specify the number of iterations to run.
|
||||
type B struct {
|
||||
common
|
||||
hasSub bool // TODO: should be in common, and atomic
|
||||
start time.Time // TODO: should be in common
|
||||
duration time.Duration // TODO: should be in common
|
||||
N int
|
||||
benchFunc func(b *B)
|
||||
benchTime benchTimeFlag
|
||||
timerOn bool
|
||||
result BenchmarkResult
|
||||
}
|
||||
|
||||
// InternalBenchmark is an internal type but exported because it is cross-package;
|
||||
// it is part of the implementation of the "go test" command.
|
||||
type InternalBenchmark struct {
|
||||
Name string
|
||||
F func(b *B)
|
||||
}
|
||||
|
||||
// BenchmarkResult contains the results of a benchmark run.
|
||||
type BenchmarkResult struct {
|
||||
N int // The number of iterations.
|
||||
T time.Duration // The total time taken.
|
||||
}
|
||||
|
||||
// NsPerOp returns the "ns/op" metric.
|
||||
func (r BenchmarkResult) NsPerOp() int64 {
|
||||
if r.N <= 0 {
|
||||
return 0
|
||||
}
|
||||
return r.T.Nanoseconds() / int64(r.N)
|
||||
}
|
||||
|
||||
// AllocsPerOp returns the "allocs/op" metric,
|
||||
// which is calculated as r.MemAllocs / r.N.
|
||||
func (r BenchmarkResult) AllocsPerOp() int64 {
|
||||
return 0 // Dummy version to allow running e.g. golang.org/test/fibo.go
|
||||
}
|
||||
|
||||
// AllocedBytesPerOp returns the "B/op" metric,
|
||||
// which is calculated as r.MemBytes / r.N.
|
||||
func (r BenchmarkResult) AllocedBytesPerOp() int64 {
|
||||
return 0 // Dummy version to allow running e.g. golang.org/test/fibo.go
|
||||
}
|
||||
|
||||
func (b *B) SetBytes(n int64) {
|
||||
panic("testing: unimplemented: B.SetBytes")
|
||||
}
|
||||
|
||||
func (b *B) ResetTimer() {
|
||||
panic("testing: unimplemented: B.ResetTimer")
|
||||
// StartTimer starts timing a test. This function is called automatically
|
||||
// before a benchmark starts, but it can also be used to resume timing after
|
||||
// a call to StopTimer.
|
||||
func (b *B) StartTimer() {
|
||||
if !b.timerOn {
|
||||
b.start = time.Now()
|
||||
b.timerOn = true
|
||||
}
|
||||
}
|
||||
|
||||
func (b *B) Run(name string, f func(b *B)) bool {
|
||||
panic("testing: unimplemented: B.Run")
|
||||
// StopTimer stops timing a test. This can be used to pause the timer
|
||||
// while performing complex initialization that you don't
|
||||
// want to measure.
|
||||
func (b *B) StopTimer() {
|
||||
if b.timerOn {
|
||||
b.duration += time.Since(b.start)
|
||||
b.timerOn = false
|
||||
}
|
||||
}
|
||||
|
||||
// ResetTimer zeroes the elapsed benchmark time.
|
||||
// It does not affect whether the timer is running.
|
||||
func (b *B) ResetTimer() {
|
||||
if b.timerOn {
|
||||
b.start = time.Now()
|
||||
}
|
||||
b.duration = 0
|
||||
}
|
||||
|
||||
// runN runs a single benchmark for the specified number of iterations.
|
||||
func (b *B) runN(n int) {
|
||||
b.N = n
|
||||
b.ResetTimer()
|
||||
b.StartTimer()
|
||||
b.benchFunc(b)
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func min(x, y int64) int64 {
|
||||
if x > y {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func max(x, y int64) int64 {
|
||||
if x < y {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// run1 runs the first iteration of benchFunc. It reports whether more
|
||||
// iterations of this benchmarks should be run.
|
||||
func (b *B) run1() bool {
|
||||
b.runN(1)
|
||||
return !b.hasSub
|
||||
}
|
||||
|
||||
// run executes the benchmark.
|
||||
func (b *B) run() {
|
||||
b.launch()
|
||||
}
|
||||
|
||||
// launch launches the benchmark function. It gradually increases the number
|
||||
// of benchmark iterations until the benchmark runs for the requested benchtime.
|
||||
// run1 must have been called on b.
|
||||
func (b *B) launch() {
|
||||
d := b.benchTime.d
|
||||
for n := int64(1); !b.failed && b.duration < d && n < 1e9; {
|
||||
last := n
|
||||
// Predict required iterations.
|
||||
goalns := d.Nanoseconds()
|
||||
prevIters := int64(b.N)
|
||||
prevns := b.duration.Nanoseconds()
|
||||
if prevns <= 0 {
|
||||
// Round up, to avoid div by zero.
|
||||
prevns = 1
|
||||
}
|
||||
// Order of operations matters.
|
||||
// For very fast benchmarks, prevIters ~= prevns.
|
||||
// If you divide first, you get 0 or 1,
|
||||
// which can hide an order of magnitude in execution time.
|
||||
// So multiply first, then divide.
|
||||
n = goalns * prevIters / prevns
|
||||
// Run more iterations than we think we'll need (1.2x).
|
||||
n += n / 5
|
||||
// Don't grow too fast in case we had timing errors previously.
|
||||
n = min(n, 100*last)
|
||||
// Be sure to run at least one more than last time.
|
||||
n = max(n, last+1)
|
||||
// Don't run more than 1e9 times. (This also keeps n in int range on 32 bit platforms.)
|
||||
n = min(n, 1e9)
|
||||
b.runN(int(n))
|
||||
}
|
||||
b.result = BenchmarkResult{b.N, b.duration}
|
||||
}
|
||||
|
||||
// Run benchmarks f as a subbenchmark with the given name. It reports
|
||||
// true if the subbenchmark succeeded.
|
||||
//
|
||||
// A subbenchmark is like any other benchmark. A benchmark that calls Run at
|
||||
// least once will not be measured itself and will be called once with N=1.
|
||||
func (b *B) Run(name string, f func(b *B)) bool {
|
||||
b.hasSub = true
|
||||
sub := &B{
|
||||
common: common{name: name},
|
||||
benchFunc: f,
|
||||
benchTime: b.benchTime,
|
||||
}
|
||||
if sub.run1() {
|
||||
sub.run()
|
||||
}
|
||||
b.add(sub.result)
|
||||
return !sub.failed
|
||||
}
|
||||
|
||||
// Benchmark benchmarks a single function. It is useful for creating
|
||||
// custom benchmarks that do not use the "go test" command.
|
||||
//
|
||||
// If f calls Run, the result will be an estimate of running all its
|
||||
// subbenchmarks that don't call Run in sequence in a single benchmark.
|
||||
func Benchmark(f func(b *B)) BenchmarkResult {
|
||||
b := &B{
|
||||
benchFunc: f,
|
||||
benchTime: benchTime,
|
||||
}
|
||||
if b.run1() {
|
||||
b.run()
|
||||
}
|
||||
return b.result
|
||||
}
|
||||
|
||||
// add simulates running benchmarks in sequence in a single iteration. It is
|
||||
// used to give some meaningful results in case func Benchmark is used in
|
||||
// combination with Run.
|
||||
func (b *B) add(other BenchmarkResult) {
|
||||
r := &b.result
|
||||
// The aggregated BenchmarkResults resemble running all subbenchmarks as
|
||||
// in sequence in a single benchmark.
|
||||
r.N = 1
|
||||
r.T += time.Duration(other.NsPerOp())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package testing_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var buf = make([]byte, 13579)
|
||||
|
||||
func NonASCII(b []byte, i int, offset int) int {
|
||||
for i = offset; i < len(b)+offset; i++ {
|
||||
if b[i%len(b)] >= 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func BenchmarkFastNonASCII(b *testing.B) {
|
||||
var val int
|
||||
for i := 0; i < b.N; i++ {
|
||||
val += NonASCII(buf, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSlowNonASCII(b *testing.B) {
|
||||
var val int
|
||||
for i := 0; i < b.N; i++ {
|
||||
val += NonASCII(buf, 0, 0)
|
||||
val += NonASCII(buf, 0, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// TestBenchmark simply uses Benchmark twice and makes sure it does not crash.
|
||||
func TestBenchmark(t *testing.T) {
|
||||
// FIXME: reduce runtime from the current 3 seconds.
|
||||
rslow := testing.Benchmark(BenchmarkSlowNonASCII)
|
||||
rfast := testing.Benchmark(BenchmarkFastNonASCII)
|
||||
tslow := rslow.NsPerOp()
|
||||
tfast := rfast.NsPerOp()
|
||||
|
||||
// Be exceedingly forgiving; do not fail even if system gets busy.
|
||||
speedup := float64(tslow) / float64(tfast)
|
||||
if speedup < 0.3 {
|
||||
t.Errorf("Expected speedup >= 0.3, got %f", speedup)
|
||||
}
|
||||
}
|
||||
+62
-16
@@ -10,15 +10,34 @@ package testing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Testing flags.
|
||||
var (
|
||||
flagVerbose bool
|
||||
)
|
||||
|
||||
var initRan bool
|
||||
|
||||
// Init registers testing flags. It has no effect if it has already run.
|
||||
func Init() {
|
||||
if initRan {
|
||||
return
|
||||
}
|
||||
initRan = true
|
||||
|
||||
flag.BoolVar(&flagVerbose, "test.v", false, "verbose: print additional output")
|
||||
}
|
||||
|
||||
// common holds the elements common between T and B and
|
||||
// captures common methods such as Errorf.
|
||||
type common struct {
|
||||
output io.Writer
|
||||
output bytes.Buffer
|
||||
indent string
|
||||
|
||||
failed bool // Test or benchmark has failed.
|
||||
skipped bool // Test of benchmark has been skipped.
|
||||
@@ -53,7 +72,6 @@ var _ TB = (*B)(nil)
|
||||
//
|
||||
type T struct {
|
||||
common
|
||||
indent string
|
||||
}
|
||||
|
||||
// Name returns the name of the running test or benchmark.
|
||||
@@ -85,8 +103,22 @@ func (c *common) FailNow() {
|
||||
// log generates the output.
|
||||
func (c *common) log(s string) {
|
||||
// This doesn't print the same as in upstream go, but works for now.
|
||||
fmt.Fprintf(c.output, "\t")
|
||||
fmt.Fprintln(c.output, s)
|
||||
if len(s) != 0 && s[len(s)-1] == '\n' {
|
||||
s = s[:len(s)-1]
|
||||
}
|
||||
lines := strings.Split(s, "\n")
|
||||
// First line.
|
||||
c.output.WriteString(c.indent)
|
||||
c.output.WriteString(" ") // 4 spaces
|
||||
c.output.WriteString(lines[0])
|
||||
c.output.WriteByte('\n')
|
||||
// More lines.
|
||||
for _, line := range lines[1:] {
|
||||
c.output.WriteString(c.indent)
|
||||
c.output.WriteString(" ") // 8 spaces
|
||||
c.output.WriteString(line)
|
||||
c.output.WriteByte('\n')
|
||||
}
|
||||
}
|
||||
|
||||
// Log formats its arguments using default formatting, analogous to Println,
|
||||
@@ -165,25 +197,30 @@ func (c *common) Helper() {
|
||||
func (t *T) Run(name string, f func(t *T)) bool {
|
||||
// Create a subtest.
|
||||
sub := T{
|
||||
indent: t.indent + " ",
|
||||
common: common{
|
||||
name: t.name + "/" + name,
|
||||
output: &bytes.Buffer{},
|
||||
indent: t.indent + " ",
|
||||
},
|
||||
}
|
||||
|
||||
// Run the test.
|
||||
fmt.Printf("=== RUN %s\n", sub.name)
|
||||
if flagVerbose {
|
||||
fmt.Fprintf(&t.output, "=== RUN %s\n", sub.name)
|
||||
|
||||
}
|
||||
f(&sub)
|
||||
|
||||
// Process the result (pass or fail).
|
||||
if sub.failed {
|
||||
t.failed = true
|
||||
fmt.Printf(sub.indent+"--- FAIL: %s\n", sub.name)
|
||||
fmt.Fprintf(&t.output, sub.indent+"--- FAIL: %s\n", sub.name)
|
||||
t.output.Write(sub.output.Bytes())
|
||||
} else {
|
||||
fmt.Printf(sub.indent+"--- PASS: %s\n", sub.name)
|
||||
if flagVerbose {
|
||||
fmt.Fprintf(&t.output, sub.indent+"--- PASS: %s\n", sub.name)
|
||||
t.output.Write(sub.output.Bytes())
|
||||
}
|
||||
}
|
||||
fmt.Print(sub.output)
|
||||
return !sub.failed
|
||||
}
|
||||
|
||||
@@ -205,24 +242,32 @@ func (m *M) Run() int {
|
||||
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
|
||||
}
|
||||
|
||||
if !flag.Parsed() {
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
failures := 0
|
||||
for _, test := range m.Tests {
|
||||
t := &T{
|
||||
common: common{
|
||||
name: test.Name,
|
||||
output: &bytes.Buffer{},
|
||||
name: test.Name,
|
||||
},
|
||||
}
|
||||
|
||||
fmt.Printf("=== RUN %s\n", test.Name)
|
||||
if flagVerbose {
|
||||
fmt.Printf("=== RUN %s\n", test.Name)
|
||||
}
|
||||
test.F(t)
|
||||
|
||||
if t.failed {
|
||||
fmt.Printf("--- FAIL: %s\n", test.Name)
|
||||
os.Stdout.Write(t.output.Bytes())
|
||||
} else {
|
||||
fmt.Printf("--- PASS: %s\n", test.Name)
|
||||
if flagVerbose {
|
||||
fmt.Printf("--- PASS: %s\n", test.Name)
|
||||
os.Stdout.Write(t.output.Bytes())
|
||||
}
|
||||
}
|
||||
fmt.Print(t.output)
|
||||
|
||||
if t.failed {
|
||||
failures++
|
||||
@@ -242,6 +287,7 @@ func TestMain(m *M) {
|
||||
}
|
||||
|
||||
func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
|
||||
Init()
|
||||
return &M{
|
||||
Tests: tests,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"inherits": ["atsamd21g18a"],
|
||||
"build-tags": ["arduino_mkrwifi1010"],
|
||||
"serial": "usb",
|
||||
"serial-port": ["acm:2341:8054", "acm:2341:0054"],
|
||||
"flash-command": "bossac -i -e -w -v -R -U --port={port} --offset=0x2000 {bin}",
|
||||
"flash-1200-bps-reset": "true"
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
MEMORY
|
||||
{
|
||||
FLASH_TEXT (rw) : ORIGIN = 0x00000000+0x26000, LENGTH = 0xED000-0x26000 /* SoftDevice S140. See https://learn.adafruit.com/introducing-the-adafruit-nrf52840-feather/hathach-memory-map. Application starts at 0x26000; user data starts at 0xED000 */
|
||||
RAM (xrw) : ORIGIN = 0x20004180, LENGTH = 37K
|
||||
RAM (xrw) : ORIGIN = 0x20004180, LENGTH = 0x20040000-0x20004180
|
||||
}
|
||||
|
||||
_stack_size = 2K;
|
||||
|
||||
+42
-10
@@ -5,16 +5,48 @@
|
||||
|
||||
_start:
|
||||
b start_vector
|
||||
.fill 156,1,0 // Nintendo Logo Character Data (8000004h)
|
||||
.fill 16,1,0 // Game Title
|
||||
.byte 0x30,0x31 // Maker Code (80000B0h)
|
||||
.byte 0x96 // Fixed Value (80000B2h)
|
||||
.byte 0x00 // Main Unit Code (80000B3h)
|
||||
.byte 0x00 // Device Type (80000B4h)
|
||||
.fill 7,1,0 // unused
|
||||
.byte 0x00 // Software Version No (80000BCh)
|
||||
.byte 0xf0 // Complement Check (80000BDh)
|
||||
.byte 0x00,0x00 // Checksum (80000BEh)
|
||||
|
||||
// ROM header
|
||||
.byte 0x24,0xff,0xae,0x51,0x69,0x9a,0xa2,0x21,0x3d,0x84,0x82,0x0a,0x84,0xe4,0x09,0xad
|
||||
.byte 0x11,0x24,0x8b,0x98,0xc0,0x81,0x7f,0x21,0xa3,0x52,0xbe,0x19,0x93,0x09,0xce,0x20
|
||||
.byte 0x10,0x46,0x4a,0x4a,0xf8,0x27,0x31,0xec,0x58,0xc7,0xe8,0x33,0x82,0xe3,0xce,0xbf
|
||||
.byte 0x85,0xf4,0xdf,0x94,0xce,0x4b,0x09,0xc1,0x94,0x56,0x8a,0xc0,0x13,0x72,0xa7,0xfc
|
||||
.byte 0x9f,0x84,0x4d,0x73,0xa3,0xca,0x9a,0x61,0x58,0x97,0xa3,0x27,0xfc,0x03,0x98,0x76
|
||||
.byte 0x23,0x1d,0xc7,0x61,0x03,0x04,0xae,0x56,0xbf,0x38,0x84,0x00,0x40,0xa7,0x0e,0xfd
|
||||
.byte 0xff,0x52,0xfe,0x03,0x6f,0x95,0x30,0xf1,0x97,0xfb,0xc0,0x85,0x60,0xd6,0x80,0x25
|
||||
.byte 0xa9,0x63,0xbe,0x03,0x01,0x4e,0x38,0xe2,0xf9,0xa2,0x34,0xff,0xbb,0x3e,0x03,0x44
|
||||
.byte 0x78,0x00,0x90,0xcb,0x88,0x11,0x3a,0x94,0x65,0xc0,0x7c,0x63,0x87,0xf0,0x3c,0xaf
|
||||
.byte 0xd6,0x25,0xe4,0x8b,0x38,0x0a,0xac,0x72,0x21,0xd4,0xf8,0x07
|
||||
|
||||
// Game title
|
||||
.byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
|
||||
// Game code
|
||||
.byte 0x00,0x00,0x00,0x00
|
||||
|
||||
// Maker code
|
||||
.byte 0x00,0x00
|
||||
|
||||
// Fixed value
|
||||
.byte 0x96
|
||||
|
||||
// Main unit code
|
||||
.byte 0x00
|
||||
|
||||
// Device type (0x00 retail, 0x80 debug)
|
||||
.byte 0x00
|
||||
|
||||
// Reserved
|
||||
.byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
|
||||
// Software version
|
||||
.byte 0x00
|
||||
|
||||
// Complement check
|
||||
.byte 0x51
|
||||
|
||||
// Reserved area
|
||||
.space 98
|
||||
|
||||
start_vector:
|
||||
// Configure stacks
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"inherits": ["nrf52840"],
|
||||
"build-tags": ["mdbt50qrx", "nrf52840_reset_uf2", "softdevice", "s140v6"],
|
||||
"serial": "usb",
|
||||
"flash-1200-bps-reset": "true",
|
||||
"flash-method": "msd",
|
||||
"serial-port": ["acm:239a:801b", "acm:239a:010b", "acm:239a:810c"],
|
||||
"msd-volume-name": "MDBT50QBOOT",
|
||||
"msd-firmware-name": "firmware.uf2",
|
||||
"uf2-family-id": "0xADA52840",
|
||||
"linkerscript": "targets/circuitplay-bluefruit.ld"
|
||||
}
|
||||
+3
-1
@@ -9,5 +9,7 @@
|
||||
"rp2040-boot-patch": true,
|
||||
"extra-files": [
|
||||
"src/device/rp/rp2040.s"
|
||||
]
|
||||
],
|
||||
"openocd-transport": "swd",
|
||||
"openocd-target": "rp2040"
|
||||
}
|
||||
|
||||
@@ -285,6 +285,10 @@
|
||||
throw 'trying to exit with code ' + code;
|
||||
}
|
||||
},
|
||||
random_get: (bufPtr, bufLen) => {
|
||||
crypto.getRandomValues(loadSlice(bufPtr, bufLen));
|
||||
return 0;
|
||||
},
|
||||
},
|
||||
env: {
|
||||
// func ticks() float64
|
||||
|
||||
Vendored
-23
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -21,26 +20,4 @@ func main() {
|
||||
for _, arg := range os.Args[1:] {
|
||||
println("arg:", arg)
|
||||
}
|
||||
|
||||
// Check for crypto/rand support.
|
||||
checkRand()
|
||||
}
|
||||
|
||||
func checkRand() {
|
||||
buf := make([]byte, 500)
|
||||
n, err := rand.Read(buf)
|
||||
if n != len(buf) || err != nil {
|
||||
println("could not read random numbers:", err)
|
||||
}
|
||||
|
||||
// Very simple test that random numbers are at least somewhat random.
|
||||
sum := 0
|
||||
for _, b := range buf {
|
||||
sum += int(b)
|
||||
}
|
||||
if sum < 95*len(buf) || sum > 159*len(buf) {
|
||||
println("random numbers don't seem that random, the average byte is", sum/len(buf))
|
||||
} else {
|
||||
println("random number check was successful")
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
-1
@@ -3,4 +3,3 @@ ENV2: VALUE2
|
||||
|
||||
arg: first
|
||||
arg: second
|
||||
random number check was successful
|
||||
|
||||
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
// Test new language features introduced in Go 1.17:
|
||||
// https://tip.golang.org/doc/go1.17#language
|
||||
// Once this becomes the minimum Go version of TinyGo, these tests should be
|
||||
// merged with the regular slice tests.
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func main() {
|
||||
// Test conversion from array to slice.
|
||||
slice1 := []int{1, 2, 3, 4}
|
||||
arr1 := (*[4]int)(slice1)
|
||||
arr1[1] = -2
|
||||
arr1[2] = 20
|
||||
println("slice to array pointer:", arr1[0], arr1[1], arr1[2], arr1[3])
|
||||
|
||||
// Test unsafe.Add.
|
||||
arr2 := [...]int{1, 2, 3, 4}
|
||||
*(*int)(unsafe.Add(unsafe.Pointer(&arr2[0]), unsafe.Sizeof(int(1))*1)) = 5
|
||||
*addInt(&arr2[0], 2) = 8
|
||||
println("unsafe.Add array:", arr2[0], arr2[1], arr2[2], arr2[3])
|
||||
|
||||
// Test unsafe.Slice.
|
||||
arr3 := [...]int{1, 2, 3, 4}
|
||||
slice3 := unsafe.Slice(&arr3[1], 3)
|
||||
slice3[0] = 9
|
||||
slice3[1] = 15
|
||||
println("unsafe.Slice array:", len(slice3), cap(slice3), slice3[0], slice3[1], slice3[2])
|
||||
}
|
||||
|
||||
func addInt(ptr *int, index uintptr) *int {
|
||||
return (*int)(unsafe.Add(unsafe.Pointer(ptr), unsafe.Sizeof(int(1))*index))
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
slice to array pointer: 1 -2 20 4
|
||||
unsafe.Add array: 1 5 8 4
|
||||
unsafe.Slice array: 3 3 9 15 4
|
||||
Vendored
+12
@@ -76,6 +76,8 @@ func main() {
|
||||
testGoOnBuiltins()
|
||||
|
||||
testCond()
|
||||
|
||||
testIssue1790()
|
||||
}
|
||||
|
||||
func acquire(m *sync.Mutex) {
|
||||
@@ -198,3 +200,13 @@ func testCond() {
|
||||
panic("missing queued notification")
|
||||
}
|
||||
}
|
||||
|
||||
var once sync.Once
|
||||
|
||||
// This tests a fix for issue 1790:
|
||||
// https://github.com/tinygo-org/tinygo/issues/1790
|
||||
func testIssue1790() *int {
|
||||
once.Do(func() {})
|
||||
i := 0
|
||||
return &i
|
||||
}
|
||||
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import "crypto/rand"
|
||||
|
||||
// TODO: make this a test in the crypto/rand package.
|
||||
|
||||
func main() {
|
||||
buf := make([]byte, 500)
|
||||
n, err := rand.Read(buf)
|
||||
if n != len(buf) || err != nil {
|
||||
println("could not read random numbers:", err)
|
||||
}
|
||||
|
||||
// Very simple test that random numbers are at least somewhat random.
|
||||
sum := 0
|
||||
for _, b := range buf {
|
||||
sum += int(b)
|
||||
}
|
||||
if sum < 95*len(buf) || sum > 159*len(buf) {
|
||||
println("random numbers don't seem that random, the average byte is", sum/len(buf))
|
||||
} else {
|
||||
println("random number check was successful")
|
||||
}
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
random number check was successful
|
||||
Vendored
+1
@@ -398,6 +398,7 @@ func showValue(rv reflect.Value, indent string) {
|
||||
println(indent+" field:", i, field.Name)
|
||||
println(indent+" tag:", field.Tag)
|
||||
println(indent+" embedded:", field.Anonymous)
|
||||
println(indent+" exported:", field.IsExported())
|
||||
showValue(rv.Field(i), indent+" ")
|
||||
}
|
||||
default:
|
||||
|
||||
Vendored
+13
@@ -235,6 +235,7 @@ reflect type: struct
|
||||
field: 0 error
|
||||
tag:
|
||||
embedded: true
|
||||
exported: false
|
||||
reflect type: interface
|
||||
interface
|
||||
nil: true
|
||||
@@ -243,16 +244,19 @@ reflect type: struct
|
||||
field: 0 a
|
||||
tag:
|
||||
embedded: false
|
||||
exported: false
|
||||
reflect type: uint8
|
||||
uint: 42
|
||||
field: 1 b
|
||||
tag:
|
||||
embedded: false
|
||||
exported: false
|
||||
reflect type: int16
|
||||
int: 321
|
||||
field: 2 c
|
||||
tag:
|
||||
embedded: false
|
||||
exported: false
|
||||
reflect type: int8
|
||||
int: 123
|
||||
reflect type: struct comparable=false
|
||||
@@ -260,31 +264,37 @@ reflect type: struct comparable=false
|
||||
field: 0 n
|
||||
tag: foo:"bar"
|
||||
embedded: false
|
||||
exported: false
|
||||
reflect type: int
|
||||
int: 5
|
||||
field: 1 some
|
||||
tag:
|
||||
embedded: false
|
||||
exported: false
|
||||
reflect type: struct
|
||||
struct: 2
|
||||
field: 0 X
|
||||
tag:
|
||||
embedded: false
|
||||
exported: true
|
||||
reflect type: int16
|
||||
int: -5
|
||||
field: 1 Y
|
||||
tag:
|
||||
embedded: false
|
||||
exported: true
|
||||
reflect type: int16
|
||||
int: 3
|
||||
field: 2 zero
|
||||
tag:
|
||||
embedded: false
|
||||
exported: false
|
||||
reflect type: struct
|
||||
struct: 0
|
||||
field: 3 buf
|
||||
tag:
|
||||
embedded: false
|
||||
exported: false
|
||||
reflect type: slice comparable=false
|
||||
slice: uint8 2 2
|
||||
pointer: true
|
||||
@@ -298,6 +308,7 @@ reflect type: struct comparable=false
|
||||
field: 4 Buf
|
||||
tag:
|
||||
embedded: false
|
||||
exported: true
|
||||
reflect type: slice comparable=false
|
||||
slice: uint8 1 1
|
||||
pointer: true
|
||||
@@ -313,12 +324,14 @@ reflect type: ptr
|
||||
field: 0 next
|
||||
tag: description:"chain"
|
||||
embedded: false
|
||||
exported: false
|
||||
reflect type: ptr addrable=true
|
||||
pointer: false struct
|
||||
nil: true
|
||||
field: 1 foo
|
||||
tag:
|
||||
embedded: false
|
||||
exported: false
|
||||
reflect type: int addrable=true
|
||||
int: 42
|
||||
reflect type: ptr
|
||||
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
// TODO: also test the verbose version.
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFoo(t *testing.T) {
|
||||
t.Log("log Foo.a")
|
||||
t.Log("log Foo.b")
|
||||
}
|
||||
|
||||
func TestBar(t *testing.T) {
|
||||
t.Log("log Bar")
|
||||
t.Log("log g\nh\ni\n")
|
||||
t.Run("Bar1", func(t *testing.T) {})
|
||||
t.Run("Bar2", func(t *testing.T) {
|
||||
t.Log("log Bar2\na\nb\nc")
|
||||
t.Error("failed")
|
||||
t.Log("after failed")
|
||||
})
|
||||
t.Run("Bar3", func(t *testing.T) {})
|
||||
t.Log("log Bar end")
|
||||
}
|
||||
|
||||
var tests = []testing.InternalTest{
|
||||
{"TestFoo", TestFoo},
|
||||
{"TestBar", TestBar},
|
||||
}
|
||||
|
||||
var benchmarks = []testing.InternalBenchmark{}
|
||||
|
||||
var examples = []testing.InternalExample{}
|
||||
|
||||
func main() {
|
||||
m := testing.MainStart(nil, tests, benchmarks, examples)
|
||||
exitcode := m.Run()
|
||||
if exitcode != 0 {
|
||||
println("exitcode:", exitcode)
|
||||
}
|
||||
}
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
--- FAIL: TestBar
|
||||
log Bar
|
||||
log g
|
||||
h
|
||||
i
|
||||
|
||||
--- FAIL: TestBar/Bar2
|
||||
log Bar2
|
||||
a
|
||||
b
|
||||
c
|
||||
failed
|
||||
after failed
|
||||
log Bar end
|
||||
FAIL
|
||||
exitcode: 1
|
||||
@@ -11,8 +11,7 @@ func TestChan(t *testing.T) {
|
||||
|
||||
t.Parallel()
|
||||
|
||||
wasmTmpDir, server, cleanup := startServer(t)
|
||||
defer cleanup()
|
||||
wasmTmpDir, server := startServer(t)
|
||||
|
||||
err := run("tinygo build -o " + wasmTmpDir + "/chan.wasm -target wasm testdata/chan.go")
|
||||
if err != nil {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// +build go1.14
|
||||
|
||||
package wasm
|
||||
|
||||
import (
|
||||
@@ -13,8 +11,7 @@ func TestEvent(t *testing.T) {
|
||||
|
||||
t.Parallel()
|
||||
|
||||
wasmTmpDir, server, cleanup := startServer(t)
|
||||
defer cleanup()
|
||||
wasmTmpDir, server := startServer(t)
|
||||
|
||||
err := run("tinygo build -o " + wasmTmpDir + "/event.wasm -target wasm testdata/event.go")
|
||||
if err != nil {
|
||||
|
||||
+1
-11
@@ -1,14 +1,5 @@
|
||||
// +build go1.14
|
||||
|
||||
package wasm
|
||||
|
||||
// NOTE: this should work in go1.13 but panics with:
|
||||
// panic: syscall/js: call of Value.Get on string
|
||||
// which is coming from here: https://github.com/golang/go/blob/release-branch.go1.13/src/syscall/js/js.go#L252
|
||||
// But I'm not sure how import "fmt" results in this.
|
||||
// To reproduce, install Go 1.13.x and change the build tag above
|
||||
// to go1.13 and run this test.
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
@@ -20,8 +11,7 @@ func TestFmt(t *testing.T) {
|
||||
|
||||
t.Parallel()
|
||||
|
||||
wasmTmpDir, server, cleanup := startServer(t)
|
||||
defer cleanup()
|
||||
wasmTmpDir, server := startServer(t)
|
||||
|
||||
err := run("tinygo build -o " + wasmTmpDir + "/fmt.wasm -target wasm testdata/fmt.go")
|
||||
if err != nil {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// +build go1.14
|
||||
|
||||
package wasm
|
||||
|
||||
import (
|
||||
@@ -13,8 +11,7 @@ func TestFmtprint(t *testing.T) {
|
||||
|
||||
t.Parallel()
|
||||
|
||||
wasmTmpDir, server, cleanup := startServer(t)
|
||||
defer cleanup()
|
||||
wasmTmpDir, server := startServer(t)
|
||||
|
||||
err := run("tinygo build -o " + wasmTmpDir + "/fmtprint.wasm -target wasm testdata/fmtprint.go")
|
||||
if err != nil {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// +build go1.14
|
||||
|
||||
package wasm
|
||||
|
||||
import (
|
||||
@@ -13,8 +11,7 @@ func TestLog(t *testing.T) {
|
||||
|
||||
t.Parallel()
|
||||
|
||||
wasmTmpDir, server, cleanup := startServer(t)
|
||||
defer cleanup()
|
||||
wasmTmpDir, server := startServer(t)
|
||||
|
||||
err := run("tinygo build -o " + wasmTmpDir + "/log.wasm -target wasm testdata/log.go")
|
||||
if err != nil {
|
||||
|
||||
@@ -4,11 +4,9 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -47,12 +45,8 @@ func chromectx(timeout time.Duration) (context.Context, context.CancelFunc) {
|
||||
return ctx, cancel
|
||||
}
|
||||
|
||||
func startServer(t *testing.T) (string, *httptest.Server, func()) {
|
||||
// In Go 1.15, all this can be replaced by t.TempDir()
|
||||
tmpDir, err := ioutil.TempDir("", "wasm_test")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create temp dir: %v", err)
|
||||
}
|
||||
func startServer(t *testing.T) (string, *httptest.Server) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
fsh := http.FileServer(http.Dir(tmpDir))
|
||||
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -124,18 +118,9 @@ if (wasmSupported) {
|
||||
|
||||
server := httptest.NewServer(h)
|
||||
t.Logf("Started server at %q for dir: %s", server.URL, tmpDir)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
// In Go 1.14+, this can be replaced by t.Cleanup()
|
||||
cleanup := func() {
|
||||
err := os.RemoveAll(tmpDir)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
server.Close()
|
||||
}
|
||||
|
||||
return tmpDir, server, cleanup
|
||||
return tmpDir, server
|
||||
}
|
||||
|
||||
// waitLog blocks until the log output equals the text provided (ignoring whitespace before and after)
|
||||
|
||||
+34
-2
@@ -246,6 +246,7 @@ func MakeGCStackSlots(mod llvm.Module) bool {
|
||||
}
|
||||
|
||||
// Do a store to the stack object after each new pointer that is created.
|
||||
pointerStores := make(map[llvm.Value]struct{})
|
||||
for i, ptr := range pointers {
|
||||
// Insert the store after the pointer value is created.
|
||||
insertionPoint := llvm.NextInstruction(ptr)
|
||||
@@ -263,13 +264,44 @@ func MakeGCStackSlots(mod llvm.Module) bool {
|
||||
}, "")
|
||||
|
||||
// Store the pointer into the stack slot.
|
||||
builder.CreateStore(ptr, gep)
|
||||
store := builder.CreateStore(ptr, gep)
|
||||
pointerStores[store] = struct{}{}
|
||||
}
|
||||
|
||||
// Make sure this stack object is popped from the linked list of stack
|
||||
// objects at return.
|
||||
for _, ret := range returns {
|
||||
builder.SetInsertPointBefore(ret)
|
||||
inst := ret
|
||||
// Try to do the popping of the stack object earlier, by inserting
|
||||
// it not right before the return instruction but moving the insert
|
||||
// position up.
|
||||
// This is necessary so that the GC stack slot pass doesn't
|
||||
// interfere with tail calls (in particular, musttail calls).
|
||||
for {
|
||||
prevInst := llvm.PrevInstruction(inst)
|
||||
if prevInst == parent {
|
||||
break
|
||||
}
|
||||
if _, ok := pointerStores[prevInst]; ok {
|
||||
// Pop the stack object after the last store instruction.
|
||||
// This can probably be made more efficient: storing to the
|
||||
// stack chain object and then immediately popping isn't
|
||||
// useful.
|
||||
break
|
||||
}
|
||||
if prevInst.IsNil() {
|
||||
// Start of basic block. Pop the stack object here.
|
||||
break
|
||||
}
|
||||
if !prevInst.IsAPHINode().IsNil() {
|
||||
// Do not insert before a PHI node. PHI nodes must be
|
||||
// grouped at the beginning of a basic block before any
|
||||
// other instruction.
|
||||
break
|
||||
}
|
||||
inst = prevInst
|
||||
}
|
||||
builder.SetInsertPointBefore(inst)
|
||||
builder.CreateStore(parent, stackChainStart)
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+8
@@ -20,6 +20,10 @@ define i8* @needsStackSlots() {
|
||||
; so tracking it is not really necessary.
|
||||
%ptr = call i8* @runtime.alloc(i32 4)
|
||||
call void @runtime.trackPointer(i8* %ptr)
|
||||
; Restoring the stack pointer can happen at this position, before the return.
|
||||
; This avoids issues with tail calls.
|
||||
call void @someArbitraryFunction()
|
||||
%val = load i8, i8* @someGlobal
|
||||
ret i8* %ptr
|
||||
}
|
||||
|
||||
@@ -95,3 +99,7 @@ define void @testGEPBitcast() {
|
||||
call void @runtime.trackPointer(i8* %other)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @someArbitraryFunction() {
|
||||
ret void
|
||||
}
|
||||
|
||||
+7
-1
@@ -26,6 +26,8 @@ define i8* @needsStackSlots() {
|
||||
%4 = getelementptr { %runtime.stackChainObject*, i32, i8* }, { %runtime.stackChainObject*, i32, i8* }* %gc.stackobject, i32 0, i32 2
|
||||
store i8* %ptr, i8** %4
|
||||
store %runtime.stackChainObject* %1, %runtime.stackChainObject** @runtime.stackChainStart
|
||||
call void @someArbitraryFunction()
|
||||
%val = load i8, i8* @someGlobal
|
||||
ret i8* %ptr
|
||||
}
|
||||
|
||||
@@ -73,8 +75,8 @@ define i8* @fibNext(i8* %x, i8* %y) {
|
||||
%out.alloc = call i8* @runtime.alloc(i32 1)
|
||||
%4 = getelementptr { %runtime.stackChainObject*, i32, i8* }, { %runtime.stackChainObject*, i32, i8* }* %gc.stackobject, i32 0, i32 2
|
||||
store i8* %out.alloc, i8** %4
|
||||
store i8 %out.val, i8* %out.alloc
|
||||
store %runtime.stackChainObject* %1, %runtime.stackChainObject** @runtime.stackChainStart
|
||||
store i8 %out.val, i8* %out.alloc
|
||||
ret i8* %out.alloc
|
||||
}
|
||||
|
||||
@@ -135,3 +137,7 @@ define void @testGEPBitcast() {
|
||||
store %runtime.stackChainObject* %1, %runtime.stackChainObject** @runtime.stackChainStart
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @someArbitraryFunction() {
|
||||
ret void
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user