Compare commits

...

27 Commits

Author SHA1 Message Date
Ayke van Laethem b1744db2c8 main: version 0.4.0 2019-03-09 20:41:38 +01:00
Ayke van Laethem bd6a7b69ce compiler: inline slice bounds checking
This improves code size in all tests by about 1% and up to 5% in some
cases, likely because LLVM can better reason about inline bounds checks.
2019-03-08 19:11:22 +01:00
Ayke van Laethem 051ad07755 compiler: refactor slice related asserts
Move these asserts into compiler/asserts.go, to keep them together.

The make([]T) asserts aren't moved yet because that code is (still!)
quite ugly and in need of some clean up.
2019-03-08 19:11:22 +01:00
Ron Evans 09e85b7859 machine/stm32f103xx: correct convertion for fractional timing of RTC as used in ticks() function
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-08 17:52:51 +01:00
Ayke van Laethem 622d0ebde6 compiler: implement nil checks
This commit implements nil checks for all platforms. These nil checks
can be optimized on systems with a MMU, but since a major target is
systems without MMU, keep it this way for now.

It implements three checks:
  * Nil checks before dereferencing a pointer.
  * Nil checks before calculating an address (*ssa.FieldAddr and
    *ssa.IndexAddr)
  * Nil checks before calling a function pointer.

The first check has by far the biggest impact, with around 5% increase
in code size. The other checks only trigger in only some test cases and
have a minimal impact on code size.
This first nil check is also the one that is easiest to avoid on systems
with MMU, if necessary.
2019-03-08 17:36:53 +01:00
Ayke van Laethem b7cdf8cd0c interp: refactor to eliminate lots of code
This may cause a small performance penalty, but the code is easier to
maange as a result.
2019-03-08 17:36:53 +01:00
Ayke van Laethem cfc1a66e8d interp: use correct initialization order on panic() calls
Whenever interp hits an unreachable instruction, it bails out at that
point. However, it used to insert new instructions at the bottom with
the old init calls still at the top. So when a panic() happened in a
non-main package, the last packages to init would actually be called
first.

This commit fixes this by setting the insert point at the top of
runtime.initAll before starting interpretation, so the initialization
order is still correct when a panic() happens during init.
2019-03-07 16:22:06 +01:00
Ayke van Laethem 4ad9bd8643 wasm: ignore arguments and environment variables
The wasm_exec.js file copied from the main Go repository did write those
values to address 4096 in linear memory, which led to memory corruption
in linear memory. Remove these things for now, until they're actually
supported, if support is ever added.
2019-03-07 13:13:11 +01:00
Ron Evans 2a1dd98661 compiler: support output file using UF2 bootloader format
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-06 18:18:23 +01:00
Ayke van Laethem 2c03192691 LICENSE: update author and year 2019-03-06 17:15:31 +01:00
Ron Evans 9d6df2b4c7 machine/samd21: implement ADC
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-06 17:01:16 +01:00
Ayke van Laethem 5939729c45 main: only run WebAssembly tests on Linux
The WebAssembly target is not yet considered stable in LLVM 7, but has
been enabled in the Debian builds so tests can run on Debian. However,
the Homebrew builds don't have it enabled which results in test
failures.

Temporarily run WebAssembly tests only on Linux to fix this. This can be
reverted after a switch to LLVM 8, which has WebAssembly enabled by
default.
2019-03-06 11:28:59 +01:00
Ayke van Laethem c7b91da8c4 compiler: support function pointers outside of addrspace 0
In LLVM 8, the AVR backend has moved all function pointers to address
space 1 by default. Much of the code still assumes function pointers
live in address space 0, leading to assertion failures.

This commit fixes this problem by autodetecting function pointers and
avoiding them in interface pseudo-calls.
2019-03-05 19:54:55 +01:00
Ayke van Laethem c7fdb6741f compiler: rename biggestInt → capacityType 2019-03-05 19:25:42 +01:00
Ayke van Laethem b837c94366 compiler: calculate max number of entries in slice at compile time
This avoids difficult multiply-with-overflow code and avoids a multiply
at runtime.
2019-03-05 19:25:42 +01:00
Ayke van Laethem 26e7e93478 compiler: make sure make([]T, ...) checks for Ts bigger than 1
Without this, the following code would not panic:

    func getInt(i int) { return i }
    make([][1<<18], getInt(1<<18))

Or this code would be allowed to compile for 32-bit systems:

    make([][1<<18], 1<<18)
2019-03-05 19:25:42 +01:00
Ayke van Laethem 8e99c3313b compiler: fix make([]T, ...) with big integers on 32-bit systems or less
Previously, this would have resulted in a LLVM verification error
because runtime.sliceBoundsCheckMake would not accept 64-bit integers on
these platforms.
2019-03-05 19:25:42 +01:00
Ron Evans 28987ae061 docs: update README with recently added Adafruit Circuit Playground Express board
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-05 10:08:39 +01:00
Ayke van Laethem b594f212fb test: add WebAssembly tests 2019-03-04 21:58:40 +01:00
Ayke van Laethem 41e093d7bb wasm: switch emulator to node.js
Unfortunately, the olin/cwa emulator does not handle floats correctly.
Node.js does, and because it is also supported by the Go WebAssembly
implementation it has better support in general.
2019-03-04 21:58:40 +01:00
Ron Evans 665c3bdaa6 machine/samd21: implement SPI interface for currently supported SAMD21 boards
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-04 21:47:09 +01:00
Ayke van Laethem ea3d232c84 circleci: replace Linux tests on Travis CI with CircleCI
CircleCI is faster and has more features than Travis CI. Additionally,
based on the recent news, the future of Travis CI is rather uncertain.

