mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-01 17:47:46 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c68da89af | |||
| 4424fe087d | |||
| 34939ab422 | |||
| c56b2a45fa | |||
| b1c70d85f7 | |||
| 714d98354c | |||
| 6e8df2fc40 | |||
| 902f40867f | |||
| 5438f16fcb | |||
| 7f027ddd33 | |||
| acaf096586 | |||
| 942d4903ce | |||
| 0b212cf2f6 | |||
| 2d5bc836f5 | |||
| 856e5fa179 | |||
| 07733ca056 | |||
| 92d9b780b5 | |||
| da345e8723 | |||
| fab38a0749 | |||
| 0a3dbbd1cb | |||
| 4c29f0fdb6 |
+14
-3
@@ -4,6 +4,9 @@ matrix:
|
||||
include:
|
||||
- dist: xenial
|
||||
go: "1.11"
|
||||
- os: osx
|
||||
go: "1.11"
|
||||
env: PATH="/usr/local/opt/llvm/bin:$PATH"
|
||||
|
||||
addons:
|
||||
apt:
|
||||
@@ -24,8 +27,16 @@ addons:
|
||||
- qemu-user
|
||||
- gcc-avr
|
||||
- avr-libc
|
||||
homebrew:
|
||||
update: true
|
||||
taps: ArmMbed/homebrew-formulae
|
||||
packages:
|
||||
- llvm@7
|
||||
- qemu
|
||||
- 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
|
||||
|
||||
@@ -35,14 +46,14 @@ script:
|
||||
- make gen-device
|
||||
- tinygo build -size short -o blinky1.nrf.elf -target=pca10040 examples/blinky1
|
||||
- tinygo build -size short -o blinky2.nrf.elf -target=pca10040 examples/blinky2
|
||||
- tinygo build -size short -o blinky2 examples/blinky2
|
||||
- tinygo build -o blinky2 examples/blinky2 # TODO: re-enable -size flag with MachO support
|
||||
- tinygo build -size short -o test.nrf.elf -target=pca10040 examples/test
|
||||
- tinygo build -size short -o blinky1.nrf51.elf -target=microbit examples/echo
|
||||
- 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
|
||||
- tinygo build -size short -o blinky1.avr.elf -target=arduino examples/blinky1
|
||||
- tinygo build -size short -o blinky1.avr.elf -target=digispark 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
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
0.3.0
|
||||
---
|
||||
- **compiler**
|
||||
- remove old `-initinterp` flag
|
||||
- add support for macOS
|
||||
- **cgo**
|
||||
- add support for bool/float/complex types
|
||||
- **standard library**
|
||||
- `device/arm`: add support to disable/enable hardware interrupts
|
||||
- `machine`: add CPU frequency for nrf-based boards
|
||||
- `syscall`: add support for darwin/amd64
|
||||
- **targets**
|
||||
- `circuitplay_express`: add support for this board
|
||||
- `microbit`: add regular pin constants
|
||||
- `samd21`: fix time function for goroutine support
|
||||
- `samd21`: add support for USB-CDC (serial over USB)
|
||||
- `samd21`: add support for pins in port B
|
||||
- `samd21`: add support for pullup and pulldown pins
|
||||
- `wasm`: add support for Safari in example
|
||||
|
||||
|
||||
0.2.0
|
||||
---
|
||||
- **command line**
|
||||
|
||||
Generated
+3
-3
@@ -11,15 +11,15 @@
|
||||
"go/types/typeutil",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "40960b6deb8ecdb8bcde6a8f44722731939b8ddc"
|
||||
revision = "3744606dbb67b99c60d3f11cb10bd3f9e6dad472"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:3611159788efdd4e0cfae18b6ebcccbad25a2815968b0e4323b42647d201031a"
|
||||
digest = "1:a6a25fd8906c74978f1ed811bc9fd3422da8093be863b458874b02a782b6ae3e"
|
||||
name = "tinygo.org/x/go-llvm"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "f420620d1a0f54417a5712260153fe861780d030"
|
||||
revision = "d5f730401f5069618b275a5241c6417eb0c38a65"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
|
||||
@@ -108,7 +108,7 @@ gen-device-stm32:
|
||||
go fmt ./src/device/stm32
|
||||
|
||||
# Build the Go compiler.
|
||||
tinygo:
|
||||
build/tinygo:
|
||||
@mkdir -p build
|
||||
go build -o build/tinygo .
|
||||
|
||||
|
||||
+45
-387
@@ -30,19 +30,18 @@ func init() {
|
||||
|
||||
// Configure the compiler.
|
||||
type Config struct {
|
||||
Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default)
|
||||
CPU string // LLVM CPU name, e.g. atmega328p (empty string means default)
|
||||
GOOS string //
|
||||
GOARCH string //
|
||||
GC string // garbage collection strategy
|
||||
CFlags []string // cflags to pass to cgo
|
||||
LDFlags []string // ldflags to pass to cgo
|
||||
DumpSSA bool // dump Go SSA, for compiler debugging
|
||||
Debug bool // add debug symbols for gdb
|
||||
RootDir string // GOROOT for TinyGo
|
||||
GOPATH string // GOPATH, like `go env GOPATH`
|
||||
BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH})
|
||||
InitInterp bool // use new init interpretation, meaning the old one is disabled
|
||||
Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default)
|
||||
CPU string // LLVM CPU name, e.g. atmega328p (empty string means default)
|
||||
GOOS string //
|
||||
GOARCH string //
|
||||
GC string // garbage collection strategy
|
||||
CFlags []string // cflags to pass to cgo
|
||||
LDFlags []string // ldflags to pass to cgo
|
||||
DumpSSA bool // dump Go SSA, for compiler debugging
|
||||
Debug bool // add debug symbols for gdb
|
||||
RootDir string // GOROOT for TinyGo
|
||||
GOPATH string // GOPATH, like `go env GOPATH`
|
||||
BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH})
|
||||
}
|
||||
|
||||
type Compiler struct {
|
||||
@@ -250,8 +249,7 @@ func (c *Compiler) Compile(mainPath string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Declare all globals. These will get an initializer when parsing "package
|
||||
// initializer" functions.
|
||||
// Declare all globals.
|
||||
for _, g := range c.ir.Globals {
|
||||
typ := g.Type().(*types.Pointer).Elem()
|
||||
llvmType, err := c.getLLVMType(typ)
|
||||
@@ -282,54 +280,18 @@ func (c *Compiler) Compile(mainPath string) error {
|
||||
frames = append(frames, frame)
|
||||
}
|
||||
|
||||
// Find and interpret package initializers.
|
||||
// Add definitions to declarations.
|
||||
for _, frame := range frames {
|
||||
if frame.fn.Synthetic == "package initializer" {
|
||||
c.initFuncs = append(c.initFuncs, frame.fn.LLVMFn)
|
||||
// Try to interpret as much as possible of the init() function.
|
||||
// Whenever it hits an instruction that it doesn't understand, it
|
||||
// bails out and leaves the rest to the compiler (so initialization
|
||||
// continues at runtime).
|
||||
// This should only happen when it hits a function call or the end
|
||||
// of the block, ideally.
|
||||
if !c.InitInterp {
|
||||
err := c.ir.Interpret(frame.fn.Blocks[0], c.DumpSSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = c.parseFunc(frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set values for globals (after package initializer has been interpreted).
|
||||
for _, g := range c.ir.Globals {
|
||||
if g.Initializer() == nil {
|
||||
continue
|
||||
}
|
||||
err := c.parseGlobalInitializer(g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Add definitions to declarations.
|
||||
for _, frame := range frames {
|
||||
if frame.fn.CName() != "" {
|
||||
continue
|
||||
}
|
||||
if frame.fn.Blocks == nil {
|
||||
continue // external function
|
||||
}
|
||||
var err error
|
||||
if frame.fn.Synthetic == "package initializer" {
|
||||
continue // already done
|
||||
} else {
|
||||
err = c.parseFunc(frame)
|
||||
}
|
||||
err := c.parseFunc(frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -418,9 +380,9 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) {
|
||||
case types.Float64:
|
||||
return c.ctx.DoubleType(), nil
|
||||
case types.Complex64:
|
||||
return llvm.VectorType(c.ctx.FloatType(), 2), nil
|
||||
return c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false), nil
|
||||
case types.Complex128:
|
||||
return llvm.VectorType(c.ctx.DoubleType(), 2), nil
|
||||
return c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false), nil
|
||||
case types.String, types.UntypedString:
|
||||
return c.mod.GetTypeByName("runtime._string"), nil
|
||||
case types.Uintptr:
|
||||
@@ -567,16 +529,6 @@ func (c *Compiler) getZeroValue(typ llvm.Type) (llvm.Value, error) {
|
||||
} else {
|
||||
return c.ctx.ConstStruct(vals, false), nil
|
||||
}
|
||||
case llvm.VectorTypeKind:
|
||||
zero, err := c.getZeroValue(typ.ElementType())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
vals := make([]llvm.Value, typ.VectorSize())
|
||||
for i := range vals {
|
||||
vals[i] = zero
|
||||
}
|
||||
return llvm.ConstVector(vals, false), nil
|
||||
default:
|
||||
return llvm.Value{}, errors.New("todo: LLVM zero initializer: " + typ.String())
|
||||
}
|
||||
@@ -735,282 +687,6 @@ func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix,
|
||||
return difunc, nil
|
||||
}
|
||||
|
||||
// Create a new global hashmap bucket, for map initialization.
|
||||
func (c *Compiler) initMapNewBucket(prefix string, mapType *types.Map) (llvm.Value, uint64, uint64, error) {
|
||||
llvmKeyType, err := c.getLLVMType(mapType.Key().Underlying())
|
||||
if err != nil {
|
||||
return llvm.Value{}, 0, 0, err
|
||||
}
|
||||
llvmValueType, err := c.getLLVMType(mapType.Elem().Underlying())
|
||||
if err != nil {
|
||||
return llvm.Value{}, 0, 0, err
|
||||
}
|
||||
keySize := c.targetData.TypeAllocSize(llvmKeyType)
|
||||
valueSize := c.targetData.TypeAllocSize(llvmValueType)
|
||||
bucketType := c.ctx.StructType([]llvm.Type{
|
||||
llvm.ArrayType(c.ctx.Int8Type(), 8), // tophash
|
||||
c.i8ptrType, // next bucket
|
||||
llvm.ArrayType(llvmKeyType, 8), // key type
|
||||
llvm.ArrayType(llvmValueType, 8), // value type
|
||||
}, false)
|
||||
bucketValue, err := c.getZeroValue(bucketType)
|
||||
if err != nil {
|
||||
return llvm.Value{}, 0, 0, err
|
||||
}
|
||||
bucket := llvm.AddGlobal(c.mod, bucketType, prefix+"$hashmap$bucket")
|
||||
bucket.SetInitializer(bucketValue)
|
||||
bucket.SetLinkage(llvm.InternalLinkage)
|
||||
return bucket, keySize, valueSize, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) parseGlobalInitializer(g *ir.Global) error {
|
||||
if g.IsExtern() {
|
||||
return nil
|
||||
}
|
||||
llvmValue, err := c.getInterpretedValue(g.LinkName(), g.Initializer())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.LLVMGlobal.SetInitializer(llvmValue)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Turn a computed Value type (ConstValue, ArrayValue, etc.) into a LLVM value.
|
||||
// This is used to set the initializer of globals after they have been
|
||||
// calculated by the package initializer interpreter.
|
||||
func (c *Compiler) getInterpretedValue(prefix string, value ir.Value) (llvm.Value, error) {
|
||||
switch value := value.(type) {
|
||||
case *ir.ArrayValue:
|
||||
vals := make([]llvm.Value, len(value.Elems))
|
||||
for i, elem := range value.Elems {
|
||||
val, err := c.getInterpretedValue(prefix+"$arrayval", elem)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
vals[i] = val
|
||||
}
|
||||
subTyp, err := c.getLLVMType(value.ElemType)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return llvm.ConstArray(subTyp, vals), nil
|
||||
|
||||
case *ir.ConstValue:
|
||||
return c.parseConst(prefix, value.Expr)
|
||||
|
||||
case *ir.FunctionValue:
|
||||
if value.Elem == nil {
|
||||
llvmType, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return c.getZeroValue(llvmType)
|
||||
}
|
||||
fn := c.ir.GetFunction(value.Elem)
|
||||
ptr := fn.LLVMFn
|
||||
// Create closure value: {context, function pointer}
|
||||
ptr = c.ctx.ConstStruct([]llvm.Value{llvm.ConstPointerNull(c.i8ptrType), ptr}, false)
|
||||
return ptr, nil
|
||||
|
||||
case *ir.GlobalValue:
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
ptr := llvm.ConstInBoundsGEP(value.Global.LLVMGlobal, []llvm.Value{zero})
|
||||
return ptr, nil
|
||||
|
||||
case *ir.MapValue:
|
||||
// Create initial bucket.
|
||||
firstBucketGlobal, keySize, valueSize, err := c.initMapNewBucket(prefix, value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
|
||||
// Insert each key/value pair in the hashmap.
|
||||
bucketGlobal := firstBucketGlobal
|
||||
for i, key := range value.Keys {
|
||||
llvmKey, err := c.getInterpretedValue(prefix, key)
|
||||
if err != nil {
|
||||
return llvm.Value{}, nil
|
||||
}
|
||||
llvmValue, err := c.getInterpretedValue(prefix, value.Values[i])
|
||||
if err != nil {
|
||||
return llvm.Value{}, nil
|
||||
}
|
||||
|
||||
constVal := key.(*ir.ConstValue).Expr
|
||||
var keyBuf []byte
|
||||
switch constVal.Type().Underlying().(*types.Basic).Kind() {
|
||||
case types.String, types.UntypedString:
|
||||
keyBuf = []byte(constant.StringVal(constVal.Value))
|
||||
case types.Int:
|
||||
keyBuf = make([]byte, c.targetData.TypeAllocSize(c.intType))
|
||||
n, _ := constant.Uint64Val(constVal.Value)
|
||||
for i := range keyBuf {
|
||||
keyBuf[i] = byte(n)
|
||||
n >>= 8
|
||||
}
|
||||
default:
|
||||
return llvm.Value{}, errors.New("todo: init: map key not implemented: " + constVal.Type().Underlying().String())
|
||||
}
|
||||
hash := hashmapHash(keyBuf)
|
||||
|
||||
if i%8 == 0 && i != 0 {
|
||||
// Bucket is full, create a new one.
|
||||
newBucketGlobal, _, _, err := c.initMapNewBucket(prefix, value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
newBucketPtr := llvm.ConstInBoundsGEP(newBucketGlobal, []llvm.Value{zero})
|
||||
newBucketPtrCast := llvm.ConstBitCast(newBucketPtr, c.i8ptrType)
|
||||
// insert pointer into old bucket
|
||||
bucket := bucketGlobal.Initializer()
|
||||
bucket = llvm.ConstInsertValue(bucket, newBucketPtrCast, []uint32{1})
|
||||
bucketGlobal.SetInitializer(bucket)
|
||||
// switch to next bucket
|
||||
bucketGlobal = newBucketGlobal
|
||||
}
|
||||
|
||||
tophashValue := llvm.ConstInt(c.ctx.Int8Type(), uint64(hashmapTopHash(hash)), false)
|
||||
bucket := bucketGlobal.Initializer()
|
||||
bucket = llvm.ConstInsertValue(bucket, tophashValue, []uint32{0, uint32(i % 8)})
|
||||
bucket = llvm.ConstInsertValue(bucket, llvmKey, []uint32{2, uint32(i % 8)})
|
||||
bucket = llvm.ConstInsertValue(bucket, llvmValue, []uint32{3, uint32(i % 8)})
|
||||
bucketGlobal.SetInitializer(bucket)
|
||||
}
|
||||
|
||||
// Create the hashmap itself.
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
bucketPtr := llvm.ConstInBoundsGEP(firstBucketGlobal, []llvm.Value{zero})
|
||||
hashmapType := c.mod.GetTypeByName("runtime.hashmap")
|
||||
hashmap := llvm.ConstNamedStruct(hashmapType, []llvm.Value{
|
||||
llvm.ConstPointerNull(llvm.PointerType(hashmapType, 0)), // next
|
||||
llvm.ConstBitCast(bucketPtr, c.i8ptrType), // buckets
|
||||
llvm.ConstInt(c.uintptrType, uint64(len(value.Keys)), false), // count
|
||||
llvm.ConstInt(c.ctx.Int8Type(), keySize, false), // keySize
|
||||
llvm.ConstInt(c.ctx.Int8Type(), valueSize, false), // valueSize
|
||||
llvm.ConstInt(c.ctx.Int8Type(), 0, false), // bucketBits
|
||||
})
|
||||
|
||||
// Create a pointer to this hashmap.
|
||||
hashmapPtr := llvm.AddGlobal(c.mod, hashmap.Type(), prefix+"$hashmap")
|
||||
hashmapPtr.SetInitializer(hashmap)
|
||||
hashmapPtr.SetLinkage(llvm.InternalLinkage)
|
||||
return llvm.ConstInBoundsGEP(hashmapPtr, []llvm.Value{zero}), nil
|
||||
|
||||
case *ir.PointerBitCastValue:
|
||||
elem, err := c.getInterpretedValue(prefix, value.Elem)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
llvmType, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return llvm.ConstBitCast(elem, llvmType), nil
|
||||
|
||||
case *ir.PointerToUintptrValue:
|
||||
elem, err := c.getInterpretedValue(prefix, value.Elem)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return llvm.ConstPtrToInt(elem, c.uintptrType), nil
|
||||
|
||||
case *ir.PointerValue:
|
||||
if value.Elem == nil {
|
||||
typ, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return llvm.ConstPointerNull(typ), nil
|
||||
}
|
||||
elem, err := c.getInterpretedValue(prefix, *value.Elem)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
|
||||
obj := llvm.AddGlobal(c.mod, elem.Type(), prefix+"$ptrvalue")
|
||||
obj.SetInitializer(elem)
|
||||
obj.SetLinkage(llvm.InternalLinkage)
|
||||
elem = obj
|
||||
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
ptr := llvm.ConstInBoundsGEP(elem, []llvm.Value{zero})
|
||||
return ptr, nil
|
||||
|
||||
case *ir.SliceValue:
|
||||
var globalPtr llvm.Value
|
||||
var arrayLength uint64
|
||||
if value.Array == nil {
|
||||
arrayType, err := c.getLLVMType(value.Type.Elem())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
globalPtr = llvm.ConstPointerNull(llvm.PointerType(arrayType, 0))
|
||||
} else {
|
||||
// make array
|
||||
array, err := c.getInterpretedValue(prefix, value.Array)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
// make global from array
|
||||
global := llvm.AddGlobal(c.mod, array.Type(), prefix+"$array")
|
||||
global.SetInitializer(array)
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
|
||||
// get pointer to global
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
globalPtr = c.builder.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
|
||||
|
||||
arrayLength = uint64(len(value.Array.Elems))
|
||||
}
|
||||
|
||||
// make slice
|
||||
sliceTyp, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
llvmLen := llvm.ConstInt(c.uintptrType, arrayLength, false)
|
||||
slice := llvm.ConstNamedStruct(sliceTyp, []llvm.Value{
|
||||
globalPtr, // ptr
|
||||
llvmLen, // len
|
||||
llvmLen, // cap
|
||||
})
|
||||
return slice, nil
|
||||
|
||||
case *ir.StructValue:
|
||||
fields := make([]llvm.Value, len(value.Fields))
|
||||
for i, elem := range value.Fields {
|
||||
field, err := c.getInterpretedValue(prefix, elem)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
fields[i] = field
|
||||
}
|
||||
switch value.Type.(type) {
|
||||
case *types.Named:
|
||||
llvmType, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return llvm.ConstNamedStruct(llvmType, fields), nil
|
||||
case *types.Struct:
|
||||
return c.ctx.ConstStruct(fields, false), nil
|
||||
default:
|
||||
return llvm.Value{}, errors.New("init: unknown struct type: " + value.Type.String())
|
||||
}
|
||||
|
||||
case *ir.ZeroBasicValue:
|
||||
llvmType, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return c.getZeroValue(llvmType)
|
||||
|
||||
default:
|
||||
return llvm.Value{}, errors.New("init: unknown initializer type: " + fmt.Sprintf("%#v", value))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) parseFunc(frame *Frame) error {
|
||||
if c.DumpSSA {
|
||||
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
||||
@@ -1195,10 +871,6 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
||||
switch instr := instr.(type) {
|
||||
case ssa.Value:
|
||||
value, err := c.parseExpr(frame, instr)
|
||||
if err == ir.ErrCGoWrapper {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
return nil
|
||||
}
|
||||
frame.locals[instr] = value
|
||||
return err
|
||||
case *ssa.DebugRef:
|
||||
@@ -1309,10 +981,6 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
||||
return c.emitChanSend(frame, instr)
|
||||
case *ssa.Store:
|
||||
llvmAddr, err := c.parseExpr(frame, instr.Addr)
|
||||
if err == ir.ErrCGoWrapper {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1396,14 +1064,14 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string,
|
||||
var cplx llvm.Value
|
||||
switch t.Kind() {
|
||||
case types.Float32:
|
||||
cplx = llvm.Undef(llvm.VectorType(c.ctx.FloatType(), 2))
|
||||
cplx = llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
||||
case types.Float64:
|
||||
cplx = llvm.Undef(llvm.VectorType(c.ctx.DoubleType(), 2))
|
||||
cplx = llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "unsupported type in complex builtin: "+t.String())
|
||||
}
|
||||
cplx = c.builder.CreateInsertElement(cplx, r, llvm.ConstInt(c.ctx.Int8Type(), 0, false), "")
|
||||
cplx = c.builder.CreateInsertElement(cplx, i, llvm.ConstInt(c.ctx.Int8Type(), 1, false), "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
case "copy":
|
||||
dst, err := c.parseExpr(frame, args[0])
|
||||
@@ -1438,8 +1106,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string,
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
index := llvm.ConstInt(c.ctx.Int32Type(), 1, false)
|
||||
return c.builder.CreateExtractElement(cplx, index, "imag"), nil
|
||||
return c.builder.CreateExtractValue(cplx, 1, "imag"), nil
|
||||
case "len":
|
||||
value, err := c.parseExpr(frame, args[0])
|
||||
if err != nil {
|
||||
@@ -1528,8 +1195,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string,
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
index := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
return c.builder.CreateExtractElement(cplx, index, "real"), nil
|
||||
return c.builder.CreateExtractValue(cplx, 0, "real"), nil
|
||||
case "recover":
|
||||
return c.createRuntimeCall("_recover", nil, ""), nil
|
||||
case "ssa:wrapnilchk":
|
||||
@@ -1687,7 +1353,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
||||
}
|
||||
|
||||
switch fn.RelString(nil) {
|
||||
case "syscall.Syscall", "syscall.Syscall6":
|
||||
case "syscall.Syscall", "syscall.Syscall6", "syscall.Syscall9":
|
||||
return c.emitSyscall(frame, instr)
|
||||
}
|
||||
|
||||
@@ -1912,10 +1578,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
fn.LLVMFn,
|
||||
}, false), nil
|
||||
case *ssa.Global:
|
||||
if strings.HasPrefix(expr.Name(), "__cgofn__cgo_") || strings.HasPrefix(expr.Name(), "_cgo_") {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
return llvm.Value{}, ir.ErrCGoWrapper
|
||||
}
|
||||
value := c.ir.GetGlobal(expr).LLVMGlobal
|
||||
if value.IsNil() {
|
||||
return llvm.Value{}, c.makeError(expr.Pos(), "global not found: "+c.ir.GetGlobal(expr).LinkName())
|
||||
@@ -2033,7 +1695,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return c.parseMakeInterface(val, expr.X.Type(), "", expr.Pos())
|
||||
return c.parseMakeInterface(val, expr.X.Type(), expr.Pos())
|
||||
case *ssa.MakeMap:
|
||||
mapType := expr.Type().Underlying().(*types.Map)
|
||||
llvmKeyType, err := c.getLLVMType(mapType.Key().Underlying())
|
||||
@@ -2440,12 +2102,10 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
||||
panic("binop on float: " + op.String())
|
||||
}
|
||||
} else if typ.Info()&types.IsComplex != 0 {
|
||||
indexr := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
indexi := llvm.ConstInt(c.ctx.Int32Type(), 1, false)
|
||||
r1 := c.builder.CreateExtractElement(x, indexr, "r1")
|
||||
r2 := c.builder.CreateExtractElement(y, indexr, "r2")
|
||||
i1 := c.builder.CreateExtractElement(x, indexi, "i1")
|
||||
i2 := c.builder.CreateExtractElement(y, indexi, "i2")
|
||||
r1 := c.builder.CreateExtractValue(x, 0, "r1")
|
||||
r2 := c.builder.CreateExtractValue(y, 0, "r2")
|
||||
i1 := c.builder.CreateExtractValue(x, 1, "i1")
|
||||
i2 := c.builder.CreateExtractValue(y, 1, "i2")
|
||||
switch op {
|
||||
case token.EQL: // ==
|
||||
req := c.builder.CreateFCmp(llvm.FloatOEQ, r1, r2, "")
|
||||
@@ -2579,7 +2239,6 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "unknown: binop on struct: "+op.String())
|
||||
}
|
||||
return result, nil
|
||||
case *types.Struct:
|
||||
// Compare each struct field and combine the result. From the spec:
|
||||
// Struct values are comparable if all their fields are comparable.
|
||||
@@ -2608,7 +2267,6 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "unknown: binop on struct: "+op.String())
|
||||
}
|
||||
return result, nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(pos, "todo: binop type: "+typ.String())
|
||||
}
|
||||
@@ -2665,9 +2323,9 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) (llvm.Value, error
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
cplx := llvm.Undef(llvm.VectorType(c.ctx.FloatType(), 2))
|
||||
cplx = c.builder.CreateInsertElement(cplx, r, llvm.ConstInt(c.ctx.Int8Type(), 0, false), "")
|
||||
cplx = c.builder.CreateInsertElement(cplx, i, llvm.ConstInt(c.ctx.Int8Type(), 1, false), "")
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
} else if typ.Kind() == types.Complex128 {
|
||||
r, err := c.parseConst(prefix, ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
|
||||
@@ -2678,9 +2336,9 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) (llvm.Value, error
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
cplx := llvm.Undef(llvm.VectorType(c.ctx.DoubleType(), 2))
|
||||
cplx = c.builder.CreateInsertElement(cplx, r, llvm.ConstInt(c.ctx.Int8Type(), 0, false), "")
|
||||
cplx = c.builder.CreateInsertElement(cplx, i, llvm.ConstInt(c.ctx.Int8Type(), 1, false), "")
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
} else {
|
||||
return llvm.Value{}, errors.New("todo: unknown constant: " + expr.String())
|
||||
@@ -2848,25 +2506,25 @@ func (c *Compiler) parseConvert(typeFrom, typeTo types.Type, value llvm.Value, p
|
||||
|
||||
if typeFrom.Kind() == types.Complex128 && typeTo.Kind() == types.Complex64 {
|
||||
// Conversion from complex128 to complex64.
|
||||
r := c.builder.CreateExtractElement(value, llvm.ConstInt(c.ctx.Int32Type(), 0, false), "real.f64")
|
||||
i := c.builder.CreateExtractElement(value, llvm.ConstInt(c.ctx.Int32Type(), 1, false), "imag.f64")
|
||||
r := c.builder.CreateExtractValue(value, 0, "real.f64")
|
||||
i := c.builder.CreateExtractValue(value, 1, "imag.f64")
|
||||
r = c.builder.CreateFPTrunc(r, c.ctx.FloatType(), "real.f32")
|
||||
i = c.builder.CreateFPTrunc(i, c.ctx.FloatType(), "imag.f32")
|
||||
cplx := llvm.Undef(llvm.VectorType(c.ctx.FloatType(), 2))
|
||||
cplx = c.builder.CreateInsertElement(cplx, r, llvm.ConstInt(c.ctx.Int8Type(), 0, false), "")
|
||||
cplx = c.builder.CreateInsertElement(cplx, i, llvm.ConstInt(c.ctx.Int8Type(), 1, false), "")
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
}
|
||||
|
||||
if typeFrom.Kind() == types.Complex64 && typeTo.Kind() == types.Complex128 {
|
||||
// Conversion from complex64 to complex128.
|
||||
r := c.builder.CreateExtractElement(value, llvm.ConstInt(c.ctx.Int32Type(), 0, false), "real.f32")
|
||||
i := c.builder.CreateExtractElement(value, llvm.ConstInt(c.ctx.Int32Type(), 1, false), "imag.f32")
|
||||
r := c.builder.CreateExtractValue(value, 0, "real.f32")
|
||||
i := c.builder.CreateExtractValue(value, 1, "imag.f32")
|
||||
r = c.builder.CreateFPExt(r, c.ctx.DoubleType(), "real.f64")
|
||||
i = c.builder.CreateFPExt(i, c.ctx.DoubleType(), "imag.f64")
|
||||
cplx := llvm.Undef(llvm.VectorType(c.ctx.DoubleType(), 2))
|
||||
cplx = c.builder.CreateInsertElement(cplx, r, llvm.ConstInt(c.ctx.Int8Type(), 0, false), "")
|
||||
cplx = c.builder.CreateInsertElement(cplx, i, llvm.ConstInt(c.ctx.Int8Type(), 1, false), "")
|
||||
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
|
||||
cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
|
||||
cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
|
||||
return cplx, nil
|
||||
}
|
||||
|
||||
|
||||
+9
-20
@@ -22,28 +22,17 @@ import (
|
||||
// value field.
|
||||
//
|
||||
// An interface value is a {typecode, value} tuple, or {i16, i8*} to be exact.
|
||||
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global string, pos token.Pos) (llvm.Value, error) {
|
||||
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) (llvm.Value, error) {
|
||||
var itfValue llvm.Value
|
||||
size := c.targetData.TypeAllocSize(val.Type())
|
||||
if size > c.targetData.TypeAllocSize(c.i8ptrType) {
|
||||
if global != "" {
|
||||
// Allocate in a global variable.
|
||||
global := llvm.AddGlobal(c.mod, val.Type(), global+"$itfvalue")
|
||||
global.SetInitializer(val)
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
itfValueRaw := llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero})
|
||||
itfValue = llvm.ConstBitCast(itfValueRaw, c.i8ptrType)
|
||||
} else {
|
||||
// Allocate on the heap and put a pointer in the interface.
|
||||
// TODO: escape analysis.
|
||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||
alloc := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "makeinterface.alloc")
|
||||
itfValueCast := c.builder.CreateBitCast(alloc, llvm.PointerType(val.Type(), 0), "makeinterface.cast.value")
|
||||
c.builder.CreateStore(val, itfValueCast)
|
||||
itfValue = c.builder.CreateBitCast(itfValueCast, c.i8ptrType, "makeinterface.cast.i8ptr")
|
||||
}
|
||||
// Allocate on the heap and put a pointer in the interface.
|
||||
// TODO: escape analysis.
|
||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||
alloc := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "makeinterface.alloc")
|
||||
itfValueCast := c.builder.CreateBitCast(alloc, llvm.PointerType(val.Type(), 0), "makeinterface.cast.value")
|
||||
c.builder.CreateStore(val, itfValueCast)
|
||||
itfValue = c.builder.CreateBitCast(itfValueCast, c.i8ptrType, "makeinterface.cast.i8ptr")
|
||||
} else if size == 0 {
|
||||
itfValue = llvm.ConstPointerNull(c.i8ptrType)
|
||||
} else {
|
||||
@@ -53,7 +42,7 @@ func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global str
|
||||
itfValue = c.builder.CreateIntToPtr(val, c.i8ptrType, "makeinterface.cast.int")
|
||||
case llvm.PointerTypeKind:
|
||||
itfValue = c.builder.CreateBitCast(val, c.i8ptrType, "makeinterface.cast.ptr")
|
||||
case llvm.StructTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind, llvm.VectorTypeKind:
|
||||
case llvm.StructTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||
// A bitcast would be useful here, but bitcast doesn't allow
|
||||
// aggregate types. So we'll bitcast it using an alloca.
|
||||
// Hopefully this will get optimized away.
|
||||
|
||||
+55
-18
@@ -17,7 +17,20 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||
num, _ := constant.Uint64Val(call.Args[0].(*ssa.Const).Value)
|
||||
var syscallResult llvm.Value
|
||||
switch {
|
||||
case c.GOARCH == "amd64" && c.GOOS == "linux":
|
||||
case c.GOARCH == "amd64":
|
||||
if c.GOOS == "darwin" {
|
||||
// Darwin adds this magic number to system call numbers:
|
||||
//
|
||||
// > Syscall classes for 64-bit system call entry.
|
||||
// > For 64-bit users, the 32-bit syscall number is partitioned
|
||||
// > with the high-order bits representing the class and low-order
|
||||
// > bits being the syscall number within that class.
|
||||
// > The high-order 32-bits of the 64-bit syscall number are unused.
|
||||
// > All system classes enter the kernel via the syscall instruction.
|
||||
//
|
||||
// Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h
|
||||
num += 0x2000000
|
||||
}
|
||||
// Sources:
|
||||
// https://stackoverflow.com/a/2538212
|
||||
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
|
||||
@@ -34,6 +47,9 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||
"{r10}",
|
||||
"{r8}",
|
||||
"{r9}",
|
||||
"{r11}",
|
||||
"{r12}",
|
||||
"{r13}",
|
||||
}[i]
|
||||
llvmValue, err := c.parseExpr(frame, arg)
|
||||
if err != nil {
|
||||
@@ -119,21 +135,42 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH)
|
||||
}
|
||||
// Return values: r0, r1, err uintptr
|
||||
// Pseudocode:
|
||||
// var err uintptr
|
||||
// if syscallResult < 0 && syscallResult > -4096 {
|
||||
// err = -syscallResult
|
||||
// }
|
||||
// return syscallResult, 0, err
|
||||
zero := llvm.ConstInt(c.uintptrType, 0, false)
|
||||
inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "")
|
||||
inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096
|
||||
hasError := c.builder.CreateAnd(inrange1, inrange2, "")
|
||||
errResult := c.builder.CreateSelect(hasError, c.builder.CreateNot(syscallResult, ""), zero, "syscallError")
|
||||
retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false))
|
||||
retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "")
|
||||
retval = c.builder.CreateInsertValue(retval, zero, 1, "")
|
||||
retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
|
||||
return retval, nil
|
||||
switch c.GOOS {
|
||||
case "linux":
|
||||
// Return values: r0, r1 uintptr, err Errno
|
||||
// Pseudocode:
|
||||
// var err uintptr
|
||||
// if syscallResult < 0 && syscallResult > -4096 {
|
||||
// err = -syscallResult
|
||||
// }
|
||||
// return syscallResult, 0, err
|
||||
zero := llvm.ConstInt(c.uintptrType, 0, false)
|
||||
inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "")
|
||||
inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096
|
||||
hasError := c.builder.CreateAnd(inrange1, inrange2, "")
|
||||
errResult := c.builder.CreateSelect(hasError, c.builder.CreateSub(zero, syscallResult, ""), zero, "syscallError")
|
||||
retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false))
|
||||
retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "")
|
||||
retval = c.builder.CreateInsertValue(retval, zero, 1, "")
|
||||
retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
|
||||
return retval, nil
|
||||
case "darwin":
|
||||
// Return values: r0, r1 uintptr, err Errno
|
||||
// Pseudocode:
|
||||
// var err uintptr
|
||||
// if syscallResult != 0 {
|
||||
// err = syscallResult
|
||||
// }
|
||||
// return syscallResult, 0, err
|
||||
zero := llvm.ConstInt(c.uintptrType, 0, false)
|
||||
hasError := c.builder.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "")
|
||||
errResult := c.builder.CreateSelect(hasError, syscallResult, zero, "syscallError")
|
||||
retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false))
|
||||
retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "")
|
||||
retval = c.builder.CreateInsertValue(retval, zero, 1, "")
|
||||
retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
|
||||
return retval, nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,522 +0,0 @@
|
||||
package ir
|
||||
|
||||
// This file provides functionality to interpret very basic Go SSA, for
|
||||
// compile-time initialization of globals.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
var ErrCGoWrapper = errors.New("tinygo internal: cgo wrapper") // a signal, not an error
|
||||
|
||||
// Ignore these calls (replace with a zero return value) when encountered during
|
||||
// interpretation.
|
||||
var ignoreInitCalls = map[string]struct{}{
|
||||
"syscall.runtime_envs": struct{}{},
|
||||
"syscall/js.predefValue": struct{}{},
|
||||
"(syscall/js.Value).Get": struct{}{},
|
||||
"(syscall/js.Value).New": struct{}{},
|
||||
"(syscall/js.Value).Int": struct{}{},
|
||||
"os.init$1": struct{}{},
|
||||
}
|
||||
|
||||
// Interpret instructions as far as possible, and drop those instructions from
|
||||
// the basic block.
|
||||
func (p *Program) Interpret(block *ssa.BasicBlock, dumpSSA bool) error {
|
||||
if dumpSSA {
|
||||
fmt.Printf("\ninterpret: %s\n", block.Parent().Pkg.Pkg.Path())
|
||||
}
|
||||
for {
|
||||
i, err := p.interpret(block.Instrs, nil, nil, nil, dumpSSA)
|
||||
if err == ErrCGoWrapper {
|
||||
// skip this instruction
|
||||
block.Instrs = block.Instrs[i+1:]
|
||||
continue
|
||||
}
|
||||
block.Instrs = block.Instrs[i:]
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Interpret instructions as far as possible, and return the index of the first
|
||||
// unknown instruction.
|
||||
func (p *Program) interpret(instrs []ssa.Instruction, paramKeys []*ssa.Parameter, paramValues []Value, results []Value, dumpSSA bool) (int, error) {
|
||||
locals := map[ssa.Value]Value{}
|
||||
for i, key := range paramKeys {
|
||||
locals[key] = paramValues[i]
|
||||
}
|
||||
for i, instr := range instrs {
|
||||
if _, ok := instr.(*ssa.DebugRef); ok {
|
||||
continue
|
||||
}
|
||||
if dumpSSA {
|
||||
if val, ok := instr.(ssa.Value); ok && val.Name() != "" {
|
||||
fmt.Printf("\t%s: %s = %s\n", instr.Parent().RelString(nil), val.Name(), val.String())
|
||||
} else {
|
||||
fmt.Printf("\t%s: %s\n", instr.Parent().RelString(nil), instr.String())
|
||||
}
|
||||
}
|
||||
switch instr := instr.(type) {
|
||||
case *ssa.Alloc:
|
||||
alloc, err := p.getZeroValue(instr.Type().Underlying().(*types.Pointer).Elem())
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
locals[instr] = &PointerValue{nil, &alloc}
|
||||
case *ssa.BinOp:
|
||||
if typ, ok := instr.Type().(*types.Basic); ok && typ.Kind() == types.String {
|
||||
// Concatenate two strings.
|
||||
// This happens in the time package, for example.
|
||||
x, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
y, err := p.getValue(instr.Y, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
xstr := constant.StringVal(x.(*ConstValue).Expr.Value)
|
||||
ystr := constant.StringVal(y.(*ConstValue).Expr.Value)
|
||||
locals[instr] = &ConstValue{ssa.NewConst(constant.MakeString(xstr+ystr), types.Typ[types.String])}
|
||||
} else {
|
||||
return i, errors.New("init: unknown binop: " + instr.String())
|
||||
}
|
||||
case *ssa.Call:
|
||||
common := instr.Common()
|
||||
callee := common.StaticCallee()
|
||||
if callee == nil {
|
||||
return i, nil // don't understand dynamic dispatch
|
||||
}
|
||||
if _, ok := ignoreInitCalls[callee.String()]; ok {
|
||||
// These calls are not needed and can be ignored, for the time
|
||||
// being.
|
||||
results := make([]Value, callee.Signature.Results().Len())
|
||||
for i := range results {
|
||||
var err error
|
||||
results[i], err = p.getZeroValue(callee.Signature.Results().At(i).Type())
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
if len(results) == 1 {
|
||||
locals[instr] = results[0]
|
||||
} else if len(results) > 1 {
|
||||
locals[instr] = &StructValue{Fields: results}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if callee.String() == "os.NewFile" {
|
||||
// Emulate the creation of os.Stdin, os.Stdout and os.Stderr.
|
||||
resultPtrType := callee.Signature.Results().At(0).Type().(*types.Pointer)
|
||||
resultStructOuterType := resultPtrType.Elem().Underlying().(*types.Struct)
|
||||
if resultStructOuterType.NumFields() != 1 {
|
||||
panic("expected 1 field in os.File struct")
|
||||
}
|
||||
fileInnerPtrType := resultStructOuterType.Field(0).Type().(*types.Pointer)
|
||||
fileInnerType := fileInnerPtrType.Elem().(*types.Named)
|
||||
fileInnerStructType := fileInnerType.Underlying().(*types.Struct)
|
||||
fileInner, err := p.getZeroValue(fileInnerType) // os.file
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
for fieldIndex := 0; fieldIndex < fileInnerStructType.NumFields(); fieldIndex++ {
|
||||
field := fileInnerStructType.Field(fieldIndex)
|
||||
if field.Name() == "name" {
|
||||
// Set the 'name' field.
|
||||
name, err := p.getValue(common.Args[1], locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
fileInner.(*StructValue).Fields[fieldIndex] = name
|
||||
} else if field.Type().String() == "internal/poll.FD" {
|
||||
// Set the file descriptor field.
|
||||
field := field.Type().Underlying().(*types.Struct)
|
||||
for subfieldIndex := 0; subfieldIndex < field.NumFields(); subfieldIndex++ {
|
||||
subfield := field.Field(subfieldIndex)
|
||||
if subfield.Name() == "Sysfd" {
|
||||
sysfd, err := p.getValue(common.Args[0], locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
sysfd = &ConstValue{Expr: ssa.NewConst(sysfd.(*ConstValue).Expr.Value, subfield.Type())}
|
||||
fileInner.(*StructValue).Fields[fieldIndex].(*StructValue).Fields[subfieldIndex] = sysfd
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fileInnerPtr := &PointerValue{fileInnerPtrType, &fileInner} // *os.file
|
||||
var fileOuter Value = &StructValue{Type: resultPtrType.Elem(), Fields: []Value{fileInnerPtr}} // os.File
|
||||
result := &PointerValue{resultPtrType.Elem(), &fileOuter} // *os.File
|
||||
locals[instr] = result
|
||||
continue
|
||||
}
|
||||
if canInterpret(callee) {
|
||||
params := make([]Value, len(common.Args))
|
||||
for i, arg := range common.Args {
|
||||
val, err := p.getValue(arg, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
params[i] = val
|
||||
}
|
||||
results := make([]Value, callee.Signature.Results().Len())
|
||||
subi, err := p.interpret(callee.Blocks[0].Instrs, callee.Params, params, results, dumpSSA)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
if subi != len(callee.Blocks[0].Instrs) {
|
||||
return i, errors.New("init: could not interpret all instructions of subroutine")
|
||||
}
|
||||
if len(results) == 1 {
|
||||
locals[instr] = results[0]
|
||||
} else {
|
||||
panic("unimplemented: not exactly 1 result")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if callee.Object() == nil || callee.Object().Name() == "init" {
|
||||
return i, nil // arrived at the init#num functions
|
||||
}
|
||||
return i, errors.New("todo: init call: " + callee.String())
|
||||
case *ssa.ChangeType:
|
||||
x, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
// The only case when we need to bitcast is when casting between named
|
||||
// struct types, as those are actually different in LLVM. Let's just
|
||||
// bitcast all struct types for ease of use.
|
||||
if _, ok := instr.Type().Underlying().(*types.Struct); ok {
|
||||
return i, errors.New("todo: init: " + instr.String())
|
||||
}
|
||||
locals[instr] = x
|
||||
case *ssa.Convert:
|
||||
x, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
typeFrom := instr.X.Type().Underlying()
|
||||
switch typeTo := instr.Type().Underlying().(type) {
|
||||
case *types.Basic:
|
||||
if typeTo.Kind() == types.String {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
if _, ok := typeFrom.(*types.Pointer); ok && typeTo.Kind() == types.UnsafePointer {
|
||||
locals[instr] = &PointerBitCastValue{typeTo, x}
|
||||
} else if typeFrom, ok := typeFrom.(*types.Basic); ok {
|
||||
if typeFrom.Kind() == types.UnsafePointer && typeTo.Kind() == types.Uintptr {
|
||||
locals[instr] = &PointerToUintptrValue{x}
|
||||
} else if typeFrom.Info()&types.IsInteger != 0 && typeTo.Info()&types.IsInteger != 0 {
|
||||
locals[instr] = &ConstValue{Expr: ssa.NewConst(x.(*ConstValue).Expr.Value, typeTo)}
|
||||
} else {
|
||||
return i, nil
|
||||
}
|
||||
} else {
|
||||
return i, nil
|
||||
}
|
||||
case *types.Pointer:
|
||||
if typeFrom, ok := typeFrom.(*types.Basic); ok && typeFrom.Kind() == types.UnsafePointer {
|
||||
locals[instr] = &PointerBitCastValue{typeTo, x}
|
||||
} else {
|
||||
panic("expected unsafe pointer conversion")
|
||||
}
|
||||
default:
|
||||
return i, nil
|
||||
}
|
||||
case *ssa.DebugRef:
|
||||
// ignore
|
||||
case *ssa.Extract:
|
||||
tuple, err := p.getValue(instr.Tuple, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
locals[instr] = tuple.(*StructValue).Fields[instr.Index]
|
||||
case *ssa.FieldAddr:
|
||||
x, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
var structVal *StructValue
|
||||
switch x := x.(type) {
|
||||
case *GlobalValue:
|
||||
structVal = x.Global.initializer.(*StructValue)
|
||||
case *PointerValue:
|
||||
structVal = (*x.Elem).(*StructValue)
|
||||
default:
|
||||
panic("expected a pointer")
|
||||
}
|
||||
locals[instr] = &PointerValue{nil, &structVal.Fields[instr.Field]}
|
||||
case *ssa.IndexAddr:
|
||||
x, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
if cnst, ok := instr.Index.(*ssa.Const); ok {
|
||||
index, _ := constant.Int64Val(cnst.Value)
|
||||
switch xPtr := x.(type) {
|
||||
case *GlobalValue:
|
||||
x = xPtr.Global.initializer
|
||||
case *PointerValue:
|
||||
x = *xPtr.Elem
|
||||
default:
|
||||
panic("expected a pointer")
|
||||
}
|
||||
switch x := x.(type) {
|
||||
case *ArrayValue:
|
||||
locals[instr] = &PointerValue{nil, &x.Elems[index]}
|
||||
default:
|
||||
return i, errors.New("todo: init IndexAddr not on an array or struct")
|
||||
}
|
||||
} else {
|
||||
return i, errors.New("todo: init IndexAddr index: " + instr.Index.String())
|
||||
}
|
||||
case *ssa.MakeMap:
|
||||
locals[instr] = &MapValue{instr.Type().Underlying().(*types.Map), nil, nil}
|
||||
case *ssa.MapUpdate:
|
||||
// Assume no duplicate keys exist. This is most likely true for
|
||||
// autogenerated code, but may not be true when trying to interpret
|
||||
// user code.
|
||||
key, err := p.getValue(instr.Key, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
value, err := p.getValue(instr.Value, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
x := locals[instr.Map].(*MapValue)
|
||||
x.Keys = append(x.Keys, key)
|
||||
x.Values = append(x.Values, value)
|
||||
case *ssa.Return:
|
||||
for i, r := range instr.Results {
|
||||
val, err := p.getValue(r, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
results[i] = val
|
||||
}
|
||||
case *ssa.Slice:
|
||||
// Turn a just-allocated array into a slice.
|
||||
if instr.Low != nil || instr.High != nil || instr.Max != nil {
|
||||
return i, errors.New("init: slice expression with bounds")
|
||||
}
|
||||
source, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
switch source := source.(type) {
|
||||
case *PointerValue: // pointer to array
|
||||
array := (*source.Elem).(*ArrayValue)
|
||||
locals[instr] = &SliceValue{instr.Type().Underlying().(*types.Slice), array}
|
||||
default:
|
||||
return i, errors.New("init: unknown slice type")
|
||||
}
|
||||
case *ssa.Store:
|
||||
if addr, ok := instr.Addr.(*ssa.Global); ok {
|
||||
if strings.HasPrefix(instr.Addr.Name(), "__cgofn__cgo_") || strings.HasPrefix(instr.Addr.Name(), "_cgo_") {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
continue
|
||||
}
|
||||
value, err := p.getValue(instr.Val, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
p.GetGlobal(addr).initializer = value
|
||||
} else if addr, ok := locals[instr.Addr]; ok {
|
||||
value, err := p.getValue(instr.Val, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
if addr, ok := addr.(*PointerValue); ok {
|
||||
*(addr.Elem) = value
|
||||
} else {
|
||||
panic("store to non-pointer")
|
||||
}
|
||||
} else {
|
||||
return i, errors.New("todo: init Store: " + instr.String())
|
||||
}
|
||||
case *ssa.UnOp:
|
||||
if instr.Op != token.MUL || instr.CommaOk {
|
||||
return i, errors.New("init: unknown unop: " + instr.String())
|
||||
}
|
||||
valPtr, err := p.getValue(instr.X, locals)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
switch valPtr := valPtr.(type) {
|
||||
case *GlobalValue:
|
||||
locals[instr] = valPtr.Global.initializer
|
||||
case *PointerValue:
|
||||
locals[instr] = *valPtr.Elem
|
||||
default:
|
||||
panic("expected a pointer")
|
||||
}
|
||||
default:
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
return len(instrs), nil
|
||||
}
|
||||
|
||||
// Check whether this function can be interpreted at compile time. For that, it
|
||||
// needs to only contain relatively simple instructions (for example, no control
|
||||
// flow).
|
||||
func canInterpret(callee *ssa.Function) bool {
|
||||
if len(callee.Blocks) != 1 || callee.Signature.Results().Len() != 1 {
|
||||
// No control flow supported so only one basic block.
|
||||
// Only exactly one return value supported right now so check that as
|
||||
// well.
|
||||
return false
|
||||
}
|
||||
for _, instr := range callee.Blocks[0].Instrs {
|
||||
switch instr.(type) {
|
||||
// Ignore all functions fully supported by Program.interpret()
|
||||
// above.
|
||||
case *ssa.Alloc:
|
||||
case *ssa.ChangeType:
|
||||
case *ssa.DebugRef:
|
||||
case *ssa.Extract:
|
||||
case *ssa.FieldAddr:
|
||||
case *ssa.IndexAddr:
|
||||
case *ssa.MakeMap:
|
||||
case *ssa.MapUpdate:
|
||||
case *ssa.Return:
|
||||
case *ssa.Slice:
|
||||
case *ssa.Store:
|
||||
case *ssa.UnOp:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *Program) getValue(value ssa.Value, locals map[ssa.Value]Value) (Value, error) {
|
||||
switch value := value.(type) {
|
||||
case *ssa.Const:
|
||||
return &ConstValue{value}, nil
|
||||
case *ssa.Function:
|
||||
return &FunctionValue{value.Type(), value}, nil
|
||||
case *ssa.Global:
|
||||
if strings.HasPrefix(value.Name(), "__cgofn__cgo_") || strings.HasPrefix(value.Name(), "_cgo_") {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
return nil, ErrCGoWrapper
|
||||
}
|
||||
g := p.GetGlobal(value)
|
||||
if g.initializer == nil {
|
||||
value, err := p.getZeroValue(value.Type().Underlying().(*types.Pointer).Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.initializer = value
|
||||
}
|
||||
return &GlobalValue{g}, nil
|
||||
default:
|
||||
if local, ok := locals[value]; ok {
|
||||
return local, nil
|
||||
} else {
|
||||
return nil, errors.New("todo: init: unknown value: " + value.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Program) getZeroValue(t types.Type) (Value, error) {
|
||||
switch typ := t.Underlying().(type) {
|
||||
case *types.Array:
|
||||
elems := make([]Value, typ.Len())
|
||||
for i := range elems {
|
||||
elem, err := p.getZeroValue(typ.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
elems[i] = elem
|
||||
}
|
||||
return &ArrayValue{typ.Elem(), elems}, nil
|
||||
case *types.Basic:
|
||||
return &ZeroBasicValue{typ}, nil
|
||||
case *types.Signature:
|
||||
return &FunctionValue{typ, nil}, nil
|
||||
case *types.Map:
|
||||
return &MapValue{typ, nil, nil}, nil
|
||||
case *types.Pointer:
|
||||
return &PointerValue{typ, nil}, nil
|
||||
case *types.Struct:
|
||||
elems := make([]Value, typ.NumFields())
|
||||
for i := range elems {
|
||||
elem, err := p.getZeroValue(typ.Field(i).Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
elems[i] = elem
|
||||
}
|
||||
return &StructValue{t, elems}, nil
|
||||
case *types.Slice:
|
||||
return &SliceValue{typ, nil}, nil
|
||||
default:
|
||||
return nil, errors.New("todo: init: unknown global type: " + typ.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Boxed value for interpreter.
|
||||
type Value interface {
|
||||
}
|
||||
|
||||
type ConstValue struct {
|
||||
Expr *ssa.Const
|
||||
}
|
||||
|
||||
type ZeroBasicValue struct {
|
||||
Type *types.Basic
|
||||
}
|
||||
|
||||
type PointerValue struct {
|
||||
Type types.Type
|
||||
Elem *Value
|
||||
}
|
||||
|
||||
type FunctionValue struct {
|
||||
Type types.Type
|
||||
Elem *ssa.Function
|
||||
}
|
||||
|
||||
type PointerBitCastValue struct {
|
||||
Type types.Type
|
||||
Elem Value
|
||||
}
|
||||
|
||||
type PointerToUintptrValue struct {
|
||||
Elem Value
|
||||
}
|
||||
|
||||
type GlobalValue struct {
|
||||
Global *Global
|
||||
}
|
||||
|
||||
type ArrayValue struct {
|
||||
ElemType types.Type
|
||||
Elems []Value
|
||||
}
|
||||
|
||||
type StructValue struct {
|
||||
Type types.Type // types.Struct or types.Named
|
||||
Fields []Value
|
||||
}
|
||||
|
||||
type SliceValue struct {
|
||||
Type *types.Slice
|
||||
Array *ArrayValue
|
||||
}
|
||||
|
||||
type MapValue struct {
|
||||
Type *types.Map
|
||||
Keys []Value
|
||||
Values []Value
|
||||
}
|
||||
@@ -43,11 +43,10 @@ type Function struct {
|
||||
// Global variable, possibly constant.
|
||||
type Global struct {
|
||||
*ssa.Global
|
||||
program *Program
|
||||
LLVMGlobal llvm.Value
|
||||
linkName string // go:extern
|
||||
extern bool // go:extern
|
||||
initializer Value
|
||||
program *Program
|
||||
LLVMGlobal llvm.Value
|
||||
linkName string // go:extern
|
||||
extern bool // go:extern
|
||||
}
|
||||
|
||||
// Type with a name and possibly methods.
|
||||
@@ -188,9 +187,6 @@ func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
||||
func (p *Program) AddPackage(pkg *ssa.Package) {
|
||||
memberNames := make([]string, 0)
|
||||
for name := range pkg.Members {
|
||||
if isCGoInternal(name) {
|
||||
continue
|
||||
}
|
||||
memberNames = append(memberNames, name)
|
||||
}
|
||||
sort.Strings(memberNames)
|
||||
@@ -199,9 +195,6 @@ func (p *Program) AddPackage(pkg *ssa.Package) {
|
||||
member := pkg.Members[name]
|
||||
switch member := member.(type) {
|
||||
case *ssa.Function:
|
||||
if isCGoInternal(member.Name()) {
|
||||
continue
|
||||
}
|
||||
p.addFunction(member)
|
||||
case *ssa.Type:
|
||||
t := &NamedType{Type: member}
|
||||
@@ -416,10 +409,6 @@ func (g *Global) CName() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (g *Global) Initializer() Value {
|
||||
return g.initializer
|
||||
}
|
||||
|
||||
// Return true if this named type is annotated with the //go:volatile pragma,
|
||||
// for volatile loads and stores.
|
||||
func (p *Program) IsVolatile(t types.Type) bool {
|
||||
@@ -443,18 +432,6 @@ func (p *Program) IsVolatile(t types.Type) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if this is a CGo-internal function that can be ignored.
|
||||
func isCGoInternal(name string) bool {
|
||||
if strings.HasPrefix(name, "_Cgo_") || strings.HasPrefix(name, "_cgo") {
|
||||
// _Cgo_ptr, _Cgo_use, _cgoCheckResult, _cgo_runtime_cgocall
|
||||
return true // CGo-internal functions
|
||||
}
|
||||
if strings.HasPrefix(name, "__cgofn__cgo_") {
|
||||
return true // CGo function pointer in global scope
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get all methods of a type.
|
||||
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
||||
ms := prog.MethodSets.MethodSet(typ)
|
||||
|
||||
+2
-2
@@ -73,7 +73,7 @@ func (p *Program) SimpleDCE() {
|
||||
worklist := []*ssa.Function{main}
|
||||
for _, f := range p.Functions {
|
||||
if f.exported || f.Synthetic == "package initializer" || f.Pkg == runtimePkg || (f.Pkg == mathPkg && f.Pkg != nil) {
|
||||
if f.flag || isCGoInternal(f.Name()) {
|
||||
if f.flag {
|
||||
continue
|
||||
}
|
||||
f.flag = true
|
||||
@@ -103,7 +103,7 @@ func (p *Program) SimpleDCE() {
|
||||
}
|
||||
}
|
||||
for _, operand := range instr.Operands(nil) {
|
||||
if operand == nil || *operand == nil || isCGoInternal((*operand).Name()) {
|
||||
if operand == nil || *operand == nil {
|
||||
continue
|
||||
}
|
||||
switch operand := (*operand).(type) {
|
||||
|
||||
+33
-13
@@ -177,25 +177,44 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
|
||||
var typeName string
|
||||
switch typ.kind {
|
||||
case C.CXType_SChar:
|
||||
typeName = "schar"
|
||||
typeName = "C.schar"
|
||||
case C.CXType_UChar:
|
||||
typeName = "uchar"
|
||||
typeName = "C.uchar"
|
||||
case C.CXType_Short:
|
||||
typeName = "short"
|
||||
typeName = "C.short"
|
||||
case C.CXType_UShort:
|
||||
typeName = "ushort"
|
||||
typeName = "C.ushort"
|
||||
case C.CXType_Int:
|
||||
typeName = "int"
|
||||
typeName = "C.int"
|
||||
case C.CXType_UInt:
|
||||
typeName = "uint"
|
||||
typeName = "C.uint"
|
||||
case C.CXType_Long:
|
||||
typeName = "long"
|
||||
typeName = "C.long"
|
||||
case C.CXType_ULong:
|
||||
typeName = "ulong"
|
||||
typeName = "C.ulong"
|
||||
case C.CXType_LongLong:
|
||||
typeName = "longlong"
|
||||
typeName = "C.longlong"
|
||||
case C.CXType_ULongLong:
|
||||
typeName = "ulonglong"
|
||||
typeName = "C.ulonglong"
|
||||
case C.CXType_Bool:
|
||||
typeName = "bool"
|
||||
case C.CXType_Float, C.CXType_Double, C.CXType_LongDouble:
|
||||
switch C.clang_Type_getSizeOf(typ) {
|
||||
case 4:
|
||||
typeName = "float32"
|
||||
case 8:
|
||||
typeName = "float64"
|
||||
default:
|
||||
// Don't do anything, rely on the fallback code to show a somewhat
|
||||
// sensible error message like "undeclared name: C.long double".
|
||||
}
|
||||
case C.CXType_Complex:
|
||||
switch C.clang_Type_getSizeOf(typ) {
|
||||
case 8:
|
||||
typeName = "complex64"
|
||||
case 16:
|
||||
typeName = "complex128"
|
||||
}
|
||||
case C.CXType_Pointer:
|
||||
return &ast.StarExpr{
|
||||
Star: info.importCPos,
|
||||
@@ -218,13 +237,14 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
|
||||
Name: "byte",
|
||||
},
|
||||
}
|
||||
default:
|
||||
}
|
||||
if typeName == "" {
|
||||
// Fallback, probably incorrect but at least the error points to an odd
|
||||
// type name.
|
||||
typeName = getString(C.clang_getTypeSpelling(typ))
|
||||
typeName = "C." + getString(C.clang_getTypeSpelling(typ))
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: info.importCPos,
|
||||
Name: "C." + typeName,
|
||||
Name: typeName,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
package loader
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -I/usr/lib/llvm-7/include
|
||||
#cgo LDFLAGS: -L/usr/lib/llvm-7/lib -lclang
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-7/include
|
||||
#cgo darwin CFLAGS: -I/usr/local/opt/llvm/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-7/lib -lclang
|
||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm/lib -lclang -lffi
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
var commands = map[string]string{
|
||||
"ar": "ar",
|
||||
"ar": "llvm-ar",
|
||||
"clang": "clang-7",
|
||||
"ld.lld": "ld.lld-7",
|
||||
"wasm-ld": "wasm-ld-7",
|
||||
@@ -47,7 +47,6 @@ type BuildConfig struct {
|
||||
dumpSSA bool
|
||||
debug bool
|
||||
printSizes string
|
||||
initInterp bool
|
||||
cFlags []string
|
||||
ldFlags []string
|
||||
wasmAbi string
|
||||
@@ -64,19 +63,18 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
||||
spec.LDFlags = append(spec.LDFlags, config.ldFlags...)
|
||||
|
||||
compilerConfig := compiler.Config{
|
||||
Triple: spec.Triple,
|
||||
CPU: spec.CPU,
|
||||
GOOS: spec.GOOS,
|
||||
GOARCH: spec.GOARCH,
|
||||
GC: config.gc,
|
||||
CFlags: spec.CFlags,
|
||||
LDFlags: spec.LDFlags,
|
||||
Debug: config.debug,
|
||||
DumpSSA: config.dumpSSA,
|
||||
RootDir: sourceDir(),
|
||||
GOPATH: getGopath(),
|
||||
BuildTags: spec.BuildTags,
|
||||
InitInterp: config.initInterp,
|
||||
Triple: spec.Triple,
|
||||
CPU: spec.CPU,
|
||||
GOOS: spec.GOOS,
|
||||
GOARCH: spec.GOARCH,
|
||||
GC: config.gc,
|
||||
CFlags: spec.CFlags,
|
||||
LDFlags: spec.LDFlags,
|
||||
Debug: config.debug,
|
||||
DumpSSA: config.dumpSSA,
|
||||
RootDir: sourceDir(),
|
||||
GOPATH: getGopath(),
|
||||
BuildTags: spec.BuildTags,
|
||||
}
|
||||
c, err := compiler.NewCompiler(pkgName, compilerConfig)
|
||||
if err != nil {
|
||||
@@ -96,17 +94,17 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
||||
return errors.New("verification error after IR construction")
|
||||
}
|
||||
|
||||
if config.initInterp {
|
||||
err = interp.Run(c.Module(), c.TargetData(), config.dumpSSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Verify(); err != nil {
|
||||
return errors.New("verification error after interpreting runtime.initAll")
|
||||
}
|
||||
err = interp.Run(c.Module(), c.TargetData(), config.dumpSSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Verify(); err != nil {
|
||||
return errors.New("verification error after interpreting runtime.initAll")
|
||||
}
|
||||
|
||||
c.ApplyFunctionSections() // -ffunction-sections
|
||||
if spec.GOOS != "darwin" {
|
||||
c.ApplyFunctionSections() // -ffunction-sections
|
||||
}
|
||||
if err := c.Verify(); err != nil {
|
||||
return errors.New("verification error after applying function sections")
|
||||
}
|
||||
@@ -320,14 +318,29 @@ func Flash(pkgName, target, port string, config *BuildConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return Compile(pkgName, ".hex", spec, config, func(tmppath string) error {
|
||||
// determine the type of file to compile
|
||||
var fileExt string
|
||||
|
||||
switch {
|
||||
case strings.Contains(spec.Flasher, "{hex}"):
|
||||
fileExt = ".hex"
|
||||
case strings.Contains(spec.Flasher, "{elf}"):
|
||||
fileExt = ".elf"
|
||||
case strings.Contains(spec.Flasher, "{bin}"):
|
||||
fileExt = ".bin"
|
||||
default:
|
||||
return errors.New("invalid target file - did you forget the {hex} token in the 'flash' section?")
|
||||
}
|
||||
|
||||
return Compile(pkgName, fileExt, spec, config, func(tmppath string) error {
|
||||
if spec.Flasher == "" {
|
||||
return errors.New("no flash command specified - did you miss a -target flag?")
|
||||
}
|
||||
|
||||
// Create the command.
|
||||
flashCmd := spec.Flasher
|
||||
flashCmd = strings.Replace(flashCmd, "{hex}", tmppath, -1)
|
||||
fileToken := "{" + fileExt[1:] + "}"
|
||||
flashCmd = strings.Replace(flashCmd, fileToken, tmppath, -1)
|
||||
flashCmd = strings.Replace(flashCmd, "{port}", port, -1)
|
||||
|
||||
// Execute the command.
|
||||
@@ -501,7 +514,6 @@ func main() {
|
||||
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||
initInterp := flag.Bool("initinterp", true, "enable/disable partial evaluator of generated IR")
|
||||
port := flag.String("port", "/dev/ttyACM0", "flash port")
|
||||
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
||||
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
||||
@@ -522,7 +534,6 @@ func main() {
|
||||
dumpSSA: *dumpSSA,
|
||||
debug: !*nodebug,
|
||||
printSizes: *printSize,
|
||||
initInterp: *initInterp,
|
||||
wasmAbi: *wasmAbi,
|
||||
}
|
||||
|
||||
|
||||
+23
-21
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
@@ -53,32 +54,34 @@ func TestCompiler(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
t.Log("running tests for linux/arm...")
|
||||
for _, path := range matches {
|
||||
if path == "testdata/cgo/" {
|
||||
continue // TODO: improve CGo
|
||||
}
|
||||
t.Run(path, func(t *testing.T) {
|
||||
runTest(path, tmpdir, "arm--linux-gnueabi", t)
|
||||
})
|
||||
}
|
||||
|
||||
t.Log("running tests for linux/arm64...")
|
||||
for _, path := range matches {
|
||||
if path == "testdata/cgo/" {
|
||||
continue // TODO: improve CGo
|
||||
}
|
||||
t.Run(path, func(t *testing.T) {
|
||||
runTest(path, tmpdir, "aarch64--linux-gnueabi", t)
|
||||
})
|
||||
}
|
||||
|
||||
t.Log("running tests for emulated cortex-m3...")
|
||||
for _, path := range matches {
|
||||
t.Run(path, func(t *testing.T) {
|
||||
runTest(path, tmpdir, "qemu", t)
|
||||
})
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
t.Log("running tests for linux/arm...")
|
||||
for _, path := range matches {
|
||||
if path == "testdata/cgo/" {
|
||||
continue // TODO: improve CGo
|
||||
}
|
||||
t.Run(path, func(t *testing.T) {
|
||||
runTest(path, tmpdir, "arm--linux-gnueabi", t)
|
||||
})
|
||||
}
|
||||
|
||||
t.Log("running tests for linux/arm64...")
|
||||
for _, path := range matches {
|
||||
if path == "testdata/cgo/" {
|
||||
continue // TODO: improve CGo
|
||||
}
|
||||
t.Run(path, func(t *testing.T) {
|
||||
runTest(path, tmpdir, "aarch64--linux-gnueabi", t)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runTest(path, tmpdir string, target string, t *testing.T) {
|
||||
@@ -103,7 +106,6 @@ func runTest(path, tmpdir string, target string, t *testing.T) {
|
||||
dumpSSA: false,
|
||||
debug: false,
|
||||
printSizes: "",
|
||||
initInterp: true,
|
||||
}
|
||||
binary := filepath.Join(tmpdir, "test")
|
||||
err = Build("./"+path, binary, target, config)
|
||||
|
||||
@@ -118,3 +118,21 @@ func SetPriority(irq uint32, priority uint32) {
|
||||
priority = priority << (regpos * 8) // bits to set
|
||||
NVIC.IPR[regnum] = RegValue((uint32(NVIC.IPR[regnum]) &^ mask) | priority)
|
||||
}
|
||||
|
||||
// DisableInterrupts disables all interrupts, and returns the old state.
|
||||
//
|
||||
// TODO: it doesn't actually return the old state, meaning that it cannot be
|
||||
// nested.
|
||||
func DisableInterrupts() uintptr {
|
||||
Asm("cpsid if")
|
||||
return 0
|
||||
}
|
||||
|
||||
// EnableInterrupts enables all interrupts again. The value passed in must be
|
||||
// the mask returned by DisableInterrupts.
|
||||
//
|
||||
// TODO: it doesn't actually use the old state, meaning that it cannot be
|
||||
// nested.
|
||||
func EnableInterrupts(mask uintptr) {
|
||||
Asm("cpsie if")
|
||||
}
|
||||
|
||||
@@ -13,11 +13,23 @@ function init() {
|
||||
document.querySelector('#b').oninput = updateResult;
|
||||
|
||||
const go = new Go();
|
||||
WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function(obj) {
|
||||
wasm = obj.instance;
|
||||
go.run(wasm);
|
||||
updateResult();
|
||||
})
|
||||
if ('instantiateStreaming' in WebAssembly) {
|
||||
WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function(obj) {
|
||||
wasm = obj.instance;
|
||||
go.run(wasm);
|
||||
updateResult();
|
||||
})
|
||||
} else {
|
||||
fetch(WASM_URL).then(resp =>
|
||||
resp.arrayBuffer()
|
||||
).then(bytes =>
|
||||
WebAssembly.instantiate(bytes, go.importObject).then(function(obj) {
|
||||
wasm = obj.instance;
|
||||
go.run(wasm);
|
||||
updateResult();
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// +build sam,atsamd21,circuitplay_express
|
||||
|
||||
package machine
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = PB09
|
||||
D1 = PB08
|
||||
D2 = PB02
|
||||
D3 = PB03
|
||||
D4 = PA28
|
||||
D5 = PA14
|
||||
D6 = PA05
|
||||
D7 = PA15
|
||||
D8 = PB23
|
||||
D9 = PA06
|
||||
D10 = PA07
|
||||
D11 = 0xff // does not seem to exist
|
||||
D12 = PA02
|
||||
D13 = PA17 // PWM available
|
||||
)
|
||||
|
||||
// Analog Pins
|
||||
const (
|
||||
A0 = PA02 // PWM available, also ADC/AIN[0]
|
||||
A1 = PA05 // ADC/AIN[5]
|
||||
A2 = PA06 // PWM available, also ADC/AIN[6]
|
||||
A3 = PA07 // PWM available, also ADC/AIN[7]
|
||||
A4 = PB03 // PORTB
|
||||
A5 = PB02 // PORTB
|
||||
A6 = PB09 // PORTB
|
||||
A7 = PB08 // PORTB
|
||||
A8 = PA11 // ADC/AIN[19]
|
||||
A9 = PA09 // ADC/AIN[17]
|
||||
A10 = PA04
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
NEOPIXELS = D8
|
||||
|
||||
BUTTONA = D4
|
||||
BUTTONB = D5
|
||||
SLIDER = D7 // built-in slide switch
|
||||
|
||||
BUTTON = BUTTONA
|
||||
BUTTON1 = BUTTONB
|
||||
|
||||
LIGHTSENSOR = A8
|
||||
TEMPSENSOR = A9
|
||||
PROXIMITY = A10
|
||||
)
|
||||
|
||||
// USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// UART0 pins
|
||||
const (
|
||||
UART_TX_PIN = PB08 // PORTB
|
||||
UART_RX_PIN = PB09 // PORTB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PA00 // SDA: SERCOM3/PAD[0]
|
||||
SCL_PIN = PA01 // SCL: SERCOM3/PAD[1]
|
||||
)
|
||||
@@ -1,40 +1,46 @@
|
||||
// +build sam,atsamd21g18a,itsybitsy_m0
|
||||
// +build sam,atsamd21,itsybitsy_m0
|
||||
|
||||
package machine
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = 11 // UART0 RX
|
||||
D1 = 10 // UART0 TX
|
||||
D2 = 14
|
||||
D3 = 9 // PWM available
|
||||
D4 = 8 // PWM available
|
||||
D5 = 15 // PWM available
|
||||
D6 = 20 // PWM available
|
||||
D7 = 21 // PWM available
|
||||
D8 = 6 // PWM available
|
||||
D9 = 7 // PWM available
|
||||
D10 = 18 // can be used for PWM or UART1 TX
|
||||
D11 = 16 // can be used for PWM or UART1 RX
|
||||
D12 = 19 // PWM available
|
||||
D13 = 17 // PWM available
|
||||
D0 = PA11 // UART0 RX
|
||||
D1 = PA10 // UART0 TX
|
||||
D2 = PA14
|
||||
D3 = PA09 // PWM available
|
||||
D4 = PA08 // PWM available
|
||||
D5 = PA15 // PWM available
|
||||
D6 = PA20 // PWM available
|
||||
D7 = PA21 // PWM available
|
||||
D8 = PA06 // PWM available
|
||||
D9 = PA07 // PWM available
|
||||
D10 = PA18 // can be used for PWM or UART1 TX
|
||||
D11 = PA16 // can be used for PWM or UART1 RX
|
||||
D12 = PA19 // PWM available
|
||||
D13 = PA17 // PWM available
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = 2 // ADC/AIN[0]
|
||||
// A1 = 8 // ADC/AIN[2] TODO: requires PORTB
|
||||
// A2 = 9 // ADC/AIN[3] TODO: requires PORTB
|
||||
A3 = 4 // ADC/AIN[4]
|
||||
A4 = 5 // ADC/AIN[5]
|
||||
//A5 = 2 // ADC/AIN[10] TODO: requires PORTB
|
||||
A0 = PA02 // ADC/AIN[0]
|
||||
A1 = PB08 // ADC/AIN[2]
|
||||
A2 = PB09 // ADC/AIN[3]
|
||||
A3 = PA04 // ADC/AIN[4]
|
||||
A4 = PA05 // ADC/AIN[5]
|
||||
A5 = PB02 // ADC/AIN[10]
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
)
|
||||
|
||||
// UART0 pins
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN = D1
|
||||
UART_RX_PIN = D0
|
||||
@@ -42,6 +48,6 @@ const (
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = 22 // SDA: SERCOM3/PAD[0]
|
||||
SCL_PIN = 23 // SCL: SERCOM3/PAD[1]
|
||||
SDA_PIN = PA22 // SDA: SERCOM3/PAD[0]
|
||||
SCL_PIN = PA23 // SCL: SERCOM3/PAD[1]
|
||||
)
|
||||
|
||||
@@ -43,6 +43,27 @@ const (
|
||||
SPI0_MISO_PIN = 22 // P14 on the board
|
||||
)
|
||||
|
||||
// GPIO/Analog pins
|
||||
const (
|
||||
P0 = 3
|
||||
P1 = 2
|
||||
P2 = 1
|
||||
P3 = 4
|
||||
P4 = 5
|
||||
P5 = 17
|
||||
P6 = 12
|
||||
P7 = 11
|
||||
P8 = 18
|
||||
P9 = 10
|
||||
P10 = 6
|
||||
P11 = 26
|
||||
P12 = 20
|
||||
P13 = 23
|
||||
P14 = 22
|
||||
P15 = 21
|
||||
P16 = 16
|
||||
)
|
||||
|
||||
// LED matrix pins
|
||||
const (
|
||||
LED_COL_1 = 4
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,908 +0,0 @@
|
||||
// +build sam,atsamd21g18a
|
||||
|
||||
// Peripheral abstraction layer for the atsamd21.
|
||||
//
|
||||
// Datasheet:
|
||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/SAMD21-Family-DataSheet-DS40001882D.pdf
|
||||
//
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 48000000
|
||||
|
||||
type GPIOMode uint8
|
||||
|
||||
const (
|
||||
GPIO_ANALOG = 1
|
||||
GPIO_SERCOM = 2
|
||||
GPIO_SERCOM_ALT = 3
|
||||
GPIO_TIMER = 4
|
||||
GPIO_TIMER_ALT = 5
|
||||
GPIO_COM = 6
|
||||
GPIO_AC_CLK = 7
|
||||
GPIO_DIGITAL = 8
|
||||
GPIO_INPUT = 9
|
||||
GPIO_INPUT_PULLUP = 10
|
||||
GPIO_OUTPUT = 11
|
||||
GPIO_PWM = GPIO_TIMER
|
||||
GPIO_PWM_ALT = GPIO_TIMER_ALT
|
||||
)
|
||||
|
||||
// Configure this pin with the given configuration.
|
||||
func (p GPIO) Configure(config GPIOConfig) {
|
||||
switch config.Mode {
|
||||
case GPIO_OUTPUT:
|
||||
sam.PORT.DIRSET0 = (1 << p.Pin)
|
||||
// output is also set to input enable so pin can read back its own value
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
|
||||
case GPIO_INPUT:
|
||||
sam.PORT.DIRCLR0 = (1 << p.Pin)
|
||||
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||
|
||||
case GPIO_SERCOM:
|
||||
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 << 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 << sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
// enable port config
|
||||
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR | sam.PORT_PINCFG0_INEN)
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the current value of a GPIO pin.
|
||||
func (p GPIO) Get() bool {
|
||||
return (sam.PORT.IN0>>p.Pin)&1 > 0
|
||||
}
|
||||
|
||||
// Set the pin to high or low.
|
||||
// Warning: only use this on an output pin!
|
||||
func (p GPIO) Set(high bool) {
|
||||
if high {
|
||||
sam.PORT.OUTSET0 = (1 << p.Pin)
|
||||
} else {
|
||||
sam.PORT.OUTCLR0 = (1 << p.Pin)
|
||||
}
|
||||
}
|
||||
|
||||
// getPMux returns the value for the correct PMUX register for this pin.
|
||||
func (p GPIO) getPMux() sam.RegValue8 {
|
||||
return getPMux(p.Pin)
|
||||
}
|
||||
|
||||
// setPMux sets the value for the correct PMUX register for this pin.
|
||||
func (p GPIO) setPMux(val sam.RegValue8) {
|
||||
setPMux(p.Pin, val)
|
||||
}
|
||||
|
||||
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||
func (p GPIO) getPinCfg() sam.RegValue8 {
|
||||
return getPinCfg(p.Pin)
|
||||
}
|
||||
|
||||
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||
func (p GPIO) setPinCfg(val sam.RegValue8) {
|
||||
setPinCfg(p.Pin, val)
|
||||
}
|
||||
|
||||
// UART on the SAMD21.
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
Bus *sam.SERCOM_USART_Type
|
||||
}
|
||||
|
||||
var (
|
||||
// The first hardware serial port on the SAMD21. Uses the SERCOM0 interface.
|
||||
UART0 = UART{Bus: sam.SERCOM0_USART, Buffer: NewRingBuffer()}
|
||||
|
||||
// The second hardware serial port on the SAMD21. Uses the SERCOM1 interface.
|
||||
UART1 = UART{Bus: sam.SERCOM1_USART, Buffer: NewRingBuffer()}
|
||||
)
|
||||
|
||||
const (
|
||||
sampleRate16X = 16
|
||||
lsbFirst = 1
|
||||
sercomRXPad0 = 0
|
||||
sercomRXPad1 = 1
|
||||
sercomRXPad2 = 2
|
||||
sercomRXPad3 = 3
|
||||
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
|
||||
)
|
||||
|
||||
// Configure the UART.
|
||||
func (uart UART) Configure(config UARTConfig) {
|
||||
// Default baud rate to 115200.
|
||||
if config.BaudRate == 0 {
|
||||
config.BaudRate = 115200
|
||||
}
|
||||
|
||||
// determine pins
|
||||
if config.TX == 0 {
|
||||
// use default pins
|
||||
config.TX = UART_TX_PIN
|
||||
config.RX = UART_RX_PIN
|
||||
}
|
||||
|
||||
// determine pads
|
||||
var txpad, rxpad int
|
||||
switch config.TX {
|
||||
case UART_TX_PIN:
|
||||
txpad = sercomTXPad2
|
||||
case D10:
|
||||
txpad = sercomTXPad2
|
||||
case D11:
|
||||
txpad = sercomTXPad0
|
||||
default:
|
||||
panic("Invalid TX pin for UART")
|
||||
}
|
||||
|
||||
switch config.RX {
|
||||
case UART_RX_PIN:
|
||||
rxpad = sercomRXPad3
|
||||
case D10:
|
||||
rxpad = sercomRXPad2
|
||||
case D11:
|
||||
rxpad = sercomRXPad0
|
||||
case D12:
|
||||
rxpad = sercomRXPad3
|
||||
case D13:
|
||||
rxpad = sercomRXPad1
|
||||
default:
|
||||
panic("Invalid RX pin for UART")
|
||||
}
|
||||
|
||||
// configure pins
|
||||
GPIO{config.TX}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
|
||||
GPIO{config.RX}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
|
||||
|
||||
// reset SERCOM0
|
||||
uart.Bus.CTRLA |= sam.SERCOM_USART_CTRLA_SWRST
|
||||
for (uart.Bus.CTRLA&sam.SERCOM_USART_CTRLA_SWRST) > 0 ||
|
||||
(uart.Bus.SYNCBUSY&sam.SERCOM_USART_SYNCBUSY_SWRST) > 0 {
|
||||
}
|
||||
|
||||
// set UART mode/sample rate
|
||||
// SERCOM_USART_CTRLA_MODE(mode) |
|
||||
// SERCOM_USART_CTRLA_SAMPR(sampleRate);
|
||||
uart.Bus.CTRLA = (sam.SERCOM_USART_CTRLA_MODE_USART_INT_CLK << sam.SERCOM_USART_CTRLA_MODE_Pos) |
|
||||
(1 << sam.SERCOM_USART_CTRLA_SAMPR_Pos) // sample rate of 16x
|
||||
|
||||
// Set baud rate
|
||||
uart.SetBaudRate(config.BaudRate)
|
||||
|
||||
// setup UART frame
|
||||
// SERCOM_USART_CTRLA_FORM( (parityMode == SERCOM_NO_PARITY ? 0 : 1) ) |
|
||||
// dataOrder << SERCOM_USART_CTRLA_DORD_Pos;
|
||||
uart.Bus.CTRLA |= (0 << sam.SERCOM_USART_CTRLA_FORM_Pos) | // no parity
|
||||
(lsbFirst << sam.SERCOM_USART_CTRLA_DORD_Pos) // data order
|
||||
|
||||
// set UART stop bits/parity
|
||||
// SERCOM_USART_CTRLB_CHSIZE(charSize) |
|
||||
// nbStopBits << SERCOM_USART_CTRLB_SBMODE_Pos |
|
||||
// (parityMode == SERCOM_NO_PARITY ? 0 : parityMode) << SERCOM_USART_CTRLB_PMODE_Pos; //If no parity use default value
|
||||
uart.Bus.CTRLB |= (0 << sam.SERCOM_USART_CTRLB_CHSIZE_Pos) | // 8 bits is 0
|
||||
(0 << sam.SERCOM_USART_CTRLB_SBMODE_Pos) | // 1 stop bit is zero
|
||||
(0 << sam.SERCOM_USART_CTRLB_PMODE_Pos) // no parity
|
||||
|
||||
// set UART pads. This is not same as pins...
|
||||
// SERCOM_USART_CTRLA_TXPO(txPad) |
|
||||
// SERCOM_USART_CTRLA_RXPO(rxPad);
|
||||
uart.Bus.CTRLA |= sam.RegValue((txpad << sam.SERCOM_USART_CTRLA_TXPO_Pos) |
|
||||
(rxpad << sam.SERCOM_USART_CTRLA_RXPO_Pos))
|
||||
|
||||
// Enable Transceiver and Receiver
|
||||
//sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ;
|
||||
uart.Bus.CTRLB |= (sam.SERCOM_USART_CTRLB_TXEN | sam.SERCOM_USART_CTRLB_RXEN)
|
||||
|
||||
// Enable USART1 port.
|
||||
// sercom->USART.CTRLA.bit.ENABLE = 0x1u;
|
||||
uart.Bus.CTRLA |= sam.SERCOM_USART_CTRLA_ENABLE
|
||||
for (uart.Bus.SYNCBUSY & sam.SERCOM_USART_SYNCBUSY_ENABLE) > 0 {
|
||||
}
|
||||
|
||||
// setup interrupt on receive
|
||||
uart.Bus.INTENSET = sam.SERCOM_USART_INTENSET_RXC
|
||||
|
||||
// Enable RX IRQ.
|
||||
if config.TX == UART_TX_PIN {
|
||||
// UART0
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM0)
|
||||
} else {
|
||||
// UART1
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM1)
|
||||
}
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the UART.
|
||||
func (uart UART) SetBaudRate(br uint32) {
|
||||
// Asynchronous fractional mode (Table 24-2 in datasheet)
|
||||
// BAUD = fref / (sampleRateValue * fbaud)
|
||||
// (multiply by 8, to calculate fractional piece)
|
||||
// uint32_t baudTimes8 = (SystemCoreClock * 8) / (16 * baudrate);
|
||||
baud := (CPU_FREQUENCY * 8) / (sampleRate16X * br)
|
||||
|
||||
// sercom->USART.BAUD.FRAC.FP = (baudTimes8 % 8);
|
||||
// sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8);
|
||||
uart.Bus.BAUD = sam.RegValue16(((baud % 8) << sam.SERCOM_USART_BAUD_FRAC_MODE_FP_Pos) |
|
||||
((baud / 8) << sam.SERCOM_USART_BAUD_FRAC_MODE_BAUD_Pos))
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
func (uart UART) WriteByte(c byte) error {
|
||||
// wait until ready to receive
|
||||
for (uart.Bus.INTFLAG & sam.SERCOM_USART_INTFLAG_DRE) == 0 {
|
||||
}
|
||||
uart.Bus.DATA = sam.RegValue16(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:export SERCOM0_IRQHandler
|
||||
func handleUART0() {
|
||||
// should reset IRQ
|
||||
UART0.Receive(byte((UART0.Bus.DATA & 0xFF)))
|
||||
UART0.Bus.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC
|
||||
}
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
// should reset IRQ
|
||||
UART1.Receive(byte((UART1.Bus.DATA & 0xFF)))
|
||||
UART1.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
|
||||
SCL uint8
|
||||
SDA uint8
|
||||
}
|
||||
|
||||
const (
|
||||
// Default rise time in nanoseconds, based on 4.7K ohm pull up resistors
|
||||
riseTimeNanoseconds = 125
|
||||
|
||||
// wire bus states
|
||||
wireUnknownState = 0
|
||||
wireIdleState = 1
|
||||
wireOwnerState = 2
|
||||
wireBusyState = 3
|
||||
|
||||
// wire commands
|
||||
wireCmdNoAction = 0
|
||||
wireCmdRepeatStart = 1
|
||||
wireCmdRead = 2
|
||||
wireCmdStop = 3
|
||||
)
|
||||
|
||||
const i2cTimeout = 1000
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) {
|
||||
// Default I2C bus speed is 100 kHz.
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = TWI_FREQ_100KHZ
|
||||
}
|
||||
|
||||
// reset SERCOM3
|
||||
i2c.Bus.CTRLA |= sam.SERCOM_I2CM_CTRLA_SWRST
|
||||
for (i2c.Bus.CTRLA&sam.SERCOM_I2CM_CTRLA_SWRST) > 0 ||
|
||||
(i2c.Bus.SYNCBUSY&sam.SERCOM_I2CM_SYNCBUSY_SWRST) > 0 {
|
||||
}
|
||||
|
||||
// Set i2c master mode
|
||||
//SERCOM_I2CM_CTRLA_MODE( I2C_MASTER_OPERATION )
|
||||
i2c.Bus.CTRLA = (sam.SERCOM_I2CM_CTRLA_MODE_I2C_MASTER << sam.SERCOM_I2CM_CTRLA_MODE_Pos) // |
|
||||
|
||||
i2c.SetBaudRate(config.Frequency)
|
||||
|
||||
// Enable I2CM port.
|
||||
// sercom->USART.CTRLA.bit.ENABLE = 0x1u;
|
||||
i2c.Bus.CTRLA |= sam.SERCOM_I2CM_CTRLA_ENABLE
|
||||
for (i2c.Bus.SYNCBUSY & sam.SERCOM_I2CM_SYNCBUSY_ENABLE) > 0 {
|
||||
}
|
||||
|
||||
// set bus idle mode
|
||||
i2c.Bus.STATUS |= (wireIdleState << sam.SERCOM_I2CM_STATUS_BUSSTATE_Pos)
|
||||
for (i2c.Bus.SYNCBUSY & sam.SERCOM_I2CM_SYNCBUSY_SYSOP) > 0 {
|
||||
}
|
||||
|
||||
// enable pins
|
||||
GPIO{SDA_PIN}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
|
||||
GPIO{SCL_PIN}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the I2C.
|
||||
func (i2c I2C) SetBaudRate(br uint32) {
|
||||
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
|
||||
// SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
|
||||
baud := CPU_FREQUENCY/(2*br) - 5 - (((CPU_FREQUENCY / 1000000) * riseTimeNanoseconds) / (2 * 1000))
|
||||
i2c.Bus.BAUD = sam.RegValue(baud)
|
||||
}
|
||||
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
var err error
|
||||
if len(w) != 0 {
|
||||
// send start/address for write
|
||||
i2c.sendAddress(addr, true)
|
||||
|
||||
// wait until transmission complete
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_MB) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on ready to write data")
|
||||
}
|
||||
}
|
||||
|
||||
// ACK received (0: ACK, 1: NACK)
|
||||
if (i2c.Bus.STATUS & sam.SERCOM_I2CM_STATUS_RXNACK) > 0 {
|
||||
return errors.New("I2C write error: expected ACK not NACK")
|
||||
}
|
||||
|
||||
// write data
|
||||
for _, b := range w {
|
||||
err = i2c.WriteByte(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = i2c.signalStop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(r) != 0 {
|
||||
// send start/address for read
|
||||
i2c.sendAddress(addr, false)
|
||||
|
||||
// wait transmission complete
|
||||
for (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_SB) == 0 {
|
||||
// If the slave NACKS the address, the MB bit will be set.
|
||||
// In that case, send a stop condition and return error.
|
||||
if (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_MB) > 0 {
|
||||
i2c.Bus.CTRLB |= (wireCmdStop << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Stop condition
|
||||
return errors.New("I2C read error: expected ACK not NACK")
|
||||
}
|
||||
}
|
||||
|
||||
// ACK received (0: ACK, 1: NACK)
|
||||
if (i2c.Bus.STATUS & sam.SERCOM_I2CM_STATUS_RXNACK) > 0 {
|
||||
return errors.New("I2C read error: expected ACK not NACK")
|
||||
}
|
||||
|
||||
// read first byte
|
||||
r[0] = i2c.readByte()
|
||||
for i := 1; i < len(r); i++ {
|
||||
// Send an ACK
|
||||
i2c.Bus.CTRLB &^= sam.SERCOM_I2CM_CTRLB_ACKACT
|
||||
|
||||
i2c.signalRead()
|
||||
|
||||
// Read data and send the ACK
|
||||
r[i] = i2c.readByte()
|
||||
}
|
||||
|
||||
// Send NACK to end transmission
|
||||
i2c.Bus.CTRLB |= sam.SERCOM_I2CM_CTRLB_ACKACT
|
||||
|
||||
err = i2c.signalStop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte to the I2C bus.
|
||||
func (i2c I2C) WriteByte(data byte) error {
|
||||
// Send data byte
|
||||
i2c.Bus.DATA = sam.RegValue8(data)
|
||||
|
||||
// wait until transmission successful
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_MB) == 0 {
|
||||
// check for bus error
|
||||
if (sam.SERCOM3_I2CM.STATUS & sam.SERCOM_I2CM_STATUS_BUSERR) > 0 {
|
||||
return errors.New("I2C bus error")
|
||||
}
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on write data")
|
||||
}
|
||||
}
|
||||
|
||||
if (i2c.Bus.STATUS & sam.SERCOM_I2CM_STATUS_RXNACK) > 0 {
|
||||
return errors.New("I2C write error: expected ACK not NACK")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendAddress sends the address and start signal
|
||||
func (i2c I2C) sendAddress(address uint16, write bool) error {
|
||||
data := (address << 1)
|
||||
if !write {
|
||||
data |= 1 // set read flag
|
||||
}
|
||||
|
||||
// wait until bus ready
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.STATUS&(wireIdleState<<sam.SERCOM_I2CM_STATUS_BUSSTATE_Pos)) == 0 &&
|
||||
(i2c.Bus.STATUS&(wireOwnerState<<sam.SERCOM_I2CM_STATUS_BUSSTATE_Pos)) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on bus ready")
|
||||
}
|
||||
}
|
||||
i2c.Bus.ADDR = sam.RegValue(data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) signalStop() error {
|
||||
i2c.Bus.CTRLB |= (wireCmdStop << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Stop command
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.SYNCBUSY & sam.SERCOM_I2CM_SYNCBUSY_SYSOP) > 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on signal stop")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) signalRead() error {
|
||||
i2c.Bus.CTRLB |= (wireCmdRead << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Read command
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.SYNCBUSY & sam.SERCOM_I2CM_SYNCBUSY_SYSOP) > 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on signal read")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) readByte() byte {
|
||||
for (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_SB) == 0 {
|
||||
}
|
||||
return byte(i2c.Bus.DATA)
|
||||
}
|
||||
|
||||
// PWM
|
||||
const period = 0xFFFF
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// turn on timer clocks used for PWM
|
||||
sam.PM.APBCMASK |= sam.PM_APBCMASK_TCC0_ | sam.PM_APBCMASK_TCC1_ | sam.PM_APBCMASK_TCC2_
|
||||
|
||||
// Use GCLK0 for TCC0/TCC1
|
||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_TCC0_TCC1 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
for (sam.GCLK.STATUS & sam.GCLK_STATUS_SYNCBUSY) > 0 {
|
||||
}
|
||||
|
||||
// Use GCLK0 for TCC2/TC3
|
||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_TCC2_TC3 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
for (sam.GCLK.STATUS & sam.GCLK_STATUS_SYNCBUSY) > 0 {
|
||||
}
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() {
|
||||
// figure out which TCCX timer for this pin
|
||||
timer := pwm.getTimer()
|
||||
|
||||
// disable timer
|
||||
timer.CTRLA &^= sam.TCC_CTRLA_ENABLE
|
||||
// Wait for synchronization
|
||||
for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_ENABLE) > 0 {
|
||||
}
|
||||
|
||||
// Use "Normal PWM" (single-slope PWM)
|
||||
timer.WAVE |= sam.TCC_WAVE_WAVEGEN_NPWM
|
||||
// Wait for synchronization
|
||||
for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_WAVE) > 0 {
|
||||
}
|
||||
|
||||
// Set the period (the number to count to (TOP) before resetting timer)
|
||||
//TCC0->PER.reg = period;
|
||||
timer.PER = period
|
||||
// Wait for synchronization
|
||||
for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_PER) > 0 {
|
||||
}
|
||||
|
||||
// Set pin as output
|
||||
sam.PORT.DIRSET0 = (1 << pwm.Pin)
|
||||
// Set pin to low
|
||||
sam.PORT.OUTCLR0 = (1 << pwm.Pin)
|
||||
|
||||
// Enable the port multiplexer for pin
|
||||
pwm.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
||||
|
||||
// Connect TCCX timer to pin.
|
||||
// we normally use the F channel aka ALT
|
||||
pwmConfig := GPIO_PWM_ALT
|
||||
|
||||
// in the case of PA6 or PA7 we have to use E channel
|
||||
if pwm.Pin == 6 || pwm.Pin == 7 {
|
||||
pwmConfig = GPIO_PWM
|
||||
}
|
||||
|
||||
if pwm.Pin&1 > 0 {
|
||||
// odd pin, so save the even pins
|
||||
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||
pwm.setPMux(val | sam.RegValue8(pwmConfig<<sam.PORT_PMUX0_PMUXO_Pos))
|
||||
} else {
|
||||
// even pin, so save the odd pins
|
||||
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||
pwm.setPMux(val | sam.RegValue8(pwmConfig<<sam.PORT_PMUX0_PMUXE_Pos))
|
||||
}
|
||||
}
|
||||
|
||||
// Set turns on the duty cycle for a PWM pin using the provided value.
|
||||
func (pwm PWM) Set(value uint16) {
|
||||
// figure out which TCCX timer for this pin
|
||||
timer := pwm.getTimer()
|
||||
|
||||
// disable output
|
||||
timer.CTRLA &^= sam.TCC_CTRLA_ENABLE
|
||||
|
||||
// Wait for synchronization
|
||||
for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_ENABLE) > 0 {
|
||||
}
|
||||
|
||||
// Set PWM signal to output duty cycle
|
||||
pwm.setChannel(sam.RegValue(value))
|
||||
|
||||
// Wait for synchronization on all channels
|
||||
for (timer.SYNCBUSY & (sam.TCC_SYNCBUSY_CC0 |
|
||||
sam.TCC_SYNCBUSY_CC1 |
|
||||
sam.TCC_SYNCBUSY_CC2 |
|
||||
sam.TCC_SYNCBUSY_CC3)) > 0 {
|
||||
}
|
||||
|
||||
// enable
|
||||
timer.CTRLA |= sam.TCC_CTRLA_ENABLE
|
||||
// Wait for synchronization
|
||||
for (timer.SYNCBUSY & sam.TCC_SYNCBUSY_ENABLE) > 0 {
|
||||
}
|
||||
}
|
||||
|
||||
// getPMux returns the value for the correct PMUX register for this pin.
|
||||
func (pwm PWM) getPMux() sam.RegValue8 {
|
||||
return getPMux(pwm.Pin)
|
||||
}
|
||||
|
||||
// setPMux sets the value for the correct PMUX register for this pin.
|
||||
func (pwm PWM) setPMux(val sam.RegValue8) {
|
||||
setPMux(pwm.Pin, val)
|
||||
}
|
||||
|
||||
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||
func (pwm PWM) getPinCfg() sam.RegValue8 {
|
||||
return getPinCfg(pwm.Pin)
|
||||
}
|
||||
|
||||
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||
func (pwm PWM) setPinCfg(val sam.RegValue8) {
|
||||
setPinCfg(pwm.Pin, val)
|
||||
}
|
||||
|
||||
// getPMux returns the value for the correct PMUX register for this pin.
|
||||
func getPMux(p uint8) sam.RegValue8 {
|
||||
pin := p >> 1
|
||||
switch pin {
|
||||
case 0:
|
||||
return sam.PORT.PMUX0_0
|
||||
case 1:
|
||||
return sam.PORT.PMUX0_1
|
||||
case 2:
|
||||
return sam.PORT.PMUX0_2
|
||||
case 3:
|
||||
return sam.PORT.PMUX0_3
|
||||
case 4:
|
||||
return sam.PORT.PMUX0_4
|
||||
case 5:
|
||||
return sam.PORT.PMUX0_5
|
||||
case 6:
|
||||
return sam.PORT.PMUX0_6
|
||||
case 7:
|
||||
return sam.PORT.PMUX0_7
|
||||
case 8:
|
||||
return sam.PORT.PMUX0_8
|
||||
case 9:
|
||||
return sam.PORT.PMUX0_9
|
||||
case 10:
|
||||
return sam.PORT.PMUX0_10
|
||||
case 11:
|
||||
return sam.PORT.PMUX0_11
|
||||
case 12:
|
||||
return sam.PORT.PMUX0_12
|
||||
case 13:
|
||||
return sam.PORT.PMUX0_13
|
||||
case 14:
|
||||
return sam.PORT.PMUX0_14
|
||||
case 15:
|
||||
return sam.PORT.PMUX0_15
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// setPMux sets the value for the correct PMUX register for this pin.
|
||||
func setPMux(p uint8, val sam.RegValue8) {
|
||||
pin := p >> 1
|
||||
switch pin {
|
||||
case 0:
|
||||
sam.PORT.PMUX0_0 = val
|
||||
case 1:
|
||||
sam.PORT.PMUX0_1 = val
|
||||
case 2:
|
||||
sam.PORT.PMUX0_2 = val
|
||||
case 3:
|
||||
sam.PORT.PMUX0_3 = val
|
||||
case 4:
|
||||
sam.PORT.PMUX0_4 = val
|
||||
case 5:
|
||||
sam.PORT.PMUX0_5 = val
|
||||
case 6:
|
||||
sam.PORT.PMUX0_6 = val
|
||||
case 7:
|
||||
sam.PORT.PMUX0_7 = val
|
||||
case 8:
|
||||
sam.PORT.PMUX0_8 = val
|
||||
case 9:
|
||||
sam.PORT.PMUX0_9 = val
|
||||
case 10:
|
||||
sam.PORT.PMUX0_10 = val
|
||||
case 11:
|
||||
sam.PORT.PMUX0_11 = val
|
||||
case 12:
|
||||
sam.PORT.PMUX0_12 = val
|
||||
case 13:
|
||||
sam.PORT.PMUX0_13 = val
|
||||
case 14:
|
||||
sam.PORT.PMUX0_14 = val
|
||||
case 15:
|
||||
sam.PORT.PMUX0_15 = val
|
||||
}
|
||||
}
|
||||
|
||||
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||
func getPinCfg(p uint8) sam.RegValue8 {
|
||||
switch p {
|
||||
case 0:
|
||||
return sam.PORT.PINCFG0_0
|
||||
case 1:
|
||||
return sam.PORT.PINCFG0_1
|
||||
case 2:
|
||||
return sam.PORT.PINCFG0_2
|
||||
case 3:
|
||||
return sam.PORT.PINCFG0_3
|
||||
case 4:
|
||||
return sam.PORT.PINCFG0_4
|
||||
case 5:
|
||||
return sam.PORT.PINCFG0_5
|
||||
case 6:
|
||||
return sam.PORT.PINCFG0_6
|
||||
case 7:
|
||||
return sam.PORT.PINCFG0_7
|
||||
case 8:
|
||||
return sam.PORT.PINCFG0_8
|
||||
case 9:
|
||||
return sam.PORT.PINCFG0_9
|
||||
case 10:
|
||||
return sam.PORT.PINCFG0_10
|
||||
case 11:
|
||||
return sam.PORT.PINCFG0_11
|
||||
case 12:
|
||||
return sam.PORT.PINCFG0_12
|
||||
case 13:
|
||||
return sam.PORT.PINCFG0_13
|
||||
case 14:
|
||||
return sam.PORT.PINCFG0_14
|
||||
case 15:
|
||||
return sam.PORT.PINCFG0_15
|
||||
case 16:
|
||||
return sam.PORT.PINCFG0_16
|
||||
case 17:
|
||||
return sam.PORT.PINCFG0_17
|
||||
case 18:
|
||||
return sam.PORT.PINCFG0_18
|
||||
case 19:
|
||||
return sam.PORT.PINCFG0_19
|
||||
case 20:
|
||||
return sam.PORT.PINCFG0_20
|
||||
case 21:
|
||||
return sam.PORT.PINCFG0_21
|
||||
case 22:
|
||||
return sam.PORT.PINCFG0_22
|
||||
case 23:
|
||||
return sam.PORT.PINCFG0_23
|
||||
case 24:
|
||||
return sam.PORT.PINCFG0_24
|
||||
case 25:
|
||||
return sam.PORT.PINCFG0_25
|
||||
case 26:
|
||||
return sam.PORT.PINCFG0_26
|
||||
case 27:
|
||||
return sam.PORT.PINCFG0_27
|
||||
case 28:
|
||||
return sam.PORT.PINCFG0_28
|
||||
case 29:
|
||||
return sam.PORT.PINCFG0_29
|
||||
case 30:
|
||||
return sam.PORT.PINCFG0_30
|
||||
case 31:
|
||||
return sam.PORT.PINCFG0_31
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||
func setPinCfg(p uint8, val sam.RegValue8) {
|
||||
switch p {
|
||||
case 0:
|
||||
sam.PORT.PINCFG0_0 = val
|
||||
case 1:
|
||||
sam.PORT.PINCFG0_1 = val
|
||||
case 2:
|
||||
sam.PORT.PINCFG0_2 = val
|
||||
case 3:
|
||||
sam.PORT.PINCFG0_3 = val
|
||||
case 4:
|
||||
sam.PORT.PINCFG0_4 = val
|
||||
case 5:
|
||||
sam.PORT.PINCFG0_5 = val
|
||||
case 6:
|
||||
sam.PORT.PINCFG0_6 = val
|
||||
case 7:
|
||||
sam.PORT.PINCFG0_7 = val
|
||||
case 8:
|
||||
sam.PORT.PINCFG0_8 = val
|
||||
case 9:
|
||||
sam.PORT.PINCFG0_9 = val
|
||||
case 10:
|
||||
sam.PORT.PINCFG0_10 = val
|
||||
case 11:
|
||||
sam.PORT.PINCFG0_11 = val
|
||||
case 12:
|
||||
sam.PORT.PINCFG0_12 = val
|
||||
case 13:
|
||||
sam.PORT.PINCFG0_13 = val
|
||||
case 14:
|
||||
sam.PORT.PINCFG0_14 = val
|
||||
case 15:
|
||||
sam.PORT.PINCFG0_15 = val
|
||||
case 16:
|
||||
sam.PORT.PINCFG0_16 = val
|
||||
case 17:
|
||||
sam.PORT.PINCFG0_17 = val
|
||||
case 18:
|
||||
sam.PORT.PINCFG0_18 = val
|
||||
case 19:
|
||||
sam.PORT.PINCFG0_19 = val
|
||||
case 20:
|
||||
sam.PORT.PINCFG0_20 = val
|
||||
case 21:
|
||||
sam.PORT.PINCFG0_21 = val
|
||||
case 22:
|
||||
sam.PORT.PINCFG0_22 = val
|
||||
case 23:
|
||||
sam.PORT.PINCFG0_23 = val
|
||||
case 24:
|
||||
sam.PORT.PINCFG0_24 = val
|
||||
case 25:
|
||||
sam.PORT.PINCFG0_25 = val
|
||||
case 26:
|
||||
sam.PORT.PINCFG0_26 = val
|
||||
case 27:
|
||||
sam.PORT.PINCFG0_27 = val
|
||||
case 28:
|
||||
sam.PORT.PINCFG0_28 = val
|
||||
case 29:
|
||||
sam.PORT.PINCFG0_29 = val
|
||||
case 30:
|
||||
sam.PORT.PINCFG0_30 = val
|
||||
case 31:
|
||||
sam.PORT.PINCFG0_31 = val
|
||||
}
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case 6:
|
||||
return sam.TCC1
|
||||
case 7:
|
||||
return sam.TCC1
|
||||
case 8:
|
||||
return sam.TCC1
|
||||
case 9:
|
||||
return sam.TCC1
|
||||
case 14:
|
||||
return sam.TCC0
|
||||
case 15:
|
||||
return sam.TCC0
|
||||
case 16:
|
||||
return sam.TCC0
|
||||
case 17:
|
||||
return sam.TCC0
|
||||
case 18:
|
||||
return sam.TCC0
|
||||
case 19:
|
||||
return sam.TCC0
|
||||
case 20:
|
||||
return sam.TCC0
|
||||
case 21:
|
||||
return sam.TCC0
|
||||
default:
|
||||
return nil // not supported on this pin
|
||||
}
|
||||
}
|
||||
|
||||
// setChannel sets the value for the correct channel for PWM on this pin
|
||||
func (pwm PWM) setChannel(val sam.RegValue) {
|
||||
switch pwm.Pin {
|
||||
case 6:
|
||||
pwm.getTimer().CC0 = val
|
||||
case 7:
|
||||
pwm.getTimer().CC1 = val
|
||||
case 8:
|
||||
pwm.getTimer().CC0 = val
|
||||
case 9:
|
||||
pwm.getTimer().CC1 = val
|
||||
case 14:
|
||||
pwm.getTimer().CC0 = val
|
||||
case 15:
|
||||
pwm.getTimer().CC1 = val
|
||||
case 16:
|
||||
pwm.getTimer().CC2 = val
|
||||
case 17:
|
||||
pwm.getTimer().CC3 = val
|
||||
case 18:
|
||||
pwm.getTimer().CC2 = val
|
||||
case 19:
|
||||
pwm.getTimer().CC3 = val
|
||||
case 20:
|
||||
pwm.getTimer().CC2 = val
|
||||
case 21:
|
||||
pwm.getTimer().CC3 = val
|
||||
default:
|
||||
return // not supported on this pin
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"device/nrf"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 16000000
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) {
|
||||
return nrf.GPIO, p.Pin
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 64000000
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) {
|
||||
return nrf.P0, p.Pin
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 64000000
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) {
|
||||
if p.Pin >= 32 {
|
||||
|
||||
@@ -0,0 +1,599 @@
|
||||
// +build sam
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"device/sam"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const deviceDescriptorSize = 18
|
||||
|
||||
// DeviceDescriptor implements the USB standard device descriptor.
|
||||
//
|
||||
// Table 9-8. Standard Device Descriptor
|
||||
// bLength, bDescriptorType, bcdUSB, bDeviceClass, bDeviceSubClass, bDeviceProtocol, bMaxPacketSize0,
|
||||
// idVendor, idProduct, bcdDevice, iManufacturer, iProduct, iSerialNumber, bNumConfigurations */
|
||||
//
|
||||
type DeviceDescriptor struct {
|
||||
bLength uint8 // 18
|
||||
bDescriptorType uint8 // 1 USB_DEVICE_DESCRIPTOR_TYPE
|
||||
bcdUSB uint16 // 0x200
|
||||
bDeviceClass uint8
|
||||
bDeviceSubClass uint8
|
||||
bDeviceProtocol uint8
|
||||
bMaxPacketSize0 uint8 // Packet 0
|
||||
idVendor uint16
|
||||
idProduct uint16
|
||||
bcdDevice uint16 // 0x100
|
||||
iManufacturer uint8
|
||||
iProduct uint8
|
||||
iSerialNumber uint8
|
||||
bNumConfigurations uint8
|
||||
}
|
||||
|
||||
// NewDeviceDescriptor returns a USB DeviceDescriptor.
|
||||
func NewDeviceDescriptor(class, subClass, proto, packetSize0 uint8, vid, pid, version uint16, im, ip, is, configs uint8) DeviceDescriptor {
|
||||
return DeviceDescriptor{deviceDescriptorSize, 1, 0x200, class, subClass, proto, packetSize0, vid, pid, version, im, ip, is, configs}
|
||||
}
|
||||
|
||||
// Bytes returns DeviceDescriptor data
|
||||
func (d DeviceDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, deviceDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bcdUSB)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDeviceClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDeviceSubClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDeviceProtocol)
|
||||
binary.Write(buf, binary.LittleEndian, d.bMaxPacketSize0)
|
||||
binary.Write(buf, binary.LittleEndian, d.idVendor)
|
||||
binary.Write(buf, binary.LittleEndian, d.idProduct)
|
||||
binary.Write(buf, binary.LittleEndian, d.bcdDevice)
|
||||
binary.Write(buf, binary.LittleEndian, d.iManufacturer)
|
||||
binary.Write(buf, binary.LittleEndian, d.iProduct)
|
||||
binary.Write(buf, binary.LittleEndian, d.iSerialNumber)
|
||||
binary.Write(buf, binary.LittleEndian, d.bNumConfigurations)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const configDescriptorSize = 9
|
||||
|
||||
// ConfigDescriptor implements the standard USB configuration descriptor.
|
||||
//
|
||||
// Table 9-10. Standard Configuration Descriptor
|
||||
// bLength, bDescriptorType, wTotalLength, bNumInterfaces, bConfigurationValue, iConfiguration
|
||||
// bmAttributes, bMaxPower
|
||||
//
|
||||
type ConfigDescriptor struct {
|
||||
bLength uint8 // 9
|
||||
bDescriptorType uint8 // 2
|
||||
wTotalLength uint16 // total length
|
||||
bNumInterfaces uint8
|
||||
bConfigurationValue uint8
|
||||
iConfiguration uint8
|
||||
bmAttributes uint8
|
||||
bMaxPower uint8
|
||||
}
|
||||
|
||||
// NewConfigDescriptor returns a new USB ConfigDescriptor.
|
||||
func NewConfigDescriptor(totalLength uint16, interfaces uint8) ConfigDescriptor {
|
||||
return ConfigDescriptor{configDescriptorSize, 2, totalLength, interfaces, 1, 0, usb_CONFIG_BUS_POWERED | usb_CONFIG_REMOTE_WAKEUP, 50}
|
||||
}
|
||||
|
||||
// Bytes returns ConfigDescriptor data.
|
||||
func (d ConfigDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, configDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.wTotalLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bNumInterfaces)
|
||||
binary.Write(buf, binary.LittleEndian, d.bConfigurationValue)
|
||||
binary.Write(buf, binary.LittleEndian, d.iConfiguration)
|
||||
binary.Write(buf, binary.LittleEndian, d.bmAttributes)
|
||||
binary.Write(buf, binary.LittleEndian, d.bMaxPower)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const interfaceDescriptorSize = 9
|
||||
|
||||
// InterfaceDescriptor implements the standard USB interface descriptor.
|
||||
//
|
||||
// Table 9-12. Standard Interface Descriptor
|
||||
// bLength, bDescriptorType, bInterfaceNumber, bAlternateSetting, bNumEndpoints, bInterfaceClass,
|
||||
// bInterfaceSubClass, bInterfaceProtocol, iInterface
|
||||
//
|
||||
type InterfaceDescriptor struct {
|
||||
bLength uint8 // 9
|
||||
bDescriptorType uint8 // 4
|
||||
bInterfaceNumber uint8
|
||||
bAlternateSetting uint8
|
||||
bNumEndpoints uint8
|
||||
bInterfaceClass uint8
|
||||
bInterfaceSubClass uint8
|
||||
bInterfaceProtocol uint8
|
||||
iInterface uint8
|
||||
}
|
||||
|
||||
// NewInterfaceDescriptor returns a new USB InterfaceDescriptor.
|
||||
func NewInterfaceDescriptor(n, numEndpoints, class, subClass, protocol uint8) InterfaceDescriptor {
|
||||
return InterfaceDescriptor{interfaceDescriptorSize, 4, n, 0, numEndpoints, class, subClass, protocol, 0}
|
||||
}
|
||||
|
||||
// Bytes returns InterfaceDescriptor data.
|
||||
func (d InterfaceDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, interfaceDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceNumber)
|
||||
binary.Write(buf, binary.LittleEndian, d.bAlternateSetting)
|
||||
binary.Write(buf, binary.LittleEndian, d.bNumEndpoints)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceSubClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceProtocol)
|
||||
binary.Write(buf, binary.LittleEndian, d.iInterface)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const endpointDescriptorSize = 7
|
||||
|
||||
// EndpointDescriptor implements the standard USB endpoint descriptor.
|
||||
//
|
||||
// Table 9-13. Standard Endpoint Descriptor
|
||||
// bLength, bDescriptorType, bEndpointAddress, bmAttributes, wMaxPacketSize, bInterval
|
||||
//
|
||||
type EndpointDescriptor struct {
|
||||
bLength uint8 // 7
|
||||
bDescriptorType uint8 // 5
|
||||
bEndpointAddress uint8
|
||||
bmAttributes uint8
|
||||
wMaxPacketSize uint16
|
||||
bInterval uint8
|
||||
}
|
||||
|
||||
// NewEndpointDescriptor returns a new USB EndpointDescriptor.
|
||||
func NewEndpointDescriptor(addr, attr uint8, packetSize uint16, interval uint8) EndpointDescriptor {
|
||||
return EndpointDescriptor{endpointDescriptorSize, 5, addr, attr, packetSize, interval}
|
||||
}
|
||||
|
||||
// Bytes returns EndpointDescriptor data.
|
||||
func (d EndpointDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, endpointDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bEndpointAddress)
|
||||
binary.Write(buf, binary.LittleEndian, d.bmAttributes)
|
||||
binary.Write(buf, binary.LittleEndian, d.wMaxPacketSize)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterval)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const iadDescriptorSize = 8
|
||||
|
||||
// IADDescriptor is an Interface Association Descriptor, which is used
|
||||
// to bind 2 interfaces together in CDC composite device.
|
||||
//
|
||||
// Standard Interface Association Descriptor:
|
||||
// bLength, bDescriptorType, bFirstInterface, bInterfaceCount, bFunctionClass, bFunctionSubClass,
|
||||
// bFunctionProtocol, iFunction
|
||||
//
|
||||
type IADDescriptor struct {
|
||||
bLength uint8 // 8
|
||||
bDescriptorType uint8 // 11
|
||||
bFirstInterface uint8
|
||||
bInterfaceCount uint8
|
||||
bFunctionClass uint8
|
||||
bFunctionSubClass uint8
|
||||
bFunctionProtocol uint8
|
||||
iFunction uint8
|
||||
}
|
||||
|
||||
// NewIADDescriptor returns a new USB IADDescriptor.
|
||||
func NewIADDescriptor(firstInterface, count, class, subClass, protocol uint8) IADDescriptor {
|
||||
return IADDescriptor{iadDescriptorSize, 11, firstInterface, count, class, subClass, protocol, 0}
|
||||
}
|
||||
|
||||
// Bytes returns IADDescriptor data.
|
||||
func (d IADDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, iadDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bFirstInterface)
|
||||
binary.Write(buf, binary.LittleEndian, d.bInterfaceCount)
|
||||
binary.Write(buf, binary.LittleEndian, d.bFunctionClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bFunctionSubClass)
|
||||
binary.Write(buf, binary.LittleEndian, d.bFunctionProtocol)
|
||||
binary.Write(buf, binary.LittleEndian, d.iFunction)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const cdcCSInterfaceDescriptorSize = 5
|
||||
|
||||
// CDCCSInterfaceDescriptor is a CDC CS interface descriptor.
|
||||
type CDCCSInterfaceDescriptor struct {
|
||||
len uint8 // 5
|
||||
dtype uint8 // 0x24
|
||||
subtype uint8
|
||||
d0 uint8
|
||||
d1 uint8
|
||||
}
|
||||
|
||||
// NewCDCCSInterfaceDescriptor returns a new USB CDCCSInterfaceDescriptor.
|
||||
func NewCDCCSInterfaceDescriptor(subtype, d0, d1 uint8) CDCCSInterfaceDescriptor {
|
||||
return CDCCSInterfaceDescriptor{cdcCSInterfaceDescriptorSize, 0x24, subtype, d0, d1}
|
||||
}
|
||||
|
||||
// Bytes returns CDCCSInterfaceDescriptor data.
|
||||
func (d CDCCSInterfaceDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, cdcCSInterfaceDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.len)
|
||||
binary.Write(buf, binary.LittleEndian, d.dtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.subtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.d0)
|
||||
binary.Write(buf, binary.LittleEndian, d.d1)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const cmFunctionalDescriptorSize = 5
|
||||
|
||||
// CMFunctionalDescriptor is the functional descriptor general format.
|
||||
type CMFunctionalDescriptor struct {
|
||||
bFunctionLength uint8
|
||||
bDescriptorType uint8 // 0x24
|
||||
bDescriptorSubtype uint8 // 1
|
||||
bmCapabilities uint8
|
||||
bDataInterface uint8
|
||||
}
|
||||
|
||||
// NewCMFunctionalDescriptor returns a new USB CMFunctionalDescriptor.
|
||||
func NewCMFunctionalDescriptor(subtype, d0, d1 uint8) CMFunctionalDescriptor {
|
||||
return CMFunctionalDescriptor{5, 0x24, subtype, d0, d1}
|
||||
}
|
||||
|
||||
// Bytes returns the CMFunctionalDescriptor data.
|
||||
func (d CMFunctionalDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, cmFunctionalDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.bFunctionLength)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorType)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDescriptorSubtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.bmCapabilities)
|
||||
binary.Write(buf, binary.LittleEndian, d.bDataInterface)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
const acmFunctionalDescriptorSize = 4
|
||||
|
||||
// ACMFunctionalDescriptor is a Abstract Control Model (ACM) USB descriptor.
|
||||
type ACMFunctionalDescriptor struct {
|
||||
len uint8
|
||||
dtype uint8 // 0x24
|
||||
subtype uint8 // 1
|
||||
bmCapabilities uint8
|
||||
}
|
||||
|
||||
// NewACMFunctionalDescriptor returns a new USB ACMFunctionalDescriptor.
|
||||
func NewACMFunctionalDescriptor(subtype, d0 uint8) ACMFunctionalDescriptor {
|
||||
return ACMFunctionalDescriptor{4, 0x24, subtype, d0}
|
||||
}
|
||||
|
||||
// Bytes returns the ACMFunctionalDescriptor data.
|
||||
func (d ACMFunctionalDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, acmFunctionalDescriptorSize))
|
||||
binary.Write(buf, binary.LittleEndian, d.len)
|
||||
binary.Write(buf, binary.LittleEndian, d.dtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.subtype)
|
||||
binary.Write(buf, binary.LittleEndian, d.bmCapabilities)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// CDCDescriptor is the Communication Device Class (CDC) descriptor.
|
||||
type CDCDescriptor struct {
|
||||
// IAD
|
||||
iad IADDescriptor // Only needed on compound device
|
||||
|
||||
// Control
|
||||
cif InterfaceDescriptor
|
||||
header CDCCSInterfaceDescriptor
|
||||
|
||||
// CDC control
|
||||
controlManagement ACMFunctionalDescriptor // ACM
|
||||
functionalDescriptor CDCCSInterfaceDescriptor // CDC_UNION
|
||||
callManagement CMFunctionalDescriptor // Call Management
|
||||
cifin EndpointDescriptor
|
||||
|
||||
// CDC Data
|
||||
dif InterfaceDescriptor
|
||||
in EndpointDescriptor
|
||||
out EndpointDescriptor
|
||||
}
|
||||
|
||||
func NewCDCDescriptor(i IADDescriptor, c InterfaceDescriptor,
|
||||
h CDCCSInterfaceDescriptor,
|
||||
cm ACMFunctionalDescriptor,
|
||||
fd CDCCSInterfaceDescriptor,
|
||||
callm CMFunctionalDescriptor,
|
||||
ci EndpointDescriptor,
|
||||
di InterfaceDescriptor,
|
||||
inp EndpointDescriptor,
|
||||
outp EndpointDescriptor) CDCDescriptor {
|
||||
return CDCDescriptor{iad: i,
|
||||
cif: c,
|
||||
header: h,
|
||||
controlManagement: cm,
|
||||
functionalDescriptor: fd,
|
||||
callManagement: callm,
|
||||
cifin: ci,
|
||||
dif: di,
|
||||
in: inp,
|
||||
out: outp}
|
||||
}
|
||||
|
||||
const cdcSize = iadDescriptorSize +
|
||||
interfaceDescriptorSize +
|
||||
cdcCSInterfaceDescriptorSize +
|
||||
acmFunctionalDescriptorSize +
|
||||
cdcCSInterfaceDescriptorSize +
|
||||
cmFunctionalDescriptorSize +
|
||||
endpointDescriptorSize +
|
||||
interfaceDescriptorSize +
|
||||
endpointDescriptorSize +
|
||||
endpointDescriptorSize
|
||||
|
||||
// Bytes returns CDCDescriptor data.
|
||||
func (d CDCDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, cdcSize))
|
||||
buf.Write(d.iad.Bytes())
|
||||
buf.Write(d.cif.Bytes())
|
||||
buf.Write(d.header.Bytes())
|
||||
buf.Write(d.controlManagement.Bytes())
|
||||
buf.Write(d.functionalDescriptor.Bytes())
|
||||
buf.Write(d.callManagement.Bytes())
|
||||
buf.Write(d.cifin.Bytes())
|
||||
buf.Write(d.dif.Bytes())
|
||||
buf.Write(d.in.Bytes())
|
||||
buf.Write(d.out.Bytes())
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// MSCDescriptor is not used yet.
|
||||
type MSCDescriptor struct {
|
||||
msc InterfaceDescriptor
|
||||
in EndpointDescriptor
|
||||
out EndpointDescriptor
|
||||
}
|
||||
|
||||
type cdcLineInfo struct {
|
||||
dwDTERate uint32
|
||||
bCharFormat uint8
|
||||
bParityType uint8
|
||||
bDataBits uint8
|
||||
lineState uint8
|
||||
}
|
||||
|
||||
var (
|
||||
// TODO: allow setting these
|
||||
usb_STRING_LANGUAGE = [2]uint16{(3 << 8) | (2 + 2), 0x0409} // English
|
||||
usb_STRING_PRODUCT = "Arduino Zero"
|
||||
usb_STRING_MANUFACTURER = "Arduino"
|
||||
|
||||
usb_VID uint16 = 0x2341
|
||||
usb_PID uint16 = 0x004d
|
||||
)
|
||||
|
||||
const (
|
||||
usb_IMANUFACTURER = 1
|
||||
usb_IPRODUCT = 2
|
||||
usb_ISERIAL = 3
|
||||
|
||||
usb_ENDPOINT_TYPE_CONTROL = 0x00
|
||||
usb_ENDPOINT_TYPE_ISOCHRONOUS = 0x01
|
||||
usb_ENDPOINT_TYPE_BULK = 0x02
|
||||
usb_ENDPOINT_TYPE_INTERRUPT = 0x03
|
||||
|
||||
usb_DEVICE_DESCRIPTOR_TYPE = 1
|
||||
usb_CONFIGURATION_DESCRIPTOR_TYPE = 2
|
||||
usb_STRING_DESCRIPTOR_TYPE = 3
|
||||
usb_INTERFACE_DESCRIPTOR_TYPE = 4
|
||||
usb_ENDPOINT_DESCRIPTOR_TYPE = 5
|
||||
usb_DEVICE_QUALIFIER = 6
|
||||
usb_OTHER_SPEED_CONFIGURATION = 7
|
||||
|
||||
usbEndpointOut = 0x00
|
||||
usbEndpointIn = 0x80
|
||||
|
||||
usbEndpointPacketSize = 64 // 64 for Full Speed, EPT size max is 1024
|
||||
usb_EPT_NUM = 7
|
||||
|
||||
// standard requests
|
||||
usb_GET_STATUS = 0
|
||||
usb_CLEAR_FEATURE = 1
|
||||
usb_SET_FEATURE = 3
|
||||
usb_SET_ADDRESS = 5
|
||||
usb_GET_DESCRIPTOR = 6
|
||||
usb_SET_DESCRIPTOR = 7
|
||||
usb_GET_CONFIGURATION = 8
|
||||
usb_SET_CONFIGURATION = 9
|
||||
usb_GET_INTERFACE = 10
|
||||
usb_SET_INTERFACE = 11
|
||||
|
||||
usb_DEVICE_CLASS_COMMUNICATIONS = 0x02
|
||||
usb_DEVICE_CLASS_HUMAN_INTERFACE = 0x03
|
||||
usb_DEVICE_CLASS_STORAGE = 0x08
|
||||
usb_DEVICE_CLASS_VENDOR_SPECIFIC = 0xFF
|
||||
|
||||
usb_CONFIG_POWERED_MASK = 0x40
|
||||
usb_CONFIG_BUS_POWERED = 0x80
|
||||
usb_CONFIG_SELF_POWERED = 0xC0
|
||||
usb_CONFIG_REMOTE_WAKEUP = 0x20
|
||||
|
||||
// CDC
|
||||
usb_CDC_ACM_INTERFACE = 0 // CDC ACM
|
||||
usb_CDC_DATA_INTERFACE = 1 // CDC Data
|
||||
usb_CDC_FIRST_ENDPOINT = 1
|
||||
usb_CDC_ENDPOINT_ACM = 1
|
||||
usb_CDC_ENDPOINT_OUT = 2
|
||||
usb_CDC_ENDPOINT_IN = 3
|
||||
|
||||
// bmRequestType
|
||||
usb_REQUEST_HOSTTODEVICE = 0x00
|
||||
usb_REQUEST_DEVICETOHOST = 0x80
|
||||
usb_REQUEST_DIRECTION = 0x80
|
||||
|
||||
usb_REQUEST_STANDARD = 0x00
|
||||
usb_REQUEST_CLASS = 0x20
|
||||
usb_REQUEST_VENDOR = 0x40
|
||||
usb_REQUEST_TYPE = 0x60
|
||||
|
||||
usb_REQUEST_DEVICE = 0x00
|
||||
usb_REQUEST_INTERFACE = 0x01
|
||||
usb_REQUEST_ENDPOINT = 0x02
|
||||
usb_REQUEST_OTHER = 0x03
|
||||
usb_REQUEST_RECIPIENT = 0x1F
|
||||
|
||||
usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
|
||||
usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE = (usb_REQUEST_HOSTTODEVICE | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
|
||||
usb_REQUEST_DEVICETOHOST_STANDARD_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_STANDARD | usb_REQUEST_INTERFACE)
|
||||
|
||||
// CDC Class requests
|
||||
usb_CDC_SET_LINE_CODING = 0x20
|
||||
usb_CDC_GET_LINE_CODING = 0x21
|
||||
usb_CDC_SET_CONTROL_LINE_STATE = 0x22
|
||||
usb_CDC_SEND_BREAK = 0x23
|
||||
|
||||
usb_CDC_V1_10 = 0x0110
|
||||
usb_CDC_COMMUNICATION_INTERFACE_CLASS = 0x02
|
||||
|
||||
usb_CDC_CALL_MANAGEMENT = 0x01
|
||||
usb_CDC_ABSTRACT_CONTROL_MODEL = 0x02
|
||||
usb_CDC_HEADER = 0x00
|
||||
usb_CDC_ABSTRACT_CONTROL_MANAGEMENT = 0x02
|
||||
usb_CDC_UNION = 0x06
|
||||
usb_CDC_CS_INTERFACE = 0x24
|
||||
usb_CDC_CS_ENDPOINT = 0x25
|
||||
usb_CDC_DATA_INTERFACE_CLASS = 0x0A
|
||||
)
|
||||
|
||||
// usbDeviceDescBank is the USB device endpoint descriptor.
|
||||
// typedef struct {
|
||||
// __IO USB_DEVICE_ADDR_Type ADDR; /**< \brief Offset: 0x000 (R/W 32) DEVICE_DESC_BANK Endpoint Bank, Adress of Data Buffer */
|
||||
// __IO USB_DEVICE_PCKSIZE_Type PCKSIZE; /**< \brief Offset: 0x004 (R/W 32) DEVICE_DESC_BANK Endpoint Bank, Packet Size */
|
||||
// __IO USB_DEVICE_EXTREG_Type EXTREG; /**< \brief Offset: 0x008 (R/W 16) DEVICE_DESC_BANK Endpoint Bank, Extended */
|
||||
// __IO USB_DEVICE_STATUS_BK_Type STATUS_BK; /**< \brief Offset: 0x00A (R/W 8) DEVICE_DESC_BANK Enpoint Bank, Status of Bank */
|
||||
// RoReg8 Reserved1[0x5];
|
||||
// } UsbDeviceDescBank;
|
||||
type usbDeviceDescBank struct {
|
||||
ADDR sam.RegValue
|
||||
PCKSIZE sam.RegValue
|
||||
EXTREG sam.RegValue16
|
||||
STATUS_BK sam.RegValue8
|
||||
_reserved [5]sam.RegValue8
|
||||
}
|
||||
|
||||
type usbDeviceDescriptor struct {
|
||||
DeviceDescBank [2]usbDeviceDescBank
|
||||
}
|
||||
|
||||
// typedef struct {
|
||||
// union {
|
||||
// uint8_t bmRequestType;
|
||||
// struct {
|
||||
// uint8_t direction : 5;
|
||||
// uint8_t type : 2;
|
||||
// uint8_t transferDirection : 1;
|
||||
// };
|
||||
// };
|
||||
// uint8_t bRequest;
|
||||
// uint8_t wValueL;
|
||||
// uint8_t wValueH;
|
||||
// uint16_t wIndex;
|
||||
// uint16_t wLength;
|
||||
// } USBSetup;
|
||||
type usbSetup struct {
|
||||
bmRequestType uint8
|
||||
bRequest uint8
|
||||
wValueL uint8
|
||||
wValueH uint8
|
||||
wIndex uint16
|
||||
wLength uint16
|
||||
}
|
||||
|
||||
func newUSBSetup(data []byte) usbSetup {
|
||||
buf := bytes.NewBuffer(data)
|
||||
u := usbSetup{}
|
||||
binary.Read(buf, binary.LittleEndian, &(u.bmRequestType))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.bRequest))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.wValueL))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.wValueH))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.wIndex))
|
||||
binary.Read(buf, binary.LittleEndian, &(u.wLength))
|
||||
return u
|
||||
}
|
||||
|
||||
// USBCDC is the serial interface that works over the USB port.
|
||||
// To implement the USBCDC interface for a board, you must declare a concrete type as follows:
|
||||
//
|
||||
// type USBCDC struct {
|
||||
// Buffer *RingBuffer
|
||||
// }
|
||||
//
|
||||
// You can also add additional members to this struct depending on your implementation,
|
||||
// but the *RingBuffer is required.
|
||||
// When you are declaring the USBCDC for your board, make sure that you also declare the
|
||||
// RingBuffer using the NewRingBuffer() function:
|
||||
//
|
||||
// USBCDC{Buffer: NewRingBuffer()}
|
||||
//
|
||||
|
||||
// Read from the RX buffer.
|
||||
func (usbcdc USBCDC) Read(data []byte) (n int, err error) {
|
||||
// check if RX buffer is empty
|
||||
size := usbcdc.Buffered()
|
||||
if size == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Make sure we do not read more from buffer than the data slice can hold.
|
||||
if len(data) < size {
|
||||
size = len(data)
|
||||
}
|
||||
|
||||
// only read number of bytes used from buffer
|
||||
for i := 0; i < size; i++ {
|
||||
v, _ := usbcdc.ReadByte()
|
||||
data[i] = v
|
||||
}
|
||||
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// Write data to the USBCDC.
|
||||
func (usbcdc USBCDC) Write(data []byte) (n int, err error) {
|
||||
for _, v := range data {
|
||||
usbcdc.WriteByte(v)
|
||||
}
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// ReadByte reads a single byte from the RX buffer.
|
||||
// If there is no data in the buffer, returns an error.
|
||||
func (usbcdc USBCDC) ReadByte() (byte, error) {
|
||||
// check if RX buffer is empty
|
||||
buf, ok := usbcdc.Buffer.Get()
|
||||
if !ok {
|
||||
return 0, errors.New("Buffer empty")
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Buffered returns the number of bytes currently stored in the RX buffer.
|
||||
func (usbcdc USBCDC) Buffered() int {
|
||||
return int(usbcdc.Buffer.Used())
|
||||
}
|
||||
|
||||
// Receive handles adding data to the UART's data buffer.
|
||||
// Usually called by the IRQ handler for a machine.
|
||||
func (usbcdc USBCDC) Receive(data byte) {
|
||||
usbcdc.Buffer.Put(data)
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build linux
|
||||
// +build darwin linux
|
||||
|
||||
package os
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// +build darwin
|
||||
|
||||
package runtime
|
||||
|
||||
const GOOS = "darwin"
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build sam,atsamd21g18a
|
||||
// +build sam,atsamd21
|
||||
|
||||
package runtime
|
||||
|
||||
@@ -24,8 +24,9 @@ func init() {
|
||||
initRTC()
|
||||
initUARTClock()
|
||||
initI2CClock()
|
||||
initUSBClock()
|
||||
|
||||
// connect to UART
|
||||
// connect to USB CDC interface
|
||||
machine.UART0.Configure(machine.UARTConfig{})
|
||||
}
|
||||
|
||||
@@ -205,11 +206,7 @@ func initRTC() {
|
||||
|
||||
// set Mode0 to 32-bit counter (mode 0) with prescaler 1 and GCLK2 is 32KHz/1
|
||||
sam.RTC_MODE0.CTRL = sam.RegValue16((sam.RTC_MODE0_CTRL_MODE_COUNT32 << sam.RTC_MODE0_CTRL_MODE_Pos) |
|
||||
(sam.RTC_MODE0_CTRL_PRESCALER_DIV1 << sam.RTC_MODE0_CTRL_PRESCALER_Pos) |
|
||||
sam.RTC_MODE0_CTRL_MATCHCLR)
|
||||
waitForSync()
|
||||
|
||||
sam.RTC_MODE0.COMP0 = 0xffffffff
|
||||
(sam.RTC_MODE0_CTRL_PRESCALER_DIV1 << sam.RTC_MODE0_CTRL_PRESCALER_Pos))
|
||||
waitForSync()
|
||||
|
||||
// re-enable RTC
|
||||
@@ -256,8 +253,8 @@ func ticks() timeUnit {
|
||||
sam.RTC_MODE0.READREQ = sam.RTC_MODE0_READREQ_RREQ
|
||||
waitForSync()
|
||||
|
||||
rtcCounter := uint64(sam.RTC_MODE0.COUNT) * 30 // each counter tick == 30.5us
|
||||
offset := (rtcCounter - timerLastCounter) // change since last measurement
|
||||
rtcCounter := (uint64(sam.RTC_MODE0.COUNT) * 305) / 10 // each counter tick == 30.5us
|
||||
offset := (rtcCounter - timerLastCounter) // change since last measurement
|
||||
timerLastCounter = rtcCounter
|
||||
timestamp += timeUnit(offset) // TODO: not precise
|
||||
return timestamp
|
||||
@@ -277,7 +274,7 @@ func timerSleep(ticks uint32) {
|
||||
|
||||
// set compare value
|
||||
cnt := sam.RTC_MODE0.COUNT
|
||||
sam.RTC_MODE0.COMP0 = sam.RegValue(uint32(cnt) + (ticks / 30)) // each counter tick == 30.5us
|
||||
sam.RTC_MODE0.COMP0 = sam.RegValue(uint32(cnt) + (ticks * 10 / 305)) // each counter tick == 30.5us
|
||||
waitForSync()
|
||||
|
||||
// enable IRQ for CMP0 compare
|
||||
@@ -335,3 +332,14 @@ func initI2CClock() {
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
waitForSync()
|
||||
}
|
||||
|
||||
func initUSBClock() {
|
||||
// Turn on clock for USB
|
||||
sam.PM.APBBMASK |= sam.PM_APBBMASK_USB_
|
||||
|
||||
// Put Generic Clock Generator 0 as source for Generic Clock Multiplexer 6 (USB reference)
|
||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_USB << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
waitForSync()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build linux
|
||||
// +build darwin linux
|
||||
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -189,6 +189,9 @@ func LoadTarget(target string) (*TargetSpec, error) {
|
||||
return nil, errors.New("expected a full LLVM target or a custom target in -target flag")
|
||||
}
|
||||
goos := tripleSplit[2]
|
||||
if strings.HasPrefix(goos, "darwin") {
|
||||
goos = "darwin"
|
||||
}
|
||||
goarch := map[string]string{ // map from LLVM arch to Go arch
|
||||
"i386": "386",
|
||||
"x86_64": "amd64",
|
||||
@@ -211,11 +214,15 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
BuildTags: []string{goos, goarch},
|
||||
Compiler: commands["clang"],
|
||||
Linker: "cc",
|
||||
LDFlags: []string{"-no-pie", "-Wl,--gc-sections"}, // WARNING: clang < 5.0 requires -nopie
|
||||
Objcopy: "objcopy",
|
||||
GDB: "gdb",
|
||||
GDBCmds: []string{"run"},
|
||||
}
|
||||
if goos == "darwin" {
|
||||
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
|
||||
} else {
|
||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||
}
|
||||
if goarch != runtime.GOARCH {
|
||||
// Some educated guesses as to how to invoke helper programs.
|
||||
if goarch == "arm" && goos == "linux" {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"inherits": ["cortex-m"],
|
||||
"llvm-target": "armv6m-none-eabi",
|
||||
"build-tags": ["atsamd21g18", "sam"],
|
||||
"build-tags": ["atsamd21g18", "atsamd21", "sam"],
|
||||
"cflags": [
|
||||
"--target=armv6m-none-eabi",
|
||||
"-Qunused-arguments"
|
||||
],
|
||||
"ldflags": [
|
||||
"-T", "targets/atsamd21g18.ld"
|
||||
"-T", "targets/atsamd21.ld"
|
||||
],
|
||||
"extra-files": [
|
||||
"src/device/sam/atsamd21g18a.s"
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"inherits": ["atsamd21g18a"],
|
||||
"build-tags": ["sam", "atsamd21g18a", "circuitplay_express"],
|
||||
"flash": "uf2conv.py {bin}"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"inherits": ["atsamd21g18"],
|
||||
"inherits": ["atsamd21g18a"],
|
||||
"build-tags": ["sam", "atsamd21g18a", "itsybitsy_m0"],
|
||||
"flash": "bossac -d -i -e -w -v -R --offset=0x2000 {hex}"
|
||||
}
|
||||
|
||||
Vendored
+7
@@ -1,6 +1,13 @@
|
||||
#include "main.h"
|
||||
|
||||
int global = 3;
|
||||
_Bool globalBool = 1;
|
||||
_Bool globalBool2 = 10; // test narrowing
|
||||
float globalFloat = 3.1;
|
||||
double globalDouble = 3.2;
|
||||
_Complex float globalComplexFloat = 4.1+3.3i;
|
||||
_Complex double globalComplexDouble = 4.2+3.4i;
|
||||
_Complex double globalComplexLongDouble = 4.3+3.5i;
|
||||
|
||||
int fortytwo() {
|
||||
return 42;
|
||||
|
||||
Vendored
+8
@@ -28,6 +28,14 @@ func main() {
|
||||
println("callback 1:", C.doCallback(20, 30, cb))
|
||||
cb = C.binop_t(C.mul)
|
||||
println("callback 2:", C.doCallback(20, 30, cb))
|
||||
|
||||
// more globals
|
||||
println("bool:", C.globalBool, C.globalBool2 == true)
|
||||
println("float:", C.globalFloat)
|
||||
println("double:", C.globalDouble)
|
||||
println("complex float:", C.globalComplexFloat)
|
||||
println("complex double:", C.globalComplexDouble)
|
||||
println("complex long double:", C.globalComplexLongDouble)
|
||||
}
|
||||
|
||||
//export mul
|
||||
|
||||
Vendored
+10
-1
@@ -3,9 +3,18 @@ int add(int a, int b);
|
||||
typedef int (*binop_t) (int, int);
|
||||
int doCallback(int a, int b, binop_t cb);
|
||||
typedef int * intPointer;
|
||||
extern int global;
|
||||
void store(int value, int *ptr);
|
||||
|
||||
// test globals
|
||||
extern int global;
|
||||
extern _Bool globalBool;
|
||||
extern _Bool globalBool2;
|
||||
extern float globalFloat;
|
||||
extern double globalDouble;
|
||||
extern _Complex float globalComplexFloat;
|
||||
extern _Complex double globalComplexDouble;
|
||||
extern _Complex double globalComplexLongDouble;
|
||||
|
||||
// test duplicate definitions
|
||||
int add(int a, int b);
|
||||
extern int global;
|
||||
|
||||
Vendored
+6
@@ -8,3 +8,9 @@ global: 3
|
||||
25: 25
|
||||
callback 1: 50
|
||||
callback 2: 600
|
||||
bool: true true
|
||||
float: +3.100000e+000
|
||||
double: +3.200000e+000
|
||||
complex float: (+4.100000e+000+3.300000e+000i)
|
||||
complex double: (+4.200000e+000+3.400000e+000i)
|
||||
complex long double: (+4.300000e+000+3.500000e+000i)
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ package main
|
||||
|
||||
// version of this package.
|
||||
// Update this value before release of new version of software.
|
||||
const version = "0.2.0"
|
||||
const version = "0.3.0"
|
||||
|
||||
Reference in New Issue
Block a user