Keep using Travis CI for macOS testing at the moment, as open source
projects will need to get special permission to use CircleCI for macOS
tests.
2019-03-04 21:42:12 +01:00
Ayke van Laethem 4f932b6e66 all: use internal objcopy implementation
This lessens the dependency on binutils (e.g. arm-none-eabi-objcopy).
2019-03-04 21:17:56 +01:00
Ron Evans 3538ba943c machine/samd21: move definitions for I2C interfaces into board files, since pin connections on each SAMD21-based board implementation can differ
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-04 20:54:13 +01:00
Ron Evans 543696eafc machine/samd21: correct get/setPinCfg and get/setPMux functions for PORTB pins
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-04 20:53:07 +01:00
Ron Evans 6e5ae83302 machine/samd21: init all SERCOM clocks to better handle board variants
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-03-04 17:17:03 +01:00
Ayke van Laethem 9b4071237f arm: switch to hardfloat ABI for Linux
This avoids an error on the Raspberry Pi 3.
2019-03-01 20:36:12 +01:00
32 changed files with 1200 additions and 702 deletions
+102
View File
@@ -0,0 +1,102 @@
version: 2.1
commands:
submodules:
steps:
- run:
name: "Pull submodules"
command: git submodule update --init
apt-dependencies:
parameters:
llvm:
type: string
steps:
- run:
name: "Install apt dependencies"
command: |
echo 'deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch<<parameters.llvm>> main' | sudo tee /etc/apt/sources.list.d/llvm.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
sudo apt-get update
sudo apt-get install \
llvm \
python3 \
llvm<<parameters.llvm>>-dev \
clang<<parameters.llvm>> \
libclang<<parameters.llvm>>-dev \
lld<<parameters.llvm>> \
gcc-arm-linux-gnueabihf \
binutils-arm-none-eabi \
libc6-dev-armel-cross \
gcc-aarch64-linux-gnu \
libc6-dev-arm64-cross \
qemu-system-arm \
qemu-user \
gcc-avr \
avr-libc
install-node:
steps:
- run:
name: "Install node.js"
command: |
wget https://nodejs.org/dist/v10.15.1/node-v10.15.1-linux-x64.tar.xz
sudo tar -C /usr/local -xf node-v10.15.1-linux-x64.tar.xz
sudo ln -s /usr/local/node-v10.15.1-linux-x64/bin/node /usr/bin/node
rm node-v10.15.1-linux-x64.tar.xz
dep:
steps:
- run:
name: "Install Go dependencies"
command: |
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure --vendor-only
smoketest:
steps:
- run: tinygo build -size short -o test.elf -target=pca10040 examples/blinky1
- run: tinygo build -size short -o test.elf -target=pca10040 examples/blinky2
- run: tinygo build -size short -o blinky2 examples/blinky2
- run: tinygo build -size short -o test.elf -target=pca10040 examples/test
- run: tinygo build -size short -o test.elf -target=microbit examples/echo
- run: tinygo build -size short -o test.elf -target=nrf52840-mdk examples/blinky1
- run: tinygo build -size short -o test.elf -target=pca10031 examples/blinky1
- run: tinygo build -size short -o test.elf -target=bluepill examples/blinky1
- run: tinygo build -size short -o test.elf -target=arduino examples/blinky1
- run: tinygo build -size short -o test.elf -target=digispark examples/blinky1
- run: tinygo build -size short -o test.elf -target=reelboard examples/blinky1
- run: tinygo build -size short -o test.elf -target=reelboard examples/blinky2
- run: tinygo build -size short -o test.elf -target=pca10056 examples/blinky1
- run: tinygo build -size short -o test.elf -target=pca10056 examples/blinky2
- run: tinygo build -size short -o test.elf -target=itsybitsy-m0 examples/blinky1
- run: tinygo build -size short -o test.elf -target=circuitplay-express examples/blinky1
jobs:
test-llvm7-go111:
docker:
- image: circleci/golang:1.11
working_directory: /go/src/github.com/tinygo-org/tinygo
steps:
- checkout
- submodules
- apt-dependencies:
llvm: "-7"
- install-node
- restore_cache:
keys:
- go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-{{ checksum "Gopkg.lock" }}
- dep
- run: go install .
- run: make test
- run: make gen-device -j4
- smoketest
- save_cache:
key: go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
- ~/.cache/go-build
- ~/.cache/tinygo
workflows:
test-all:
jobs:
- test-llvm7-go111
+2 -23
View File
@@ -2,31 +2,13 @@ language: go
matrix:
include:
- dist: xenial
go: "1.11"
- os: osx
go: "1.11"
env: PATH="/usr/local/opt/llvm/bin:$PATH"
before_install:
- mkdir -p /Users/travis/gopath/bin
addons:
apt:
sources:
- sourceline: 'ppa:ubuntu-toolchain-r'
- sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-7 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
packages:
- llvm-7-dev
- clang-7
- libclang-7-dev
- gcc-arm-linux-gnueabi
- binutils-arm-none-eabi
- libc6-dev-armel-cross
- gcc-aarch64-linux-gnu
- libc6-dev-arm64-cross
- qemu-system-arm
- qemu-user
- gcc-avr
- avr-libc
homebrew:
update: true
taps: ArmMbed/homebrew-formulae
@@ -36,7 +18,6 @@ addons:
- arm-none-eabi-gcc
install:
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then mkdir -p /Users/travis/gopath/bin; fi
- curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
- dep ensure --vendor-only
@@ -52,8 +33,6 @@ script:
- tinygo build -size short -o test.nrf.elf -target=nrf52840-mdk examples/blinky1
- tinygo build -size short -o blinky1.nrf51d.elf -target=pca10031 examples/blinky1
- tinygo build -size short -o blinky1.stm32.elf -target=bluepill examples/blinky1
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then tinygo build -size short -o blinky1.avr.elf -target=arduino examples/blinky1; fi
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then tinygo build -size short -o blinky1.avr.elf -target=digispark examples/blinky1; fi
- tinygo build -size short -o blinky1.reel.elf -target=reelboard examples/blinky1
- tinygo build -size short -o blinky2.reel.elf -target=reelboard examples/blinky2
- tinygo build -size short -o blinky1.pca10056.elf -target=pca10056 examples/blinky1
+19
View File
@@ -1,3 +1,22 @@
0.4.0
---
- **compiler**
- switch to the hardfloat ABI on ARM, which is more widely used
- avoid a dependency on `objcopy` (`arm-none-eabi-objcopy` etc.)
- fix a bug in `make([]T, n)` where `n` is 64-bits on a 32-bit platform
- adapt to a change in the AVR backend in LLVM 8
- directly support the .uf2 firmware format as used on Adafruit boards
- fix a bug when calling `panic()` at init time outside of the main package
- implement nil checks, which results in a ~5% increase in code size
- inline slice bounds checking, which results in a ~1% decrease in code size
- **targets**
- `samd21`: fix a bug in port B pins
- `samd21`: implement SPI peripheral
- `samd21`: implement ADC peripheral
- `stm32`: fix a bug in timekeeping
- `wasm`: fix a bug in `wasm_exec.js` that caused corruption in linear memory
when running on Node.js.
0.3.0
---
- **compiler**
Generated
+9
View File
@@ -1,6 +1,14 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
digest = "1:00b45e06c7843541372fc17d982242bd6adfc2fc382b6f2e9ef9ce53d87a50b9"
name = "github.com/marcinbor85/gohex"
packages = ["."]
pruneopts = "UT"
revision = "7a43cd876e46e0f6ddc553f10f91731a78e6e949"
[[projects]]
branch = "master"
digest = "1:ba70784a3deee74c0ca3c87bcac3c2f93d3b2d27d8f237b768c358b45ba47da8"
@@ -25,6 +33,7 @@
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/marcinbor85/gohex",
"golang.org/x/tools/go/ast/astutil",
"golang.org/x/tools/go/ssa",
"tinygo.org/x/go-llvm",
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2018 Ayke van Laethem. All rights reserved.
Copyright (c) 2018-2019 TinyGo Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
+3 -1
View File
@@ -1,6 +1,7 @@
# TinyGo - Go compiler for small places
[![Build Status](https://travis-ci.com/tinygo-org/tinygo.svg?branch=dev)](https://travis-ci.com/tinygo-org/tinygo)
[![Travis CI](https://travis-ci.com/tinygo-org/tinygo.svg?branch=dev)](https://travis-ci.com/tinygo-org/tinygo)
[![CircleCI](https://circleci.com/gh/tinygo-org/tinygo/tree/dev.svg?style=svg)](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (WASM), and command-line tools.
@@ -45,6 +46,7 @@ You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
The following microcontroller boards are currently supported:
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
* [BBC:Microbit](https://microbit.org/)
+129
View File
@@ -0,0 +1,129 @@
package compiler
// This file implements functions that do certain safety checks that are
// required by the Go programming language.
import (
"go/types"
"tinygo.org/x/go-llvm"
)
// emitLookupBoundsCheck emits a bounds check before doing a lookup into a
// slice. This is required by the Go language spec: an index out of bounds must
// cause a panic.
func (c *Compiler) emitLookupBoundsCheck(frame *Frame, arrayLen, index llvm.Value, indexType types.Type) {
if frame.fn.IsNoBounds() {
// The //go:nobounds pragma was added to the function to avoid bounds
// checking.
return
}
if index.Type().IntTypeWidth() < arrayLen.Type().IntTypeWidth() {
// Sometimes, the index can be e.g. an uint8 or int8, and we have to
// correctly extend that type.
if indexType.(*types.Basic).Info()&types.IsUnsigned == 0 {
index = c.builder.CreateZExt(index, arrayLen.Type(), "")
} else {
index = c.builder.CreateSExt(index, arrayLen.Type(), "")
}
} else if index.Type().IntTypeWidth() > arrayLen.Type().IntTypeWidth() {
// The index is bigger than the array length type, so extend it.
arrayLen = c.builder.CreateZExt(arrayLen, index.Type(), "")
}
faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "lookup.outofbounds")
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "lookup.next")
frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes
// Now do the bounds check: index >= arrayLen
outOfBounds := c.builder.CreateICmp(llvm.IntUGE, index, arrayLen, "")
c.builder.CreateCondBr(outOfBounds, faultBlock, nextBlock)
// Fail: this is a nil pointer, exit with a panic.
c.builder.SetInsertPointAtEnd(faultBlock)
c.createRuntimeCall("lookuppanic", nil, "")
c.builder.CreateUnreachable()
// Ok: this is a valid pointer.
c.builder.SetInsertPointAtEnd(nextBlock)
}
// emitSliceBoundsCheck emits a bounds check before a slicing operation to make
// sure it is within bounds.
func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.Value, lowType, highType *types.Basic) {
if frame.fn.IsNoBounds() {
// The //go:nobounds pragma was added to the function to avoid bounds
// checking.
return
}
// Extend the capacity integer to be at least as wide as low and high.
capacityType := capacity.Type()
if low.Type().IntTypeWidth() > capacityType.IntTypeWidth() {
capacityType = low.Type()
}
if high.Type().IntTypeWidth() > capacityType.IntTypeWidth() {
capacityType = high.Type()
}
if capacityType != capacity.Type() {
capacity = c.builder.CreateZExt(capacity, capacityType, "")
}
// Extend low and high to be the same size as capacity.
if low.Type().IntTypeWidth() < capacityType.IntTypeWidth() {
if lowType.Info()&types.IsUnsigned != 0 {
low = c.builder.CreateZExt(low, capacityType, "")
} else {
low = c.builder.CreateSExt(low, capacityType, "")
}
}
if high.Type().IntTypeWidth() < capacityType.IntTypeWidth() {
if highType.Info()&types.IsUnsigned != 0 {
high = c.builder.CreateZExt(high, capacityType, "")
} else {
high = c.builder.CreateSExt(high, capacityType, "")
}
}
faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "slice.outofbounds")
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "slice.next")
frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes
// Now do the bounds check: low > high || high > capacity
outOfBounds1 := c.builder.CreateICmp(llvm.IntUGT, low, high, "slice.lowhigh")
outOfBounds2 := c.builder.CreateICmp(llvm.IntUGT, high, capacity, "slice.highcap")
outOfBounds := c.builder.CreateOr(outOfBounds1, outOfBounds2, "slice.outofbounds")
c.builder.CreateCondBr(outOfBounds, faultBlock, nextBlock)
// Fail: this is a nil pointer, exit with a panic.
c.builder.SetInsertPointAtEnd(faultBlock)
c.createRuntimeCall("slicepanic", nil, "")
c.builder.CreateUnreachable()
// Ok: this is a valid pointer.
c.builder.SetInsertPointAtEnd(nextBlock)
}
// emitNilCheck checks whether the given pointer is nil, and panics if it is. It
// has no effect in well-behaved programs, but makes sure no uncaught nil
// pointer dereferences exist in valid Go code.
func (c *Compiler) emitNilCheck(frame *Frame, ptr llvm.Value, blockPrefix string) {
// Check whether this is a nil pointer.
faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".nil")
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".next")
frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes
// Compare against nil.
nilptr := llvm.ConstPointerNull(ptr.Type())
isnil := c.builder.CreateICmp(llvm.IntEQ, ptr, nilptr, "")
c.builder.CreateCondBr(isnil, faultBlock, nextBlock)
// Fail: this is a nil pointer, exit with a panic.
c.builder.SetInsertPointAtEnd(faultBlock)
c.createRuntimeCall("nilpanic", nil, "")
c.builder.CreateUnreachable()
// Ok: this is a valid pointer.
c.builder.SetInsertPointAtEnd(nextBlock)
}
+72 -83
View File
@@ -57,6 +57,7 @@ type Compiler struct {
targetData llvm.TargetData
intType llvm.Type
i8ptrType llvm.Type // for convenience
funcPtrAddrSpace int
uintptrType llvm.Type
initFuncs []llvm.Value
interfaceInvokeWrappers []interfaceInvokeWrapper
@@ -125,6 +126,11 @@ func NewCompiler(pkgName string, config Config) (*Compiler, error) {
}
c.i8ptrType = llvm.PointerType(c.ctx.Int8Type(), 0)
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
dummyFunc.EraseFromParentAsFunction()
return c, nil
}
@@ -338,6 +344,14 @@ func (c *Compiler) Compile(mainPath string) error {
c.mod.NamedFunction("runtime.activateTask").SetLinkage(llvm.ExternalLinkage)
c.mod.NamedFunction("runtime.scheduler").SetLinkage(llvm.ExternalLinkage)
// Tell the optimizer that runtime.alloc is an allocator, meaning that it
// returns values that are never null and never alias to an existing value.
for _, name := range []string{"noalias", "nonnull"} {
attrKind := llvm.AttributeKindID(name)
attr := c.ctx.CreateEnumAttribute(attrKind, 0)
c.mod.NamedFunction("runtime.alloc").AddAttributeAtIndex(0, attr)
}
// see: https://reviews.llvm.org/D18355
if c.Debug {
c.mod.AddNamedMetadataOperand("llvm.module.flags",
@@ -462,7 +476,7 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) {
// {context, funcptr}
paramTypes = append(paramTypes, c.i8ptrType) // context
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
ptr := llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), 0)
ptr := llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), c.funcPtrAddrSpace)
ptr = c.ctx.StructType([]llvm.Type{c.i8ptrType, ptr}, false)
return ptr, nil
case *types.Slice:
@@ -1394,76 +1408,11 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
// closure: {context, function pointer}
context := c.builder.CreateExtractValue(value, 0, "")
value = c.builder.CreateExtractValue(value, 1, "")
c.emitNilCheck(frame, value, "fpcall")
return c.parseFunctionCall(frame, instr.Args, value, context, false)
}
}
func (c *Compiler) emitBoundsCheck(frame *Frame, arrayLen, index llvm.Value, indexType types.Type) {
if frame.fn.IsNoBounds() {
// The //go:nobounds pragma was added to the function to avoid bounds
// checking.
return
}
// Sometimes, the index can be e.g. an uint8 or int8, and we have to
// correctly extend that type.
if index.Type().IntTypeWidth() < arrayLen.Type().IntTypeWidth() {
if indexType.(*types.Basic).Info()&types.IsUnsigned == 0 {
index = c.builder.CreateZExt(index, arrayLen.Type(), "")
} else {
index = c.builder.CreateSExt(index, arrayLen.Type(), "")
}
}
// Optimize away trivial cases.
// LLVM would do this anyway with interprocedural optimizations, but it
// helps to see cases where bounds check elimination would really help.
if index.IsConstant() && arrayLen.IsConstant() && !arrayLen.IsUndef() {
index := index.SExtValue()
arrayLen := arrayLen.SExtValue()
if index >= 0 && index < arrayLen {
return
}
}
if index.Type().IntTypeWidth() > c.intType.IntTypeWidth() {
// Index is too big for the regular bounds check. Use the one for int64.
c.createRuntimeCall("lookupBoundsCheckLong", []llvm.Value{arrayLen, index}, "")
} else {
c.createRuntimeCall("lookupBoundsCheck", []llvm.Value{arrayLen, index}, "")
}
}
func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.Value, lowType, highType *types.Basic) {
if frame.fn.IsNoBounds() {
// The //go:nobounds pragma was added to the function to avoid bounds
// checking.
return
}
uintptrWidth := c.uintptrType.IntTypeWidth()
if low.Type().IntTypeWidth() > uintptrWidth || high.Type().IntTypeWidth() > uintptrWidth {
if low.Type().IntTypeWidth() < 64 {
if lowType.Info()&types.IsUnsigned != 0 {
low = c.builder.CreateZExt(low, c.ctx.Int64Type(), "")
} else {
low = c.builder.CreateSExt(low, c.ctx.Int64Type(), "")
}
}
if high.Type().IntTypeWidth() < 64 {
if highType.Info()&types.IsUnsigned != 0 {
high = c.builder.CreateZExt(high, c.ctx.Int64Type(), "")
} else {
high = c.builder.CreateSExt(high, c.ctx.Int64Type(), "")
}
}
// TODO: 32-bit or even 16-bit slice bounds checks for 8-bit platforms
c.createRuntimeCall("sliceBoundsCheck64", []llvm.Value{capacity, low, high}, "")
} else {
c.createRuntimeCall("sliceBoundsCheck", []llvm.Value{capacity, low, high}, "")
}
}
func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
if value, ok := frame.locals[expr]; ok {
// Value is a local variable that has already been computed.
@@ -1481,9 +1430,16 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
}
var buf llvm.Value
if expr.Heap {
size := c.targetData.TypeAllocSize(typ)
// Calculate ^uintptr(0)
maxSize := llvm.ConstNot(llvm.ConstInt(c.uintptrType, 0, false)).ZExtValue()
if size > maxSize {
// Size would be truncated if truncated to uintptr.
return llvm.Value{}, c.makeError(expr.Pos(), fmt.Sprintf("value is too big (%v bytes)", size))
}
// TODO: escape analysis
size := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(typ), false)
buf = c.createRuntimeCall("alloc", []llvm.Value{size}, expr.Comment)
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
buf = c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, expr.Comment)
buf = c.builder.CreateBitCast(buf, llvm.PointerType(typ, 0), "")
} else {
buf = c.builder.CreateAlloca(typ, expr.Comment)
@@ -1565,6 +1521,11 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
llvm.ConstInt(c.ctx.Int32Type(), uint64(expr.Field), false),
}
// Check for nil pointer before calculating the address, from the spec:
// > For an operand x of type T, the address operation &x generates a
// > pointer of type *T to x. [...] If the evaluation of x would cause a
// > run-time panic, then the evaluation of &x does too.
c.emitNilCheck(frame, val, "gep")
return c.builder.CreateGEP(val, indices, ""), nil
case *ssa.Function:
fn := c.ir.GetFunction(expr)
@@ -1596,7 +1557,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
// Check bounds.
arrayLen := expr.X.Type().(*types.Array).Len()
arrayLenLLVM := llvm.ConstInt(c.uintptrType, uint64(arrayLen), false)
c.emitBoundsCheck(frame, arrayLenLLVM, index, expr.Index.Type())
c.emitLookupBoundsCheck(frame, arrayLenLLVM, index, expr.Index.Type())
// Can't load directly from array (as index is non-constant), so have to
// do it using an alloca+gep+load.
@@ -1624,6 +1585,13 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
case *types.Array:
bufptr = val
buflen = llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false)
// Check for nil pointer before calculating the address, from
// the spec:
// > For an operand x of type T, the address operation &x
// > generates a pointer of type *T to x. [...] If the
// > evaluation of x would cause a run-time panic, then the
// > evaluation of &x does too.
c.emitNilCheck(frame, bufptr, "gep")
default:
return llvm.Value{}, c.makeError(expr.Pos(), "todo: indexaddr: "+typ.String())
}
@@ -1635,8 +1603,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
}
// Bounds check.
// LLVM optimizes this away in most cases.
c.emitBoundsCheck(frame, buflen, index, expr.Index.Type())
c.emitLookupBoundsCheck(frame, buflen, index, expr.Index.Type())
switch expr.X.Type().Underlying().(type) {
case *types.Pointer:
@@ -1667,9 +1634,8 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
}
// Bounds check.
// LLVM optimizes this away in most cases.
length := c.builder.CreateExtractValue(value, 1, "len")
c.emitBoundsCheck(frame, length, index, expr.Index.Type())
c.emitLookupBoundsCheck(frame, length, index, expr.Index.Type())
// Lookup byte
buf := c.builder.CreateExtractValue(value, 0, "")
@@ -1727,29 +1693,52 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
return llvm.Value{}, nil
}
elemSize := c.targetData.TypeAllocSize(llvmElemType)
elemSizeValue := llvm.ConstInt(c.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(c.uintptrType, 0, false)), llvm.ConstInt(c.uintptrType, 1, false))
if elemSize > maxSize.ZExtValue() {
// This seems to be checked by the typechecker already, but let's
// check it again just to be sure.
return llvm.Value{}, c.makeError(expr.Pos(), fmt.Sprintf("slice element type is too big (%v bytes)", elemSize))
}
// Bounds checking.
if !frame.fn.IsNoBounds() {
if sliceLen.Type().IntTypeWidth() < c.uintptrType.IntTypeWidth() {
checkFunc := "sliceBoundsCheckMake"
capacityType := c.uintptrType
capacityTypeWidth := capacityType.IntTypeWidth()
if sliceLen.Type().IntTypeWidth() > capacityTypeWidth || sliceCap.Type().IntTypeWidth() > capacityTypeWidth {
// System that is less than 64bit, meaning that the slice make
// params are bigger than uintptr.
checkFunc = "sliceBoundsCheckMake64"
capacityType = c.ctx.Int64Type()
capacityTypeWidth = capacityType.IntTypeWidth()
}
if sliceLen.Type().IntTypeWidth() < capacityTypeWidth {
if expr.Len.Type().(*types.Basic).Info()&types.IsUnsigned != 0 {
sliceLen = c.builder.CreateZExt(sliceLen, c.uintptrType, "")
sliceLen = c.builder.CreateZExt(sliceLen, capacityType, "")
} else {
sliceLen = c.builder.CreateSExt(sliceLen, c.uintptrType, "")
sliceLen = c.builder.CreateSExt(sliceLen, capacityType, "")
}
}
if sliceCap.Type().IntTypeWidth() < c.uintptrType.IntTypeWidth() {
if sliceCap.Type().IntTypeWidth() < capacityTypeWidth {
if expr.Cap.Type().(*types.Basic).Info()&types.IsUnsigned != 0 {
sliceCap = c.builder.CreateZExt(sliceCap, c.uintptrType, "")
sliceCap = c.builder.CreateZExt(sliceCap, capacityType, "")
} else {
sliceCap = c.builder.CreateSExt(sliceCap, c.uintptrType, "")
sliceCap = c.builder.CreateSExt(sliceCap, capacityType, "")
}
}
c.createRuntimeCall("sliceBoundsCheckMake", []llvm.Value{sliceLen, sliceCap}, "")
maxSliceSize := maxSize
if elemSize != 0 { // avoid divide by zero
maxSliceSize = llvm.ConstSDiv(maxSize, llvm.ConstInt(c.uintptrType, elemSize, false))
}
c.createRuntimeCall(checkFunc, []llvm.Value{sliceLen, sliceCap, maxSliceSize}, "")
}
// Allocate the backing array.
// TODO: escape analysis
elemSizeValue := llvm.ConstInt(c.uintptrType, elemSize, false)
sliceCapCast, err := c.parseConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
if err != nil {
return llvm.Value{}, err
@@ -1892,7 +1881,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
low,
}
// This check is optimized away in most cases.
c.emitSliceBoundsCheck(frame, llvmLen, low, high, lowType, highType)
if c.targetData.TypeAllocSize(high.Type()) > c.targetData.TypeAllocSize(c.uintptrType) {
@@ -2659,6 +2647,7 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
fn := c.mod.NamedFunction(name)
return c.builder.CreateBitCast(fn, c.i8ptrType, ""), nil
} else {
c.emitNilCheck(frame, x, "deref")
load := c.builder.CreateLoad(x, "")
if c.ir.IsVolatile(valType) {
// Volatile load, for memory-mapped registers.
+17 -17
View File
@@ -304,7 +304,7 @@ func (p *lowerInterfacesPass) run() {
// interface value should already have returned false.
// Replace the function pointer with undef (which will then be
// called), indicating to the optimizer this code is unreachable.
use.ReplaceAllUsesWith(llvm.Undef(p.i8ptrType))
use.ReplaceAllUsesWith(llvm.Undef(p.uintptrType))
use.EraseFromParentAsInstruction()
} else if len(itf.types) == 1 {
// There is only one implementation of the given type.
@@ -314,12 +314,12 @@ func (p *lowerInterfacesPass) run() {
// There are multiple types implementing this interface, thus there
// are multiple possible functions to call. Delegate calling the
// right function to a special wrapper function.
bitcasts := getUses(use)
if len(bitcasts) != 1 || bitcasts[0].IsABitCastInst().IsNil() {
panic("expected exactly one bitcast use of runtime.interfaceMethod")
inttoptrs := getUses(use)
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
panic("expected exactly one inttoptr use of runtime.interfaceMethod")
}
bitcast := bitcasts[0]
calls := getUses(bitcast)
inttoptr := inttoptrs[0]
calls := getUses(inttoptr)
if len(calls) != 1 || calls[0].IsACallInst().IsNil() {
panic("expected exactly one call use of runtime.interfaceMethod")
}
@@ -340,14 +340,14 @@ func (p *lowerInterfacesPass) run() {
// call, after selecting the right concrete type.
redirector := p.getInterfaceMethodFunc(itf, signature, call.Type(), paramTypes)
// Replace the old lookup/bitcast/call with the new call.
// Replace the old lookup/inttoptr/call with the new call.
p.builder.SetInsertPointBefore(call)
retval := p.builder.CreateCall(redirector, params, "")
if retval.Type().TypeKind() != llvm.VoidTypeKind {
call.ReplaceAllUsesWith(retval)
}
call.EraseFromParentAsInstruction()
bitcast.EraseFromParentAsInstruction()
inttoptr.EraseFromParentAsInstruction()
use.EraseFromParentAsInstruction()
}
}
@@ -542,22 +542,22 @@ func (p *lowerInterfacesPass) getSignature(name string) *signatureInfo {
return p.signatures[name]
}
// replaceInvokeWithCall replaces a runtime.interfaceMethod + bitcast with a
// replaceInvokeWithCall replaces a runtime.interfaceMethod + inttoptr with a
// concrete method. This can be done when only one type implements the
// interface.
func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInfo, signature *signatureInfo) {
bitcasts := getUses(use)
if len(bitcasts) != 1 || bitcasts[0].IsABitCastInst().IsNil() {
panic("expected exactly one bitcast use of runtime.interfaceMethod")
inttoptrs := getUses(use)
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
panic("expected exactly one inttoptr use of runtime.interfaceMethod")
}
bitcast := bitcasts[0]
inttoptr := inttoptrs[0]
function := typ.getMethod(signature).function
if bitcast.Type() != function.Type() {
if inttoptr.Type() != function.Type() {
p.builder.SetInsertPointBefore(use)
function = p.builder.CreateBitCast(function, bitcast.Type(), "")
function = p.builder.CreateBitCast(function, inttoptr.Type(), "")
}
bitcast.ReplaceAllUsesWith(function)
bitcast.EraseFromParentAsInstruction()
inttoptr.ReplaceAllUsesWith(function)
inttoptr.EraseFromParentAsInstruction()
use.EraseFromParentAsInstruction()
}
+2 -2
View File
@@ -203,7 +203,7 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) (llvm.Value, error) {
}
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
signatureGlobal,
llvm.ConstBitCast(fn, c.i8ptrType),
llvm.ConstPtrToInt(fn, c.uintptrType),
})
methods[i] = methodInfo
}
@@ -400,7 +400,7 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu
c.getMethodSignature(instr.Method),
}
fn := c.createRuntimeCall("interfaceMethod", values, "invoke.func")
fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast")
fnCast := c.builder.CreateIntToPtr(fn, llvmFnType, "invoke.func.cast")
receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver")
args := []llvm.Value{receiverValue}
+16 -16
View File
@@ -84,15 +84,18 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
// Memory operators
case !inst.IsAAllocaInst().IsNil():
fr.locals[inst] = &AllocaValue{
Underlying: getZeroValue(inst.Type().ElementType()),
Dirty: false,
allocType := inst.Type().ElementType()
alloca := llvm.AddGlobal(fr.Mod, allocType, fr.pkgName+"$alloca")
alloca.SetInitializer(getZeroValue(allocType))
alloca.SetLinkage(llvm.InternalLinkage)
fr.locals[inst] = &LocalValue{
Underlying: alloca,
Eval: fr.Eval,
}
case !inst.IsALoadInst().IsNil():
operand := fr.getLocal(inst.Operand(0))
operand := fr.getLocal(inst.Operand(0)).(*LocalValue)
var value llvm.Value
if !operand.IsConstant() || inst.IsVolatile() {
if !operand.IsConstant() || inst.IsVolatile() || operand.Underlying.Opcode() == llvm.BitCast {
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
} else {
value = operand.Load()
@@ -173,11 +176,8 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
continue // special case: bitcast of alloc
}
}
value := fr.getLocal(operand)
if bc, ok := value.(*PointerCastValue); ok {
value = bc.Underlying // avoid double bitcasts
}
fr.locals[inst] = &PointerCastValue{Eval: fr.Eval, Underlying: value, CastType: inst.Type()}
value := fr.getLocal(operand).(*LocalValue)
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateBitCast(value.Value(), inst.Type(), "")}
// Other operators
case !inst.IsAICmpInst().IsNil():
@@ -222,7 +222,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
alloc := llvm.AddGlobal(fr.Mod, allocType, fr.pkgName+"$alloc")
alloc.SetInitializer(getZeroValue(allocType))
alloc.SetLinkage(llvm.InternalLinkage)
result := &GlobalValue{
result := &LocalValue{
Underlying: alloc,
Eval: fr.Eval,
}
@@ -246,15 +246,15 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
m := fr.getLocal(inst.Operand(0)).(*MapValue)
// "key" is a Go string value, which in the TinyGo calling convention is split up
// into separate pointer and length parameters.
keyBuf := fr.getLocal(inst.Operand(1))
keyLen := fr.getLocal(inst.Operand(2))
valPtr := fr.getLocal(inst.Operand(3))
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
keyLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
valPtr := fr.getLocal(inst.Operand(3)).(*LocalValue)
m.PutString(keyBuf, keyLen, valPtr)
case callee.Name() == "runtime.hashmapBinarySet":
// set a binary (int etc.) key in the map
m := fr.getLocal(inst.Operand(0)).(*MapValue)
keyBuf := fr.getLocal(inst.Operand(1))
valPtr := fr.getLocal(inst.Operand(2))
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
valPtr := fr.getLocal(inst.Operand(2)).(*LocalValue)
m.PutBinary(keyBuf, valPtr)
case callee.Name() == "runtime.stringConcat":
// adding two strings together
+11 -6
View File
@@ -42,10 +42,19 @@ func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error {
initAll := mod.NamedFunction(name)
bb := initAll.EntryBasicBlock()
e.builder.SetInsertPointBefore(bb.LastInstruction())
// Create a dummy alloca in the entry block that we can set the insert point
// to. This is necessary because otherwise we might be removing the
// instruction (init call) that we are removing after successful
// interpretation.
e.builder.SetInsertPointBefore(bb.FirstInstruction())
dummy := e.builder.CreateAlloca(e.Mod.Context().Int8Type(), "dummy")
e.builder.SetInsertPointBefore(dummy)
e.builder.SetInstDebugLocation(bb.FirstInstruction())
var initCalls []llvm.Value
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
if inst == dummy {
continue
}
if !inst.IsAReturnInst().IsNil() {
break // ret void
}
@@ -115,11 +124,7 @@ func (e *Eval) function(fn llvm.Value, params []Value, pkgName, indent string) (
// getValue determines what kind of LLVM value it gets and returns the
// appropriate Value type.
func (e *Eval) getValue(v llvm.Value) Value {
if !v.IsAGlobalVariable().IsNil() {
return &GlobalValue{e, v}
} else {
return &LocalValue{e, v}
}
return &LocalValue{e, v}
}
// markDirty marks the passed-in LLVM value dirty, recursively. For example,
+72 -309
View File
@@ -36,11 +36,17 @@ func (v *LocalValue) Type() llvm.Type {
}
func (v *LocalValue) IsConstant() bool {
if _, ok := v.Eval.dirtyGlobals[v.Underlying]; ok {
return false
}
return v.Underlying.IsConstant()
}
// Load loads a constant value if this is a constant GEP, otherwise it panics.
// Load loads a constant value if this is a constant pointer.
func (v *LocalValue) Load() llvm.Value {
if !v.Underlying.IsAGlobalVariable().IsNil() {
return v.Underlying.Initializer()
}
switch v.Underlying.Opcode() {
case llvm.GetElementPtr:
indices := v.getConstGEPIndices()
@@ -50,21 +56,32 @@ func (v *LocalValue) Load() llvm.Value {
global := v.Eval.getValue(v.Underlying.Operand(0))
agg := global.Load()
return llvm.ConstExtractValue(agg, indices[1:])
case llvm.BitCast:
panic("interp: load from a bitcast")
default:
panic("interp: load from a constant")
}
}
// Store stores to the underlying value if the value type is a constant GEP,
// Store stores to the underlying value if the value type is a pointer type,
// otherwise it panics.
func (v *LocalValue) Store(value llvm.Value) {
if !v.Underlying.IsAGlobalVariable().IsNil() {
if !value.IsConstant() {
v.MarkDirty()
v.Eval.builder.CreateStore(value, v.Underlying)
} else {
v.Underlying.SetInitializer(value)
}
return
}
switch v.Underlying.Opcode() {
case llvm.GetElementPtr:
indices := v.getConstGEPIndices()
if indices[0] != 0 {
panic("invalid GEP")
}
global := &GlobalValue{v.Eval, v.Underlying.Operand(0)}
global := &LocalValue{v.Eval, v.Underlying.Operand(0)}
agg := global.Load()
agg = llvm.ConstInsertValue(agg, value, indices[1:])
global.Store(agg)
@@ -74,10 +91,13 @@ func (v *LocalValue) Store(value llvm.Value) {
}
}
// GetElementPtr returns a constant GEP when the underlying value is also a
// constant GEP. It panics when the underlying value is not a constant GEP:
// getting the pointer to a constant is not possible.
// GetElementPtr returns a GEP when the underlying value is of pointer type.
func (v *LocalValue) GetElementPtr(indices []uint32) Value {
if !v.Underlying.IsAGlobalVariable().IsNil() {
int32Type := v.Underlying.Type().Context().Int32Type()
gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices))
return &LocalValue{v.Eval, gep}
}
switch v.Underlying.Opcode() {
case llvm.GetElementPtr, llvm.IntToPtr:
int32Type := v.Underlying.Type().Context().Int32Type()
@@ -107,283 +127,18 @@ func (v *LocalValue) getConstGEPIndices() []uint32 {
return indices
}
// GlobalValue wraps a LLVM global variable.
type GlobalValue struct {
Eval *Eval
Underlying llvm.Value
}
// Value returns the initializer for this global variable.
func (v *GlobalValue) Value() llvm.Value {
return v.Underlying
}
// Type returns the type of this global variable, which is a pointer type. Use
// Type().ElementType() to get the actual global variable type.
func (v *GlobalValue) Type() llvm.Type {
return v.Underlying.Type()
}
// IsConstant returns true if this global is not dirty, false otherwise.
func (v *GlobalValue) IsConstant() bool {
if _, ok := v.Eval.dirtyGlobals[v.Underlying]; ok {
return false
}
return true
}
// Load returns the initializer of the global variable.
func (v *GlobalValue) Load() llvm.Value {
return v.Underlying.Initializer()
}
// Store sets the initializer of the global variable.
func (v *GlobalValue) Store(value llvm.Value) {
if !value.IsConstant() {
v.MarkDirty()
v.Eval.builder.CreateStore(value, v.Underlying)
} else {
v.Underlying.SetInitializer(value)
}
}
// GetElementPtr returns a constant GEP on this global, which can be used in
// load and store instructions.
func (v *GlobalValue) GetElementPtr(indices []uint32) Value {
int32Type := v.Underlying.Type().Context().Int32Type()
gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices))
return &LocalValue{v.Eval, gep}
}
func (v *GlobalValue) String() string {
return "&GlobalValue{" + v.Underlying.Name() + "}"
}
// MarkDirty marks this global as dirty, meaning that every load from and store
// to this global (from now on) must be performed at runtime.
func (v *GlobalValue) MarkDirty() {
func (v *LocalValue) MarkDirty() {
if v.Underlying.IsAGlobalVariable().IsNil() {
panic("trying to mark a non-global as dirty")
}
if !v.IsConstant() {
return // already dirty
}
v.Eval.dirtyGlobals[v.Underlying] = struct{}{}
}
// An alloca represents a local alloca, which is a stack allocated variable.
// It is emulated by storing the constant of the alloca.
type AllocaValue struct {
Eval *Eval
Underlying llvm.Value // the constant value itself if not dirty, otherwise the alloca instruction
Dirty bool // this value must be evaluated at runtime
}
// Value turns this alloca into a runtime alloca instead of a compile-time
// constant (if not already converted), and returns the alloca itself.
func (v *AllocaValue) Value() llvm.Value {
if !v.Dirty {
// Mark this alloca a dirty, meaning it is run at runtime instead of
// compile time.
alloca := v.Eval.builder.CreateAlloca(v.Underlying.Type(), "")
v.Eval.builder.CreateStore(v.Underlying, alloca)
v.Dirty = true
v.Underlying = alloca
}
return v.Underlying
}
// Type returns the type of this alloca, which is always a pointer.
func (v *AllocaValue) Type() llvm.Type {
if v.Dirty {
return v.Underlying.Type()
} else {
return llvm.PointerType(v.Underlying.Type(), 0)
}
}
func (v *AllocaValue) IsConstant() bool {
return !v.Dirty
}
// Load returns the value this alloca contains, which may be evaluated at
// runtime.
func (v *AllocaValue) Load() llvm.Value {
if v.Dirty {
ret := v.Eval.builder.CreateLoad(v.Underlying, "")
if ret.IsNil() {
panic("alloca is nil")
}
return ret
} else {
if v.Underlying.IsNil() {
panic("alloca is nil")
}
return v.Underlying
}
}
// Store updates the value of this alloca.
func (v *AllocaValue) Store(value llvm.Value) {
if v.Underlying.Type() != value.Type() {
panic("interp: trying to store to an alloca with a different type")
}
if v.Dirty || !value.IsConstant() {
v.Eval.builder.CreateStore(value, v.Value())
} else {
v.Underlying = value
}
}
// GetElementPtr returns a value (a *GetElementPtrValue) that keeps a reference
// to this alloca, so that Load() and Store() continue to work.
func (v *AllocaValue) GetElementPtr(indices []uint32) Value {
return &GetElementPtrValue{v, indices}
}
func (v *AllocaValue) String() string {
return "&AllocaValue{Type: " + v.Type().String() + "}"
}
// GetElementPtrValue wraps an alloca, keeping track of what the GEP points to
// so it can be used as a pointer value (with Load() and Store()).
type GetElementPtrValue struct {
Alloca *AllocaValue
Indices []uint32
}
// Type returns the type of this GEP, which is always of type pointer.
func (v *GetElementPtrValue) Type() llvm.Type {
if v.Alloca.Dirty {
return v.Value().Type()
} else {
return llvm.PointerType(v.Load().Type(), 0)
}
}
func (v *GetElementPtrValue) IsConstant() bool {
return v.Alloca.IsConstant()
}
// Value creates the LLVM GEP instruction of this GetElementPtrValue wrapper and
// returns it.
func (v *GetElementPtrValue) Value() llvm.Value {
if v.Alloca.Dirty {
alloca := v.Alloca.Value()
int32Type := v.Alloca.Type().Context().Int32Type()
llvmIndices := getLLVMIndices(int32Type, v.Indices)
return v.Alloca.Eval.builder.CreateGEP(alloca, llvmIndices, "")
} else {
panic("interp: todo: pointer to alloca gep")
}
}
// Load deferences the pointer this GEP points to. For a constant GEP, it
// extracts the value from the underlying alloca.
func (v *GetElementPtrValue) Load() llvm.Value {
if v.Alloca.Dirty {
gep := v.Value()
return v.Alloca.Eval.builder.CreateLoad(gep, "")
} else {
underlying := v.Alloca.Load()
indices := v.Indices
if indices[0] != 0 {
panic("invalid GEP")
}
return llvm.ConstExtractValue(underlying, indices[1:])
}
}
// Store stores to the pointer this GEP points to. For a constant GEP, it
// updates the underlying allloca.
func (v *GetElementPtrValue) Store(value llvm.Value) {
if v.Alloca.Dirty || !value.IsConstant() {
alloca := v.Alloca.Value()
int32Type := v.Alloca.Type().Context().Int32Type()
llvmIndices := getLLVMIndices(int32Type, v.Indices)
gep := v.Alloca.Eval.builder.CreateGEP(alloca, llvmIndices, "")
v.Alloca.Eval.builder.CreateStore(value, gep)
} else {
underlying := v.Alloca.Load()
indices := v.Indices
if indices[0] != 0 {
panic("invalid GEP")
}
underlying = llvm.ConstInsertValue(underlying, value, indices[1:])
v.Alloca.Store(underlying)
}
}
func (v *GetElementPtrValue) GetElementPtr(indices []uint32) Value {
if v.Alloca.Dirty {
panic("interp: todo: gep on a dirty gep")
} else {
combined := append([]uint32{}, v.Indices...)
combined[len(combined)-1] += indices[0]
combined = append(combined, indices[1:]...)
return &GetElementPtrValue{v.Alloca, combined}
}
}
func (v *GetElementPtrValue) String() string {
indices := ""
for _, n := range v.Indices {
if indices != "" {
indices += ", "
}
indices += strconv.Itoa(int(n))
}
return "&GetElementPtrValue{Alloca: " + v.Alloca.String() + ", Indices: [" + indices + "]}"
}
// PointerCastValue represents a bitcast operation on a pointer.
type PointerCastValue struct {
Eval *Eval
Underlying Value
CastType llvm.Type
}
// Value returns a constant bitcast value.
func (v *PointerCastValue) Value() llvm.Value {
from := v.Underlying.Value()
return llvm.ConstBitCast(from, v.CastType)
}
// Type returns the type this pointer has been cast to.
func (v *PointerCastValue) Type() llvm.Type {
return v.CastType
}
func (v *PointerCastValue) IsConstant() bool {
return v.Underlying.IsConstant()
}
// Load tries to load and bitcast the given value. If this value cannot be
// bitcasted, Load panics.
func (v *PointerCastValue) Load() llvm.Value {
if v.Underlying.IsConstant() {
typeFrom := v.Underlying.Type().ElementType()
typeTo := v.CastType.ElementType()
if isScalar(typeFrom) && isScalar(typeTo) && v.Eval.TargetData.TypeAllocSize(typeFrom) == v.Eval.TargetData.TypeAllocSize(typeTo) {
return llvm.ConstBitCast(v.Underlying.Load(), v.CastType.ElementType())
}
}
panic("interp: load from a pointer bitcast: " + v.String())
}
// Store panics: it is not (yet) possible to store directly to a bitcast.
func (v *PointerCastValue) Store(value llvm.Value) {
panic("interp: store on a pointer bitcast")
}
// GetElementPtr panics: it is not (yet) possible to do a GEP operation on a
// bitcast.
func (v *PointerCastValue) GetElementPtr(indices []uint32) Value {
panic("interp: GEP on a pointer bitcast")
}
func (v *PointerCastValue) String() string {
return "&PointerCastValue{Value: " + v.Underlying.String() + ", CastType: " + v.CastType.String() + "}"
}
// MapValue implements a Go map which is created at compile time and stored as a
// global variable.
type MapValue struct {
@@ -534,27 +289,24 @@ func (v *MapValue) GetElementPtr(indices []uint32) Value {
// PutString does a map assign operation, assuming that the map is of type
// map[string]T.
func (v *MapValue) PutString(keyBuf, keyLen, valPtr Value) {
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
if !v.Underlying.IsNil() {
panic("map already created")
}
var value llvm.Value
switch valPtr := valPtr.(type) {
case *PointerCastValue:
value = valPtr.Underlying.Load()
if v.ValueType.IsNil() {
v.ValueType = value.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
panic("interp: map store value type has the wrong size")
}
} else {
if value.Type() != v.ValueType {
panic("interp: map store value type is inconsistent")
}
if valPtr.Underlying.Opcode() == llvm.BitCast {
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
}
value := valPtr.Load()
if v.ValueType.IsNil() {
v.ValueType = value.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
panic("interp: map store value type has the wrong size")
}
} else {
if value.Type() != v.ValueType {
panic("interp: map store value type is inconsistent")
}
default:
panic("interp: todo: handle map value pointer")
}
keyType := v.Eval.Mod.GetTypeByName("runtime._string")
@@ -569,31 +321,42 @@ func (v *MapValue) PutString(keyBuf, keyLen, valPtr Value) {
}
// PutBinary does a map assign operation.
func (v *MapValue) PutBinary(keyPtr, valPtr Value) {
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
if !v.Underlying.IsNil() {
panic("map already created")
}
var value llvm.Value
switch valPtr := valPtr.(type) {
case *PointerCastValue:
value = valPtr.Underlying.Load()
if v.ValueType.IsNil() {
v.ValueType = value.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
panic("interp: map store value type has the wrong size")
}
} else {
if value.Type() != v.ValueType {
panic("interp: map store value type is inconsistent")
}
if valPtr.Underlying.Opcode() == llvm.BitCast {
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
}
value := valPtr.Load()
if v.ValueType.IsNil() {
v.ValueType = value.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
panic("interp: map store value type has the wrong size")
}
} else {
if value.Type() != v.ValueType {
panic("interp: map store value type is inconsistent")
}
default:
panic("interp: todo: handle map value pointer")
}
key := keyPtr.(*PointerCastValue).Underlying.Load()
v.KeyType = key.Type()
if keyPtr.Underlying.Opcode() == llvm.BitCast {
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
} else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr {
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
}
key := keyPtr.Load()
if v.KeyType.IsNil() {
v.KeyType = key.Type()
if int(v.Eval.TargetData.TypeAllocSize(v.KeyType)) != v.KeySize {
panic("interp: map store key type has the wrong size")
}
} else {
if key.Type() != v.KeyType {
panic("interp: map store key type is inconsistent")
}
}
// TODO: avoid duplicate keys
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
+12 -10
View File
@@ -259,19 +259,19 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
}
}
// Get an Intel .hex file or .bin file from the .elf file.
if outext == ".hex" || outext == ".bin" {
// Get an Intel .hex file or .bin file from the .elf file.
tmppath = filepath.Join(dir, "main"+outext)
format := map[string]string{
".hex": "ihex",
".bin": "binary",
}[outext]
cmd := exec.Command(spec.Objcopy, "-O", format, executable, tmppath)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
err := Objcopy(executable, tmppath)
if err != nil {
return &commandError{"failed to extract " + format + " from", executable, err}
return err
}
} else if outext == ".uf2" {
// Get UF2 from the .elf file.
tmppath = filepath.Join(dir, "main"+outext)
err := ConvertELFFileToUF2File(executable, tmppath)
if err != nil {
return err
}
}
return action(tmppath)
@@ -328,6 +328,8 @@ func Flash(pkgName, target, port string, config *BuildConfig) error {
fileExt = ".elf"
case strings.Contains(spec.Flasher, "{bin}"):
fileExt = ".bin"
case strings.Contains(spec.Flasher, "{uf2}"):
fileExt = ".uf2"
default:
return errors.New("invalid target file - did you forget the {hex} token in the 'flash' section?")
}
+13 -2
View File
@@ -68,7 +68,7 @@ func TestCompiler(t *testing.T) {
continue // TODO: improve CGo
}
t.Run(path, func(t *testing.T) {
runTest(path, tmpdir, "arm--linux-gnueabi", t)
runTest(path, tmpdir, "arm--linux-gnueabihf", t)
})
}
@@ -78,7 +78,17 @@ func TestCompiler(t *testing.T) {
continue // TODO: improve CGo
}
t.Run(path, func(t *testing.T) {
runTest(path, tmpdir, "aarch64--linux-gnueabi", t)
runTest(path, tmpdir, "aarch64--linux-gnu", t)
})
}
t.Log("running tests for WebAssembly...")
for _, path := range matches {
if path == "testdata/gc.go" {
continue // known to fail
}
t.Run(path, func(t *testing.T) {
runTest(path, tmpdir, "wasm", t)
})
}
}
@@ -106,6 +116,7 @@ func runTest(path, tmpdir string, target string, t *testing.T) {
dumpSSA: false,
debug: false,
printSizes: "",
wasmAbi: "js",
}
binary := filepath.Join(tmpdir, "test")
err = Build("./"+path, binary, target, config)
+78
View File
@@ -0,0 +1,78 @@
package main
import (
"debug/elf"
"os"
"path/filepath"
"github.com/marcinbor85/gohex"
)
// ObjcopyError is an error returned by functions that act like objcopy.
type ObjcopyError struct {
Op string
Err error
}
func (e ObjcopyError) Error() string {
if e.Err == nil {
return e.Op
}
return e.Op + ": " + e.Err.Error()
}
// ExtractTextSegment returns the .text segment and the first address from the
// ELF file in the given path.
func ExtractTextSegment(path string) (uint64, []byte, error) {
f, err := elf.Open(path)
if err != nil {
return 0, nil, ObjcopyError{"failed to open ELF file to extract text segment", err}
}
defer f.Close()
text := f.Section(".text")
if text == nil {
return 0, nil, ObjcopyError{"file does not contain .text segment: " + path, nil}
}
data, err := text.Data()
if err != nil {
return 0, nil, ObjcopyError{"failed to extract .text segment from ELF file", err}
}
return text.Addr, data, nil
}
// Objcopy converts an ELF file to a different (simpler) output file format:
// .bin or .hex. It extracts only the .text section.
func Objcopy(infile, outfile string) error {
f, err := os.OpenFile(outfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
defer f.Close()
// Read the .text segment.
addr, data, err := ExtractTextSegment(infile)
if err != nil {
return err
}
// Write to the file, in the correct format.
switch filepath.Ext(outfile) {
case ".bin":
// The address is not stored in a .bin file (therefore you
// should use .hex files in most cases).
_, err := f.Write(data)
return err
case ".hex":
mem := gohex.NewMemory()
mem.SetStartAddress(uint32(addr)) // ignored in most cases (Intel-specific)
err := mem.AddBinary(uint32(addr), data)
if err != nil {
return ObjcopyError{"failed to create .hex file", err}
}
mem.DumpIntelHex(f, 32) // TODO: handle error
return nil
default:
panic("unreachable")
}
}
+27 -4
View File
@@ -2,6 +2,8 @@
package machine
import "device/sam"
// GPIO Pins
const (
D0 = PB09
@@ -51,13 +53,13 @@ const (
PROXIMITY = A10
)
// USBCDC pins
// USBCDC pins (logical UART0)
const (
USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25
)
// UART0 pins
// UART0 pins (logical UART1)
const (
UART_TX_PIN = PB08 // PORTB
UART_RX_PIN = PB09 // PORTB
@@ -65,6 +67,27 @@ const (
// I2C pins
const (
SDA_PIN = PA00 // SDA: SERCOM3/PAD[0]
SCL_PIN = PA01 // SCL: SERCOM3/PAD[1]
SDA_PIN = PB02 // I2C0 external
SCL_PIN = PB03 // I2C0 external
SDA1_PIN = PA00 // I2C1 internal
SCL1_PIN = PA01 // I2C1 internal
)
// I2C on the Circuit Playground Express.
var (
I2C0 = I2C{Bus: sam.SERCOM5_I2CM} // external device
I2C1 = I2C{Bus: sam.SERCOM1_I2CM} // internal device
)
// SPI pins (internal flash)
const (
SPI0_SCK_PIN = PA21 // SCK: SERCOM3/PAD[3]
SPI0_MOSI_PIN = PA20 // MOSI: SERCOM3/PAD[2]
SPI0_MISO_PIN = PA16 // MISO: SERCOM3/PAD[0]
)
// SPI on the Circuit Playground Express.
var (
SPI0 = SPI{Bus: sam.SERCOM3_SPI}
)
+19
View File
@@ -2,6 +2,8 @@
package machine
import "device/sam"
// GPIO Pins
const (
D0 = PA11 // UART0 RX
@@ -51,3 +53,20 @@ const (
SDA_PIN = PA22 // SDA: SERCOM3/PAD[0]
SCL_PIN = PA23 // SCL: SERCOM3/PAD[1]
)
// I2C on the ItsyBitsy M0.
var (
I2C0 = I2C{Bus: sam.SERCOM3_I2CM}
)
// SPI pins
const (
SPI0_SCK_PIN = PB11 // SCK: SERCOM4/PAD[3]
SPI0_MOSI_PIN = PB10 // MOSI: SERCOM4/PAD[2]
SPI0_MISO_PIN = PA12 // MISO: SERCOM4/PAD[0]
)
// SPI on the ItsyBitsy M0.
var (
SPI0 = SPI{Bus: sam.SERCOM4_SPI}
)
+365 -112
View File
@@ -163,6 +163,19 @@ func (p GPIO) Configure(config GPIOConfig) {
// enable port config
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR | sam.PORT_PINCFG0_INEN)
case GPIO_SERCOM_ALT:
if p.Pin&1 > 0 {
// odd pin, so save the even pins
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
p.setPMux(val | (GPIO_SERCOM_ALT << sam.PORT_PMUX0_PMUXO_Pos))
} else {
// even pin, so save the odd pins
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
p.setPMux(val | (GPIO_SERCOM_ALT << sam.PORT_PMUX0_PMUXE_Pos))
}
// enable port config
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)
case GPIO_COM:
if p.Pin&1 > 0 {
// odd pin, so save the even pins
@@ -175,6 +188,18 @@ func (p GPIO) Configure(config GPIOConfig) {
}
// enable port config
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
case GPIO_ANALOG:
if p.Pin&1 > 0 {
// odd pin, so save the even pins
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
p.setPMux(val | (GPIO_ANALOG << sam.PORT_PMUX0_PMUXO_Pos))
} else {
// even pin, so save the odd pins
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
p.setPMux(val | (GPIO_COM << sam.PORT_PMUX0_PMUXE_Pos))
}
// enable port config
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)
}
}
@@ -245,6 +270,141 @@ func (p GPIO) setPinCfg(val sam.RegValue8) {
setPinCfg(p.Pin, val)
}
// InitADC initializes the ADC.
func InitADC() {
// ADC Bias Calibration
// #define ADC_FUSES_BIASCAL_ADDR (NVMCTRL_OTP4 + 4)
// #define ADC_FUSES_BIASCAL_Pos 3 /**< \brief (NVMCTRL_OTP4) ADC Bias Calibration */
// #define ADC_FUSES_BIASCAL_Msk (0x7u << ADC_FUSES_BIASCAL_Pos)
// #define ADC_FUSES_BIASCAL(value) ((ADC_FUSES_BIASCAL_Msk & ((value) << ADC_FUSES_BIASCAL_Pos)))
// #define ADC_FUSES_LINEARITY_0_ADDR NVMCTRL_OTP4
// #define ADC_FUSES_LINEARITY_0_Pos 27 /**< \brief (NVMCTRL_OTP4) ADC Linearity bits 4:0 */
// #define ADC_FUSES_LINEARITY_0_Msk (0x1Fu << ADC_FUSES_LINEARITY_0_Pos)
// #define ADC_FUSES_LINEARITY_0(value) ((ADC_FUSES_LINEARITY_0_Msk & ((value) << ADC_FUSES_LINEARITY_0_Pos)))
// #define ADC_FUSES_LINEARITY_1_ADDR (NVMCTRL_OTP4 + 4)
// #define ADC_FUSES_LINEARITY_1_Pos 0 /**< \brief (NVMCTRL_OTP4) ADC Linearity bits 7:5 */
// #define ADC_FUSES_LINEARITY_1_Msk (0x7u << ADC_FUSES_LINEARITY_1_Pos)
// #define ADC_FUSES_LINEARITY_1(value) ((ADC_FUSES_LINEARITY_1_Msk & ((value) << ADC_FUSES_LINEARITY_1_Pos)))
biasFuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
bias := sam.RegValue16(uint16(biasFuse>>3) & uint16(0x7))
// ADC Linearity bits 4:0
linearity0Fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020)))
linearity := sam.RegValue16(uint16(linearity0Fuse>>27) & uint16(0x1f))
// ADC Linearity bits 7:5
linearity1Fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
linearity |= sam.RegValue16(uint16(linearity1Fuse)&uint16(0x7)) << 5
// set calibration
sam.ADC.CALIB = (bias << 8) | linearity
// Wait for synchronization
waitADCSync()
// Divide Clock by 32 with 12 bits resolution as default
sam.ADC.CTRLB = (sam.ADC_CTRLB_PRESCALER_DIV32 << sam.ADC_CTRLB_PRESCALER_Pos) |
(sam.ADC_CTRLB_RESSEL_12BIT << sam.ADC_CTRLB_RESSEL_Pos)
// Sampling Time Length
sam.ADC.SAMPCTRL = 5
// Wait for synchronization
waitADCSync()
// Use internal ground
sam.ADC.INPUTCTRL = (sam.ADC_INPUTCTRL_MUXNEG_GND << sam.ADC_INPUTCTRL_MUXNEG_Pos)
// Averaging (see datasheet table in AVGCTRL register description)
sam.ADC.AVGCTRL = (sam.ADC_AVGCTRL_SAMPLENUM_1 << sam.ADC_AVGCTRL_SAMPLENUM_Pos) |
(0x0 << sam.ADC_AVGCTRL_ADJRES_Pos)
// Analog Reference is AREF pin (3.3v)
sam.ADC.INPUTCTRL |= (sam.ADC_INPUTCTRL_GAIN_DIV2 << sam.ADC_INPUTCTRL_GAIN_Pos)
// 1/2 VDDANA = 0.5 * 3V3 = 1.65V
sam.ADC.REFCTRL |= (sam.ADC_REFCTRL_REFSEL_INTVCC1 << sam.ADC_REFCTRL_REFSEL_Pos)
}
// Configure configures a ADCPin to be able to be used to read data.
func (a ADC) Configure() {
GPIO{a.Pin}.Configure(GPIOConfig{Mode: GPIO_ANALOG})
return
}
// Get returns the current value of a ADC pin, in the range 0..0xffff.
func (a ADC) Get() uint16 {
ch := a.getADCChannel()
// Selection for the positive ADC input
sam.ADC.INPUTCTRL &^= sam.ADC_INPUTCTRL_MUXPOS_Msk
waitADCSync()
sam.ADC.INPUTCTRL |= sam.RegValue(ch << sam.ADC_INPUTCTRL_MUXPOS_Pos)
waitADCSync()
// Enable ADC
sam.ADC.CTRLA |= sam.ADC_CTRLA_ENABLE
waitADCSync()
// Start conversion
sam.ADC.SWTRIG |= sam.ADC_SWTRIG_START
waitADCSync()
// Clear the Data Ready flag
sam.ADC.INTFLAG = sam.ADC_INTFLAG_RESRDY
waitADCSync()
// Start conversion again, since first conversion after reference voltage changed is invalid.
sam.ADC.SWTRIG |= sam.ADC_SWTRIG_START
waitADCSync()
// Waiting for conversion to complete
for (sam.ADC.INTFLAG & sam.ADC_INTFLAG_RESRDY) == 0 {
}
val := sam.ADC.RESULT
// Disable ADC
sam.ADC.CTRLA &^= sam.ADC_CTRLA_ENABLE
waitADCSync()
return uint16(val)
}
func (a ADC) getADCChannel() uint8 {
switch a.Pin {
case PA02:
return 0
case PB08:
return 2
case PB09:
return 3
case PA04:
return 4
case PA05:
return 5
case PA06:
return 6
case PA07:
return 7
case PB02:
return 10
case PB03:
return 11
case PA09:
return 17
case PA11:
return 19
default:
return 0
}
}
func waitADCSync() {
for (sam.ADC.STATUS & sam.ADC_STATUS_SYNCBUSY) > 0 {
}
}
// UART on the SAMD21.
type UART struct {
Buffer *RingBuffer
@@ -257,9 +417,6 @@ var (
// The first hardware serial port on the SAMD21. Uses the SERCOM0 interface.
UART1 = UART{Bus: sam.SERCOM0_USART, Buffer: NewRingBuffer()}
// The second hardware serial port on the SAMD21. Uses the SERCOM1 interface.
UART2 = UART{Bus: sam.SERCOM1_USART, Buffer: NewRingBuffer()}
)
const (
@@ -272,6 +429,11 @@ const (
sercomTXPad0 = 0 // Only for UART
sercomTXPad2 = 1 // Only for UART
sercomTXPad023 = 2 // Only for UART with TX on PAD0, RTS on PAD2 and CTS on PAD3
spiTXPad0SCK1 = 0
spiTXPad2SCK3 = 1
spiTXPad3SCK1 = 2
spiTXPad0SCK3 = 3
)
// Configure the UART.
@@ -408,24 +570,11 @@ func handleUART1() {
UART1.Bus.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC
}
//go:export SERCOM1_IRQHandler
func handleUART2() {
// should reset IRQ
UART2.Receive(byte((UART2.Bus.DATA & 0xFF)))
UART2.Bus.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC
}
// I2C on the SAMD21.
type I2C struct {
Bus *sam.SERCOM_I2CM_Type
}
// Since the I2C interfaces on the SAMD21 use the SERCOMx peripherals,
// you can have multiple ones. we currently only implement one.
var (
I2C0 = I2C{Bus: sam.SERCOM3_I2CM}
)
// I2CConfig is used to store config info for I2C.
type I2CConfig struct {
Frequency uint32
@@ -650,6 +799,110 @@ func (i2c I2C) readByte() byte {
return byte(i2c.Bus.DATA)
}
// SPI
type SPI struct {
Bus *sam.SERCOM_SPI_Type
}
// SPIConfig is used to store config info for SPI.
type SPIConfig struct {
Frequency uint32
SCK uint8
MOSI uint8
MISO uint8
LSBFirst bool
Mode uint8
}
// Configure is intended to setup the SPI interface.
func (spi SPI) Configure(config SPIConfig) {
config.SCK = SPI0_SCK_PIN
config.MOSI = SPI0_MOSI_PIN
config.MISO = SPI0_MISO_PIN
doPad := spiTXPad2SCK3
diPad := sercomRXPad0
// set default frequency
if config.Frequency == 0 {
config.Frequency = 4000000
}
// Disable SPI port.
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_ENABLE
for (spi.Bus.SYNCBUSY & sam.SERCOM_SPI_SYNCBUSY_ENABLE) > 0 {
}
// enable pins
GPIO{config.SCK}.Configure(GPIOConfig{Mode: GPIO_SERCOM_ALT})
GPIO{config.MOSI}.Configure(GPIOConfig{Mode: GPIO_SERCOM_ALT})
GPIO{config.MISO}.Configure(GPIOConfig{Mode: GPIO_SERCOM_ALT})
// reset SERCOM
spi.Bus.CTRLA |= sam.SERCOM_SPI_CTRLA_SWRST
for (spi.Bus.CTRLA&sam.SERCOM_SPI_CTRLA_SWRST) > 0 ||
(spi.Bus.SYNCBUSY&sam.SERCOM_SPI_SYNCBUSY_SWRST) > 0 {
}
// set bit transfer order
dataOrder := 0
if config.LSBFirst {
dataOrder = 1
}
// Set SPI master
spi.Bus.CTRLA = (sam.SERCOM_SPI_CTRLA_MODE_SPI_MASTER << sam.SERCOM_SPI_CTRLA_MODE_Pos) |
sam.RegValue(doPad<<sam.SERCOM_SPI_CTRLA_DOPO_Pos) |
sam.RegValue(diPad<<sam.SERCOM_SPI_CTRLA_DIPO_Pos) |
sam.RegValue(dataOrder<<sam.SERCOM_SPI_CTRLA_DORD_Pos)
spi.Bus.CTRLB |= (0 << sam.SERCOM_SPI_CTRLB_CHSIZE_Pos) | // 8bit char size
sam.SERCOM_SPI_CTRLB_RXEN // receive enable
for (spi.Bus.SYNCBUSY & sam.SERCOM_SPI_SYNCBUSY_CTRLB) > 0 {
}
// set mode
switch config.Mode {
case 0:
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_CPHA
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_CPOL
case 1:
spi.Bus.CTRLA |= sam.SERCOM_SPI_CTRLA_CPHA
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_CPOL
case 2:
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_CPHA
spi.Bus.CTRLA |= sam.SERCOM_SPI_CTRLA_CPOL
case 3:
spi.Bus.CTRLA |= sam.SERCOM_SPI_CTRLA_CPHA | sam.SERCOM_SPI_CTRLA_CPOL
default: // to mode 0
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_CPHA
spi.Bus.CTRLA &^= sam.SERCOM_SPI_CTRLA_CPOL
}
// Set synch speed for SPI
baudRate := (CPU_FREQUENCY / (2 * config.Frequency)) - 1
spi.Bus.BAUD = sam.RegValue8(baudRate)
// Enable SPI port.
spi.Bus.CTRLA |= sam.SERCOM_SPI_CTRLA_ENABLE
for (spi.Bus.SYNCBUSY & sam.SERCOM_SPI_SYNCBUSY_ENABLE) > 0 {
}
}
// Transfer writes/reads a single byte using the SPI interface.
func (spi SPI) Transfer(w byte) (byte, error) {
// write data
spi.Bus.DATA = sam.RegValue(w)
// wait for receive
for (spi.Bus.INTFLAG & sam.SERCOM_SPI_INTFLAG_RXC) == 0 {
}
// return data
return byte(spi.Bus.DATA), nil
}
// PWM
const period = 0xFFFF
@@ -811,37 +1064,37 @@ func getPMux(p uint8) sam.RegValue8 {
case 15:
return sam.PORT.PMUX0_15
case 16:
return sam.RegValue8(sam.PORT.PMUX1_0>>24) & 0xff
return sam.RegValue8(sam.PORT.PMUX1_0>>0) & 0xff
case 17:
return sam.RegValue8(sam.PORT.PMUX1_0>>16) & 0xff
case 18:
return sam.RegValue8(sam.PORT.PMUX1_0>>8) & 0xff
case 18:
return sam.RegValue8(sam.PORT.PMUX1_0>>16) & 0xff
case 19:
return sam.RegValue8(sam.PORT.PMUX1_0 & 0xff)
return sam.RegValue8(sam.PORT.PMUX1_0>>24) & 0xff
case 20:
return sam.RegValue8(sam.PORT.PMUX1_4>>24) & 0xff
return sam.RegValue8(sam.PORT.PMUX1_4>>0) & 0xff
case 21:
return sam.RegValue8(sam.PORT.PMUX1_4>>16) & 0xff
case 22:
return sam.RegValue8(sam.PORT.PMUX1_4>>8) & 0xff
case 22:
return sam.RegValue8(sam.PORT.PMUX1_4>>16) & 0xff
case 23:
return sam.RegValue8(sam.PORT.PMUX1_4 & 0xff)
return sam.RegValue8(sam.PORT.PMUX1_4>>24) & 0xff
case 24:
return sam.RegValue8(sam.PORT.PMUX1_8>>24) & 0xff
return sam.RegValue8(sam.PORT.PMUX1_8>>0) & 0xff
case 25:
return sam.RegValue8(sam.PORT.PMUX1_8>>16) & 0xff
case 26:
return sam.RegValue8(sam.PORT.PMUX1_8>>8) & 0xff
case 26:
return sam.RegValue8(sam.PORT.PMUX1_8>>16) & 0xff
case 27:
return sam.RegValue8(sam.PORT.PMUX1_8 & 0xff)
return sam.RegValue8(sam.PORT.PMUX1_8>>24) & 0xff
case 28:
return sam.RegValue8(sam.PORT.PMUX1_12>>24) & 0xff
return sam.RegValue8(sam.PORT.PMUX1_12>>0) & 0xff
case 29:
return sam.RegValue8(sam.PORT.PMUX1_12>>16) & 0xff
case 30:
return sam.RegValue8(sam.PORT.PMUX1_12>>8) & 0xff
case 30:
return sam.RegValue8(sam.PORT.PMUX1_12>>16) & 0xff
case 31:
return sam.RegValue8(sam.PORT.PMUX1_12 & 0xff)
return sam.RegValue8(sam.PORT.PMUX1_12>>24) & 0xff
default:
return 0
}
@@ -884,37 +1137,37 @@ func setPMux(p uint8, val sam.RegValue8) {
case 15:
sam.PORT.PMUX0_15 = val
case 16:
sam.PORT.PMUX1_0 = (sam.PORT.PMUX1_0 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
sam.PORT.PMUX1_0 = (sam.PORT.PMUX1_0 &^ (0xff << 0)) | (sam.RegValue(val) << 0)
case 17:
sam.PORT.PMUX1_0 = (sam.PORT.PMUX1_0 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 18:
sam.PORT.PMUX1_0 = (sam.PORT.PMUX1_0 &^ (0xff << 8)) | (sam.RegValue(val) << 8)
case 18:
sam.PORT.PMUX1_0 = (sam.PORT.PMUX1_0 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 19:
sam.PORT.PMUX1_0 = (sam.PORT.PMUX1_0 &^ 0xff) | (sam.RegValue(val))
sam.PORT.PMUX1_0 = (sam.PORT.PMUX1_0 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
case 20:
sam.PORT.PMUX1_4 = (sam.PORT.PMUX1_4 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
sam.PORT.PMUX1_4 = (sam.PORT.PMUX1_4 &^ (0xff << 0)) | (sam.RegValue(val) << 0)
case 21:
sam.PORT.PMUX1_4 = (sam.PORT.PMUX1_4 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 22:
sam.PORT.PMUX1_4 = (sam.PORT.PMUX1_4 &^ (0xff << 8)) | (sam.RegValue(val) << 8)
case 22:
sam.PORT.PMUX1_4 = (sam.PORT.PMUX1_4 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 23:
sam.PORT.PMUX1_4 = (sam.PORT.PMUX1_4 &^ 0xff) | (sam.RegValue(val))
sam.PORT.PMUX1_4 = (sam.PORT.PMUX1_4 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
case 24:
sam.PORT.PMUX1_8 = (sam.PORT.PMUX1_8 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
sam.PORT.PMUX1_8 = (sam.PORT.PMUX1_8 &^ (0xff << 0)) | (sam.RegValue(val) << 0)
case 25:
sam.PORT.PMUX1_8 = (sam.PORT.PMUX1_8 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 26:
sam.PORT.PMUX1_8 = (sam.PORT.PMUX1_8 &^ (0xff << 8)) | (sam.RegValue(val) << 8)
case 26:
sam.PORT.PMUX1_8 = (sam.PORT.PMUX1_8 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 27:
sam.PORT.PMUX1_8 = (sam.PORT.PMUX1_8 &^ 0xff) | (sam.RegValue(val))
sam.PORT.PMUX1_8 = (sam.PORT.PMUX1_8 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
case 28:
sam.PORT.PMUX1_12 = (sam.PORT.PMUX1_12 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
sam.PORT.PMUX1_12 = (sam.PORT.PMUX1_12 &^ (0xff << 0)) | (sam.RegValue(val) << 0)
case 29:
sam.PORT.PMUX1_12 = (sam.PORT.PMUX1_12 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 30:
sam.PORT.PMUX1_12 = (sam.PORT.PMUX1_12 &^ (0xff << 8)) | (sam.RegValue(val) << 8)
case 30:
sam.PORT.PMUX1_12 = (sam.PORT.PMUX1_12 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 31:
sam.PORT.PMUX1_12 = (sam.PORT.PMUX1_12 &^ 0xff) | (sam.RegValue(val))
sam.PORT.PMUX1_12 = (sam.PORT.PMUX1_12 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
}
}
@@ -986,69 +1239,69 @@ func getPinCfg(p uint8) sam.RegValue8 {
case 31:
return sam.PORT.PINCFG0_31
case 32: // PB00
return sam.RegValue8(sam.PORT.PINCFG1_0>>24) & 0xff
return sam.RegValue8(sam.PORT.PINCFG1_0>>0) & 0xff
case 33: // PB01
return sam.RegValue8(sam.PORT.PINCFG1_0>>16) & 0xff
case 34: // PB02
return sam.RegValue8(sam.PORT.PINCFG1_0>>8) & 0xff
case 34: // PB02
return sam.RegValue8(sam.PORT.PINCFG1_0>>16) & 0xff
case 35: // PB03
return sam.RegValue8(sam.PORT.PINCFG1_0 & 0xff)
return sam.RegValue8(sam.PORT.PINCFG1_0>>24) & 0xff
case 37: // PB04
return sam.RegValue8(sam.PORT.PINCFG1_4>>24) & 0xff
return sam.RegValue8(sam.PORT.PINCFG1_4>>0) & 0xff
case 38: // PB05
return sam.RegValue8(sam.PORT.PINCFG1_4>>16) & 0xff
case 39: // PB06
return sam.RegValue8(sam.PORT.PINCFG1_4>>8) & 0xff
case 39: // PB06
return sam.RegValue8(sam.PORT.PINCFG1_4>>16) & 0xff
case 40: // PB07
return sam.RegValue8(sam.PORT.PINCFG1_4 & 0xff)
return sam.RegValue8(sam.PORT.PINCFG1_4>>24) & 0xff
case 41: // PB08
return sam.RegValue8(sam.PORT.PINCFG1_8>>24) & 0xff
return sam.RegValue8(sam.PORT.PINCFG1_8>>0) & 0xff
case 42: // PB09
return sam.RegValue8(sam.PORT.PINCFG1_8>>16) & 0xff
case 43: // PB10
return sam.RegValue8(sam.PORT.PINCFG1_8>>8) & 0xff
case 43: // PB10
return sam.RegValue8(sam.PORT.PINCFG1_8>>16) & 0xff
case 44: // PB11
return sam.RegValue8(sam.PORT.PINCFG1_8 & 0xff)
return sam.RegValue8(sam.PORT.PINCFG1_8>>24) & 0xff
case 45: // PB12
return sam.RegValue8(sam.PORT.PINCFG1_12>>24) & 0xff
return sam.RegValue8(sam.PORT.PINCFG1_12>>0) & 0xff
case 46: // PB13
return sam.RegValue8(sam.PORT.PINCFG1_12>>16) & 0xff
case 47: // PB14
return sam.RegValue8(sam.PORT.PINCFG1_12>>8) & 0xff
case 47: // PB14
return sam.RegValue8(sam.PORT.PINCFG1_12>>16) & 0xff
case 48: // PB15
return sam.RegValue8(sam.PORT.PINCFG1_12 & 0xff)
return sam.RegValue8(sam.PORT.PINCFG1_12>>24) & 0xff
case 49: // PB16
return sam.RegValue8(sam.PORT.PINCFG1_16>>24) & 0xff
return sam.RegValue8(sam.PORT.PINCFG1_16>>0) & 0xff
case 50: // PB17
return sam.RegValue8(sam.PORT.PINCFG1_16>>16) & 0xff
case 51: // PB18
return sam.RegValue8(sam.PORT.PINCFG1_16>>8) & 0xff
case 51: // PB18
return sam.RegValue8(sam.PORT.PINCFG1_16>>16) & 0xff
case 52: // PB19
return sam.RegValue8(sam.PORT.PINCFG1_16 & 0xff)
return sam.RegValue8(sam.PORT.PINCFG1_16>>24) & 0xff
case 53: // PB20
return sam.RegValue8(sam.PORT.PINCFG1_20>>24) & 0xff
return sam.RegValue8(sam.PORT.PINCFG1_20>>0) & 0xff
case 54: // PB21
return sam.RegValue8(sam.PORT.PINCFG1_20>>16) & 0xff
case 55: // PB22
return sam.RegValue8(sam.PORT.PINCFG1_20>>8) & 0xff
case 55: // PB22
return sam.RegValue8(sam.PORT.PINCFG1_20>>16) & 0xff
case 56: // PB23
return sam.RegValue8(sam.PORT.PINCFG1_20 & 0xff)
return sam.RegValue8(sam.PORT.PINCFG1_20>>24) & 0xff
case 57: // PB24
return sam.RegValue8(sam.PORT.PINCFG1_24>>24) & 0xff
return sam.RegValue8(sam.PORT.PINCFG1_24>>0) & 0xff
case 58: // PB25
return sam.RegValue8(sam.PORT.PINCFG1_24>>16) & 0xff
case 59: // PB26
return sam.RegValue8(sam.PORT.PINCFG1_24>>8) & 0xff
case 59: // PB26
return sam.RegValue8(sam.PORT.PINCFG1_24>>16) & 0xff
case 60: // PB27
return sam.RegValue8(sam.PORT.PINCFG1_24 & 0xff)
return sam.RegValue8(sam.PORT.PINCFG1_24>>24) & 0xff
case 61: // PB28
return sam.RegValue8(sam.PORT.PINCFG1_28>>24) & 0xff
return sam.RegValue8(sam.PORT.PINCFG1_28>>0) & 0xff
case 62: // PB29
return sam.RegValue8(sam.PORT.PINCFG1_28>>16) & 0xff
case 63: // PB30
return sam.RegValue8(sam.PORT.PINCFG1_28>>8) & 0xff
case 63: // PB30
return sam.RegValue8(sam.PORT.PINCFG1_28>>16) & 0xff
case 64: // PB31
return sam.RegValue8(sam.PORT.PINCFG1_28 & 0xff)
return sam.RegValue8(sam.PORT.PINCFG1_28>>24) & 0xff
default:
return 0
}
@@ -1122,69 +1375,69 @@ func setPinCfg(p uint8, val sam.RegValue8) {
case 31:
sam.PORT.PINCFG0_31 = val
case 32: // PB00
sam.PORT.PINCFG1_0 = (sam.PORT.PINCFG1_0 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
sam.PORT.PINCFG1_0 = (sam.PORT.PINCFG1_0 &^ (0xff << 0)) | (sam.RegValue(val) << 0)
case 33: // PB01
sam.PORT.PINCFG1_0 = (sam.PORT.PINCFG1_0 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 34: // PB02
sam.PORT.PINCFG1_0 = (sam.PORT.PINCFG1_0 &^ (0xff << 8)) | (sam.RegValue(val) << 8)
case 34: // PB02
sam.PORT.PINCFG1_0 = (sam.PORT.PINCFG1_0 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 35: // PB03
sam.PORT.PINCFG1_0 = (sam.PORT.PINCFG1_0 &^ 0xff) | (sam.RegValue(val))
sam.PORT.PINCFG1_0 = (sam.PORT.PINCFG1_0 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
case 36: // PB04
sam.PORT.PINCFG1_4 = (sam.PORT.PINCFG1_4 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
sam.PORT.PINCFG1_4 = (sam.PORT.PINCFG1_4 &^ (0xff << 0)) | (sam.RegValue(val) << 0)
case 37: // PB05
sam.PORT.PINCFG1_4 = (sam.PORT.PINCFG1_4 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 38: // PB06
sam.PORT.PINCFG1_4 = (sam.PORT.PINCFG1_4 &^ (0xff << 8)) | (sam.RegValue(val) << 8)
case 38: // PB06
sam.PORT.PINCFG1_4 = (sam.PORT.PINCFG1_4 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 39: // PB07
sam.PORT.PINCFG1_4 = (sam.PORT.PINCFG1_4 &^ 0xff) | (sam.RegValue(val))
sam.PORT.PINCFG1_4 = (sam.PORT.PINCFG1_4 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
case 40: // PB08
sam.PORT.PINCFG1_8 = (sam.PORT.PINCFG1_8 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
sam.PORT.PINCFG1_8 = (sam.PORT.PINCFG1_8 &^ (0xff << 0)) | (sam.RegValue(val) << 0)
case 41: // PB09
sam.PORT.PINCFG1_8 = (sam.PORT.PINCFG1_8 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 42: // PB10
sam.PORT.PINCFG1_8 = (sam.PORT.PINCFG1_8 &^ (0xff << 8)) | (sam.RegValue(val) << 8)
case 42: // PB10
sam.PORT.PINCFG1_8 = (sam.PORT.PINCFG1_8 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 43: // PB11
sam.PORT.PINCFG1_8 = (sam.PORT.PINCFG1_8 &^ 0xff) | (sam.RegValue(val))
sam.PORT.PINCFG1_8 = (sam.PORT.PINCFG1_8 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
case 44: // PB12
sam.PORT.PINCFG1_12 = (sam.PORT.PINCFG1_12 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
sam.PORT.PINCFG1_12 = (sam.PORT.PINCFG1_12 &^ (0xff << 0)) | (sam.RegValue(val) << 0)
case 45: // PB13
sam.PORT.PINCFG1_12 = (sam.PORT.PINCFG1_12 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 46: // PB14
sam.PORT.PINCFG1_12 = (sam.PORT.PINCFG1_12 &^ (0xff << 8)) | (sam.RegValue(val) << 8)
case 46: // PB14
sam.PORT.PINCFG1_12 = (sam.PORT.PINCFG1_12 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 47: // PB15
sam.PORT.PINCFG1_12 = (sam.PORT.PINCFG1_12 &^ 0xff) | (sam.RegValue(val))
sam.PORT.PINCFG1_12 = (sam.PORT.PINCFG1_12 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
case 48: // PB16
sam.PORT.PINCFG1_16 = (sam.PORT.PINCFG1_16 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
sam.PORT.PINCFG1_16 = (sam.PORT.PINCFG1_16 &^ (0xff << 0)) | (sam.RegValue(val) << 0)
case 49: // PB17
sam.PORT.PINCFG1_16 = (sam.PORT.PINCFG1_16 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 50: // PB18
sam.PORT.PINCFG1_16 = (sam.PORT.PINCFG1_16 &^ (0xff << 8)) | (sam.RegValue(val) << 8)
case 50: // PB18
sam.PORT.PINCFG1_16 = (sam.PORT.PINCFG1_16 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 51: // PB19
sam.PORT.PINCFG1_16 = (sam.PORT.PINCFG1_16 &^ 0xff) | (sam.RegValue(val))
sam.PORT.PINCFG1_16 = (sam.PORT.PINCFG1_16 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
case 52: // PB20
sam.PORT.PINCFG1_20 = (sam.PORT.PINCFG1_20 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
sam.PORT.PINCFG1_20 = (sam.PORT.PINCFG1_20 &^ (0xff << 0)) | (sam.RegValue(val) << 0)
case 53: // PB21
sam.PORT.PINCFG1_20 = (sam.PORT.PINCFG1_20 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 54: // PB22
sam.PORT.PINCFG1_20 = (sam.PORT.PINCFG1_20 &^ (0xff << 8)) | (sam.RegValue(val) << 8)
case 54: // PB22
sam.PORT.PINCFG1_20 = (sam.PORT.PINCFG1_20 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 55: // PB23
sam.PORT.PINCFG1_20 = (sam.PORT.PINCFG1_20 &^ 0xff) | (sam.RegValue(val))
sam.PORT.PINCFG1_20 = (sam.PORT.PINCFG1_20 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
case 56: // PB24
sam.PORT.PINCFG1_24 = (sam.PORT.PINCFG1_24 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
sam.PORT.PINCFG1_24 = (sam.PORT.PINCFG1_24 &^ (0xff << 0)) | (sam.RegValue(val) << 0)
case 57: // PB25
sam.PORT.PINCFG1_24 = (sam.PORT.PINCFG1_24 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 58: // PB26
sam.PORT.PINCFG1_24 = (sam.PORT.PINCFG1_24 &^ (0xff << 8)) | (sam.RegValue(val) << 8)
case 58: // PB26
sam.PORT.PINCFG1_24 = (sam.PORT.PINCFG1_24 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 59: // PB27
sam.PORT.PINCFG1_24 = (sam.PORT.PINCFG1_24 &^ 0xff) | (sam.RegValue(val))
sam.PORT.PINCFG1_24 = (sam.PORT.PINCFG1_24 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
case 60: // PB28
sam.PORT.PINCFG1_28 = (sam.PORT.PINCFG1_28 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
sam.PORT.PINCFG1_28 = (sam.PORT.PINCFG1_28 &^ (0xff << 0)) | (sam.RegValue(val) << 0)
case 61: // PB29
sam.PORT.PINCFG1_28 = (sam.PORT.PINCFG1_28 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 62: // PB30
sam.PORT.PINCFG1_28 = (sam.PORT.PINCFG1_28 &^ (0xff << 8)) | (sam.RegValue(val) << 8)
case 62: // PB30
sam.PORT.PINCFG1_28 = (sam.PORT.PINCFG1_28 &^ (0xff << 16)) | (sam.RegValue(val) << 16)
case 63: // PB31
sam.PORT.PINCFG1_28 = (sam.PORT.PINCFG1_28 &^ 0xff) | (sam.RegValue(val))
sam.PORT.PINCFG1_28 = (sam.PORT.PINCFG1_28 &^ (0xff << 24)) | (sam.RegValue(val) << 24)
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
// +build nrf stm32f103xx
// +build nrf stm32f103xx atsamd21g18a
package machine
+3 -3
View File
@@ -39,8 +39,8 @@ func interfaceTypeAssert(ok bool) {
// See compiler/interface-lowering.go for details.
type interfaceMethodInfo struct {
signature *uint8 // external *i8 with a name identifying the Go function signature
funcptr *uint8 // bitcast from the actual function pointer
signature *uint8 // external *i8 with a name identifying the Go function signature
funcptr uintptr // bitcast from the actual function pointer
}
// Pseudo function call used while putting a concrete value in an interface,
@@ -59,4 +59,4 @@ func interfaceImplements(typecode uintptr, interfaceMethodSet **uint8) bool
// Pseudo function that returns a function pointer to the method to call.
// See the interface lowering pass for how this is lowered to a real call.
func interfaceMethod(typecode uintptr, interfaceMethodSet **uint8, signature *uint8) *uint8
func interfaceMethod(typecode uintptr, interfaceMethodSet **uint8, signature *uint8) uintptr
+18 -25
View File
@@ -27,38 +27,31 @@ func _recover() interface{} {
return nil
}
// Check for bounds in *ssa.Index, *ssa.IndexAddr and *ssa.Lookup.
func lookupBoundsCheck(length uintptr, index int) {
if index < 0 || index >= int(length) {
runtimePanic("index out of range")
}
// Panic when trying to dereference a nil pointer.
func nilpanic() {
runtimePanic("nil pointer dereference")
}
// Check for bounds in *ssa.Index, *ssa.IndexAddr and *ssa.Lookup.
// Supports 64-bit indexes.
func lookupBoundsCheckLong(length uintptr, index int64) {
if index < 0 || index >= int64(length) {
runtimePanic("index out of range")
}
// Panic when trying to acces an array or slice out of bounds.
func lookuppanic() {
runtimePanic("index out of range")
}
// Check for bounds in *ssa.Slice.
func sliceBoundsCheck(capacity, low, high uintptr) {
if !(0 <= low && low <= high && high <= capacity) {
runtimePanic("slice out of range")
}
}
// Check for bounds in *ssa.Slice. Supports 64-bit indexes.
func sliceBoundsCheck64(capacity uintptr, low, high uint64) {
if !(0 <= low && low <= high && high <= uint64(capacity)) {
runtimePanic("slice out of range")
}
// Panic when trying to slice a slice out of bounds.
func slicepanic() {
runtimePanic("slice out of range")
}
// Check for bounds in *ssa.MakeSlice.
func sliceBoundsCheckMake(length, capacity uint) {
if !(0 <= length && length <= capacity) {
func sliceBoundsCheckMake(length, capacity uintptr, max uintptr) {
if length > capacity || capacity > max {
runtimePanic("slice size out of range")
}
}
// Check for bounds in *ssa.MakeSlice. Supports 64-bit indexes.
func sliceBoundsCheckMake64(length, capacity uint64, max uintptr) {
if length > capacity || capacity > uint64(max) {
runtimePanic("slice size out of range")
}
}
+41 -17
View File
@@ -22,9 +22,9 @@ func main() {
func init() {
initClocks()
initRTC()
initUARTClock()
initI2CClock()
initSERCOMClocks()
initUSBClock()
initADCClock()
// connect to USB CDC interface
machine.UART0.Configure(machine.UARTConfig{})
@@ -293,7 +293,7 @@ func handleRTC() {
timerWakeup = true
}
func initUARTClock() {
func initSERCOMClocks() {
// Turn on clock to SERCOM0 for UART0
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM0_
@@ -306,31 +306,44 @@ func initUARTClock() {
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
// Turn on clock to SERCOM1 for UART1
// Turn on clock to SERCOM1
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM1_
// Use GCLK0 for SERCOM1 aka UART1
// GCLK_CLKCTRL_ID( clockId ) | // Generic Clock 0 (SERCOMx)
// GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source
// GCLK_CLKCTRL_CLKEN ;
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM1_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
}
func initI2CClock() {
// Turn on clock to SERCOM3 for I2C0
// Turn on clock to SERCOM2
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM2_
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM2_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
// Turn on clock to SERCOM3
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM3_
// Use GCLK0 for SERCOM3 aka I2C0
// GCLK_CLKCTRL_ID( clockId ) | // Generic Clock 0 (SERCOMx)
// GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source
// GCLK_CLKCTRL_CLKEN ;
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM3_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
// Turn on clock to SERCOM4
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM4_
// Use GCLK0 for SERCOM4
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM4_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
// Turn on clock to SERCOM5
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM5_
// Use GCLK0 for SERCOM5
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM5_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
}
func initUSBClock() {
@@ -343,3 +356,14 @@ func initUSBClock() {
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
}
func initADCClock() {
// Turn on clock for ADC
sam.PM.APBCMASK |= sam.PM_APBCMASK_ADC_
// Put Generic Clock Generator 0 as source for Generic Clock Multiplexer for ADC.
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_ADC << sam.GCLK_CLKCTRL_ID_Pos) |
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
sam.GCLK_CLKCTRL_CLKEN)
waitForSync()
}
+2 -2
View File
@@ -124,8 +124,8 @@ func ticks() timeUnit {
// convert RTC counter from seconds to microseconds
timerCounter := uint64(stm32.RTC.CNTH<<16|stm32.RTC.CNTL) * 1000 * 1000
// add the fractional part of current time using DIV registers
timerCounter += (uint64(stm32.RTC.DIVH<<16|stm32.RTC.DIVL) / 1024 * 32 * 32) * 1000 * 1000
// add the fractional part of current time using DIV register
timerCounter += uint64(0x8000-stm32.RTC.DIVL) * 31
// change since last measurement
offset := (timerCounter - timerLastCounter)
+6 -10
View File
@@ -31,7 +31,6 @@ type TargetSpec struct {
CFlags []string `json:"cflags"`
LDFlags []string `json:"ldflags"`
ExtraFiles []string `json:"extra-files"`
Objcopy string `json:"objcopy"`
Emulator []string `json:"emulator"`
Flasher string `json:"flash"`
OCDDaemon []string `json:"ocd-daemon"`
@@ -72,9 +71,6 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
spec.CFlags = append(spec.CFlags, spec2.CFlags...)
spec.LDFlags = append(spec.LDFlags, spec2.LDFlags...)
spec.ExtraFiles = append(spec.ExtraFiles, spec2.ExtraFiles...)
if spec2.Objcopy != "" {
spec.Objcopy = spec2.Objcopy
}
if len(spec2.Emulator) != 0 {
spec.Emulator = spec2.Emulator
}
@@ -162,6 +158,9 @@ func LoadTarget(target string) (*TargetSpec, error) {
llvmarch = goarch
}
target = llvmarch + "--" + llvmos
if goarch == "arm" {
target += "-gnueabihf"
}
return defaultTarget(goos, goarch, target)
}
@@ -214,7 +213,6 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
BuildTags: []string{goos, goarch},
Compiler: commands["clang"],
Linker: "cc",
Objcopy: "objcopy",
GDB: "gdb",
GDBCmds: []string{"run"},
}
@@ -226,14 +224,12 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
if goarch != runtime.GOARCH {
// Some educated guesses as to how to invoke helper programs.
if goarch == "arm" && goos == "linux" {
spec.Linker = "arm-linux-gnueabi-gcc"
spec.Objcopy = "arm-linux-gnueabi-objcopy"
spec.GDB = "arm-linux-gnueabi-gdb"
spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabi"}
spec.Linker = "arm-linux-gnueabihf-gcc"
spec.GDB = "arm-linux-gnueabihf-gdb"
spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabihf"}
}
if goarch == "arm64" && goos == "linux" {
spec.Linker = "aarch64-linux-gnu-gcc"
spec.Objcopy = "aarch64-linux-gnu-objcopy"
spec.GDB = "aarch64-linux-gnu-gdb"
spec.Emulator = []string{"qemu-aarch64", "-L", "/usr/aarch64-linux-gnu"}
}
-1
View File
@@ -4,7 +4,6 @@
"goarch": "wasm",
"compiler": "avr-gcc",
"linker": "avr-gcc",
"objcopy": "avr-objcopy",
"ldflags": [
"-T", "targets/avr.ld",
"-Wl,--gc-sections"
-1
View File
@@ -16,6 +16,5 @@
"ldflags": [
"--gc-sections"
],
"objcopy": "arm-none-eabi-objcopy",
"gdb": "arm-none-eabi-gdb"
}
+1 -1
View File
@@ -12,5 +12,5 @@
"ldflags": [
"-allow-undefined"
],
"emulator": ["cwa"]
"emulator": ["node", "targets/wasm_exec.js"]
}
+2 -43
View File
@@ -73,13 +73,6 @@
global.Go = class {
constructor() {
this.argv = ["js"];
this.env = {};
this.exit = (code) => {
if (code !== 0) {
console.warn("exit code:", code);
}
};
this._callbackTimeouts = new Map();
this._nextCallbackTimeoutID = 1;
@@ -342,36 +335,6 @@
const mem = new DataView(this._inst.exports.memory.buffer)
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
let offset = 4096;
const strPtr = (str) => {
let ptr = offset;
new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0"));
offset += str.length + (8 - (str.length % 8));
return ptr;
};
const argc = this.argv.length;
const argvPtrs = [];
this.argv.forEach((arg) => {
argvPtrs.push(strPtr(arg));
});
const keys = Object.keys(this.env).sort();
argvPtrs.push(keys.length);
keys.forEach((key) => {
argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
});
const argv = offset;
argvPtrs.forEach((ptr) => {
mem.setUint32(offset, ptr, true);
mem.setUint32(offset + 4, 0, true);
offset += 8;
});
while (true) {
const callbackPromise = new Promise((resolve) => {
this._resolveCallbackPromise = () => {
@@ -381,7 +344,7 @@
setTimeout(resolve, 0); // make sure it is asynchronous
};
});
this._inst.exports.cwa_main(argc, argv);
this._inst.exports.cwa_main();
if (this.exited) {
break;
}
@@ -413,21 +376,17 @@
}
if (isNodeJS) {
if (process.argv.length < 3) {
if (process.argv.length != 3) {
process.stderr.write("usage: go_js_wasm_exec [wasm binary] [arguments]\n");
process.exit(1);
}
const go = new Go();
go.argv = process.argv.slice(2);
go.env = process.env;
go.exit = process.exit;
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
process.on("exit", (code) => { // Node.js exits if no callback is pending
if (code === 0 && !go.exited) {
// deadlock, make Go print error and stack traces
go._callbackShutdown = true;
go._inst.exports.run();
}
});
return go.run(result.instance);
+26 -11
View File
@@ -13,17 +13,17 @@ func main() {
println("sum foo:", sum(foo))
// creating a slice with uncommon len, cap types
assert(len(make([]int, int(2), int(3))) == 2)
assert(len(make([]int, int8(2), int8(3))) == 2)
assert(len(make([]int, int16(2), int16(3))) == 2)
assert(len(make([]int, int32(2), int32(3))) == 2)
assert(len(make([]int, int64(2), int64(3))) == 2)
assert(len(make([]int, uint(2), uint(3))) == 2)
assert(len(make([]int, uint8(2), uint8(3))) == 2)
assert(len(make([]int, uint16(2), uint16(3))) == 2)
assert(len(make([]int, uint32(2), uint32(3))) == 2)
assert(len(make([]int, uint64(2), uint64(3))) == 2)
assert(len(make([]int, uintptr(2), uintptr(3))) == 2)
assert(len(make([]int, makeInt(2), makeInt(3))) == 2)
assert(len(make([]int, makeInt8(2), makeInt8(3))) == 2)
assert(len(make([]int, makeInt16(2), makeInt16(3))) == 2)
assert(len(make([]int, makeInt32(2), makeInt32(3))) == 2)
assert(len(make([]int, makeInt64(2), makeInt64(3))) == 2)
assert(len(make([]int, makeUint(2), makeUint(3))) == 2)
assert(len(make([]int, makeUint8(2), makeUint8(3))) == 2)
assert(len(make([]int, makeUint16(2), makeUint16(3))) == 2)
assert(len(make([]int, makeUint32(2), makeUint32(3))) == 2)
assert(len(make([]int, makeUint64(2), makeUint64(3))) == 2)
assert(len(make([]int, makeUintptr(2), makeUintptr(3))) == 2)
// indexing into a slice with uncommon index types
assert(foo[int(2)] == 4)
@@ -120,3 +120,18 @@ func assert(ok bool) {
panic("assert failed")
}
}
// Helper functions used to hide const values from the compiler during IR
// construction.
func makeInt(x int) int { return x }
func makeInt8(x int8) int8 { return x }
func makeInt16(x int16) int16 { return x }
func makeInt32(x int32) int32 { return x }
func makeInt64(x int64) int64 { return x }
func makeUint(x uint) uint { return x }
func makeUint8(x uint8) uint8 { return x }
func makeUint16(x uint16) uint16 { return x }
func makeUint32(x uint32) uint32 { return x }
func makeUint64(x uint64) uint64 { return x }
func makeUintptr(x uintptr) uintptr { return x }
+130
View File
@@ -0,0 +1,130 @@
// Converts firmware files from BIN to UF2 format before flashing.
//
// For more information about the UF2 firmware file format, please see:
// https://github.com/Microsoft/uf2
//
//
package main
import (
"bytes"
"encoding/binary"
"io/ioutil"
)
// ConvertELFFileToUF2File converts an ELF file to a UF2 file.
func ConvertELFFileToUF2File(infile, outfile string) error {
// Read the .text segment.
_, data, err := ExtractTextSegment(infile)
if err != nil {
return err
}
output, _ := ConvertBinToUF2(data)
return ioutil.WriteFile(outfile, output, 0644)
}
// ConvertBinToUF2 converts the binary bytes in input to UF2 formatted data.
func ConvertBinToUF2(input []byte) ([]byte, int) {
blocks := split(input, 256)
output := make([]byte, 0)
bl := NewUF2Block()
bl.SetNumBlocks(len(blocks))
for i := 0; i < len(blocks); i++ {
bl.SetBlockNo(i)
bl.SetData(blocks[i])
output = append(output, bl.Bytes()...)
bl.IncrementAddress(bl.payloadSize)
}
return output, len(blocks)
}
const (
uf2MagicStart0 = 0x0A324655 // "UF2\n"
uf2MagicStart1 = 0x9E5D5157 // Randomly selected
uf2MagicEnd = 0x0AB16F30 // Ditto
uf2StartAddress = 0x2000
)
// UF2Block is the structure used for each UF2 code block sent to device.
type UF2Block struct {
magicStart0 uint32
magicStart1 uint32
flags uint32
targetAddr uint32
payloadSize uint32
blockNo uint32
numBlocks uint32
familyID uint32
data []uint8
magicEnd uint32
}
// NewUF2Block returns a new UF2Block struct that has been correctly populated
func NewUF2Block() *UF2Block {
return &UF2Block{magicStart0: uf2MagicStart0,
magicStart1: uf2MagicStart1,
magicEnd: uf2MagicEnd,
targetAddr: uf2StartAddress,
flags: 0x0,
familyID: 0x0,
payloadSize: 256,
data: make([]byte, 476),
}
}
// Bytes converts the UF2Block to a slice of bytes that can be written to file.
func (b *UF2Block) Bytes() []byte {
buf := bytes.NewBuffer(make([]byte, 0, 512))
binary.Write(buf, binary.LittleEndian, b.magicStart0)
binary.Write(buf, binary.LittleEndian, b.magicStart1)
binary.Write(buf, binary.LittleEndian, b.flags)
binary.Write(buf, binary.LittleEndian, b.targetAddr)
binary.Write(buf, binary.LittleEndian, b.payloadSize)
binary.Write(buf, binary.LittleEndian, b.blockNo)
binary.Write(buf, binary.LittleEndian, b.numBlocks)
binary.Write(buf, binary.LittleEndian, b.familyID)
binary.Write(buf, binary.LittleEndian, b.data)
binary.Write(buf, binary.LittleEndian, b.magicEnd)
return buf.Bytes()
}
// IncrementAddress moves the target address pointer forward by count bytes.
func (b *UF2Block) IncrementAddress(count uint32) {
b.targetAddr += b.payloadSize
}
// SetData sets the data to be used for the current block.
func (b *UF2Block) SetData(d []byte) {
b.data = make([]byte, 476)
copy(b.data[:], d)
}
// SetBlockNo sets the current block number to be used.
func (b *UF2Block) SetBlockNo(bn int) {
b.blockNo = uint32(bn)
}
// SetNumBlocks sets the total number of blocks for this UF2 file.
func (b *UF2Block) SetNumBlocks(total int) {
b.numBlocks = uint32(total)
}
// split splits a slice of bytes into a slice of byte slices of a specific size limit.
func split(input []byte, limit int) [][]byte {
var block []byte
output := make([][]byte, 0, len(input)/limit+1)
for len(input) >= limit {
block, input = input[:limit], input[limit:]
output = append(output, block)
}
if len(input) > 0 {
output = append(output, input[:len(input)])
}
return output
}
+1 -1
View File
@@ -2,4 +2,4 @@ package main
// version of this package.
// Update this value before release of new version of software.
const version = "0.3.0"
const version = "0.4.0"