mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-06 03:53:42 +00:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| da85710894 | |||
| 0ae467d3e2 | |||
| 4442b1304e | |||
| 83ab61e261 | |||
| 2504754325 | |||
| be491abc46 | |||
| 31189deb3b | |||
| 90cd3f8ea5 | |||
| 2f95a5d452 | |||
| eb1d834dd4 | |||
| 3313decb68 | |||
| 191a076956 | |||
| 0f2dcba7b3 | |||
| 94b8214529 | |||
| 421ef04efb | |||
| f7687c43aa | |||
| 5a7bab8808 | |||
| 3a73e64557 | |||
| 9f8340a970 | |||
| 0f6873cf02 | |||
| f2cd4d12e8 | |||
| 7e6a54ac62 | |||
| 7156afca9e | |||
| 87ac804642 | |||
| edcb11f9f6 | |||
| 3568254593 | |||
| 3bf4c06c99 | |||
| f1d9e7b75e | |||
| e4d53daa02 | |||
| 98a3047b58 | |||
| ba85c82fbb | |||
| 7b6ef65fe7 | |||
| de032cddd2 | |||
| 51c6b972bf | |||
| 4cd151faf5 | |||
| 5342d392aa | |||
| f94af9f61e | |||
| 7ada00790c | |||
| dfa713040a | |||
| 82dc14b741 | |||
| d3f2237d44 | |||
| fc2ed2bdd0 | |||
| e0cf74e638 | |||
| 6f6afb0515 | |||
| 397b90753c | |||
| 3c2639ad55 | |||
| 371c468e8e | |||
| 0a40219680 | |||
| c981f14e61 | |||
| 763b9d7d10 | |||
| 55fc7b904a | |||
| 17c42810d0 | |||
| 064d001550 | |||
| a4cd3bb77c | |||
| 8d3f19bc84 | |||
| d90f1947d9 | |||
| 11567c62d4 | |||
| 4619207f99 | |||
| 99587fe073 |
+17
-54
@@ -40,66 +40,36 @@ commands:
|
|||||||
sudo tar -C /usr/local -xf node-v10.15.1-linux-x64.tar.xz
|
sudo tar -C /usr/local -xf node-v10.15.1-linux-x64.tar.xz
|
||||||
sudo ln -s /usr/local/node-v10.15.1-linux-x64/bin/node /usr/bin/node
|
sudo ln -s /usr/local/node-v10.15.1-linux-x64/bin/node /usr/bin/node
|
||||||
rm node-v10.15.1-linux-x64.tar.xz
|
rm node-v10.15.1-linux-x64.tar.xz
|
||||||
dep:
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: "Install Go dependencies"
|
|
||||||
command: |
|
|
||||||
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
|
||||||
dep ensure --vendor-only
|
|
||||||
llvm-source-linux:
|
llvm-source-linux:
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-source-8-v2
|
- llvm-source-8-v3
|
||||||
- run:
|
- run:
|
||||||
name: "Fetch LLVM source"
|
name: "Fetch LLVM source"
|
||||||
command: make llvm-source
|
command: make llvm-source
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-source-8-v2
|
key: llvm-source-8-v3
|
||||||
paths:
|
paths:
|
||||||
- llvm
|
- llvm
|
||||||
smoketest:
|
smoketest:
|
||||||
steps:
|
steps:
|
||||||
- smoketest-no-avr
|
- run: make smoketest
|
||||||
- run: tinygo build -size short -o test.elf -target=arduino examples/blinky1
|
|
||||||
- run: tinygo build -size short -o test.elf -target=digispark examples/blinky1
|
|
||||||
smoketest-no-avr:
|
smoketest-no-avr:
|
||||||
steps:
|
steps:
|
||||||
- run: tinygo build -size short -o test.elf -target=pca10040 examples/blinky1
|
- run: make smoketest-no-avr
|
||||||
- run: tinygo build -size short -o test.elf -target=pca10040 examples/blinky2
|
|
||||||
- run: tinygo build -o blinky2 examples/blinky2 # TODO: re-enable -size flag with MachO support
|
|
||||||
- run: tinygo build -size short -o test.elf -target=pca10040 examples/test
|
|
||||||
- run: tinygo build -size short -o test.elf -target=microbit examples/echo
|
|
||||||
- run: tinygo build -size short -o test.elf -target=nrf52840-mdk examples/blinky1
|
|
||||||
- run: tinygo build -size short -o test.elf -target=pca10031 examples/blinky1
|
|
||||||
- run: tinygo build -size short -o test.elf -target=bluepill examples/blinky1
|
|
||||||
- run: tinygo build -size short -o test.elf -target=reelboard examples/blinky1
|
|
||||||
- run: tinygo build -size short -o test.elf -target=reelboard examples/blinky2
|
|
||||||
- run: tinygo build -size short -o test.elf -target=pca10056 examples/blinky1
|
|
||||||
- run: tinygo build -size short -o test.elf -target=pca10056 examples/blinky2
|
|
||||||
- run: tinygo build -size short -o test.elf -target=itsybitsy-m0 examples/blinky1
|
|
||||||
- run: tinygo build -size short -o test.elf -target=circuitplay-express examples/blinky1
|
|
||||||
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky1
|
|
||||||
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky2
|
|
||||||
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/export
|
|
||||||
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/main
|
|
||||||
test-linux:
|
test-linux:
|
||||||
parameters:
|
|
||||||
llvm:
|
|
||||||
type: string
|
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- submodules
|
- submodules
|
||||||
- apt-dependencies:
|
- apt-dependencies:
|
||||||
llvm: <<parameters.llvm>>
|
llvm: "-8"
|
||||||
- install-node
|
- install-node
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
- go-cache-{{ checksum "Gopkg.lock" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||||
- go-cache-{{ checksum "Gopkg.lock" }}
|
- go-cache-{{ checksum "Gopkg.lock" }}
|
||||||
- llvm-source-linux
|
- llvm-source-linux
|
||||||
- dep
|
|
||||||
- run: go install .
|
- run: go install .
|
||||||
- run: go test -v
|
- run: go test -v
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
@@ -136,7 +106,7 @@ commands:
|
|||||||
- llvm-source-linux
|
- llvm-source-linux
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-build-8-linux-v4
|
- llvm-build-8-linux-v5
|
||||||
- run:
|
- run:
|
||||||
name: "Build LLVM"
|
name: "Build LLVM"
|
||||||
command: |
|
command: |
|
||||||
@@ -154,7 +124,7 @@ commands:
|
|||||||
make llvm-build
|
make llvm-build
|
||||||
fi
|
fi
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-build-8-linux-v4
|
key: llvm-build-8-linux-v5
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- run:
|
- run:
|
||||||
@@ -163,7 +133,6 @@ commands:
|
|||||||
ln -s $PWD/llvm-build/bin/clang-8 /go/bin/clang-8
|
ln -s $PWD/llvm-build/bin/clang-8 /go/bin/clang-8
|
||||||
ln -s $PWD/llvm-build/bin/ld.lld /go/bin/ld.lld-8
|
ln -s $PWD/llvm-build/bin/ld.lld /go/bin/ld.lld-8
|
||||||
ln -s $PWD/llvm-build/bin/wasm-ld /go/bin/wasm-ld-8
|
ln -s $PWD/llvm-build/bin/wasm-ld /go/bin/wasm-ld-8
|
||||||
- dep
|
|
||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
command: make test
|
command: make test
|
||||||
@@ -194,20 +163,23 @@ commands:
|
|||||||
- run:
|
- run:
|
||||||
name: "Install dependencies"
|
name: "Install dependencies"
|
||||||
command: |
|
command: |
|
||||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install go dep qemu
|
curl https://dl.google.com/go/go1.12.5.darwin-amd64.tar.gz -o go1.12.5.darwin-amd64.tar.gz
|
||||||
|
sudo tar -C /usr/local -xzf go1.12.5.darwin-amd64.tar.gz
|
||||||
|
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||||
|
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-source-8-macos-v2
|
- llvm-source-8-macos-v3
|
||||||
- run:
|
- run:
|
||||||
name: "Fetch LLVM source"
|
name: "Fetch LLVM source"
|
||||||
command: make llvm-source
|
command: make llvm-source
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-source-8-macos-v2
|
key: llvm-source-8-macos-v3
|
||||||
paths:
|
paths:
|
||||||
- llvm
|
- llvm
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-build-8-macos-v3
|
- llvm-build-8-macos-v4
|
||||||
- run:
|
- run:
|
||||||
name: "Build LLVM"
|
name: "Build LLVM"
|
||||||
command: |
|
command: |
|
||||||
@@ -219,16 +191,13 @@ commands:
|
|||||||
make llvm-build
|
make llvm-build
|
||||||
fi
|
fi
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-build-8-macos-v3
|
key: llvm-build-8-macos-v4
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- run:
|
- run:
|
||||||
name: "Create LLVM symlinks"
|
name: "Create LLVM symlinks"
|
||||||
command: |
|
command: |
|
||||||
ln -s $PWD/llvm-build/bin/clang-8 /usr/local/bin/clang-8
|
ln -s $PWD/llvm-build/bin/clang-8 /usr/local/bin/clang-8
|
||||||
- run:
|
|
||||||
name: "Install Go dependencies"
|
|
||||||
command: dep ensure --vendor-only
|
|
||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
command: make test
|
command: make test
|
||||||
@@ -253,27 +222,21 @@ jobs:
|
|||||||
test-llvm8-go111:
|
test-llvm8-go111:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.11
|
- image: circleci/golang:1.11
|
||||||
working_directory: /go/src/github.com/tinygo-org/tinygo
|
|
||||||
steps:
|
steps:
|
||||||
- test-linux:
|
- test-linux
|
||||||
llvm: "-8"
|
|
||||||
test-llvm8-go112:
|
test-llvm8-go112:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.12
|
- image: circleci/golang:1.12
|
||||||
working_directory: /go/src/github.com/tinygo-org/tinygo
|
|
||||||
steps:
|
steps:
|
||||||
- test-linux:
|
- test-linux
|
||||||
llvm: "-8"
|
|
||||||
build-linux:
|
build-linux:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.12
|
- image: circleci/golang:1.12
|
||||||
working_directory: /go/src/github.com/tinygo-org/tinygo
|
|
||||||
steps:
|
steps:
|
||||||
- build-linux
|
- build-linux
|
||||||
build-macos:
|
build-macos:
|
||||||
macos:
|
macos:
|
||||||
xcode: "10.1.0"
|
xcode: "10.1.0"
|
||||||
working_directory: ~/go/src/github.com/tinygo-org/tinygo
|
|
||||||
steps:
|
steps:
|
||||||
- build-macos
|
- build-macos
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,52 @@
|
|||||||
|
0.6.0
|
||||||
|
---
|
||||||
|
* **command line**
|
||||||
|
- some portability improvements
|
||||||
|
- make `$GOROOT` more robust and configurable
|
||||||
|
- check for Clang at the Homebrew install location as fallback
|
||||||
|
* **compiler driver**
|
||||||
|
- support multiple variations of LLVM commands, for non-Debian distributions
|
||||||
|
* **compiler**
|
||||||
|
- improve code quality in multiple ways
|
||||||
|
- make panic configurable, adding trap on panic
|
||||||
|
- refactor many internal parts of the compiler
|
||||||
|
- print all errors encountered during compilation
|
||||||
|
- implement calling function values of a named type
|
||||||
|
- implement returning values from blocking functions
|
||||||
|
- allow larger-than-int values to be sent across a channel
|
||||||
|
- implement complex arithmetic
|
||||||
|
- improve hashmap support
|
||||||
|
- add debuginfo for function arguments
|
||||||
|
- insert nil checks on stores (increasing code size)
|
||||||
|
- implement volatile operations as compiler builtins
|
||||||
|
- add `//go:inline` pragma
|
||||||
|
- add build tags for the Go stdlib version
|
||||||
|
* **cgo**
|
||||||
|
- implement `char`, `enum` and `void*` types
|
||||||
|
- support `#include` for builtin headers
|
||||||
|
- improve typedef/struct/enum support
|
||||||
|
- only include symbols that are necessary, for broader support
|
||||||
|
- mark external function args as `nocapture`
|
||||||
|
- implement support for some `#define` constants
|
||||||
|
- implement support for multiple CGo files in a single package
|
||||||
|
- **standard library**
|
||||||
|
- `machine`: remove microbit matrix (moved to drivers repository)
|
||||||
|
- `machine`: refactor pins to use `Pin` type instead of `GPIO`
|
||||||
|
- `runtime`: print more interface types on panic, including `error`
|
||||||
|
* **targets**
|
||||||
|
- `arm`: print an error on HardFault (including stack overflows)
|
||||||
|
- `atsamd21`: fix a bug in the ADC peripheral
|
||||||
|
- `atsamd21`: add support for I2S
|
||||||
|
- `feather-m0`: add support for this board
|
||||||
|
- `nrf51`: fix a bug in I2C
|
||||||
|
- `stm32f103xx`: fix a bug in I2C
|
||||||
|
- `syscall`: implement `Exit` on unix
|
||||||
|
- `trinket-m0`: add support for this board
|
||||||
|
- `wasm`: make _main_ example smaller
|
||||||
|
- `wasm`: don't cache wasm file in the server, for ease of debugging
|
||||||
|
- `wasm`: work around bug #41508 that caused a deadlock while linking
|
||||||
|
- `wasm`: add support for `js.FuncOf`
|
||||||
|
|
||||||
0.5.0
|
0.5.0
|
||||||
---
|
---
|
||||||
- **compiler driver**
|
- **compiler driver**
|
||||||
|
|||||||
+3
-3
@@ -46,7 +46,7 @@ RUN cd /go/src/github.com/tinygo-org/tinygo/ && \
|
|||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y apt-utils python3 make binutils-avr gcc-avr avr-libc && \
|
apt-get install -y apt-utils python3 make binutils-avr gcc-avr avr-libc && \
|
||||||
make gen-device-avr && \
|
make gen-device-avr && \
|
||||||
apt-get remove -y python3 make && \
|
apt-get remove -y python3 && \
|
||||||
apt-get autoremove -y && \
|
apt-get autoremove -y && \
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ RUN cd /go/src/github.com/tinygo-org/tinygo/ && \
|
|||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y apt-utils python3 make clang-8 && \
|
apt-get install -y apt-utils python3 make clang-8 && \
|
||||||
make gen-device-nrf && make gen-device-stm32 && \
|
make gen-device-nrf && make gen-device-stm32 && \
|
||||||
apt-get remove -y python3 make && \
|
apt-get remove -y python3 && \
|
||||||
apt-get autoremove -y && \
|
apt-get autoremove -y && \
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ RUN cd /go/src/github.com/tinygo-org/tinygo/ && \
|
|||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y apt-utils python3 make clang-8 binutils-avr gcc-avr avr-libc && \
|
apt-get install -y apt-utils python3 make clang-8 binutils-avr gcc-avr avr-libc && \
|
||||||
make gen-device && \
|
make gen-device && \
|
||||||
apt-get remove -y python3 make && \
|
apt-get remove -y python3 && \
|
||||||
apt-get autoremove -y && \
|
apt-get autoremove -y && \
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ CGO_LDFLAGS=-L$(LLVM_BUILDDIR)/lib $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUIL
|
|||||||
clean:
|
clean:
|
||||||
@rm -rf build
|
@rm -rf build
|
||||||
|
|
||||||
FMT_PATHS = ./*.go compiler interp ir loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall
|
FMT_PATHS = ./*.go cgo compiler interp ir loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall
|
||||||
fmt:
|
fmt:
|
||||||
@gofmt -l -w $(FMT_PATHS)
|
@gofmt -l -w $(FMT_PATHS)
|
||||||
fmt-check:
|
fmt-check:
|
||||||
@@ -84,6 +84,45 @@ build/tinygo:
|
|||||||
test:
|
test:
|
||||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test -v -tags byollvm .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" go test -v -tags byollvm .
|
||||||
|
|
||||||
|
.PHONY: smoketest smoketest-no-avr
|
||||||
|
smoketest: smoketest-no-avr
|
||||||
|
tinygo build -size short -o test.elf -target=arduino examples/blinky1
|
||||||
|
tinygo build -size short -o test.elf -target=digispark examples/blinky1
|
||||||
|
smoketest-no-avr:
|
||||||
|
# test all examples
|
||||||
|
tinygo build -size short -o test.elf -target=pca10040 examples/blinky1
|
||||||
|
tinygo build -size short -o test.elf -target=pca10040 examples/adc
|
||||||
|
tinygo build -size short -o test.elf -target=pca10040 examples/blinkm
|
||||||
|
tinygo build -size short -o test.elf -target=pca10040 examples/blinky2
|
||||||
|
tinygo build -size short -o test.elf -target=pca10040 examples/button
|
||||||
|
tinygo build -size short -o test.elf -target=pca10040 examples/button2
|
||||||
|
tinygo build -size short -o test.elf -target=pca10040 examples/echo
|
||||||
|
tinygo build -size short -o test.elf -target=circuitplay-express examples/i2s
|
||||||
|
tinygo build -size short -o test.elf -target=pca10040 examples/mcp3008
|
||||||
|
tinygo build -size short -o test.elf -target=microbit examples/microbit-blink
|
||||||
|
tinygo build -size short -o test.elf -target=pca10040 examples/pwm
|
||||||
|
tinygo build -size short -o test.elf -target=pca10040 examples/serial
|
||||||
|
tinygo build -size short -o test.elf -target=pca10040 examples/test
|
||||||
|
# test all targets/boards
|
||||||
|
tinygo build -o test.elf examples/blinky2 # TODO: re-enable -size flag with MachO support
|
||||||
|
tinygo build -size short -o test.elf -target=microbit examples/echo
|
||||||
|
tinygo build -size short -o test.elf -target=nrf52840-mdk examples/blinky1
|
||||||
|
tinygo build -size short -o test.elf -target=pca10031 examples/blinky1
|
||||||
|
tinygo build -size short -o test.elf -target=bluepill examples/blinky1
|
||||||
|
tinygo build -size short -o test.elf -target=reelboard examples/blinky1
|
||||||
|
tinygo build -size short -o test.elf -target=reelboard examples/blinky2
|
||||||
|
tinygo build -size short -o test.elf -target=pca10056 examples/blinky1
|
||||||
|
tinygo build -size short -o test.elf -target=pca10056 examples/blinky2
|
||||||
|
tinygo build -size short -o test.elf -target=itsybitsy-m0 examples/blinky1
|
||||||
|
tinygo build -size short -o test.elf -target=feather-m0 examples/blinky1
|
||||||
|
tinygo build -size short -o test.elf -target=trinket-m0 examples/blinky1
|
||||||
|
tinygo build -size short -o test.elf -target=circuitplay-express examples/blinky1
|
||||||
|
tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky1
|
||||||
|
tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky2
|
||||||
|
tinygo build -size short -o test.elf -target=circuitplay-express examples/i2s
|
||||||
|
tinygo build -o wasm.wasm -target=wasm examples/wasm/export
|
||||||
|
tinygo build -o wasm.wasm -target=wasm examples/wasm/main
|
||||||
|
|
||||||
release: build/tinygo gen-device
|
release: build/tinygo gen-device
|
||||||
@mkdir -p build/release/tinygo/bin
|
@mkdir -p build/release/tinygo/bin
|
||||||
@mkdir -p build/release/tinygo/lib/clang/include
|
@mkdir -p build/release/tinygo/lib/clang/include
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
led := machine.GPIO{machine.LED}
|
led := machine.LED
|
||||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
for {
|
for {
|
||||||
led.Low()
|
led.Low()
|
||||||
time.Sleep(time.Millisecond * 1000)
|
time.Sleep(time.Millisecond * 1000)
|
||||||
@@ -43,13 +43,16 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
|||||||
|
|
||||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||||
|
|
||||||
The following microcontroller boards are currently supported:
|
The following 14 microcontroller boards are currently supported:
|
||||||
|
|
||||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||||
|
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
||||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||||
|
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
||||||
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
||||||
* [BBC:Microbit](https://microbit.org/)
|
* [BBC micro:bit](https://microbit.org/)
|
||||||
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
||||||
|
* [ST Micro STM32F407 "Discovery"](https://www.st.com/en/evaluation-tools/stm32f4discovery.html)
|
||||||
* [Digispark](http://digistump.com/products/1)
|
* [Digispark](http://digistump.com/products/1)
|
||||||
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle)
|
* [Nordic Semiconductor PCA10031](https://www.nordicsemi.com/eng/Products/nRF51-Dongle)
|
||||||
* [Nordic Semiconductor PCA10040](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK)
|
* [Nordic Semiconductor PCA10040](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK)
|
||||||
|
|||||||
+695
@@ -0,0 +1,695 @@
|
|||||||
|
// Package cgo implements CGo by modifying a loaded AST. It does this by parsing
|
||||||
|
// the `import "C"` statements found in the source code with libclang and
|
||||||
|
// generating stub function and global declarations.
|
||||||
|
//
|
||||||
|
// There are a few advantages to modifying the AST directly instead of doing CGo
|
||||||
|
// as a preprocessing step, with the main advantage being that debug information
|
||||||
|
// is kept intact as much as possible.
|
||||||
|
package cgo
|
||||||
|
|
||||||
|
// This file extracts the `import "C"` statement from the source and modifies
|
||||||
|
// the AST for CGo. It does not use libclang directly: see libclang.go for the C
|
||||||
|
// source file parsing.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cgoPackage holds all CGo-related information of a package.
|
||||||
|
type cgoPackage struct {
|
||||||
|
generated *ast.File
|
||||||
|
generatedPos token.Pos
|
||||||
|
errors []error
|
||||||
|
dir string
|
||||||
|
fset *token.FileSet
|
||||||
|
tokenFiles map[string]*token.File
|
||||||
|
missingSymbols map[string]struct{}
|
||||||
|
constants map[string]constantInfo
|
||||||
|
functions map[string]*functionInfo
|
||||||
|
globals map[string]globalInfo
|
||||||
|
typedefs map[string]*typedefInfo
|
||||||
|
elaboratedTypes map[string]*elaboratedTypeInfo
|
||||||
|
enums map[string]enumInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// constantInfo stores some information about a CGo constant found by libclang
|
||||||
|
// and declared in the Go AST.
|
||||||
|
type constantInfo struct {
|
||||||
|
expr *ast.BasicLit
|
||||||
|
pos token.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// functionInfo stores some information about a CGo function found by libclang
|
||||||
|
// and declared in the AST.
|
||||||
|
type functionInfo struct {
|
||||||
|
args []paramInfo
|
||||||
|
results *ast.FieldList
|
||||||
|
pos token.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// paramInfo is a parameter of a CGo function (see functionInfo).
|
||||||
|
type paramInfo struct {
|
||||||
|
name string
|
||||||
|
typeExpr ast.Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
// typedefInfo contains information about a single typedef in C.
|
||||||
|
type typedefInfo struct {
|
||||||
|
typeExpr ast.Expr
|
||||||
|
pos token.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// elaboratedTypeInfo contains some information about an elaborated type
|
||||||
|
// (struct, union) found in the C AST.
|
||||||
|
type elaboratedTypeInfo struct {
|
||||||
|
typeExpr ast.Expr
|
||||||
|
pos token.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// enumInfo contains information about an enum in the C.
|
||||||
|
type enumInfo struct {
|
||||||
|
typeExpr ast.Expr
|
||||||
|
pos token.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// globalInfo contains information about a declared global variable in C.
|
||||||
|
type globalInfo struct {
|
||||||
|
typeExpr ast.Expr
|
||||||
|
pos token.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
||||||
|
// in both languages. See addTypeAliases.
|
||||||
|
var cgoAliases = map[string]string{
|
||||||
|
"C.int8_t": "int8",
|
||||||
|
"C.int16_t": "int16",
|
||||||
|
"C.int32_t": "int32",
|
||||||
|
"C.int64_t": "int64",
|
||||||
|
"C.uint8_t": "uint8",
|
||||||
|
"C.uint16_t": "uint16",
|
||||||
|
"C.uint32_t": "uint32",
|
||||||
|
"C.uint64_t": "uint64",
|
||||||
|
"C.uintptr_t": "uintptr",
|
||||||
|
}
|
||||||
|
|
||||||
|
// builtinAliases are handled specially because they only exist on the Go side
|
||||||
|
// of CGo, not on the CGo side (they're prefixed with "_Cgo_" there).
|
||||||
|
var builtinAliases = map[string]struct{}{
|
||||||
|
"char": struct{}{},
|
||||||
|
"schar": struct{}{},
|
||||||
|
"uchar": struct{}{},
|
||||||
|
"short": struct{}{},
|
||||||
|
"ushort": struct{}{},
|
||||||
|
"int": struct{}{},
|
||||||
|
"uint": struct{}{},
|
||||||
|
"long": struct{}{},
|
||||||
|
"ulong": struct{}{},
|
||||||
|
"longlong": struct{}{},
|
||||||
|
"ulonglong": struct{}{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// cgoTypes lists some C types with ambiguous sizes that must be retrieved
|
||||||
|
// somehow from C. This is done by adding some typedefs to get the size of each
|
||||||
|
// type.
|
||||||
|
const cgoTypes = `
|
||||||
|
typedef char _Cgo_char;
|
||||||
|
typedef signed char _Cgo_schar;
|
||||||
|
typedef unsigned char _Cgo_uchar;
|
||||||
|
typedef short _Cgo_short;
|
||||||
|
typedef unsigned short _Cgo_ushort;
|
||||||
|
typedef int _Cgo_int;
|
||||||
|
typedef unsigned int _Cgo_uint;
|
||||||
|
typedef long _Cgo_long;
|
||||||
|
typedef unsigned long _Cgo_ulong;
|
||||||
|
typedef long long _Cgo_longlong;
|
||||||
|
typedef unsigned long long _Cgo_ulonglong;
|
||||||
|
`
|
||||||
|
|
||||||
|
// Process extracts `import "C"` statements from the AST, parses the comment
|
||||||
|
// with libclang, and modifies the AST to use this information. It returns a
|
||||||
|
// newly created *ast.File that should be added to the list of to-be-parsed
|
||||||
|
// files. If there is one or more error, it returns these in the []error slice
|
||||||
|
// but still modifies the AST.
|
||||||
|
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []error) {
|
||||||
|
p := &cgoPackage{
|
||||||
|
dir: dir,
|
||||||
|
fset: fset,
|
||||||
|
tokenFiles: map[string]*token.File{},
|
||||||
|
missingSymbols: map[string]struct{}{},
|
||||||
|
constants: map[string]constantInfo{},
|
||||||
|
functions: map[string]*functionInfo{},
|
||||||
|
globals: map[string]globalInfo{},
|
||||||
|
typedefs: map[string]*typedefInfo{},
|
||||||
|
elaboratedTypes: map[string]*elaboratedTypeInfo{},
|
||||||
|
enums: map[string]enumInfo{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new location for the following file.
|
||||||
|
generatedTokenPos := p.fset.AddFile(dir+"/!cgo.go", -1, 0)
|
||||||
|
generatedTokenPos.SetLines([]int{0})
|
||||||
|
p.generatedPos = generatedTokenPos.Pos(0)
|
||||||
|
|
||||||
|
// Construct a new in-memory AST for CGo declarations of this package.
|
||||||
|
unsafeImport := &ast.ImportSpec{
|
||||||
|
Path: &ast.BasicLit{
|
||||||
|
ValuePos: p.generatedPos,
|
||||||
|
Kind: token.STRING,
|
||||||
|
Value: "\"unsafe\"",
|
||||||
|
},
|
||||||
|
EndPos: p.generatedPos,
|
||||||
|
}
|
||||||
|
p.generated = &ast.File{
|
||||||
|
Package: p.generatedPos,
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: p.generatedPos,
|
||||||
|
Name: files[0].Name.Name,
|
||||||
|
},
|
||||||
|
Decls: []ast.Decl{
|
||||||
|
&ast.GenDecl{
|
||||||
|
TokPos: p.generatedPos,
|
||||||
|
Tok: token.IMPORT,
|
||||||
|
Specs: []ast.Spec{
|
||||||
|
unsafeImport,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Imports: []*ast.ImportSpec{unsafeImport},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all C.* symbols.
|
||||||
|
for _, f := range files {
|
||||||
|
astutil.Apply(f, p.findMissingCGoNames, nil)
|
||||||
|
}
|
||||||
|
for name := range builtinAliases {
|
||||||
|
p.missingSymbols["_Cgo_"+name] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find `import "C"` statements in the file.
|
||||||
|
for _, f := range files {
|
||||||
|
for i := 0; i < len(f.Decls); i++ {
|
||||||
|
decl := f.Decls[i]
|
||||||
|
genDecl, ok := decl.(*ast.GenDecl)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(genDecl.Specs) != 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
spec, ok := genDecl.Specs[0].(*ast.ImportSpec)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
path, err := strconv.Unquote(spec.Path.Value)
|
||||||
|
if err != nil {
|
||||||
|
panic("could not parse import path: " + err.Error())
|
||||||
|
}
|
||||||
|
if path != "C" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cgoComment := genDecl.Doc.Text()
|
||||||
|
|
||||||
|
pos := genDecl.Pos()
|
||||||
|
if genDecl.Doc != nil {
|
||||||
|
pos = genDecl.Doc.Pos()
|
||||||
|
}
|
||||||
|
position := fset.PositionFor(pos, true)
|
||||||
|
p.parseFragment(cgoComment+cgoTypes, cflags, position.Filename, position.Line)
|
||||||
|
|
||||||
|
// Remove this import declaration.
|
||||||
|
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the AST, for debugging.
|
||||||
|
//ast.Print(fset, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare functions found by libclang.
|
||||||
|
p.addFuncDecls()
|
||||||
|
|
||||||
|
// Declare stub function pointer values found by libclang.
|
||||||
|
p.addFuncPtrDecls()
|
||||||
|
|
||||||
|
// Declare globals found by libclang.
|
||||||
|
p.addConstDecls()
|
||||||
|
|
||||||
|
// Declare globals found by libclang.
|
||||||
|
p.addVarDecls()
|
||||||
|
|
||||||
|
// Forward C types to Go types (like C.uint32_t -> uint32).
|
||||||
|
p.addTypeAliases()
|
||||||
|
|
||||||
|
// Add type declarations for C types, declared using typedef in C.
|
||||||
|
p.addTypedefs()
|
||||||
|
|
||||||
|
// Add elaborated types for C structs and unions.
|
||||||
|
p.addElaboratedTypes()
|
||||||
|
|
||||||
|
// Add enum types and enum constants for C enums.
|
||||||
|
p.addEnumTypes()
|
||||||
|
|
||||||
|
// Patch the AST to use the declared types and functions.
|
||||||
|
for _, f := range files {
|
||||||
|
astutil.Apply(f, p.walker, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the newly generated in-memory AST, for debugging.
|
||||||
|
//ast.Print(fset, p.generated)
|
||||||
|
|
||||||
|
return p.generated, p.errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// addFuncDecls adds the C function declarations found by libclang in the
|
||||||
|
// comment above the `import "C"` statement.
|
||||||
|
func (p *cgoPackage) addFuncDecls() {
|
||||||
|
names := make([]string, 0, len(p.functions))
|
||||||
|
for name := range p.functions {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, name := range names {
|
||||||
|
fn := p.functions[name]
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Fun,
|
||||||
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
args := make([]*ast.Field, len(fn.args))
|
||||||
|
decl := &ast.FuncDecl{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: fn.pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: &ast.FuncType{
|
||||||
|
Func: fn.pos,
|
||||||
|
Params: &ast.FieldList{
|
||||||
|
Opening: fn.pos,
|
||||||
|
List: args,
|
||||||
|
Closing: fn.pos,
|
||||||
|
},
|
||||||
|
Results: fn.results,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
obj.Decl = decl
|
||||||
|
for i, arg := range fn.args {
|
||||||
|
args[i] = &ast.Field{
|
||||||
|
Names: []*ast.Ident{
|
||||||
|
&ast.Ident{
|
||||||
|
NamePos: fn.pos,
|
||||||
|
Name: arg.name,
|
||||||
|
Obj: &ast.Object{
|
||||||
|
Kind: ast.Var,
|
||||||
|
Name: arg.name,
|
||||||
|
Decl: decl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: arg.typeExpr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.generated.Decls = append(p.generated.Decls, decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addFuncPtrDecls creates stub declarations of function pointer values. These
|
||||||
|
// values will later be replaced with the real values in the compiler.
|
||||||
|
// It adds code like the following to the AST:
|
||||||
|
//
|
||||||
|
// var (
|
||||||
|
// C.add unsafe.Pointer
|
||||||
|
// C.mul unsafe.Pointer
|
||||||
|
// // ...
|
||||||
|
// )
|
||||||
|
func (p *cgoPackage) addFuncPtrDecls() {
|
||||||
|
if len(p.functions) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.VAR,
|
||||||
|
Lparen: token.NoPos,
|
||||||
|
Rparen: token.NoPos,
|
||||||
|
}
|
||||||
|
names := make([]string, 0, len(p.functions))
|
||||||
|
for name := range p.functions {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, name := range names {
|
||||||
|
fn := p.functions[name]
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: "C." + name + "$funcaddr",
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{&ast.Ident{
|
||||||
|
NamePos: fn.pos,
|
||||||
|
Name: "C." + name + "$funcaddr",
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Type: &ast.SelectorExpr{
|
||||||
|
X: &ast.Ident{
|
||||||
|
NamePos: fn.pos,
|
||||||
|
Name: "unsafe",
|
||||||
|
},
|
||||||
|
Sel: &ast.Ident{
|
||||||
|
NamePos: fn.pos,
|
||||||
|
Name: "Pointer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
}
|
||||||
|
p.generated.Decls = append(p.generated.Decls, gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addConstDecls declares external C constants in the Go source.
|
||||||
|
// It adds code like the following to the AST:
|
||||||
|
//
|
||||||
|
// const (
|
||||||
|
// C.CONST_INT = 5
|
||||||
|
// C.CONST_FLOAT = 5.8
|
||||||
|
// // ...
|
||||||
|
// )
|
||||||
|
func (p *cgoPackage) addConstDecls() {
|
||||||
|
if len(p.constants) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.CONST,
|
||||||
|
Lparen: token.NoPos,
|
||||||
|
Rparen: token.NoPos,
|
||||||
|
}
|
||||||
|
names := make([]string, 0, len(p.constants))
|
||||||
|
for name := range p.constants {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, name := range names {
|
||||||
|
constVal := p.constants[name]
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Con,
|
||||||
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{&ast.Ident{
|
||||||
|
NamePos: constVal.pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Values: []ast.Expr{constVal.expr},
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
}
|
||||||
|
p.generated.Decls = append(p.generated.Decls, gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addVarDecls declares external C globals in the Go source.
|
||||||
|
// It adds code like the following to the AST:
|
||||||
|
//
|
||||||
|
// var (
|
||||||
|
// C.globalInt int
|
||||||
|
// C.globalBool bool
|
||||||
|
// // ...
|
||||||
|
// )
|
||||||
|
func (p *cgoPackage) addVarDecls() {
|
||||||
|
if len(p.globals) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.VAR,
|
||||||
|
Lparen: token.NoPos,
|
||||||
|
Rparen: token.NoPos,
|
||||||
|
}
|
||||||
|
names := make([]string, 0, len(p.globals))
|
||||||
|
for name := range p.globals {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, name := range names {
|
||||||
|
global := p.globals[name]
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Var,
|
||||||
|
Name: "C." + name,
|
||||||
|
}
|
||||||
|
valueSpec := &ast.ValueSpec{
|
||||||
|
Names: []*ast.Ident{&ast.Ident{
|
||||||
|
NamePos: global.pos,
|
||||||
|
Name: "C." + name,
|
||||||
|
Obj: obj,
|
||||||
|
}},
|
||||||
|
Type: global.typeExpr,
|
||||||
|
}
|
||||||
|
obj.Decl = valueSpec
|
||||||
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
|
}
|
||||||
|
p.generated.Decls = append(p.generated.Decls, gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addTypeAliases aliases some built-in Go types with their equivalent C types.
|
||||||
|
// It adds code like the following to the AST:
|
||||||
|
//
|
||||||
|
// type (
|
||||||
|
// C.int8_t = int8
|
||||||
|
// C.int16_t = int16
|
||||||
|
// // ...
|
||||||
|
// )
|
||||||
|
func (p *cgoPackage) addTypeAliases() {
|
||||||
|
aliasKeys := make([]string, 0, len(cgoAliases))
|
||||||
|
for key := range cgoAliases {
|
||||||
|
aliasKeys = append(aliasKeys, key)
|
||||||
|
}
|
||||||
|
sort.Strings(aliasKeys)
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.TYPE,
|
||||||
|
Lparen: token.NoPos,
|
||||||
|
Rparen: token.NoPos,
|
||||||
|
}
|
||||||
|
for _, typeName := range aliasKeys {
|
||||||
|
goTypeName := cgoAliases[typeName]
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: typeName,
|
||||||
|
}
|
||||||
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: token.NoPos,
|
||||||
|
Name: typeName,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Assign: p.generatedPos,
|
||||||
|
Type: &ast.Ident{
|
||||||
|
NamePos: token.NoPos,
|
||||||
|
Name: goTypeName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
gen.Specs = append(gen.Specs, typeSpec)
|
||||||
|
}
|
||||||
|
p.generated.Decls = append(p.generated.Decls, gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *cgoPackage) addTypedefs() {
|
||||||
|
if len(p.typedefs) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.TYPE,
|
||||||
|
}
|
||||||
|
names := make([]string, 0, len(p.typedefs))
|
||||||
|
for name := range p.typedefs {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, name := range names {
|
||||||
|
typedef := p.typedefs[name]
|
||||||
|
typeName := "C." + name
|
||||||
|
isAlias := true
|
||||||
|
if strings.HasPrefix(name, "_Cgo_") {
|
||||||
|
typeName = "C." + name[len("_Cgo_"):]
|
||||||
|
isAlias = false // C.short etc. should not be aliased to the equivalent Go type (not portable)
|
||||||
|
}
|
||||||
|
if _, ok := cgoAliases[typeName]; ok {
|
||||||
|
// This is a type that also exists in Go (defined in stdint.h).
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: typeName,
|
||||||
|
}
|
||||||
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: typedef.pos,
|
||||||
|
Name: typeName,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: typedef.typeExpr,
|
||||||
|
}
|
||||||
|
if isAlias {
|
||||||
|
typeSpec.Assign = typedef.pos
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
gen.Specs = append(gen.Specs, typeSpec)
|
||||||
|
}
|
||||||
|
p.generated.Decls = append(p.generated.Decls, gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addElaboratedTypes adds C elaborated types as aliases. These are the "struct
|
||||||
|
// foo" or "union foo" types, often used in a typedef.
|
||||||
|
//
|
||||||
|
// See also:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/elaborated_type_specifier
|
||||||
|
func (p *cgoPackage) addElaboratedTypes() {
|
||||||
|
if len(p.elaboratedTypes) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.TYPE,
|
||||||
|
}
|
||||||
|
names := make([]string, 0, len(p.elaboratedTypes))
|
||||||
|
for name := range p.elaboratedTypes {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, name := range names {
|
||||||
|
typ := p.elaboratedTypes[name]
|
||||||
|
typeName := "C." + name
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: typeName,
|
||||||
|
}
|
||||||
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: typ.pos,
|
||||||
|
Name: typeName,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: typ.typeExpr,
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
gen.Specs = append(gen.Specs, typeSpec)
|
||||||
|
}
|
||||||
|
p.generated.Decls = append(p.generated.Decls, gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addEnumTypes adds C enums to the AST. For example, the following C code:
|
||||||
|
//
|
||||||
|
// enum option {
|
||||||
|
// optionA,
|
||||||
|
// optionB = 5,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// is translated to the following Go code equivalent:
|
||||||
|
//
|
||||||
|
// type C.enum_option int32
|
||||||
|
//
|
||||||
|
// The constants are treated just like macros so are inserted into the AST by
|
||||||
|
// addConstDecls.
|
||||||
|
// See also: https://en.cppreference.com/w/c/language/enum
|
||||||
|
func (p *cgoPackage) addEnumTypes() {
|
||||||
|
if len(p.enums) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gen := &ast.GenDecl{
|
||||||
|
TokPos: token.NoPos,
|
||||||
|
Tok: token.TYPE,
|
||||||
|
}
|
||||||
|
names := make([]string, 0, len(p.enums))
|
||||||
|
for name := range p.enums {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, name := range names {
|
||||||
|
typ := p.enums[name]
|
||||||
|
typeName := "C.enum_" + name
|
||||||
|
obj := &ast.Object{
|
||||||
|
Kind: ast.Typ,
|
||||||
|
Name: typeName,
|
||||||
|
}
|
||||||
|
typeSpec := &ast.TypeSpec{
|
||||||
|
Name: &ast.Ident{
|
||||||
|
NamePos: typ.pos,
|
||||||
|
Name: typeName,
|
||||||
|
Obj: obj,
|
||||||
|
},
|
||||||
|
Type: typ.typeExpr,
|
||||||
|
}
|
||||||
|
obj.Decl = typeSpec
|
||||||
|
gen.Specs = append(gen.Specs, typeSpec)
|
||||||
|
}
|
||||||
|
p.generated.Decls = append(p.generated.Decls, gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// findMissingCGoNames traverses the AST and finds all C.something names. Only
|
||||||
|
// these symbols are extracted from the parsed C AST and converted to the Go
|
||||||
|
// equivalent.
|
||||||
|
func (p *cgoPackage) findMissingCGoNames(cursor *astutil.Cursor) bool {
|
||||||
|
switch node := cursor.Node().(type) {
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
x, ok := node.X.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if x.Name == "C" {
|
||||||
|
name := node.Sel.Name
|
||||||
|
if _, ok := builtinAliases[name]; ok {
|
||||||
|
name = "_Cgo_" + name
|
||||||
|
}
|
||||||
|
p.missingSymbols[name] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// walker replaces all "C".<something> expressions to literal "C.<something>"
|
||||||
|
// expressions. Such expressions are impossible to write in Go (a dot cannot be
|
||||||
|
// used in the middle of a name) so in practice all C identifiers live in a
|
||||||
|
// separate namespace (no _Cgo_ hacks like in gc).
|
||||||
|
func (p *cgoPackage) walker(cursor *astutil.Cursor) bool {
|
||||||
|
switch node := cursor.Node().(type) {
|
||||||
|
case *ast.CallExpr:
|
||||||
|
fun, ok := node.Fun.(*ast.SelectorExpr)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
x, ok := fun.X.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := p.functions[fun.Sel.Name]; ok && x.Name == "C" {
|
||||||
|
node.Fun = &ast.Ident{
|
||||||
|
NamePos: x.NamePos,
|
||||||
|
Name: "C." + fun.Sel.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
x, ok := node.X.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if x.Name == "C" {
|
||||||
|
name := "C." + node.Sel.Name
|
||||||
|
if _, ok := p.functions[node.Sel.Name]; ok {
|
||||||
|
name += "$funcaddr"
|
||||||
|
}
|
||||||
|
cursor.Replace(&ast.Ident{
|
||||||
|
NamePos: x.NamePos,
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package loader
|
package cgo
|
||||||
|
|
||||||
// This file parses a fragment of C with libclang and stores the result for AST
|
// This file parses a fragment of C with libclang and stores the result for AST
|
||||||
// modification. It does not touch the AST itself.
|
// modification. It does not touch the AST itself.
|
||||||
@@ -47,15 +47,19 @@ CXType tinygo_clang_getCursorResultType(GoCXCursor c);
|
|||||||
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
|
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
|
||||||
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
|
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
|
||||||
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
|
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
|
||||||
|
CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
|
||||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
||||||
|
long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c);
|
||||||
|
CXType tinygo_clang_getEnumDeclIntegerType(GoCXCursor c);
|
||||||
|
|
||||||
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
|
int tinygo_clang_enum_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
// refMap stores references to types, used for clang_visitChildren.
|
// storedRefs stores references to types, used for clang_visitChildren.
|
||||||
var refMap RefMap
|
var storedRefs refMap
|
||||||
|
|
||||||
var diagnosticSeverity = [...]string{
|
var diagnosticSeverity = [...]string{
|
||||||
C.CXDiagnostic_Ignored: "ignored",
|
C.CXDiagnostic_Ignored: "ignored",
|
||||||
@@ -65,7 +69,7 @@ var diagnosticSeverity = [...]string{
|
|||||||
C.CXDiagnostic_Fatal: "fatal",
|
C.CXDiagnostic_Fatal: "fatal",
|
||||||
}
|
}
|
||||||
|
|
||||||
func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilename string, posLine int) []error {
|
func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename string, posLine int) {
|
||||||
index := C.clang_createIndex(0, 0)
|
index := C.clang_createIndex(0, 0)
|
||||||
defer C.clang_disposeIndex(index)
|
defer C.clang_disposeIndex(index)
|
||||||
|
|
||||||
@@ -101,7 +105,7 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilenam
|
|||||||
filenameC,
|
filenameC,
|
||||||
(**C.char)(cmdargsC), C.int(len(cflags)), // command line args
|
(**C.char)(cmdargsC), C.int(len(cflags)), // command line args
|
||||||
&unsavedFile, 1, // unsaved files
|
&unsavedFile, 1, // unsaved files
|
||||||
C.CXTranslationUnit_None,
|
C.CXTranslationUnit_DetailedPreprocessingRecord,
|
||||||
&unit)
|
&unit)
|
||||||
if errCode != 0 {
|
if errCode != 0 {
|
||||||
panic("loader: failed to parse source with libclang")
|
panic("loader: failed to parse source with libclang")
|
||||||
@@ -109,7 +113,6 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilenam
|
|||||||
defer C.clang_disposeTranslationUnit(unit)
|
defer C.clang_disposeTranslationUnit(unit)
|
||||||
|
|
||||||
if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 {
|
if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 {
|
||||||
errs := []error{}
|
|
||||||
addDiagnostic := func(diagnostic C.CXDiagnostic) {
|
addDiagnostic := func(diagnostic C.CXDiagnostic) {
|
||||||
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
||||||
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
||||||
@@ -121,12 +124,12 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilenam
|
|||||||
filename := getString(libclangFilename)
|
filename := getString(libclangFilename)
|
||||||
if filepath.IsAbs(filename) {
|
if filepath.IsAbs(filename) {
|
||||||
// Relative paths for readability, like other Go parser errors.
|
// Relative paths for readability, like other Go parser errors.
|
||||||
relpath, err := filepath.Rel(info.Program.Dir, filename)
|
relpath, err := filepath.Rel(p.dir, filename)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
filename = relpath
|
filename = relpath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
errs = append(errs, &scanner.Error{
|
p.errors = append(p.errors, &scanner.Error{
|
||||||
Pos: token.Position{
|
Pos: token.Position{
|
||||||
Filename: filename,
|
Filename: filename,
|
||||||
Offset: 0, // not provided by clang_getPresumedLocation
|
Offset: 0, // not provided by clang_getPresumedLocation
|
||||||
@@ -146,26 +149,24 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilenam
|
|||||||
addDiagnostic(C.clang_getDiagnosticInSet(diagnostics, C.uint(j)))
|
addDiagnostic(C.clang_getDiagnosticInSet(diagnostics, C.uint(j)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errs
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ref := refMap.Put(info)
|
ref := storedRefs.Put(p)
|
||||||
defer refMap.Remove(ref)
|
defer storedRefs.Remove(ref)
|
||||||
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export tinygo_clang_globals_visitor
|
//export tinygo_clang_globals_visitor
|
||||||
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||||
info := refMap.Get(unsafe.Pointer(client_data)).(*fileInfo)
|
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
||||||
kind := C.tinygo_clang_getCursorKind(c)
|
kind := C.tinygo_clang_getCursorKind(c)
|
||||||
pos := info.getCursorPosition(c)
|
pos := p.getCursorPosition(c)
|
||||||
switch kind {
|
switch kind {
|
||||||
case C.CXCursor_FunctionDecl:
|
case C.CXCursor_FunctionDecl:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
if _, required := info.missingSymbols[name]; !required {
|
if _, required := p.missingSymbols[name]; !required {
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
@@ -173,8 +174,10 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
return C.CXChildVisit_Continue // not supported
|
return C.CXChildVisit_Continue // not supported
|
||||||
}
|
}
|
||||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||||
fn := &functionInfo{}
|
fn := &functionInfo{
|
||||||
info.functions[name] = fn
|
pos: pos,
|
||||||
|
}
|
||||||
|
p.functions[name] = fn
|
||||||
for i := 0; i < numArgs; i++ {
|
for i := 0; i < numArgs; i++ {
|
||||||
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
||||||
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
||||||
@@ -184,7 +187,7 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
}
|
}
|
||||||
fn.args = append(fn.args, paramInfo{
|
fn.args = append(fn.args, paramInfo{
|
||||||
name: argName,
|
name: argName,
|
||||||
typeExpr: info.makeASTType(argType, pos),
|
typeExpr: p.makeASTType(argType, pos),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
resultType := C.tinygo_clang_getCursorResultType(c)
|
resultType := C.tinygo_clang_getCursorResultType(c)
|
||||||
@@ -192,7 +195,7 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
fn.results = &ast.FieldList{
|
fn.results = &ast.FieldList{
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
&ast.Field{
|
&ast.Field{
|
||||||
Type: info.makeASTType(resultType, pos),
|
Type: p.makeASTType(resultType, pos),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -200,25 +203,106 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
case C.CXCursor_StructDecl:
|
case C.CXCursor_StructDecl:
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
typ := C.tinygo_clang_getCursorType(c)
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
if _, required := info.missingSymbols["struct_"+name]; !required {
|
if _, required := p.missingSymbols["struct_"+name]; !required {
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
info.makeASTType(typ, pos)
|
p.makeASTType(typ, pos)
|
||||||
case C.CXCursor_TypedefDecl:
|
case C.CXCursor_TypedefDecl:
|
||||||
typedefType := C.tinygo_clang_getCursorType(c)
|
typedefType := C.tinygo_clang_getCursorType(c)
|
||||||
name := getString(C.clang_getTypedefName(typedefType))
|
name := getString(C.clang_getTypedefName(typedefType))
|
||||||
if _, required := info.missingSymbols[name]; !required {
|
if _, required := p.missingSymbols[name]; !required {
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
info.makeASTType(typedefType, pos)
|
p.makeASTType(typedefType, pos)
|
||||||
case C.CXCursor_VarDecl:
|
case C.CXCursor_VarDecl:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
if _, required := info.missingSymbols[name]; !required {
|
if _, required := p.missingSymbols[name]; !required {
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
info.globals[name] = &globalInfo{
|
p.globals[name] = globalInfo{
|
||||||
typeExpr: info.makeASTType(cursorType, pos),
|
typeExpr: p.makeASTType(cursorType, pos),
|
||||||
|
pos: pos,
|
||||||
|
}
|
||||||
|
case C.CXCursor_MacroDefinition:
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
if _, required := p.missingSymbols[name]; !required {
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
||||||
|
start := C.clang_getRangeStart(sourceRange)
|
||||||
|
end := C.clang_getRangeEnd(sourceRange)
|
||||||
|
var file, endFile C.CXFile
|
||||||
|
var startOffset, endOffset C.unsigned
|
||||||
|
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
||||||
|
if file == nil {
|
||||||
|
panic("could not find file where macro is defined")
|
||||||
|
}
|
||||||
|
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
||||||
|
if file != endFile {
|
||||||
|
panic("expected start and end location of a #define to be in the same file")
|
||||||
|
}
|
||||||
|
if startOffset > endOffset {
|
||||||
|
panic("startOffset > endOffset")
|
||||||
|
}
|
||||||
|
|
||||||
|
// read file contents and extract the relevant byte range
|
||||||
|
tu := C.tinygo_clang_Cursor_getTranslationUnit(c)
|
||||||
|
var size C.size_t
|
||||||
|
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
||||||
|
if endOffset >= C.uint(size) {
|
||||||
|
panic("endOffset lies after end of file")
|
||||||
|
}
|
||||||
|
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
||||||
|
if !strings.HasPrefix(source, name) {
|
||||||
|
panic(fmt.Sprintf("expected #define value to start with %#v, got %#v", name, source))
|
||||||
|
}
|
||||||
|
value := strings.TrimSpace(source[len(name):])
|
||||||
|
for len(value) != 0 && value[0] == '(' && value[len(value)-1] == ')' {
|
||||||
|
value = strings.TrimSpace(value[1 : len(value)-1])
|
||||||
|
}
|
||||||
|
if len(value) == 0 {
|
||||||
|
// Pretend it doesn't exist at all.
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
// For information about integer literals:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/integer_literal
|
||||||
|
if value[0] == '"' {
|
||||||
|
// string constant
|
||||||
|
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.STRING, value}, pos}
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
if value[0] == '\'' {
|
||||||
|
// char constant
|
||||||
|
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.CHAR, value}, pos}
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
// assume it's a number (int or float)
|
||||||
|
value = strings.Replace(value, "'", "", -1) // remove ' chars
|
||||||
|
value = strings.TrimRight(value, "lu") // remove llu suffixes etc.
|
||||||
|
// find the first non-number
|
||||||
|
nonnum := byte(0)
|
||||||
|
for i := 0; i < len(value); i++ {
|
||||||
|
if value[i] < '0' || value[i] > '9' {
|
||||||
|
nonnum = value[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// determine number type based on the first non-number
|
||||||
|
switch nonnum {
|
||||||
|
case 0:
|
||||||
|
// no non-number found, must be an integer
|
||||||
|
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.INT, value}, pos}
|
||||||
|
case 'x', 'X':
|
||||||
|
// hex integer constant
|
||||||
|
// TODO: may also be a floating point number per C++17.
|
||||||
|
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.INT, value}, pos}
|
||||||
|
case '.', 'e':
|
||||||
|
// float constant
|
||||||
|
value = strings.TrimRight(value, "fFlL")
|
||||||
|
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.FLOAT, value}, pos}
|
||||||
|
default:
|
||||||
|
// unknown type, ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
@@ -234,7 +318,7 @@ func getString(clangString C.CXString) (s string) {
|
|||||||
// getCursorPosition returns a usable token.Pos from a libclang cursor. If the
|
// getCursorPosition returns a usable token.Pos from a libclang cursor. If the
|
||||||
// file for this cursor has not been seen before, it is read from libclang
|
// file for this cursor has not been seen before, it is read from libclang
|
||||||
// (which already has the file in memory) and added to the token.FileSet.
|
// (which already has the file in memory) and added to the token.FileSet.
|
||||||
func (info *fileInfo) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||||
location := C.tinygo_clang_getCursorLocation(cursor)
|
location := C.tinygo_clang_getCursorLocation(cursor)
|
||||||
var file C.CXFile
|
var file C.CXFile
|
||||||
var line C.unsigned
|
var line C.unsigned
|
||||||
@@ -246,7 +330,7 @@ func (info *fileInfo) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
|||||||
return token.NoPos
|
return token.NoPos
|
||||||
}
|
}
|
||||||
filename := getString(C.clang_getFileName(file))
|
filename := getString(C.clang_getFileName(file))
|
||||||
if _, ok := info.tokenFiles[filename]; !ok {
|
if _, ok := p.tokenFiles[filename]; !ok {
|
||||||
// File has not been seen before in this package, add line information
|
// File has not been seen before in this package, add line information
|
||||||
// now by reading the file from libclang.
|
// now by reading the file from libclang.
|
||||||
tu := C.tinygo_clang_Cursor_getTranslationUnit(cursor)
|
tu := C.tinygo_clang_Cursor_getTranslationUnit(cursor)
|
||||||
@@ -259,16 +343,16 @@ func (info *fileInfo) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
|||||||
lines = append(lines, i+1)
|
lines = append(lines, i+1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f := info.fset.AddFile(filename, -1, int(size))
|
f := p.fset.AddFile(filename, -1, int(size))
|
||||||
f.SetLines(lines)
|
f.SetLines(lines)
|
||||||
info.tokenFiles[filename] = f
|
p.tokenFiles[filename] = f
|
||||||
}
|
}
|
||||||
return info.tokenFiles[filename].Pos(int(offset))
|
return p.tokenFiles[filename].Pos(int(offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
||||||
// it converts a libclang type to a type in the Go AST.
|
// it converts a libclang type to a type in the Go AST.
|
||||||
func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
var typeName string
|
var typeName string
|
||||||
switch typ.kind {
|
switch typ.kind {
|
||||||
case C.CXType_Char_S, C.CXType_Char_U:
|
case C.CXType_Char_S, C.CXType_Char_U:
|
||||||
@@ -329,7 +413,7 @@ func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
return &ast.StarExpr{
|
return &ast.StarExpr{
|
||||||
Star: pos,
|
Star: pos,
|
||||||
X: info.makeASTType(pointeeType, pos),
|
X: p.makeASTType(pointeeType, pos),
|
||||||
}
|
}
|
||||||
case C.CXType_ConstantArray:
|
case C.CXType_ConstantArray:
|
||||||
return &ast.ArrayType{
|
return &ast.ArrayType{
|
||||||
@@ -339,7 +423,7 @@ func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
Kind: token.INT,
|
Kind: token.INT,
|
||||||
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
||||||
},
|
},
|
||||||
Elt: info.makeASTType(C.clang_getElementType(typ), pos),
|
Elt: p.makeASTType(C.clang_getElementType(typ), pos),
|
||||||
}
|
}
|
||||||
case C.CXType_FunctionProto:
|
case C.CXType_FunctionProto:
|
||||||
// Be compatible with gc, which uses the *[0]byte type for function
|
// Be compatible with gc, which uses the *[0]byte type for function
|
||||||
@@ -360,11 +444,11 @@ func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
case C.CXType_Typedef:
|
case C.CXType_Typedef:
|
||||||
name := getString(C.clang_getTypedefName(typ))
|
name := getString(C.clang_getTypedefName(typ))
|
||||||
if _, ok := info.typedefs[name]; !ok {
|
if _, ok := p.typedefs[name]; !ok {
|
||||||
info.typedefs[name] = nil // don't recurse
|
p.typedefs[name] = nil // don't recurse
|
||||||
c := C.tinygo_clang_getTypeDeclaration(typ)
|
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||||
expr := info.makeASTType(underlyingType, pos)
|
expr := p.makeASTType(underlyingType, pos)
|
||||||
if strings.HasPrefix(name, "_Cgo_") {
|
if strings.HasPrefix(name, "_Cgo_") {
|
||||||
expr := expr.(*ast.Ident)
|
expr := expr.(*ast.Ident)
|
||||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||||
@@ -406,8 +490,9 @@ func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info.typedefs[name] = &typedefInfo{
|
p.typedefs[name] = &typedefInfo{
|
||||||
typeExpr: expr,
|
typeExpr: expr,
|
||||||
|
pos: pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &ast.Ident{
|
return &ast.Ident{
|
||||||
@@ -418,7 +503,9 @@ func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
underlying := C.clang_Type_getNamedType(typ)
|
underlying := C.clang_Type_getNamedType(typ)
|
||||||
switch underlying.kind {
|
switch underlying.kind {
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
return info.makeASTType(underlying, pos)
|
return p.makeASTType(underlying, pos)
|
||||||
|
case C.CXType_Enum:
|
||||||
|
return p.makeASTType(underlying, pos)
|
||||||
default:
|
default:
|
||||||
panic("unknown elaborated type")
|
panic("unknown elaborated type")
|
||||||
}
|
}
|
||||||
@@ -434,23 +521,26 @@ func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
default:
|
default:
|
||||||
panic("unknown record declaration")
|
panic("unknown record declaration")
|
||||||
}
|
}
|
||||||
if _, ok := info.elaboratedTypes[cgoName]; !ok {
|
if _, ok := p.elaboratedTypes[cgoName]; !ok {
|
||||||
info.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
||||||
fieldList := &ast.FieldList{
|
fieldList := &ast.FieldList{
|
||||||
Opening: pos,
|
Opening: pos,
|
||||||
Closing: pos,
|
Closing: pos,
|
||||||
}
|
}
|
||||||
ref := refMap.Put(struct {
|
ref := storedRefs.Put(struct {
|
||||||
fieldList *ast.FieldList
|
fieldList *ast.FieldList
|
||||||
info *fileInfo
|
pkg *cgoPackage
|
||||||
}{fieldList, info})
|
}{fieldList, p})
|
||||||
defer refMap.Remove(ref)
|
defer storedRefs.Remove(ref)
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||||
case C.CXCursor_StructDecl:
|
case C.CXCursor_StructDecl:
|
||||||
info.elaboratedTypes[cgoName] = &ast.StructType{
|
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
||||||
Struct: pos,
|
typeExpr: &ast.StructType{
|
||||||
Fields: fieldList,
|
Struct: pos,
|
||||||
|
Fields: fieldList,
|
||||||
|
},
|
||||||
|
pos: pos,
|
||||||
}
|
}
|
||||||
case C.CXCursor_UnionDecl:
|
case C.CXCursor_UnionDecl:
|
||||||
if len(fieldList.List) > 1 {
|
if len(fieldList.List) > 1 {
|
||||||
@@ -480,9 +570,12 @@ func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
fieldList.List = append([]*ast.Field{unionMarker}, fieldList.List...)
|
fieldList.List = append([]*ast.Field{unionMarker}, fieldList.List...)
|
||||||
}
|
}
|
||||||
info.elaboratedTypes[cgoName] = &ast.StructType{
|
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
||||||
Struct: pos,
|
typeExpr: &ast.StructType{
|
||||||
Fields: fieldList,
|
Struct: pos,
|
||||||
|
Fields: fieldList,
|
||||||
|
},
|
||||||
|
pos: pos,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
@@ -492,11 +585,43 @@ func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
Name: "C." + cgoName,
|
Name: "C." + cgoName,
|
||||||
}
|
}
|
||||||
|
case C.CXType_Enum:
|
||||||
|
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||||
|
underlying := C.tinygo_clang_getEnumDeclIntegerType(cursor)
|
||||||
|
if name == "" {
|
||||||
|
// anonymous enum
|
||||||
|
ref := storedRefs.Put(p)
|
||||||
|
defer storedRefs.Remove(ref)
|
||||||
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
||||||
|
return p.makeASTType(underlying, pos)
|
||||||
|
} else {
|
||||||
|
// named enum
|
||||||
|
if _, ok := p.enums[name]; !ok {
|
||||||
|
ref := storedRefs.Put(p)
|
||||||
|
defer storedRefs.Remove(ref)
|
||||||
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
|
||||||
|
p.enums[name] = enumInfo{
|
||||||
|
typeExpr: p.makeASTType(underlying, pos),
|
||||||
|
pos: pos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &ast.Ident{
|
||||||
|
NamePos: pos,
|
||||||
|
Name: "C.enum_" + name,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if typeName == "" {
|
if typeName == "" {
|
||||||
|
// Report this as an error.
|
||||||
|
spelling := getString(C.clang_getTypeSpelling(typ))
|
||||||
|
p.errors = append(p.errors, scanner.Error{
|
||||||
|
Pos: p.fset.PositionFor(pos, true),
|
||||||
|
Msg: fmt.Sprintf("unknown C type: %v (libclang type kind %d)", spelling, typ.kind),
|
||||||
|
})
|
||||||
// Fallback, probably incorrect but at least the error points to an odd
|
// Fallback, probably incorrect but at least the error points to an odd
|
||||||
// type name.
|
// type name.
|
||||||
typeName = "C." + getString(C.clang_getTypeSpelling(typ))
|
typeName = "C." + spelling
|
||||||
}
|
}
|
||||||
return &ast.Ident{
|
return &ast.Ident{
|
||||||
NamePos: pos,
|
NamePos: pos,
|
||||||
@@ -506,23 +631,23 @@ func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
|
|
||||||
//export tinygo_clang_struct_visitor
|
//export tinygo_clang_struct_visitor
|
||||||
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||||
passed := refMap.Get(unsafe.Pointer(client_data)).(struct {
|
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
||||||
fieldList *ast.FieldList
|
fieldList *ast.FieldList
|
||||||
info *fileInfo
|
pkg *cgoPackage
|
||||||
})
|
})
|
||||||
fieldList := passed.fieldList
|
fieldList := passed.fieldList
|
||||||
info := passed.info
|
p := passed.pkg
|
||||||
if C.tinygo_clang_getCursorKind(c) != C.CXCursor_FieldDecl {
|
if C.tinygo_clang_getCursorKind(c) != C.CXCursor_FieldDecl {
|
||||||
panic("expected field inside cursor")
|
panic("expected field inside cursor")
|
||||||
}
|
}
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
typ := C.tinygo_clang_getCursorType(c)
|
||||||
field := &ast.Field{
|
field := &ast.Field{
|
||||||
Type: info.makeASTType(typ, info.getCursorPosition(c)),
|
Type: p.makeASTType(typ, p.getCursorPosition(c)),
|
||||||
}
|
}
|
||||||
field.Names = []*ast.Ident{
|
field.Names = []*ast.Ident{
|
||||||
&ast.Ident{
|
&ast.Ident{
|
||||||
NamePos: info.getCursorPosition(c),
|
NamePos: p.getCursorPosition(c),
|
||||||
Name: name,
|
Name: name,
|
||||||
Obj: &ast.Object{
|
Obj: &ast.Object{
|
||||||
Kind: ast.Var,
|
Kind: ast.Var,
|
||||||
@@ -534,3 +659,16 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
|
|||||||
fieldList.List = append(fieldList.List, field)
|
fieldList.List = append(fieldList.List, field)
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export tinygo_clang_enum_visitor
|
||||||
|
func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||||
|
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
||||||
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
|
pos := p.getCursorPosition(c)
|
||||||
|
value := C.tinygo_clang_getEnumConstantDeclValue(c)
|
||||||
|
p.constants[name] = constantInfo{
|
||||||
|
expr: &ast.BasicLit{pos, token.INT, strconv.FormatInt(int64(value), 10)},
|
||||||
|
pos: pos,
|
||||||
|
}
|
||||||
|
return C.CXChildVisit_Continue
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// +build !byollvm
|
// +build !byollvm
|
||||||
|
|
||||||
package loader
|
package cgo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo linux CFLAGS: -I/usr/lib/llvm-8/include
|
#cgo linux CFLAGS: -I/usr/lib/llvm-8/include
|
||||||
@@ -49,6 +49,18 @@ CXSourceLocation tinygo_clang_getCursorLocation(CXCursor c) {
|
|||||||
return clang_getCursorLocation(c);
|
return clang_getCursorLocation(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CXSourceRange tinygo_clang_getCursorExtent(CXCursor c) {
|
||||||
|
return clang_getCursorExtent(c);
|
||||||
|
}
|
||||||
|
|
||||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(CXCursor c) {
|
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(CXCursor c) {
|
||||||
return clang_Cursor_getTranslationUnit(c);
|
return clang_Cursor_getTranslationUnit(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long long tinygo_clang_getEnumConstantDeclValue(CXCursor c) {
|
||||||
|
return clang_getEnumConstantDeclValue(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
CXType tinygo_clang_getEnumDeclIntegerType(CXCursor c) {
|
||||||
|
return clang_getEnumDeclIntegerType(c);
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package loader
|
package cgo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
@@ -8,17 +8,17 @@ import (
|
|||||||
// #include <stdlib.h>
|
// #include <stdlib.h>
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
// RefMap is a convenient way to store opaque references that can be passed to
|
// refMap is a convenient way to store opaque references that can be passed to
|
||||||
// C. It is useful if an API uses function pointers and you cannot pass a Go
|
// C. It is useful if an API uses function pointers and you cannot pass a Go
|
||||||
// pointer but only a C pointer.
|
// pointer but only a C pointer.
|
||||||
type RefMap struct {
|
type refMap struct {
|
||||||
refs map[unsafe.Pointer]interface{}
|
refs map[unsafe.Pointer]interface{}
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put stores a value in the map. It can later be retrieved using Get. It must
|
// Put stores a value in the map. It can later be retrieved using Get. It must
|
||||||
// be removed using Remove to avoid memory leaks.
|
// be removed using Remove to avoid memory leaks.
|
||||||
func (m *RefMap) Put(v interface{}) unsafe.Pointer {
|
func (m *refMap) Put(v interface{}) unsafe.Pointer {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
if m.refs == nil {
|
if m.refs == nil {
|
||||||
@@ -31,14 +31,14 @@ func (m *RefMap) Put(v interface{}) unsafe.Pointer {
|
|||||||
|
|
||||||
// Get returns a stored value previously inserted with Put. Use the same
|
// Get returns a stored value previously inserted with Put. Use the same
|
||||||
// reference as you got from Put.
|
// reference as you got from Put.
|
||||||
func (m *RefMap) Get(ref unsafe.Pointer) interface{} {
|
func (m *refMap) Get(ref unsafe.Pointer) interface{} {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
return m.refs[ref]
|
return m.refs[ref]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove deletes a single reference from the map.
|
// Remove deletes a single reference from the map.
|
||||||
func (m *RefMap) Remove(ref unsafe.Pointer) {
|
func (m *refMap) Remove(ref unsafe.Pointer) {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
delete(m.refs, ref)
|
delete(m.refs, ref)
|
||||||
+11
@@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,6 +16,16 @@ var commands = map[string][]string{
|
|||||||
"wasm-ld": {"wasm-ld-8", "wasm-ld"},
|
"wasm-ld": {"wasm-ld-8", "wasm-ld"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Add the path to a Homebrew-installed LLVM 8 for ease of use (no need to
|
||||||
|
// manually set $PATH).
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
commands["clang"] = append(commands["clang"], "/usr/local/opt/llvm/bin/clang-8")
|
||||||
|
commands["ld.lld"] = append(commands["ld.lld"], "/usr/local/opt/llvm/bin/ld.lld")
|
||||||
|
commands["wasm-ld"] = append(commands["wasm-ld"], "/usr/local/opt/llvm/bin/wasm-ld")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func execCommand(cmdNames []string, args ...string) error {
|
func execCommand(cmdNames []string, args ...string) error {
|
||||||
for _, cmdName := range cmdNames {
|
for _, cmdName := range cmdNames {
|
||||||
cmd := exec.Command(cmdName, args...)
|
cmd := exec.Command(cmdName, args...)
|
||||||
|
|||||||
+8
-3
@@ -42,7 +42,7 @@ func (c *Compiler) emitLookupBoundsCheck(frame *Frame, arrayLen, index llvm.Valu
|
|||||||
|
|
||||||
// Fail: this is a nil pointer, exit with a panic.
|
// Fail: this is a nil pointer, exit with a panic.
|
||||||
c.builder.SetInsertPointAtEnd(faultBlock)
|
c.builder.SetInsertPointAtEnd(faultBlock)
|
||||||
c.createRuntimeCall("lookuppanic", nil, "")
|
c.createRuntimeCall("lookupPanic", nil, "")
|
||||||
c.builder.CreateUnreachable()
|
c.builder.CreateUnreachable()
|
||||||
|
|
||||||
// Ok: this is a valid pointer.
|
// Ok: this is a valid pointer.
|
||||||
@@ -103,7 +103,7 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.V
|
|||||||
|
|
||||||
// Fail: this is a nil pointer, exit with a panic.
|
// Fail: this is a nil pointer, exit with a panic.
|
||||||
c.builder.SetInsertPointAtEnd(faultBlock)
|
c.builder.SetInsertPointAtEnd(faultBlock)
|
||||||
c.createRuntimeCall("slicepanic", nil, "")
|
c.createRuntimeCall("slicePanic", nil, "")
|
||||||
c.builder.CreateUnreachable()
|
c.builder.CreateUnreachable()
|
||||||
|
|
||||||
// Ok: this is a valid pointer.
|
// Ok: this is a valid pointer.
|
||||||
@@ -114,6 +114,11 @@ func (c *Compiler) emitSliceBoundsCheck(frame *Frame, capacity, low, high llvm.V
|
|||||||
// has no effect in well-behaved programs, but makes sure no uncaught nil
|
// has no effect in well-behaved programs, but makes sure no uncaught nil
|
||||||
// pointer dereferences exist in valid Go code.
|
// pointer dereferences exist in valid Go code.
|
||||||
func (c *Compiler) emitNilCheck(frame *Frame, ptr llvm.Value, blockPrefix string) {
|
func (c *Compiler) emitNilCheck(frame *Frame, ptr llvm.Value, blockPrefix string) {
|
||||||
|
// Check whether we need to emit this check at all.
|
||||||
|
if !ptr.IsAGlobalValue().IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Check whether this is a nil pointer.
|
// Check whether this is a nil pointer.
|
||||||
faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".nil")
|
faultBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".nil")
|
||||||
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".next")
|
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, blockPrefix+".next")
|
||||||
@@ -141,7 +146,7 @@ func (c *Compiler) emitNilCheck(frame *Frame, ptr llvm.Value, blockPrefix string
|
|||||||
|
|
||||||
// Fail: this is a nil pointer, exit with a panic.
|
// Fail: this is a nil pointer, exit with a panic.
|
||||||
c.builder.SetInsertPointAtEnd(faultBlock)
|
c.builder.SetInsertPointAtEnd(faultBlock)
|
||||||
c.createRuntimeCall("nilpanic", nil, "")
|
c.createRuntimeCall("nilPanic", nil, "")
|
||||||
c.builder.CreateUnreachable()
|
c.builder.CreateUnreachable()
|
||||||
|
|
||||||
// Ok: this is a valid pointer.
|
// Ok: this is a valid pointer.
|
||||||
|
|||||||
@@ -55,6 +55,25 @@ func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expand an argument type to a list of offsets from the start of the object.
|
||||||
|
// Used together with expandFormalParam to get the offset of each value from the
|
||||||
|
// start of the non-expanded value.
|
||||||
|
func (c *Compiler) expandFormalParamOffsets(t llvm.Type) []uint64 {
|
||||||
|
switch t.TypeKind() {
|
||||||
|
case llvm.StructTypeKind:
|
||||||
|
fields := c.flattenAggregateTypeOffsets(t)
|
||||||
|
if len(fields) <= MaxFieldsPerParam {
|
||||||
|
return fields
|
||||||
|
} else {
|
||||||
|
// failed to lower
|
||||||
|
return []uint64{0}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// TODO: split small arrays
|
||||||
|
return []uint64{0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Equivalent of expandFormalParamType for parameter values.
|
// Equivalent of expandFormalParamType for parameter values.
|
||||||
func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value {
|
func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||||
switch v.Type().TypeKind() {
|
switch v.Type().TypeKind() {
|
||||||
@@ -92,6 +111,27 @@ func (c *Compiler) flattenAggregateType(t llvm.Type) []llvm.Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the offsets from the start of the object if this object type were
|
||||||
|
// flattened like in flattenAggregate. Used together with flattenAggregate to
|
||||||
|
// know the start indices of each value in the non-flattened object.
|
||||||
|
func (c *Compiler) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
|
||||||
|
switch t.TypeKind() {
|
||||||
|
case llvm.StructTypeKind:
|
||||||
|
fields := make([]uint64, 0, t.StructElementTypesCount())
|
||||||
|
for fieldIndex, field := range t.StructElementTypes() {
|
||||||
|
suboffsets := c.flattenAggregateTypeOffsets(field)
|
||||||
|
offset := c.targetData.ElementOffset(t, fieldIndex)
|
||||||
|
for i := range suboffsets {
|
||||||
|
suboffsets[i] += offset
|
||||||
|
}
|
||||||
|
fields = append(fields, suboffsets...)
|
||||||
|
}
|
||||||
|
return fields
|
||||||
|
default:
|
||||||
|
return []uint64{0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Break down a struct into its elementary types for argument passing. The value
|
// Break down a struct into its elementary types for argument passing. The value
|
||||||
// equivalent of flattenAggregateType
|
// equivalent of flattenAggregateType
|
||||||
func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value {
|
func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value {
|
||||||
|
|||||||
+13
-19
@@ -23,47 +23,41 @@ func (c *Compiler) emitMakeChan(expr *ssa.MakeChan) (llvm.Value, error) {
|
|||||||
// emitChanSend emits a pseudo chan send operation. It is lowered to the actual
|
// emitChanSend emits a pseudo chan send operation. It is lowered to the actual
|
||||||
// channel send operation during goroutine lowering.
|
// channel send operation during goroutine lowering.
|
||||||
func (c *Compiler) emitChanSend(frame *Frame, instr *ssa.Send) {
|
func (c *Compiler) emitChanSend(frame *Frame, instr *ssa.Send) {
|
||||||
valueType := c.getLLVMType(instr.X.Type())
|
|
||||||
ch := c.getValue(frame, instr.Chan)
|
ch := c.getValue(frame, instr.Chan)
|
||||||
chanValue := c.getValue(frame, instr.X)
|
chanValue := c.getValue(frame, instr.X)
|
||||||
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(chanValue.Type()), false)
|
|
||||||
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
|
||||||
|
|
||||||
// store value-to-send
|
// store value-to-send
|
||||||
c.builder.SetInsertPointBefore(coroutine.InstructionParent().Parent().EntryBasicBlock().FirstInstruction())
|
valueType := c.getLLVMType(instr.X.Type())
|
||||||
valueAlloca := c.builder.CreateAlloca(valueType, "chan.value")
|
valueAlloca, valueAllocaCast, valueAllocaSize := c.createTemporaryAlloca(valueType, "chan.value")
|
||||||
c.builder.SetInsertPointBefore(coroutine)
|
|
||||||
c.builder.SetInsertPointAtEnd(coroutine.InstructionParent())
|
|
||||||
c.builder.CreateStore(chanValue, valueAlloca)
|
c.builder.CreateStore(chanValue, valueAlloca)
|
||||||
valueAllocaCast := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "chan.value.i8ptr")
|
|
||||||
|
|
||||||
// Do the send.
|
// Do the send.
|
||||||
|
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
||||||
|
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(chanValue.Type()), false)
|
||||||
c.createRuntimeCall("chanSend", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "")
|
c.createRuntimeCall("chanSend", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "")
|
||||||
|
|
||||||
// Make sure CoroSplit includes the alloca in the coroutine frame.
|
// End the lifetime of the alloca.
|
||||||
// This is a bit dirty, but it works (at least in LLVM 8).
|
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||||
valueSizeI64 := llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(chanValue.Type()), false)
|
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
||||||
c.builder.CreateCall(c.getLifetimeEndFunc(), []llvm.Value{valueSizeI64, valueAllocaCast}, "")
|
c.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// emitChanRecv emits a pseudo chan receive operation. It is lowered to the
|
// emitChanRecv emits a pseudo chan receive operation. It is lowered to the
|
||||||
// actual channel receive operation during goroutine lowering.
|
// actual channel receive operation during goroutine lowering.
|
||||||
func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) llvm.Value {
|
func (c *Compiler) emitChanRecv(frame *Frame, unop *ssa.UnOp) llvm.Value {
|
||||||
valueType := c.getLLVMType(unop.X.Type().(*types.Chan).Elem())
|
valueType := c.getLLVMType(unop.X.Type().(*types.Chan).Elem())
|
||||||
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false)
|
|
||||||
ch := c.getValue(frame, unop.X)
|
ch := c.getValue(frame, unop.X)
|
||||||
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
|
||||||
|
|
||||||
// Allocate memory to receive into.
|
// Allocate memory to receive into.
|
||||||
c.builder.SetInsertPointBefore(coroutine.InstructionParent().Parent().EntryBasicBlock().FirstInstruction())
|
valueAlloca, valueAllocaCast, valueAllocaSize := c.createTemporaryAlloca(valueType, "chan.value")
|
||||||
valueAlloca := c.builder.CreateAlloca(valueType, "chan.value")
|
|
||||||
c.builder.SetInsertPointBefore(coroutine)
|
|
||||||
c.builder.SetInsertPointAtEnd(coroutine.InstructionParent())
|
|
||||||
valueAllocaCast := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "chan.value.i8ptr")
|
|
||||||
|
|
||||||
// Do the receive.
|
// Do the receive.
|
||||||
|
coroutine := c.createRuntimeCall("getCoroutine", nil, "")
|
||||||
|
valueSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(valueType), false)
|
||||||
c.createRuntimeCall("chanRecv", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "")
|
c.createRuntimeCall("chanRecv", []llvm.Value{coroutine, ch, valueAllocaCast, valueSize}, "")
|
||||||
received := c.builder.CreateLoad(valueAlloca, "chan.received")
|
received := c.builder.CreateLoad(valueAlloca, "chan.received")
|
||||||
|
c.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||||
|
|
||||||
if unop.CommaOk {
|
if unop.CommaOk {
|
||||||
commaOk := c.createRuntimeCall("getTaskPromiseData", []llvm.Value{coroutine}, "chan.commaOk.wide")
|
commaOk := c.createRuntimeCall("getTaskPromiseData", []llvm.Value{coroutine}, "chan.commaOk.wide")
|
||||||
commaOk = c.builder.CreateTrunc(commaOk, c.ctx.Int1Type(), "chan.commaOk")
|
commaOk = c.builder.CreateTrunc(commaOk, c.ctx.Int1Type(), "chan.commaOk")
|
||||||
|
|||||||
+243
-59
@@ -30,6 +30,7 @@ func init() {
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default)
|
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)
|
CPU string // LLVM CPU name, e.g. atmega328p (empty string means default)
|
||||||
|
Features []string // LLVM CPU features
|
||||||
GOOS string //
|
GOOS string //
|
||||||
GOARCH string //
|
GOARCH string //
|
||||||
GC string // garbage collection strategy
|
GC string // garbage collection strategy
|
||||||
@@ -52,7 +53,6 @@ type Compiler struct {
|
|||||||
dibuilder *llvm.DIBuilder
|
dibuilder *llvm.DIBuilder
|
||||||
cu llvm.Metadata
|
cu llvm.Metadata
|
||||||
difiles map[string]llvm.Metadata
|
difiles map[string]llvm.Metadata
|
||||||
ditypes map[string]llvm.Metadata
|
|
||||||
machine llvm.TargetMachine
|
machine llvm.TargetMachine
|
||||||
targetData llvm.TargetData
|
targetData llvm.TargetData
|
||||||
intType llvm.Type
|
intType llvm.Type
|
||||||
@@ -96,14 +96,17 @@ func NewCompiler(pkgName string, config Config) (*Compiler, error) {
|
|||||||
c := &Compiler{
|
c := &Compiler{
|
||||||
Config: config,
|
Config: config,
|
||||||
difiles: make(map[string]llvm.Metadata),
|
difiles: make(map[string]llvm.Metadata),
|
||||||
ditypes: make(map[string]llvm.Metadata),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target, err := llvm.GetTargetFromTriple(config.Triple)
|
target, err := llvm.GetTargetFromTriple(config.Triple)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c.machine = target.CreateTargetMachine(config.Triple, config.CPU, "", llvm.CodeGenLevelDefault, llvm.RelocStatic, llvm.CodeModelDefault)
|
features := ""
|
||||||
|
if len(config.Features) > 0 {
|
||||||
|
features = strings.Join(config.Features, `,`)
|
||||||
|
}
|
||||||
|
c.machine = target.CreateTargetMachine(config.Triple, config.CPU, features, llvm.CodeGenLevelDefault, llvm.RelocStatic, llvm.CodeModelDefault)
|
||||||
c.targetData = c.machine.CreateTargetData()
|
c.targetData = c.machine.CreateTargetData()
|
||||||
|
|
||||||
c.ctx = llvm.NewContext()
|
c.ctx = llvm.NewContext()
|
||||||
@@ -197,7 +200,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
},
|
},
|
||||||
ShouldOverlay: func(path string) bool {
|
ShouldOverlay: func(path string) bool {
|
||||||
switch path {
|
switch path {
|
||||||
case "machine", "os", "reflect", "runtime", "sync":
|
case "machine", "os", "reflect", "runtime", "runtime/volatile", "sync":
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
||||||
@@ -252,7 +255,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
// Initialize debug information.
|
// Initialize debug information.
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||||
Language: llvm.DW_LANG_Go,
|
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
|
||||||
File: mainPath,
|
File: mainPath,
|
||||||
Dir: "",
|
Dir: "",
|
||||||
Producer: "TinyGo",
|
Producer: "TinyGo",
|
||||||
@@ -387,6 +390,13 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||||
|
c.ctx.MDNode([]llvm.Metadata{
|
||||||
|
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(),
|
||||||
|
llvm.GlobalContext().MDString("Dwarf Version"),
|
||||||
|
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
c.dibuilder.Finalize()
|
c.dibuilder.Finalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,39 +560,166 @@ func isPointer(typ types.Type) bool {
|
|||||||
|
|
||||||
// Get the DWARF type for this Go type.
|
// Get the DWARF type for this Go type.
|
||||||
func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
|
func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
|
||||||
name := typ.String()
|
llvmType := c.getLLVMType(typ)
|
||||||
if dityp, ok := c.ditypes[name]; ok {
|
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
||||||
return dityp
|
switch typ := typ.(type) {
|
||||||
} else {
|
case *types.Array:
|
||||||
llvmType := c.getLLVMType(typ)
|
return c.dibuilder.CreateArrayType(llvm.DIArrayType{
|
||||||
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
SizeInBits: sizeInBytes * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||||
|
ElementType: c.getDIType(typ.Elem()),
|
||||||
|
Subscripts: []llvm.DISubrange{
|
||||||
|
llvm.DISubrange{
|
||||||
|
Lo: 0,
|
||||||
|
Count: typ.Len(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case *types.Basic:
|
||||||
var encoding llvm.DwarfTypeEncoding
|
var encoding llvm.DwarfTypeEncoding
|
||||||
switch typ := typ.(type) {
|
if typ.Info()&types.IsBoolean != 0 {
|
||||||
case *types.Basic:
|
encoding = llvm.DW_ATE_boolean
|
||||||
if typ.Info()&types.IsBoolean != 0 {
|
} else if typ.Info()&types.IsFloat != 0 {
|
||||||
encoding = llvm.DW_ATE_boolean
|
encoding = llvm.DW_ATE_float
|
||||||
} else if typ.Info()&types.IsFloat != 0 {
|
} else if typ.Info()&types.IsComplex != 0 {
|
||||||
encoding = llvm.DW_ATE_float
|
encoding = llvm.DW_ATE_complex_float
|
||||||
} else if typ.Info()&types.IsComplex != 0 {
|
} else if typ.Info()&types.IsUnsigned != 0 {
|
||||||
encoding = llvm.DW_ATE_complex_float
|
encoding = llvm.DW_ATE_unsigned
|
||||||
} else if typ.Info()&types.IsUnsigned != 0 {
|
} else if typ.Info()&types.IsInteger != 0 {
|
||||||
encoding = llvm.DW_ATE_unsigned
|
encoding = llvm.DW_ATE_signed
|
||||||
} else if typ.Info()&types.IsInteger != 0 {
|
} else if typ.Kind() == types.UnsafePointer {
|
||||||
encoding = llvm.DW_ATE_signed
|
return c.dibuilder.CreatePointerType(llvm.DIPointerType{
|
||||||
} else if typ.Kind() == types.UnsafePointer {
|
Name: "unsafe.Pointer",
|
||||||
encoding = llvm.DW_ATE_address
|
SizeInBits: c.targetData.TypeAllocSize(llvmType) * 8,
|
||||||
}
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||||
case *types.Pointer:
|
AddressSpace: 0,
|
||||||
encoding = llvm.DW_ATE_address
|
})
|
||||||
|
} else if typ.Info()&types.IsString != 0 {
|
||||||
|
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
||||||
|
Name: "string",
|
||||||
|
SizeInBits: sizeInBytes * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||||
|
Elements: []llvm.Metadata{
|
||||||
|
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||||
|
Name: "ptr",
|
||||||
|
SizeInBits: c.targetData.TypeAllocSize(c.i8ptrType) * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.i8ptrType)) * 8,
|
||||||
|
OffsetInBits: 0,
|
||||||
|
Type: c.getDIType(types.NewPointer(types.Typ[types.Byte])),
|
||||||
|
}),
|
||||||
|
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||||
|
Name: "len",
|
||||||
|
SizeInBits: c.targetData.TypeAllocSize(c.uintptrType) * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
|
||||||
|
OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
|
||||||
|
Type: c.getDIType(types.Typ[types.Uintptr]),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
panic("unknown basic type")
|
||||||
}
|
}
|
||||||
// TODO: other types
|
return c.dibuilder.CreateBasicType(llvm.DIBasicType{
|
||||||
dityp = c.dibuilder.CreateBasicType(llvm.DIBasicType{
|
Name: typ.String(),
|
||||||
Name: name,
|
|
||||||
SizeInBits: sizeInBytes * 8,
|
SizeInBits: sizeInBytes * 8,
|
||||||
Encoding: encoding,
|
Encoding: encoding,
|
||||||
})
|
})
|
||||||
c.ditypes[name] = dityp
|
case *types.Chan:
|
||||||
return dityp
|
return c.getDIType(types.NewPointer(c.ir.Program.ImportedPackage("runtime").Members["channel"].(*ssa.Type).Type()))
|
||||||
|
case *types.Interface:
|
||||||
|
return c.getDIType(c.ir.Program.ImportedPackage("runtime").Members["_interface"].(*ssa.Type).Type())
|
||||||
|
case *types.Map:
|
||||||
|
return c.getDIType(types.NewPointer(c.ir.Program.ImportedPackage("runtime").Members["hashmap"].(*ssa.Type).Type()))
|
||||||
|
case *types.Named:
|
||||||
|
return c.dibuilder.CreateTypedef(llvm.DITypedef{
|
||||||
|
Type: c.getDIType(typ.Underlying()),
|
||||||
|
Name: typ.String(),
|
||||||
|
})
|
||||||
|
case *types.Pointer:
|
||||||
|
return c.dibuilder.CreatePointerType(llvm.DIPointerType{
|
||||||
|
Pointee: c.getDIType(typ.Elem()),
|
||||||
|
SizeInBits: c.targetData.TypeAllocSize(llvmType) * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||||
|
AddressSpace: 0,
|
||||||
|
})
|
||||||
|
case *types.Signature:
|
||||||
|
// actually a closure
|
||||||
|
fields := llvmType.StructElementTypes()
|
||||||
|
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
||||||
|
SizeInBits: sizeInBytes * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||||
|
Elements: []llvm.Metadata{
|
||||||
|
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||||
|
Name: "context",
|
||||||
|
SizeInBits: c.targetData.TypeAllocSize(fields[1]) * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(fields[1])) * 8,
|
||||||
|
OffsetInBits: 0,
|
||||||
|
Type: c.getDIType(types.Typ[types.UnsafePointer]),
|
||||||
|
}),
|
||||||
|
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||||
|
Name: "fn",
|
||||||
|
SizeInBits: c.targetData.TypeAllocSize(fields[0]) * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(fields[0])) * 8,
|
||||||
|
OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
|
||||||
|
Type: c.getDIType(types.Typ[types.UnsafePointer]),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case *types.Slice:
|
||||||
|
fields := llvmType.StructElementTypes()
|
||||||
|
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
||||||
|
Name: typ.String(),
|
||||||
|
SizeInBits: sizeInBytes * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||||
|
Elements: []llvm.Metadata{
|
||||||
|
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||||
|
Name: "ptr",
|
||||||
|
SizeInBits: c.targetData.TypeAllocSize(fields[0]) * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(fields[0])) * 8,
|
||||||
|
OffsetInBits: 0,
|
||||||
|
Type: c.getDIType(types.NewPointer(typ.Elem())),
|
||||||
|
}),
|
||||||
|
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||||
|
Name: "len",
|
||||||
|
SizeInBits: c.targetData.TypeAllocSize(c.uintptrType) * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
|
||||||
|
OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
|
||||||
|
Type: c.getDIType(types.Typ[types.Uintptr]),
|
||||||
|
}),
|
||||||
|
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||||
|
Name: "cap",
|
||||||
|
SizeInBits: c.targetData.TypeAllocSize(c.uintptrType) * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
|
||||||
|
OffsetInBits: c.targetData.ElementOffset(llvmType, 2) * 8,
|
||||||
|
Type: c.getDIType(types.Typ[types.Uintptr]),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case *types.Struct:
|
||||||
|
elements := make([]llvm.Metadata, typ.NumFields())
|
||||||
|
for i := range elements {
|
||||||
|
field := typ.Field(i)
|
||||||
|
fieldType := field.Type()
|
||||||
|
if _, ok := fieldType.Underlying().(*types.Pointer); ok {
|
||||||
|
// XXX hack to avoid recursive types
|
||||||
|
fieldType = types.Typ[types.UnsafePointer]
|
||||||
|
}
|
||||||
|
llvmField := c.getLLVMType(fieldType)
|
||||||
|
elements[i] = c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
||||||
|
Name: field.Name(),
|
||||||
|
SizeInBits: c.targetData.TypeAllocSize(llvmField) * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmField)) * 8,
|
||||||
|
OffsetInBits: c.targetData.ElementOffset(llvmType, i) * 8,
|
||||||
|
Type: c.getDIType(fieldType),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
||||||
|
SizeInBits: sizeInBytes * 8,
|
||||||
|
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
||||||
|
Elements: elements,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
panic("unknown type while generating DWARF debug type: " + typ.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,6 +825,9 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
|||||||
if c.DumpSSA {
|
if c.DumpSSA {
|
||||||
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
||||||
}
|
}
|
||||||
|
if !frame.fn.LLVMFn.IsDeclaration() {
|
||||||
|
panic("function is already defined: " + frame.fn.LLVMFn.Name())
|
||||||
|
}
|
||||||
if !frame.fn.IsExported() {
|
if !frame.fn.IsExported() {
|
||||||
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
||||||
frame.fn.LLVMFn.SetUnnamedAddr(true)
|
frame.fn.LLVMFn.SetUnnamedAddr(true)
|
||||||
@@ -696,6 +836,14 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
|||||||
frame.fn.LLVMFn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
|
frame.fn.LLVMFn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some functions have a pragma controlling the inlining level.
|
||||||
|
switch frame.fn.Inline() {
|
||||||
|
case ir.InlineHint:
|
||||||
|
// Add LLVM inline hint to functions with //go:inline pragma.
|
||||||
|
inline := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("inlinehint"), 0)
|
||||||
|
frame.fn.LLVMFn.AddFunctionAttr(inline)
|
||||||
|
}
|
||||||
|
|
||||||
// Add debug info, if needed.
|
// Add debug info, if needed.
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
if frame.fn.Synthetic == "package initializer" {
|
if frame.fn.Synthetic == "package initializer" {
|
||||||
@@ -733,15 +881,30 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
|||||||
// Add debug information to this parameter (if available)
|
// Add debug information to this parameter (if available)
|
||||||
if c.Debug && frame.fn.Syntax() != nil {
|
if c.Debug && frame.fn.Syntax() != nil {
|
||||||
pos := c.ir.Program.Fset.Position(frame.fn.Syntax().Pos())
|
pos := c.ir.Program.Fset.Position(frame.fn.Syntax().Pos())
|
||||||
c.dibuilder.CreateParameterVariable(frame.difunc, llvm.DIParameterVariable{
|
diType := c.getDIType(param.Type())
|
||||||
|
dbgParam := c.dibuilder.CreateParameterVariable(frame.difunc, llvm.DIParameterVariable{
|
||||||
Name: param.Name(),
|
Name: param.Name(),
|
||||||
File: c.difiles[pos.Filename],
|
File: c.difiles[pos.Filename],
|
||||||
Line: pos.Line,
|
Line: pos.Line,
|
||||||
Type: c.getDIType(param.Type()),
|
Type: diType,
|
||||||
AlwaysPreserve: true,
|
AlwaysPreserve: true,
|
||||||
ArgNo: i + 1,
|
ArgNo: i + 1,
|
||||||
})
|
})
|
||||||
// TODO: set the value of this parameter.
|
loc := c.builder.GetCurrentDebugLocation()
|
||||||
|
if len(fields) == 1 {
|
||||||
|
expr := c.dibuilder.CreateExpression(nil)
|
||||||
|
c.dibuilder.InsertValueAtEnd(fields[0], dbgParam, expr, loc, entryBlock)
|
||||||
|
} else {
|
||||||
|
fieldOffsets := c.expandFormalParamOffsets(llvmType)
|
||||||
|
for i, field := range fields {
|
||||||
|
expr := c.dibuilder.CreateExpression([]int64{
|
||||||
|
0x1000, // DW_OP_LLVM_fragment
|
||||||
|
int64(fieldOffsets[i]) * 8, // offset in bits
|
||||||
|
int64(c.targetData.TypeAllocSize(field.Type())) * 8, // size in bits
|
||||||
|
})
|
||||||
|
c.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -907,6 +1070,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
|
|||||||
case *ssa.Store:
|
case *ssa.Store:
|
||||||
llvmAddr := c.getValue(frame, instr.Addr)
|
llvmAddr := c.getValue(frame, instr.Addr)
|
||||||
llvmVal := c.getValue(frame, instr.Val)
|
llvmVal := c.getValue(frame, instr.Val)
|
||||||
|
c.emitNilCheck(frame, llvmAddr, "store")
|
||||||
if c.targetData.TypeAllocSize(llvmVal.Type()) == 0 {
|
if c.targetData.TypeAllocSize(llvmVal.Type()) == 0 {
|
||||||
// nothing to store
|
// nothing to store
|
||||||
return
|
return
|
||||||
@@ -1117,17 +1281,22 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
|||||||
|
|
||||||
// Try to call the function directly for trivially static calls.
|
// Try to call the function directly for trivially static calls.
|
||||||
if fn := instr.StaticCallee(); fn != nil {
|
if fn := instr.StaticCallee(); fn != nil {
|
||||||
switch fn.RelString(nil) {
|
name := fn.RelString(nil)
|
||||||
case "device/arm.ReadRegister":
|
switch {
|
||||||
|
case name == "device/arm.ReadRegister":
|
||||||
return c.emitReadRegister(instr.Args)
|
return c.emitReadRegister(instr.Args)
|
||||||
case "device/arm.Asm", "device/avr.Asm":
|
case name == "device/arm.Asm" || name == "device/avr.Asm":
|
||||||
return c.emitAsm(instr.Args)
|
return c.emitAsm(instr.Args)
|
||||||
case "device/arm.AsmFull", "device/avr.AsmFull":
|
case name == "device/arm.AsmFull" || name == "device/avr.AsmFull":
|
||||||
return c.emitAsmFull(frame, instr)
|
return c.emitAsmFull(frame, instr)
|
||||||
case "device/arm.SVCall0", "device/arm.SVCall1", "device/arm.SVCall2", "device/arm.SVCall3", "device/arm.SVCall4":
|
case strings.HasPrefix(name, "device/arm.SVCall"):
|
||||||
return c.emitSVCall(frame, instr.Args)
|
return c.emitSVCall(frame, instr.Args)
|
||||||
case "syscall.Syscall", "syscall.Syscall6", "syscall.Syscall9":
|
case strings.HasPrefix(name, "syscall.Syscall"):
|
||||||
return c.emitSyscall(frame, instr)
|
return c.emitSyscall(frame, instr)
|
||||||
|
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||||
|
return c.emitVolatileLoad(frame, instr)
|
||||||
|
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||||
|
return c.emitVolatileStore(frame, instr)
|
||||||
}
|
}
|
||||||
|
|
||||||
targetFunc := c.ir.GetFunction(fn)
|
targetFunc := c.ir.GetFunction(fn)
|
||||||
@@ -1208,7 +1377,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
switch expr := expr.(type) {
|
switch expr := expr.(type) {
|
||||||
case *ssa.Alloc:
|
case *ssa.Alloc:
|
||||||
typ := c.getLLVMType(expr.Type().Underlying().(*types.Pointer).Elem())
|
typ := c.getLLVMType(expr.Type().Underlying().(*types.Pointer).Elem())
|
||||||
var buf llvm.Value
|
|
||||||
if expr.Heap {
|
if expr.Heap {
|
||||||
size := c.targetData.TypeAllocSize(typ)
|
size := c.targetData.TypeAllocSize(typ)
|
||||||
// Calculate ^uintptr(0)
|
// Calculate ^uintptr(0)
|
||||||
@@ -1219,15 +1387,16 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
}
|
}
|
||||||
// TODO: escape analysis
|
// TODO: escape analysis
|
||||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||||
buf = c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, expr.Comment)
|
buf := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, expr.Comment)
|
||||||
buf = c.builder.CreateBitCast(buf, llvm.PointerType(typ, 0), "")
|
buf = c.builder.CreateBitCast(buf, llvm.PointerType(typ, 0), "")
|
||||||
|
return buf, nil
|
||||||
} else {
|
} else {
|
||||||
buf = c.builder.CreateAlloca(typ, expr.Comment)
|
buf := c.createEntryBlockAlloca(typ, expr.Comment)
|
||||||
if c.targetData.TypeAllocSize(typ) != 0 {
|
if c.targetData.TypeAllocSize(typ) != 0 {
|
||||||
c.builder.CreateStore(c.getZeroValue(typ), buf) // zero-initialize var
|
c.builder.CreateStore(c.getZeroValue(typ), buf) // zero-initialize var
|
||||||
}
|
}
|
||||||
|
return buf, nil
|
||||||
}
|
}
|
||||||
return buf, nil
|
|
||||||
case *ssa.BinOp:
|
case *ssa.BinOp:
|
||||||
x := c.getValue(frame, expr.X)
|
x := c.getValue(frame, expr.X)
|
||||||
y := c.getValue(frame, expr.Y)
|
y := c.getValue(frame, expr.Y)
|
||||||
@@ -1290,10 +1459,12 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
// This could be done directly, but as this is a very infrequent
|
// This could be done directly, but as this is a very infrequent
|
||||||
// operation it's much easier to bitcast it through an alloca.
|
// operation it's much easier to bitcast it through an alloca.
|
||||||
resultType := c.getLLVMType(expr.Type())
|
resultType := c.getLLVMType(expr.Type())
|
||||||
alloca := c.builder.CreateAlloca(value.Type(), "")
|
alloca, allocaPtr, allocaSize := c.createTemporaryAlloca(value.Type(), "union.alloca")
|
||||||
c.builder.CreateStore(value, alloca)
|
c.builder.CreateStore(value, alloca)
|
||||||
bitcast := c.builder.CreateBitCast(alloca, llvm.PointerType(resultType, 0), "")
|
bitcast := c.builder.CreateBitCast(alloca, llvm.PointerType(resultType, 0), "union.bitcast")
|
||||||
return c.builder.CreateLoad(bitcast, ""), nil
|
result := c.builder.CreateLoad(bitcast, "union.result")
|
||||||
|
c.emitLifetimeEnd(allocaPtr, allocaSize)
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
result := c.builder.CreateExtractValue(value, expr.Field, "")
|
result := c.builder.CreateExtractValue(value, expr.Field, "")
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -1333,11 +1504,13 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
|
|
||||||
// Can't load directly from array (as index is non-constant), so have to
|
// Can't load directly from array (as index is non-constant), so have to
|
||||||
// do it using an alloca+gep+load.
|
// do it using an alloca+gep+load.
|
||||||
alloca := c.builder.CreateAlloca(array.Type(), "index.alloca")
|
alloca, allocaPtr, allocaSize := c.createTemporaryAlloca(array.Type(), "index.alloca")
|
||||||
c.builder.CreateStore(array, alloca)
|
c.builder.CreateStore(array, alloca)
|
||||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||||
ptr := c.builder.CreateInBoundsGEP(alloca, []llvm.Value{zero, index}, "index.gep")
|
ptr := c.builder.CreateInBoundsGEP(alloca, []llvm.Value{zero, index}, "index.gep")
|
||||||
return c.builder.CreateLoad(ptr, "index.load"), nil
|
result := c.builder.CreateLoad(ptr, "index.load")
|
||||||
|
c.emitLifetimeEnd(allocaPtr, allocaSize)
|
||||||
|
return result, nil
|
||||||
case *ssa.IndexAddr:
|
case *ssa.IndexAddr:
|
||||||
val := c.getValue(frame, expr.X)
|
val := c.getValue(frame, expr.X)
|
||||||
index := c.getValue(frame, expr.Index)
|
index := c.getValue(frame, expr.Index)
|
||||||
@@ -1416,7 +1589,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
return c.parseMakeClosure(frame, expr)
|
return c.parseMakeClosure(frame, expr)
|
||||||
case *ssa.MakeInterface:
|
case *ssa.MakeInterface:
|
||||||
val := c.getValue(frame, expr.X)
|
val := c.getValue(frame, expr.X)
|
||||||
return c.parseMakeInterface(val, expr.X.Type(), expr.Pos())
|
return c.parseMakeInterface(val, expr.X.Type(), expr.Pos()), nil
|
||||||
case *ssa.MakeMap:
|
case *ssa.MakeMap:
|
||||||
mapType := expr.Type().Underlying().(*types.Map)
|
mapType := expr.Type().Underlying().(*types.Map)
|
||||||
llvmKeyType := c.getLLVMType(mapType.Key().Underlying())
|
llvmKeyType := c.getLLVMType(mapType.Key().Underlying())
|
||||||
@@ -1425,7 +1598,16 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
valueSize := c.targetData.TypeAllocSize(llvmValueType)
|
valueSize := c.targetData.TypeAllocSize(llvmValueType)
|
||||||
llvmKeySize := llvm.ConstInt(c.ctx.Int8Type(), keySize, false)
|
llvmKeySize := llvm.ConstInt(c.ctx.Int8Type(), keySize, false)
|
||||||
llvmValueSize := llvm.ConstInt(c.ctx.Int8Type(), valueSize, false)
|
llvmValueSize := llvm.ConstInt(c.ctx.Int8Type(), valueSize, false)
|
||||||
hashmap := c.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize}, "")
|
sizeHint := llvm.ConstInt(c.uintptrType, 8, false)
|
||||||
|
if expr.Reserve != nil {
|
||||||
|
sizeHint = c.getValue(frame, expr.Reserve)
|
||||||
|
var err error
|
||||||
|
sizeHint, err = c.parseConvert(expr.Reserve.Type(), types.Typ[types.Uintptr], sizeHint, expr.Pos())
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hashmap := c.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
|
||||||
return hashmap, nil
|
return hashmap, nil
|
||||||
case *ssa.MakeSlice:
|
case *ssa.MakeSlice:
|
||||||
sliceLen := c.getValue(frame, expr.Len)
|
sliceLen := c.getValue(frame, expr.Len)
|
||||||
@@ -1488,16 +1670,16 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
llvmKeyType := c.getLLVMType(rangeVal.Type().Underlying().(*types.Map).Key())
|
llvmKeyType := c.getLLVMType(rangeVal.Type().Underlying().(*types.Map).Key())
|
||||||
llvmValueType := c.getLLVMType(rangeVal.Type().Underlying().(*types.Map).Elem())
|
llvmValueType := c.getLLVMType(rangeVal.Type().Underlying().(*types.Map).Elem())
|
||||||
|
|
||||||
mapKeyAlloca := c.builder.CreateAlloca(llvmKeyType, "range.key")
|
mapKeyAlloca, mapKeyPtr, mapKeySize := c.createTemporaryAlloca(llvmKeyType, "range.key")
|
||||||
mapKeyPtr := c.builder.CreateBitCast(mapKeyAlloca, c.i8ptrType, "range.keyptr")
|
mapValueAlloca, mapValuePtr, mapValueSize := c.createTemporaryAlloca(llvmValueType, "range.value")
|
||||||
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "range.value")
|
|
||||||
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "range.valueptr")
|
|
||||||
ok := c.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next")
|
ok := c.createRuntimeCall("hashmapNext", []llvm.Value{llvmRangeVal, it, mapKeyPtr, mapValuePtr}, "range.next")
|
||||||
|
|
||||||
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.Int1Type(), llvmKeyType, llvmValueType}, false))
|
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.Int1Type(), llvmKeyType, llvmValueType}, false))
|
||||||
tuple = c.builder.CreateInsertValue(tuple, ok, 0, "")
|
tuple = c.builder.CreateInsertValue(tuple, ok, 0, "")
|
||||||
tuple = c.builder.CreateInsertValue(tuple, c.builder.CreateLoad(mapKeyAlloca, ""), 1, "")
|
tuple = c.builder.CreateInsertValue(tuple, c.builder.CreateLoad(mapKeyAlloca, ""), 1, "")
|
||||||
tuple = c.builder.CreateInsertValue(tuple, c.builder.CreateLoad(mapValueAlloca, ""), 2, "")
|
tuple = c.builder.CreateInsertValue(tuple, c.builder.CreateLoad(mapValueAlloca, ""), 2, "")
|
||||||
|
c.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
||||||
|
c.emitLifetimeEnd(mapValuePtr, mapValueSize)
|
||||||
return tuple, nil
|
return tuple, nil
|
||||||
}
|
}
|
||||||
case *ssa.Phi:
|
case *ssa.Phi:
|
||||||
@@ -1514,7 +1696,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
default:
|
default:
|
||||||
panic("unknown type in range: " + typ.String())
|
panic("unknown type in range: " + typ.String())
|
||||||
}
|
}
|
||||||
it := c.builder.CreateAlloca(iteratorType, "range.it")
|
it, _, _ := c.createTemporaryAlloca(iteratorType, "range.it")
|
||||||
c.builder.CreateStore(c.getZeroValue(iteratorType), it)
|
c.builder.CreateStore(c.getZeroValue(iteratorType), it)
|
||||||
return it, nil
|
return it, nil
|
||||||
case *ssa.Select:
|
case *ssa.Select:
|
||||||
@@ -1944,10 +2126,12 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
|||||||
default:
|
default:
|
||||||
return llvm.Value{}, c.makeError(pos, "binop on interface: "+op.String())
|
return llvm.Value{}, c.makeError(pos, "binop on interface: "+op.String())
|
||||||
}
|
}
|
||||||
case *types.Map, *types.Pointer:
|
case *types.Chan, *types.Map, *types.Pointer:
|
||||||
// Maps are in general not comparable, but can be compared against nil
|
// Maps are in general not comparable, but can be compared against nil
|
||||||
// (which is a nil pointer). This means they can be trivially compared
|
// (which is a nil pointer). This means they can be trivially compared
|
||||||
// by treating them as a pointer.
|
// by treating them as a pointer.
|
||||||
|
// Channels behave as pointers in that they are equal as long as they
|
||||||
|
// are created with the same call to make or if both are nil.
|
||||||
switch op {
|
switch op {
|
||||||
case token.EQL: // ==
|
case token.EQL: // ==
|
||||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||||
|
|||||||
@@ -154,17 +154,17 @@ func (c *Compiler) LowerFuncValues() {
|
|||||||
// What we'll do is transform the following:
|
// What we'll do is transform the following:
|
||||||
// rawPtr := runtime.getFuncPtr(fn)
|
// rawPtr := runtime.getFuncPtr(fn)
|
||||||
// if func.rawPtr == nil {
|
// if func.rawPtr == nil {
|
||||||
// runtime.nilpanic()
|
// runtime.nilPanic()
|
||||||
// }
|
// }
|
||||||
// result := func.rawPtr(...args, func.context)
|
// result := func.rawPtr(...args, func.context)
|
||||||
// into this:
|
// into this:
|
||||||
// if false {
|
// if false {
|
||||||
// runtime.nilpanic()
|
// runtime.nilPanic()
|
||||||
// }
|
// }
|
||||||
// var result // Phi
|
// var result // Phi
|
||||||
// switch fn.id {
|
// switch fn.id {
|
||||||
// case 0:
|
// case 0:
|
||||||
// runtime.nilpanic()
|
// runtime.nilPanic()
|
||||||
// case 1:
|
// case 1:
|
||||||
// result = call first implementation...
|
// result = call first implementation...
|
||||||
// case 2:
|
// case 2:
|
||||||
@@ -222,7 +222,7 @@ func (c *Compiler) LowerFuncValues() {
|
|||||||
// The 0 case, which is actually a nil check.
|
// The 0 case, which is actually a nil check.
|
||||||
nilBlock := llvm.InsertBasicBlock(nextBlock, "func.nil")
|
nilBlock := llvm.InsertBasicBlock(nextBlock, "func.nil")
|
||||||
c.builder.SetInsertPointAtEnd(nilBlock)
|
c.builder.SetInsertPointAtEnd(nilBlock)
|
||||||
c.createRuntimeCall("nilpanic", nil, "")
|
c.createRuntimeCall("nilPanic", nil, "")
|
||||||
c.builder.CreateUnreachable()
|
c.builder.CreateUnreachable()
|
||||||
sw.AddCase(llvm.ConstInt(c.uintptrType, 0, false), nilBlock)
|
sw.AddCase(llvm.ConstInt(c.uintptrType, 0, false), nilBlock)
|
||||||
|
|
||||||
|
|||||||
@@ -389,7 +389,20 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
|
|
||||||
c.builder.SetInsertPointBefore(inst)
|
c.builder.SetInsertPointBefore(inst)
|
||||||
|
|
||||||
parentHandle := f.LastParam()
|
var parentHandle llvm.Value
|
||||||
|
if f.Linkage() == llvm.ExternalLinkage {
|
||||||
|
// Exported function.
|
||||||
|
// Note that getTaskPromisePtr will panic if it is called with
|
||||||
|
// a nil pointer, so blocking exported functions that try to
|
||||||
|
// return anything will not work.
|
||||||
|
parentHandle = llvm.ConstPointerNull(c.i8ptrType)
|
||||||
|
} else {
|
||||||
|
parentHandle = f.LastParam()
|
||||||
|
if parentHandle.IsNil() || parentHandle.Name() != "parentHandle" {
|
||||||
|
// sanity check
|
||||||
|
panic("trying to make exported function async")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Store return values.
|
// Store return values.
|
||||||
switch inst.OperandsCount() {
|
switch inst.OperandsCount() {
|
||||||
@@ -417,7 +430,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
// behavior somehow (with the unreachable instruction).
|
// behavior somehow (with the unreachable instruction).
|
||||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||||
llvm.ConstNull(c.ctx.TokenType()),
|
llvm.ConstNull(c.ctx.TokenType()),
|
||||||
llvm.ConstInt(c.ctx.Int1Type(), 1, false),
|
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||||
}, "ret")
|
}, "ret")
|
||||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), frame.unreachableBlock)
|
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), frame.unreachableBlock)
|
||||||
@@ -488,7 +501,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
c.builder.SetInsertPointBefore(deadlockCall)
|
c.builder.SetInsertPointBefore(deadlockCall)
|
||||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||||
llvm.ConstNull(c.ctx.TokenType()),
|
llvm.ConstNull(c.ctx.TokenType()),
|
||||||
llvm.ConstInt(c.ctx.Int1Type(), 1, false), // final suspend
|
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||||
}, "")
|
}, "")
|
||||||
c.splitBasicBlock(deadlockCall, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup.dead")
|
c.splitBasicBlock(deadlockCall, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup.dead")
|
||||||
c.builder.SetInsertPointBefore(deadlockCall)
|
c.builder.SetInsertPointBefore(deadlockCall)
|
||||||
|
|||||||
+12
-18
@@ -22,13 +22,10 @@ import (
|
|||||||
// value field.
|
// value field.
|
||||||
//
|
//
|
||||||
// An interface value is a {typecode, value} tuple, or {i16, i8*} to be exact.
|
// An interface value is a {typecode, value} tuple, or {i16, i8*} to be exact.
|
||||||
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) (llvm.Value, error) {
|
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
|
||||||
itfValue := c.emitPointerPack([]llvm.Value{val})
|
itfValue := c.emitPointerPack([]llvm.Value{val})
|
||||||
itfTypeCodeGlobal := c.getTypeCode(typ)
|
itfTypeCodeGlobal := c.getTypeCode(typ)
|
||||||
itfMethodSetGlobal, err := c.getTypeMethodSet(typ)
|
itfMethodSetGlobal := c.getTypeMethodSet(typ)
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, nil
|
|
||||||
}
|
|
||||||
itfConcreteTypeGlobal := c.mod.NamedGlobal("typeInInterface:" + itfTypeCodeGlobal.Name())
|
itfConcreteTypeGlobal := c.mod.NamedGlobal("typeInInterface:" + itfTypeCodeGlobal.Name())
|
||||||
if itfConcreteTypeGlobal.IsNil() {
|
if itfConcreteTypeGlobal.IsNil() {
|
||||||
typeInInterface := c.mod.GetTypeByName("runtime.typeInInterface")
|
typeInInterface := c.mod.GetTypeByName("runtime.typeInInterface")
|
||||||
@@ -41,7 +38,7 @@ func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.
|
|||||||
itf := llvm.Undef(c.mod.GetTypeByName("runtime._interface"))
|
itf := llvm.Undef(c.mod.GetTypeByName("runtime._interface"))
|
||||||
itf = c.builder.CreateInsertValue(itf, itfTypeCode, 0, "")
|
itf = c.builder.CreateInsertValue(itf, itfTypeCode, 0, "")
|
||||||
itf = c.builder.CreateInsertValue(itf, itfValue, 1, "")
|
itf = c.builder.CreateInsertValue(itf, itfValue, 1, "")
|
||||||
return itf, nil
|
return itf
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTypeCode returns a reference to a type code.
|
// getTypeCode returns a reference to a type code.
|
||||||
@@ -155,18 +152,18 @@ func getTypeCodeName(t types.Type) string {
|
|||||||
|
|
||||||
// getTypeMethodSet returns a reference (GEP) to a global method set. This
|
// getTypeMethodSet returns a reference (GEP) to a global method set. This
|
||||||
// method set should be unreferenced after the interface lowering pass.
|
// method set should be unreferenced after the interface lowering pass.
|
||||||
func (c *Compiler) getTypeMethodSet(typ types.Type) (llvm.Value, error) {
|
func (c *Compiler) getTypeMethodSet(typ types.Type) llvm.Value {
|
||||||
global := c.mod.NamedGlobal(typ.String() + "$methodset")
|
global := c.mod.NamedGlobal(typ.String() + "$methodset")
|
||||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||||
if !global.IsNil() {
|
if !global.IsNil() {
|
||||||
// the method set already exists
|
// the method set already exists
|
||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero}), nil
|
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||||
}
|
}
|
||||||
|
|
||||||
ms := c.ir.Program.MethodSets.MethodSet(typ)
|
ms := c.ir.Program.MethodSets.MethodSet(typ)
|
||||||
if ms.Len() == 0 {
|
if ms.Len() == 0 {
|
||||||
// no methods, so can leave that one out
|
// no methods, so can leave that one out
|
||||||
return llvm.ConstPointerNull(llvm.PointerType(c.mod.GetTypeByName("runtime.interfaceMethodInfo"), 0)), nil
|
return llvm.ConstPointerNull(llvm.PointerType(c.mod.GetTypeByName("runtime.interfaceMethodInfo"), 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
methods := make([]llvm.Value, ms.Len())
|
methods := make([]llvm.Value, ms.Len())
|
||||||
@@ -179,10 +176,7 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) (llvm.Value, error) {
|
|||||||
// compiler error, so panic
|
// compiler error, so panic
|
||||||
panic("cannot find function: " + f.LinkName())
|
panic("cannot find function: " + f.LinkName())
|
||||||
}
|
}
|
||||||
fn, err := c.getInterfaceInvokeWrapper(f)
|
fn := c.getInterfaceInvokeWrapper(f)
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
|
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
|
||||||
signatureGlobal,
|
signatureGlobal,
|
||||||
llvm.ConstPtrToInt(fn, c.uintptrType),
|
llvm.ConstPtrToInt(fn, c.uintptrType),
|
||||||
@@ -195,7 +189,7 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) (llvm.Value, error) {
|
|||||||
global.SetInitializer(value)
|
global.SetInitializer(value)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
global.SetLinkage(llvm.PrivateLinkage)
|
||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero}), nil
|
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInterfaceMethodSet returns a global variable with the method set of the
|
// getInterfaceMethodSet returns a global variable with the method set of the
|
||||||
@@ -365,12 +359,12 @@ type interfaceInvokeWrapper struct {
|
|||||||
// the underlying value, dereferences it, and calls the real method. This
|
// the underlying value, dereferences it, and calls the real method. This
|
||||||
// wrapper is only needed when the interface value actually doesn't fit in a
|
// wrapper is only needed when the interface value actually doesn't fit in a
|
||||||
// pointer and a pointer to the value must be created.
|
// pointer and a pointer to the value must be created.
|
||||||
func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) (llvm.Value, error) {
|
func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
||||||
wrapperName := f.LinkName() + "$invoke"
|
wrapperName := f.LinkName() + "$invoke"
|
||||||
wrapper := c.mod.NamedFunction(wrapperName)
|
wrapper := c.mod.NamedFunction(wrapperName)
|
||||||
if !wrapper.IsNil() {
|
if !wrapper.IsNil() {
|
||||||
// Wrapper already created. Return it directly.
|
// Wrapper already created. Return it directly.
|
||||||
return wrapper, nil
|
return wrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the expanded receiver type.
|
// Get the expanded receiver type.
|
||||||
@@ -383,7 +377,7 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) (llvm.Value, error)
|
|||||||
// Casting a function signature to a different signature and calling it
|
// Casting a function signature to a different signature and calling it
|
||||||
// with a receiver pointer bitcasted to *i8 (as done in calls on an
|
// with a receiver pointer bitcasted to *i8 (as done in calls on an
|
||||||
// interface) is hopefully a safe (defined) operation.
|
// interface) is hopefully a safe (defined) operation.
|
||||||
return f.LLVMFn, nil
|
return f.LLVMFn
|
||||||
}
|
}
|
||||||
|
|
||||||
// create wrapper function
|
// create wrapper function
|
||||||
@@ -396,7 +390,7 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) (llvm.Value, error)
|
|||||||
wrapper: wrapper,
|
wrapper: wrapper,
|
||||||
receiverType: receiverType,
|
receiverType: receiverType,
|
||||||
})
|
})
|
||||||
return wrapper, nil
|
return wrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
// createInterfaceInvokeWrapper finishes the work of getInterfaceInvokeWrapper,
|
// createInterfaceInvokeWrapper finishes the work of getInterfaceInvokeWrapper,
|
||||||
|
|||||||
@@ -22,6 +22,54 @@ func getUses(value llvm.Value) []llvm.Value {
|
|||||||
return uses
|
return uses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createEntryBlockAlloca creates a new alloca in the entry block, even though
|
||||||
|
// the IR builder is located elsewhere. It assumes that the insert point is
|
||||||
|
// at the end of the current block.
|
||||||
|
func (c *Compiler) createEntryBlockAlloca(t llvm.Type, name string) llvm.Value {
|
||||||
|
currentBlock := c.builder.GetInsertBlock()
|
||||||
|
entryBlock := currentBlock.Parent().EntryBasicBlock()
|
||||||
|
if entryBlock.FirstInstruction().IsNil() {
|
||||||
|
c.builder.SetInsertPointAtEnd(entryBlock)
|
||||||
|
} else {
|
||||||
|
c.builder.SetInsertPointBefore(entryBlock.FirstInstruction())
|
||||||
|
}
|
||||||
|
alloca := c.builder.CreateAlloca(t, name)
|
||||||
|
c.builder.SetInsertPointAtEnd(currentBlock)
|
||||||
|
return alloca
|
||||||
|
}
|
||||||
|
|
||||||
|
// createTemporaryAlloca creates a new alloca in the entry block and adds
|
||||||
|
// lifetime start infromation in the IR signalling that the alloca won't be used
|
||||||
|
// before this point.
|
||||||
|
//
|
||||||
|
// This is useful for creating temporary allocas for intrinsics. Don't forget to
|
||||||
|
// end the lifetime using emitLifetimeEnd after you're done with it.
|
||||||
|
func (c *Compiler) createTemporaryAlloca(t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
||||||
|
alloca = c.createEntryBlockAlloca(t, name)
|
||||||
|
bitcast = c.builder.CreateBitCast(alloca, c.i8ptrType, name+".bitcast")
|
||||||
|
size = llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(t), false)
|
||||||
|
c.builder.CreateCall(c.getLifetimeStartFunc(), []llvm.Value{size, bitcast}, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// emitLifetimeEnd signals the end of an (alloca) lifetime by calling the
|
||||||
|
// llvm.lifetime.end intrinsic. It is commonly used together with
|
||||||
|
// createTemporaryAlloca.
|
||||||
|
func (c *Compiler) emitLifetimeEnd(ptr, size llvm.Value) {
|
||||||
|
c.builder.CreateCall(c.getLifetimeEndFunc(), []llvm.Value{size, ptr}, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
|
||||||
|
// first if it doesn't exist yet.
|
||||||
|
func (c *Compiler) getLifetimeStartFunc() llvm.Value {
|
||||||
|
fn := c.mod.NamedFunction("llvm.lifetime.start.p0i8")
|
||||||
|
if fn.IsNil() {
|
||||||
|
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.ctx.Int64Type(), c.i8ptrType}, false)
|
||||||
|
fn = llvm.AddFunction(c.mod, "llvm.lifetime.start.p0i8", fnType)
|
||||||
|
}
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
// getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it
|
// getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it
|
||||||
// first if it doesn't exist yet.
|
// first if it doesn't exist yet.
|
||||||
func (c *Compiler) getLifetimeEndFunc() llvm.Value {
|
func (c *Compiler) getLifetimeEndFunc() llvm.Value {
|
||||||
|
|||||||
+26
-12
@@ -11,8 +11,13 @@ import (
|
|||||||
|
|
||||||
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
||||||
llvmValueType := c.getLLVMType(valueType)
|
llvmValueType := c.getLLVMType(valueType)
|
||||||
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "hashmap.value")
|
|
||||||
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "hashmap.valueptr")
|
// Allocate the memory for the resulting type. Do not zero this memory: it
|
||||||
|
// will be zeroed by the hashmap get implementation if the key is not
|
||||||
|
// present in the map.
|
||||||
|
mapValueAlloca, mapValuePtr, mapValueSize := c.createTemporaryAlloca(llvmValueType, "hashmap.value")
|
||||||
|
|
||||||
|
// Do the lookup. How it is done depends on the key type.
|
||||||
var commaOkValue llvm.Value
|
var commaOkValue llvm.Value
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
@@ -20,15 +25,24 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
|||||||
commaOkValue = c.createRuntimeCall("hashmapStringGet", params, "")
|
commaOkValue = c.createRuntimeCall("hashmapStringGet", params, "")
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
// key can be compared with runtime.memequal
|
// key can be compared with runtime.memequal
|
||||||
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
// Store the key in an alloca, in the entry block to avoid dynamic stack
|
||||||
c.builder.CreateStore(key, keyAlloca)
|
// growth.
|
||||||
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
mapKeyAlloca, mapKeyPtr, mapKeySize := c.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
params := []llvm.Value{m, keyPtr, mapValuePtr}
|
c.builder.CreateStore(key, mapKeyAlloca)
|
||||||
|
// Fetch the value from the hashmap.
|
||||||
|
params := []llvm.Value{m, mapKeyPtr, mapValuePtr}
|
||||||
commaOkValue = c.createRuntimeCall("hashmapBinaryGet", params, "")
|
commaOkValue = c.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||||
|
c.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
||||||
} else {
|
} else {
|
||||||
|
// Not trivially comparable using memcmp.
|
||||||
return llvm.Value{}, c.makeError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
return llvm.Value{}, c.makeError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load the resulting value from the hashmap. The value is set to the zero
|
||||||
|
// value if the key doesn't exist in the hashmap.
|
||||||
mapValue := c.builder.CreateLoad(mapValueAlloca, "")
|
mapValue := c.builder.CreateLoad(mapValueAlloca, "")
|
||||||
|
c.emitLifetimeEnd(mapValuePtr, mapValueSize)
|
||||||
|
|
||||||
if commaOk {
|
if commaOk {
|
||||||
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{llvmValueType, c.ctx.Int1Type()}, false))
|
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{llvmValueType, c.ctx.Int1Type()}, false))
|
||||||
tuple = c.builder.CreateInsertValue(tuple, mapValue, 0, "")
|
tuple = c.builder.CreateInsertValue(tuple, mapValue, 0, "")
|
||||||
@@ -40,9 +54,8 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
||||||
valueAlloca := c.builder.CreateAlloca(value.Type(), "hashmap.value")
|
valueAlloca, valuePtr, valueSize := c.createTemporaryAlloca(value.Type(), "hashmap.value")
|
||||||
c.builder.CreateStore(value, valueAlloca)
|
c.builder.CreateStore(value, valueAlloca)
|
||||||
valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr")
|
|
||||||
keyType = keyType.Underlying()
|
keyType = keyType.Underlying()
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
@@ -50,14 +63,15 @@ func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, p
|
|||||||
c.createRuntimeCall("hashmapStringSet", params, "")
|
c.createRuntimeCall("hashmapStringSet", params, "")
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
// key can be compared with runtime.memequal
|
// key can be compared with runtime.memequal
|
||||||
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
keyAlloca, keyPtr, keySize := c.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
c.builder.CreateStore(key, keyAlloca)
|
c.builder.CreateStore(key, keyAlloca)
|
||||||
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
|
||||||
params := []llvm.Value{m, keyPtr, valuePtr}
|
params := []llvm.Value{m, keyPtr, valuePtr}
|
||||||
c.createRuntimeCall("hashmapBinarySet", params, "")
|
c.createRuntimeCall("hashmapBinarySet", params, "")
|
||||||
|
c.emitLifetimeEnd(keyPtr, keySize)
|
||||||
} else {
|
} else {
|
||||||
c.addError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
c.addError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||||
}
|
}
|
||||||
|
c.emitLifetimeEnd(valuePtr, valueSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error {
|
func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error {
|
||||||
@@ -68,11 +82,11 @@ func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos toke
|
|||||||
c.createRuntimeCall("hashmapStringDelete", params, "")
|
c.createRuntimeCall("hashmapStringDelete", params, "")
|
||||||
return nil
|
return nil
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
keyAlloca, keyPtr, keySize := c.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
c.builder.CreateStore(key, keyAlloca)
|
c.builder.CreateStore(key, keyAlloca)
|
||||||
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
|
||||||
params := []llvm.Value{m, keyPtr}
|
params := []llvm.Value{m, keyPtr}
|
||||||
c.createRuntimeCall("hashmapBinaryDelete", params, "")
|
c.createRuntimeCall("hashmapBinaryDelete", params, "")
|
||||||
|
c.emitLifetimeEnd(keyPtr, keySize)
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return c.makeError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
return c.makeError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
// This file implements volatile loads/stores in runtime/volatile.LoadT and
|
||||||
|
// runtime/volatile.StoreT as compiler builtins.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/tools/go/ssa"
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Compiler) emitVolatileLoad(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
||||||
|
addr := c.getValue(frame, instr.Args[0])
|
||||||
|
c.emitNilCheck(frame, addr, "deref")
|
||||||
|
val := c.builder.CreateLoad(addr, "")
|
||||||
|
val.SetVolatile(true)
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) emitVolatileStore(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
||||||
|
addr := c.getValue(frame, instr.Args[0])
|
||||||
|
val := c.getValue(frame, instr.Args[1])
|
||||||
|
c.emitNilCheck(frame, addr, "deref")
|
||||||
|
store := c.builder.CreateStore(val, addr)
|
||||||
|
store.SetVolatile(true)
|
||||||
|
return llvm.Value{}, nil
|
||||||
|
}
|
||||||
+13
-4
@@ -34,7 +34,7 @@ func (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value {
|
|||||||
}
|
}
|
||||||
// Because packedType is a struct and we have to cast it to a *i8, store
|
// Because packedType is a struct and we have to cast it to a *i8, store
|
||||||
// it in an alloca first for bitcasting (store+bitcast+load).
|
// it in an alloca first for bitcasting (store+bitcast+load).
|
||||||
packedAlloc = c.builder.CreateAlloca(packedType, "")
|
packedAlloc, _, _ = c.createTemporaryAlloca(packedType, "")
|
||||||
} else {
|
} else {
|
||||||
// Packed data is bigger than a pointer, so allocate it on the heap.
|
// Packed data is bigger than a pointer, so allocate it on the heap.
|
||||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||||
@@ -54,7 +54,11 @@ func (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value {
|
|||||||
if packedHeapAlloc.IsNil() {
|
if packedHeapAlloc.IsNil() {
|
||||||
// Load value (as *i8) from the alloca.
|
// Load value (as *i8) from the alloca.
|
||||||
packedAlloc = c.builder.CreateBitCast(packedAlloc, llvm.PointerType(c.i8ptrType, 0), "")
|
packedAlloc = c.builder.CreateBitCast(packedAlloc, llvm.PointerType(c.i8ptrType, 0), "")
|
||||||
return c.builder.CreateLoad(packedAlloc, "")
|
result := c.builder.CreateLoad(packedAlloc, "")
|
||||||
|
packedPtr := c.builder.CreateBitCast(packedAlloc, c.i8ptrType, "")
|
||||||
|
packedSize := llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(packedAlloc.Type()), false)
|
||||||
|
c.emitLifetimeEnd(packedPtr, packedSize)
|
||||||
|
return result
|
||||||
} else {
|
} else {
|
||||||
// Get the original heap allocation pointer, which already is an *i8.
|
// Get the original heap allocation pointer, which already is an *i8.
|
||||||
return packedHeapAlloc
|
return packedHeapAlloc
|
||||||
@@ -66,7 +70,7 @@ func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []l
|
|||||||
packedType := c.ctx.StructType(valueTypes, false)
|
packedType := c.ctx.StructType(valueTypes, false)
|
||||||
|
|
||||||
// Get a correctly-typed pointer to the packed data.
|
// Get a correctly-typed pointer to the packed data.
|
||||||
var packedAlloc llvm.Value
|
var packedAlloc, packedRawAlloc llvm.Value
|
||||||
size := c.targetData.TypeAllocSize(packedType)
|
size := c.targetData.TypeAllocSize(packedType)
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
// No data to unpack.
|
// No data to unpack.
|
||||||
@@ -80,7 +84,7 @@ func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []l
|
|||||||
return []llvm.Value{c.builder.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
|
return []llvm.Value{c.builder.CreatePtrToInt(ptr, valueTypes[0], "unpack.int")}
|
||||||
}
|
}
|
||||||
// Fallback: load it using an alloca.
|
// Fallback: load it using an alloca.
|
||||||
packedRawAlloc := c.builder.CreateAlloca(llvm.PointerType(c.i8ptrType, 0), "unpack.raw.alloc")
|
packedRawAlloc, _, _ = c.createTemporaryAlloca(llvm.PointerType(c.i8ptrType, 0), "unpack.raw.alloc")
|
||||||
packedRawValue := c.builder.CreateBitCast(ptr, llvm.PointerType(c.i8ptrType, 0), "unpack.raw.value")
|
packedRawValue := c.builder.CreateBitCast(ptr, llvm.PointerType(c.i8ptrType, 0), "unpack.raw.value")
|
||||||
c.builder.CreateStore(packedRawValue, packedRawAlloc)
|
c.builder.CreateStore(packedRawValue, packedRawAlloc)
|
||||||
packedAlloc = c.builder.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
|
packedAlloc = c.builder.CreateBitCast(packedRawAlloc, llvm.PointerType(packedType, 0), "unpack.alloc")
|
||||||
@@ -104,5 +108,10 @@ func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []l
|
|||||||
gep := c.builder.CreateInBoundsGEP(packedAlloc, indices, "")
|
gep := c.builder.CreateInBoundsGEP(packedAlloc, indices, "")
|
||||||
values[i] = c.builder.CreateLoad(gep, "")
|
values[i] = c.builder.CreateLoad(gep, "")
|
||||||
}
|
}
|
||||||
|
if !packedRawAlloc.IsNil() {
|
||||||
|
allocPtr := c.builder.CreateBitCast(packedRawAlloc, c.i8ptrType, "")
|
||||||
|
allocSize := llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(c.uintptrType), false)
|
||||||
|
c.emitLifetimeEnd(allocPtr, allocSize)
|
||||||
|
}
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
module github.com/tinygo-org/tinygo
|
||||||
|
|
||||||
|
go 1.11
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||||
|
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46
|
||||||
|
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef
|
||||||
|
tinygo.org/x/go-llvm v0.0.0-20190224120431-7707ae5d1261
|
||||||
|
)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
|
||||||
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46 h1:wXG2bA8fO7Vv7lLk2PihFMTqmbT173Tje39oKzQ50Mo=
|
||||||
|
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef h1:ymc9FeDom3RIEA3coKokSllBB1hRcMT0tZ1W3Jf9Ids=
|
||||||
|
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
tinygo.org/x/go-llvm v0.0.0-20190224120431-7707ae5d1261 h1:rJS2Hga39YAnm7DE4qrPm6Dr/67EOojL0XPzvbEeBiw=
|
||||||
|
tinygo.org/x/go-llvm v0.0.0-20190224120431-7707ae5d1261/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||||
+35
-10
@@ -184,6 +184,25 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
||||||
rhs := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
|
rhs := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
|
||||||
predicate := inst.IntPredicate()
|
predicate := inst.IntPredicate()
|
||||||
|
if predicate == llvm.IntEQ && lhs.Type().TypeKind() == llvm.PointerTypeKind {
|
||||||
|
// Unfortunately, the const propagation in the IR builder
|
||||||
|
// doesn't handle pointer compares of inttoptr values. So we
|
||||||
|
// implement it manually here.
|
||||||
|
lhsNil, ok1 := isPointerNil(lhs)
|
||||||
|
rhsNil, ok2 := isPointerNil(rhs)
|
||||||
|
if ok1 && ok2 {
|
||||||
|
if lhsNil && rhsNil {
|
||||||
|
// Both are nil, so this icmp is always evaluated to true.
|
||||||
|
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), 1, false)}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if lhsNil != rhsNil {
|
||||||
|
// Only one of them is nil, so this comparison must return false.
|
||||||
|
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), 0, false)}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateICmp(predicate, lhs, rhs, "")}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateICmp(predicate, lhs, rhs, "")}
|
||||||
case !inst.IsAFCmpInst().IsNil():
|
case !inst.IsAFCmpInst().IsNil():
|
||||||
lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
||||||
@@ -347,6 +366,8 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), implements, false)}
|
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), implements, false)}
|
||||||
case callee.Name() == "runtime.nanotime":
|
case callee.Name() == "runtime.nanotime":
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
|
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
|
||||||
|
case callee.Name() == "llvm.dbg.value":
|
||||||
|
// do nothing
|
||||||
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
||||||
// This are all print instructions, which necessarily have side
|
// This are all print instructions, which necessarily have side
|
||||||
// effects but no results.
|
// effects but no results.
|
||||||
@@ -459,17 +480,21 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
}
|
}
|
||||||
thenBB := inst.Operand(1)
|
thenBB := inst.Operand(1)
|
||||||
elseBB := inst.Operand(2)
|
elseBB := inst.Operand(2)
|
||||||
if !cond.IsConstant() {
|
if !cond.IsAInstruction().IsNil() {
|
||||||
return nil, nil, errors.New("interp: branch on a non-constant")
|
return nil, nil, errors.New("interp: branch on a non-constant")
|
||||||
} else {
|
}
|
||||||
switch cond.ZExtValue() {
|
if !cond.IsAConstantExpr().IsNil() {
|
||||||
case 0: // false
|
// This may happen when the instruction builder could not
|
||||||
return nil, []llvm.Value{thenBB}, nil // then
|
// const-fold some instructions.
|
||||||
case 1: // true
|
return nil, nil, errors.New("interp: branch on a non-const-propagated constant expression")
|
||||||
return nil, []llvm.Value{elseBB}, nil // else
|
}
|
||||||
default:
|
switch cond {
|
||||||
panic("branch was not true or false")
|
case llvm.ConstInt(fr.Mod.Context().Int1Type(), 0, false): // false
|
||||||
}
|
return nil, []llvm.Value{thenBB}, nil // then
|
||||||
|
case llvm.ConstInt(fr.Mod.Context().Int1Type(), 1, false): // true
|
||||||
|
return nil, []llvm.Value{elseBB}, nil // else
|
||||||
|
default:
|
||||||
|
panic("branch was not true or false")
|
||||||
}
|
}
|
||||||
case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 1:
|
case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 1:
|
||||||
// unconditional branch (goto)
|
// unconditional branch (goto)
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ type Eval struct {
|
|||||||
TargetData llvm.TargetData
|
TargetData llvm.TargetData
|
||||||
Debug bool
|
Debug bool
|
||||||
builder llvm.Builder
|
builder llvm.Builder
|
||||||
dibuilder *llvm.DIBuilder
|
|
||||||
dirtyGlobals map[llvm.Value]struct{}
|
dirtyGlobals map[llvm.Value]struct{}
|
||||||
sideEffectFuncs map[llvm.Value]*sideEffectResult // cache of side effect scan results
|
sideEffectFuncs map[llvm.Value]*sideEffectResult // cache of side effect scan results
|
||||||
}
|
}
|
||||||
@@ -38,7 +37,6 @@ func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error {
|
|||||||
dirtyGlobals: map[llvm.Value]struct{}{},
|
dirtyGlobals: map[llvm.Value]struct{}{},
|
||||||
}
|
}
|
||||||
e.builder = mod.Context().NewBuilder()
|
e.builder = mod.Context().NewBuilder()
|
||||||
e.dibuilder = llvm.NewDIBuilder(mod)
|
|
||||||
|
|
||||||
initAll := mod.NamedFunction(name)
|
initAll := mod.NamedFunction(name)
|
||||||
bb := initAll.EntryBasicBlock()
|
bb := initAll.EntryBasicBlock()
|
||||||
@@ -49,7 +47,6 @@ func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error {
|
|||||||
e.builder.SetInsertPointBefore(bb.FirstInstruction())
|
e.builder.SetInsertPointBefore(bb.FirstInstruction())
|
||||||
dummy := e.builder.CreateAlloca(e.Mod.Context().Int8Type(), "dummy")
|
dummy := e.builder.CreateAlloca(e.Mod.Context().Int8Type(), "dummy")
|
||||||
e.builder.SetInsertPointBefore(dummy)
|
e.builder.SetInsertPointBefore(dummy)
|
||||||
e.builder.SetInstDebugLocation(bb.FirstInstruction())
|
|
||||||
var initCalls []llvm.Value
|
var initCalls []llvm.Value
|
||||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||||
if inst == dummy {
|
if inst == dummy {
|
||||||
|
|||||||
+12
-1
@@ -35,6 +35,8 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
|
|||||||
return &sideEffectResult{severity: sideEffectLimited}
|
return &sideEffectResult{severity: sideEffectLimited}
|
||||||
case "runtime.interfaceImplements":
|
case "runtime.interfaceImplements":
|
||||||
return &sideEffectResult{severity: sideEffectNone}
|
return &sideEffectResult{severity: sideEffectNone}
|
||||||
|
case "llvm.dbg.value":
|
||||||
|
return &sideEffectResult{severity: sideEffectNone}
|
||||||
}
|
}
|
||||||
if e.sideEffectFuncs == nil {
|
if e.sideEffectFuncs == nil {
|
||||||
e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult)
|
e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult)
|
||||||
@@ -107,7 +109,16 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
|
|||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
case llvm.Load, llvm.Store:
|
case llvm.Load:
|
||||||
|
if inst.IsVolatile() {
|
||||||
|
result.updateSeverity(sideEffectLimited)
|
||||||
|
}
|
||||||
|
if _, ok := e.dirtyGlobals[inst.Operand(0)]; ok {
|
||||||
|
if e.hasLocalSideEffects(dirtyLocals, inst) {
|
||||||
|
result.updateSeverity(sideEffectLimited)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case llvm.Store:
|
||||||
if inst.IsVolatile() {
|
if inst.IsVolatile() {
|
||||||
result.updateSeverity(sideEffectLimited)
|
result.updateSeverity(sideEffectLimited)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,3 +94,29 @@ func isScalar(t llvm.Type) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isPointerNil returns whether this is a nil pointer or not. The ok value
|
||||||
|
// indicates whether the result is certain: if it is false the result boolean is
|
||||||
|
// not valid.
|
||||||
|
func isPointerNil(v llvm.Value) (result bool, ok bool) {
|
||||||
|
if !v.IsAConstantExpr().IsNil() {
|
||||||
|
switch v.Opcode() {
|
||||||
|
case llvm.IntToPtr:
|
||||||
|
// Whether a constant inttoptr is nil is easy to
|
||||||
|
// determine.
|
||||||
|
operand := v.Operand(0)
|
||||||
|
if operand.IsConstant() {
|
||||||
|
return operand.ZExtValue() == 0, true
|
||||||
|
}
|
||||||
|
case llvm.BitCast, llvm.GetElementPtr:
|
||||||
|
// These const instructions are just a kind of wrappers for the
|
||||||
|
// underlying pointer.
|
||||||
|
return isPointerNil(v.Operand(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !v.IsAConstantPointerNull().IsNil() {
|
||||||
|
// A constant pointer null is always null, of course.
|
||||||
|
return true, true
|
||||||
|
}
|
||||||
|
return false, false // not valid
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,11 +33,12 @@ type Program struct {
|
|||||||
type Function struct {
|
type Function struct {
|
||||||
*ssa.Function
|
*ssa.Function
|
||||||
LLVMFn llvm.Value
|
LLVMFn llvm.Value
|
||||||
linkName string // go:linkname, go:export, go:interrupt
|
linkName string // go:linkname, go:export, go:interrupt
|
||||||
exported bool // go:export
|
exported bool // go:export
|
||||||
nobounds bool // go:nobounds
|
nobounds bool // go:nobounds
|
||||||
flag bool // used by dead code elimination
|
flag bool // used by dead code elimination
|
||||||
interrupt bool // go:interrupt
|
interrupt bool // go:interrupt
|
||||||
|
inline InlineType // go:inline
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global variable, possibly constant.
|
// Global variable, possibly constant.
|
||||||
@@ -69,7 +70,22 @@ type Interface struct {
|
|||||||
Type *types.Interface
|
Type *types.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and intialize a new *Program from a *ssa.Program.
|
type InlineType int
|
||||||
|
|
||||||
|
// How much to inline.
|
||||||
|
const (
|
||||||
|
// Default behavior. The compiler decides for itself whether any given
|
||||||
|
// function will be inlined. Whether any function is inlined depends on the
|
||||||
|
// optimization level.
|
||||||
|
InlineDefault InlineType = iota
|
||||||
|
|
||||||
|
// Inline hint, just like the C inline keyword (signalled using
|
||||||
|
// //go:inline). The compiler will be more likely to inline this function,
|
||||||
|
// but it is not a guarantee.
|
||||||
|
InlineHint
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create and initialize a new *Program from a *ssa.Program.
|
||||||
func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
||||||
comments := map[string]*ast.CommentGroup{}
|
comments := map[string]*ast.CommentGroup{}
|
||||||
for _, pkgInfo := range lprogram.Sorted() {
|
for _, pkgInfo := range lprogram.Sorted() {
|
||||||
@@ -279,6 +295,8 @@ func (f *Function) parsePragmas() {
|
|||||||
}
|
}
|
||||||
f.linkName = parts[1]
|
f.linkName = parts[1]
|
||||||
f.exported = true
|
f.exported = true
|
||||||
|
case "//go:inline":
|
||||||
|
f.inline = InlineHint
|
||||||
case "//go:interrupt":
|
case "//go:interrupt":
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
continue
|
continue
|
||||||
@@ -332,6 +350,11 @@ func (f *Function) IsInterrupt() bool {
|
|||||||
return f.interrupt
|
return f.interrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the inline directive of this function.
|
||||||
|
func (f *Function) Inline() InlineType {
|
||||||
|
return f.inline
|
||||||
|
}
|
||||||
|
|
||||||
// Return the link name for this function.
|
// Return the link name for this function.
|
||||||
func (f *Function) LinkName() string {
|
func (f *Function) LinkName() string {
|
||||||
if f.linkName != "" {
|
if f.linkName != "" {
|
||||||
|
|||||||
-521
@@ -1,521 +0,0 @@
|
|||||||
package loader
|
|
||||||
|
|
||||||
// This file extracts the `import "C"` statement from the source and modifies
|
|
||||||
// the AST for Cgo. It does not use libclang directly (see libclang.go).
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/ast"
|
|
||||||
"go/token"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/tools/go/ast/astutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// fileInfo holds all Cgo-related information of a given *ast.File.
|
|
||||||
type fileInfo struct {
|
|
||||||
*ast.File
|
|
||||||
*Package
|
|
||||||
filename string
|
|
||||||
functions map[string]*functionInfo
|
|
||||||
globals map[string]*globalInfo
|
|
||||||
typedefs map[string]*typedefInfo
|
|
||||||
elaboratedTypes map[string]ast.Expr
|
|
||||||
importCPos token.Pos
|
|
||||||
missingSymbols map[string]struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// functionInfo stores some information about a Cgo function found by libclang
|
|
||||||
// and declared in the AST.
|
|
||||||
type functionInfo struct {
|
|
||||||
args []paramInfo
|
|
||||||
results *ast.FieldList
|
|
||||||
}
|
|
||||||
|
|
||||||
// paramInfo is a parameter of a Cgo function (see functionInfo).
|
|
||||||
type paramInfo struct {
|
|
||||||
name string
|
|
||||||
typeExpr ast.Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// typedefInfo contains information about a single typedef in C.
|
|
||||||
type typedefInfo struct {
|
|
||||||
typeExpr ast.Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// globalInfo contains information about a declared global variable in C.
|
|
||||||
type globalInfo struct {
|
|
||||||
typeExpr ast.Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
|
||||||
// in both languages. See addTypeAliases.
|
|
||||||
var cgoAliases = map[string]string{
|
|
||||||
"C.int8_t": "int8",
|
|
||||||
"C.int16_t": "int16",
|
|
||||||
"C.int32_t": "int32",
|
|
||||||
"C.int64_t": "int64",
|
|
||||||
"C.uint8_t": "uint8",
|
|
||||||
"C.uint16_t": "uint16",
|
|
||||||
"C.uint32_t": "uint32",
|
|
||||||
"C.uint64_t": "uint64",
|
|
||||||
"C.uintptr_t": "uintptr",
|
|
||||||
}
|
|
||||||
|
|
||||||
// cgoBuiltinAliases are handled specially because they only exist on the Go
|
|
||||||
// side of CGo, not on the CGo (they're prefixed with "_Cgo_" there).
|
|
||||||
var cgoBuiltinAliases = map[string]struct{}{
|
|
||||||
"char": struct{}{},
|
|
||||||
"schar": struct{}{},
|
|
||||||
"uchar": struct{}{},
|
|
||||||
"short": struct{}{},
|
|
||||||
"ushort": struct{}{},
|
|
||||||
"int": struct{}{},
|
|
||||||
"uint": struct{}{},
|
|
||||||
"long": struct{}{},
|
|
||||||
"ulong": struct{}{},
|
|
||||||
"longlong": struct{}{},
|
|
||||||
"ulonglong": struct{}{},
|
|
||||||
}
|
|
||||||
|
|
||||||
// cgoTypes lists some C types with ambiguous sizes that must be retrieved
|
|
||||||
// somehow from C. This is done by adding some typedefs to get the size of each
|
|
||||||
// type.
|
|
||||||
const cgoTypes = `
|
|
||||||
typedef char _Cgo_char;
|
|
||||||
typedef signed char _Cgo_schar;
|
|
||||||
typedef unsigned char _Cgo_uchar;
|
|
||||||
typedef short _Cgo_short;
|
|
||||||
typedef unsigned short _Cgo_ushort;
|
|
||||||
typedef int _Cgo_int;
|
|
||||||
typedef unsigned int _Cgo_uint;
|
|
||||||
typedef long _Cgo_long;
|
|
||||||
typedef unsigned long _Cgo_ulong;
|
|
||||||
typedef long long _Cgo_longlong;
|
|
||||||
typedef unsigned long long _Cgo_ulonglong;
|
|
||||||
`
|
|
||||||
|
|
||||||
// processCgo extracts the `import "C"` statement from the AST, parses the
|
|
||||||
// comment with libclang, and modifies the AST to use this information.
|
|
||||||
func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []error {
|
|
||||||
info := &fileInfo{
|
|
||||||
File: f,
|
|
||||||
Package: p,
|
|
||||||
filename: filename,
|
|
||||||
functions: map[string]*functionInfo{},
|
|
||||||
globals: map[string]*globalInfo{},
|
|
||||||
typedefs: map[string]*typedefInfo{},
|
|
||||||
elaboratedTypes: map[string]ast.Expr{},
|
|
||||||
missingSymbols: map[string]struct{}{},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find all C.* symbols.
|
|
||||||
f = astutil.Apply(f, info.findMissingCGoNames, nil).(*ast.File)
|
|
||||||
for name := range cgoBuiltinAliases {
|
|
||||||
info.missingSymbols["_Cgo_"+name] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find `import "C"` statements in the file.
|
|
||||||
for i := 0; i < len(f.Decls); i++ {
|
|
||||||
decl := f.Decls[i]
|
|
||||||
genDecl, ok := decl.(*ast.GenDecl)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(genDecl.Specs) != 1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
spec, ok := genDecl.Specs[0].(*ast.ImportSpec)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
path, err := strconv.Unquote(spec.Path.Value)
|
|
||||||
if err != nil {
|
|
||||||
panic("could not parse import path: " + err.Error())
|
|
||||||
}
|
|
||||||
if path != "C" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cgoComment := genDecl.Doc.Text()
|
|
||||||
|
|
||||||
// Stored for later use by generated functions, to use a somewhat sane
|
|
||||||
// source location.
|
|
||||||
info.importCPos = spec.Path.ValuePos
|
|
||||||
|
|
||||||
pos := info.fset.PositionFor(genDecl.Doc.Pos(), true)
|
|
||||||
errs := info.parseFragment(cgoComment+cgoTypes, cflags, pos.Filename, pos.Line)
|
|
||||||
if errs != nil {
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove this import declaration.
|
|
||||||
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print the AST, for debugging.
|
|
||||||
//ast.Print(p.fset, f)
|
|
||||||
|
|
||||||
// Declare functions found by libclang.
|
|
||||||
info.addFuncDecls()
|
|
||||||
|
|
||||||
// Declare stub function pointer values found by libclang.
|
|
||||||
info.addFuncPtrDecls()
|
|
||||||
|
|
||||||
// Declare globals found by libclang.
|
|
||||||
info.addVarDecls()
|
|
||||||
|
|
||||||
// Forward C types to Go types (like C.uint32_t -> uint32).
|
|
||||||
info.addTypeAliases()
|
|
||||||
|
|
||||||
// Add type declarations for C types, declared using typedef in C.
|
|
||||||
info.addTypedefs()
|
|
||||||
|
|
||||||
// Add elaborated types for C structs and unions.
|
|
||||||
info.addElaboratedTypes()
|
|
||||||
|
|
||||||
// Patch the AST to use the declared types and functions.
|
|
||||||
f = astutil.Apply(f, info.walker, nil).(*ast.File)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// addFuncDecls adds the C function declarations found by libclang in the
|
|
||||||
// comment above the `import "C"` statement.
|
|
||||||
func (info *fileInfo) addFuncDecls() {
|
|
||||||
// TODO: replace all uses of importCPos with the real locations from
|
|
||||||
// libclang.
|
|
||||||
names := make([]string, 0, len(info.functions))
|
|
||||||
for name := range info.functions {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
for _, name := range names {
|
|
||||||
fn := info.functions[name]
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Fun,
|
|
||||||
Name: "C." + name,
|
|
||||||
}
|
|
||||||
args := make([]*ast.Field, len(fn.args))
|
|
||||||
decl := &ast.FuncDecl{
|
|
||||||
Name: &ast.Ident{
|
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: "C." + name,
|
|
||||||
Obj: obj,
|
|
||||||
},
|
|
||||||
Type: &ast.FuncType{
|
|
||||||
Func: info.importCPos,
|
|
||||||
Params: &ast.FieldList{
|
|
||||||
Opening: info.importCPos,
|
|
||||||
List: args,
|
|
||||||
Closing: info.importCPos,
|
|
||||||
},
|
|
||||||
Results: fn.results,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
obj.Decl = decl
|
|
||||||
for i, arg := range fn.args {
|
|
||||||
args[i] = &ast.Field{
|
|
||||||
Names: []*ast.Ident{
|
|
||||||
&ast.Ident{
|
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: arg.name,
|
|
||||||
Obj: &ast.Object{
|
|
||||||
Kind: ast.Var,
|
|
||||||
Name: arg.name,
|
|
||||||
Decl: decl,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Type: arg.typeExpr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info.Decls = append(info.Decls, decl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// addFuncPtrDecls creates stub declarations of function pointer values. These
|
|
||||||
// values will later be replaced with the real values in the compiler.
|
|
||||||
// It adds code like the following to the AST:
|
|
||||||
//
|
|
||||||
// var (
|
|
||||||
// C.add unsafe.Pointer
|
|
||||||
// C.mul unsafe.Pointer
|
|
||||||
// // ...
|
|
||||||
// )
|
|
||||||
func (info *fileInfo) addFuncPtrDecls() {
|
|
||||||
if len(info.functions) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
gen := &ast.GenDecl{
|
|
||||||
TokPos: info.importCPos,
|
|
||||||
Tok: token.VAR,
|
|
||||||
Lparen: info.importCPos,
|
|
||||||
Rparen: info.importCPos,
|
|
||||||
}
|
|
||||||
names := make([]string, 0, len(info.functions))
|
|
||||||
for name := range info.functions {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
for _, name := range names {
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Typ,
|
|
||||||
Name: "C." + name + "$funcaddr",
|
|
||||||
}
|
|
||||||
valueSpec := &ast.ValueSpec{
|
|
||||||
Names: []*ast.Ident{&ast.Ident{
|
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: "C." + name + "$funcaddr",
|
|
||||||
Obj: obj,
|
|
||||||
}},
|
|
||||||
Type: &ast.SelectorExpr{
|
|
||||||
X: &ast.Ident{
|
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: "unsafe",
|
|
||||||
},
|
|
||||||
Sel: &ast.Ident{
|
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: "Pointer",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
obj.Decl = valueSpec
|
|
||||||
gen.Specs = append(gen.Specs, valueSpec)
|
|
||||||
}
|
|
||||||
info.Decls = append(info.Decls, gen)
|
|
||||||
}
|
|
||||||
|
|
||||||
// addVarDecls declares external C globals in the Go source.
|
|
||||||
// It adds code like the following to the AST:
|
|
||||||
//
|
|
||||||
// var (
|
|
||||||
// C.globalInt int
|
|
||||||
// C.globalBool bool
|
|
||||||
// // ...
|
|
||||||
// )
|
|
||||||
func (info *fileInfo) addVarDecls() {
|
|
||||||
if len(info.globals) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
gen := &ast.GenDecl{
|
|
||||||
TokPos: info.importCPos,
|
|
||||||
Tok: token.VAR,
|
|
||||||
Lparen: info.importCPos,
|
|
||||||
Rparen: info.importCPos,
|
|
||||||
}
|
|
||||||
names := make([]string, 0, len(info.globals))
|
|
||||||
for name := range info.globals {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
for _, name := range names {
|
|
||||||
global := info.globals[name]
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Typ,
|
|
||||||
Name: "C." + name,
|
|
||||||
}
|
|
||||||
valueSpec := &ast.ValueSpec{
|
|
||||||
Names: []*ast.Ident{&ast.Ident{
|
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: "C." + name,
|
|
||||||
Obj: obj,
|
|
||||||
}},
|
|
||||||
Type: global.typeExpr,
|
|
||||||
}
|
|
||||||
obj.Decl = valueSpec
|
|
||||||
gen.Specs = append(gen.Specs, valueSpec)
|
|
||||||
}
|
|
||||||
info.Decls = append(info.Decls, gen)
|
|
||||||
}
|
|
||||||
|
|
||||||
// addTypeAliases aliases some built-in Go types with their equivalent C types.
|
|
||||||
// It adds code like the following to the AST:
|
|
||||||
//
|
|
||||||
// type (
|
|
||||||
// C.int8_t = int8
|
|
||||||
// C.int16_t = int16
|
|
||||||
// // ...
|
|
||||||
// )
|
|
||||||
func (info *fileInfo) addTypeAliases() {
|
|
||||||
aliasKeys := make([]string, 0, len(cgoAliases))
|
|
||||||
for key := range cgoAliases {
|
|
||||||
aliasKeys = append(aliasKeys, key)
|
|
||||||
}
|
|
||||||
sort.Strings(aliasKeys)
|
|
||||||
gen := &ast.GenDecl{
|
|
||||||
TokPos: info.importCPos,
|
|
||||||
Tok: token.TYPE,
|
|
||||||
Lparen: info.importCPos,
|
|
||||||
Rparen: info.importCPos,
|
|
||||||
}
|
|
||||||
for _, typeName := range aliasKeys {
|
|
||||||
goTypeName := cgoAliases[typeName]
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Typ,
|
|
||||||
Name: typeName,
|
|
||||||
}
|
|
||||||
typeSpec := &ast.TypeSpec{
|
|
||||||
Name: &ast.Ident{
|
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: typeName,
|
|
||||||
Obj: obj,
|
|
||||||
},
|
|
||||||
Assign: info.importCPos,
|
|
||||||
Type: &ast.Ident{
|
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: goTypeName,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
obj.Decl = typeSpec
|
|
||||||
gen.Specs = append(gen.Specs, typeSpec)
|
|
||||||
}
|
|
||||||
info.Decls = append(info.Decls, gen)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (info *fileInfo) addTypedefs() {
|
|
||||||
if len(info.typedefs) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
gen := &ast.GenDecl{
|
|
||||||
TokPos: info.importCPos,
|
|
||||||
Tok: token.TYPE,
|
|
||||||
}
|
|
||||||
names := make([]string, 0, len(info.typedefs))
|
|
||||||
for name := range info.typedefs {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
for _, name := range names {
|
|
||||||
typedef := info.typedefs[name]
|
|
||||||
typeName := "C." + name
|
|
||||||
isAlias := true
|
|
||||||
if strings.HasPrefix(name, "_Cgo_") {
|
|
||||||
typeName = "C." + name[len("_Cgo_"):]
|
|
||||||
isAlias = false // C.short etc. should not be aliased to the equivalent Go type (not portable)
|
|
||||||
}
|
|
||||||
if _, ok := cgoAliases[typeName]; ok {
|
|
||||||
// This is a type that also exists in Go (defined in stdint.h).
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Typ,
|
|
||||||
Name: typeName,
|
|
||||||
}
|
|
||||||
typeSpec := &ast.TypeSpec{
|
|
||||||
Name: &ast.Ident{
|
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: typeName,
|
|
||||||
Obj: obj,
|
|
||||||
},
|
|
||||||
Type: typedef.typeExpr,
|
|
||||||
}
|
|
||||||
if isAlias {
|
|
||||||
typeSpec.Assign = info.importCPos
|
|
||||||
}
|
|
||||||
obj.Decl = typeSpec
|
|
||||||
gen.Specs = append(gen.Specs, typeSpec)
|
|
||||||
}
|
|
||||||
info.Decls = append(info.Decls, gen)
|
|
||||||
}
|
|
||||||
|
|
||||||
// addElaboratedTypes adds C elaborated types as aliases. These are the "struct
|
|
||||||
// foo" or "union foo" types, often used in a typedef.
|
|
||||||
//
|
|
||||||
// See also:
|
|
||||||
// https://en.cppreference.com/w/cpp/language/elaborated_type_specifier
|
|
||||||
func (info *fileInfo) addElaboratedTypes() {
|
|
||||||
if len(info.elaboratedTypes) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
gen := &ast.GenDecl{
|
|
||||||
TokPos: info.importCPos,
|
|
||||||
Tok: token.TYPE,
|
|
||||||
}
|
|
||||||
names := make([]string, 0, len(info.elaboratedTypes))
|
|
||||||
for name := range info.elaboratedTypes {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
for _, name := range names {
|
|
||||||
typ := info.elaboratedTypes[name]
|
|
||||||
typeName := "C." + name
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Typ,
|
|
||||||
Name: typeName,
|
|
||||||
}
|
|
||||||
typeSpec := &ast.TypeSpec{
|
|
||||||
Name: &ast.Ident{
|
|
||||||
NamePos: info.importCPos,
|
|
||||||
Name: typeName,
|
|
||||||
Obj: obj,
|
|
||||||
},
|
|
||||||
Type: typ,
|
|
||||||
}
|
|
||||||
obj.Decl = typeSpec
|
|
||||||
gen.Specs = append(gen.Specs, typeSpec)
|
|
||||||
}
|
|
||||||
info.Decls = append(info.Decls, gen)
|
|
||||||
}
|
|
||||||
|
|
||||||
// findMissingCGoNames traverses the AST and finds all C.something names. Only
|
|
||||||
// these symbols are extracted from the parsed C AST and converted to the Go
|
|
||||||
// equivalent.
|
|
||||||
func (info *fileInfo) findMissingCGoNames(cursor *astutil.Cursor) bool {
|
|
||||||
switch node := cursor.Node().(type) {
|
|
||||||
case *ast.SelectorExpr:
|
|
||||||
x, ok := node.X.(*ast.Ident)
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if x.Name == "C" {
|
|
||||||
name := node.Sel.Name
|
|
||||||
if _, ok := cgoBuiltinAliases[name]; ok {
|
|
||||||
name = "_Cgo_" + name
|
|
||||||
}
|
|
||||||
info.missingSymbols[name] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// walker replaces all "C".<something> expressions to literal "C.<something>"
|
|
||||||
// expressions. Such expressions are impossible to write in Go (a dot cannot be
|
|
||||||
// used in the middle of a name) so in practice all C identifiers live in a
|
|
||||||
// separate namespace (no _Cgo_ hacks like in gc).
|
|
||||||
func (info *fileInfo) walker(cursor *astutil.Cursor) bool {
|
|
||||||
switch node := cursor.Node().(type) {
|
|
||||||
case *ast.CallExpr:
|
|
||||||
fun, ok := node.Fun.(*ast.SelectorExpr)
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
x, ok := fun.X.(*ast.Ident)
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if _, ok := info.functions[fun.Sel.Name]; ok && x.Name == "C" {
|
|
||||||
node.Fun = &ast.Ident{
|
|
||||||
NamePos: x.NamePos,
|
|
||||||
Name: "C." + fun.Sel.Name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *ast.SelectorExpr:
|
|
||||||
x, ok := node.X.(*ast.Ident)
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if x.Name == "C" {
|
|
||||||
name := "C." + node.Sel.Name
|
|
||||||
if _, ok := info.functions[node.Sel.Name]; ok {
|
|
||||||
name += "$funcaddr"
|
|
||||||
}
|
|
||||||
cursor.Replace(&ast.Ident{
|
|
||||||
NamePos: x.NamePos,
|
|
||||||
Name: name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
+20
-20
@@ -10,6 +10,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/cgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Program holds all packages and some metadata about the program as a whole.
|
// Program holds all packages and some metadata about the program as a whole.
|
||||||
@@ -30,11 +32,10 @@ type Program struct {
|
|||||||
type Package struct {
|
type Package struct {
|
||||||
*Program
|
*Program
|
||||||
*build.Package
|
*build.Package
|
||||||
Imports map[string]*Package
|
Imports map[string]*Package
|
||||||
Importing bool
|
Importing bool
|
||||||
Files []*ast.File
|
Files []*ast.File
|
||||||
tokenFiles map[string]*token.File
|
Pkg *types.Package
|
||||||
Pkg *types.Package
|
|
||||||
types.Info
|
types.Info
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +108,6 @@ func (p *Program) newPackage(pkg *build.Package) *Package {
|
|||||||
Scopes: make(map[ast.Node]*types.Scope),
|
Scopes: make(map[ast.Node]*types.Scope),
|
||||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||||
},
|
},
|
||||||
tokenFiles: map[string]*token.File{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,16 +295,6 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
}
|
}
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
}
|
}
|
||||||
clangIncludes := ""
|
|
||||||
if len(p.CgoFiles) != 0 {
|
|
||||||
if _, err := os.Stat(filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")); !os.IsNotExist(err) {
|
|
||||||
// Running from the source directory.
|
|
||||||
clangIncludes = filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")
|
|
||||||
} else {
|
|
||||||
// Running from the installation directory.
|
|
||||||
clangIncludes = filepath.Join(p.TINYGOROOT, "lib", "clang", "include")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, file := range p.CgoFiles {
|
for _, file := range p.CgoFiles {
|
||||||
path := filepath.Join(p.Package.Dir, file)
|
path := filepath.Join(p.Package.Dir, file)
|
||||||
f, err := p.parseFile(path, parser.ParseComments)
|
f, err := p.parseFile(path, parser.ParseComments)
|
||||||
@@ -312,12 +302,22 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
fileErrs = append(fileErrs, err)
|
fileErrs = append(fileErrs, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
errs := p.processCgo(path, f, append(p.CFlags, "-I"+p.Package.Dir, "-I"+clangIncludes))
|
files = append(files, f)
|
||||||
|
}
|
||||||
|
if len(p.CgoFiles) != 0 {
|
||||||
|
clangIncludes := ""
|
||||||
|
if _, err := os.Stat(filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")); !os.IsNotExist(err) {
|
||||||
|
// Running from the source directory.
|
||||||
|
clangIncludes = filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")
|
||||||
|
} else {
|
||||||
|
// Running from the installation directory.
|
||||||
|
clangIncludes = filepath.Join(p.TINYGOROOT, "lib", "clang", "include")
|
||||||
|
}
|
||||||
|
generated, errs := cgo.Process(files, p.Program.Dir, p.fset, append(p.CFlags, "-I"+p.Package.Dir, "-I"+clangIncludes))
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
fileErrs = append(fileErrs, errs...)
|
fileErrs = append(fileErrs, errs...)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
files = append(files, f)
|
files = append(files, generated)
|
||||||
}
|
}
|
||||||
if len(fileErrs) != 0 {
|
if len(fileErrs) != 0 {
|
||||||
return nil, Errors{p, fileErrs}
|
return nil, Errors{p, fileErrs}
|
||||||
@@ -346,7 +346,7 @@ func (p *Package) importRecursively() error {
|
|||||||
p.Importing = true
|
p.Importing = true
|
||||||
for _, to := range p.Package.Imports {
|
for _, to := range p.Package.Imports {
|
||||||
if to == "C" {
|
if to == "C" {
|
||||||
// Do Cgo processing in a later stage.
|
// Do CGo processing in a later stage.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, ok := p.Imports[to]; ok {
|
if _, ok := p.Imports[to]; ok {
|
||||||
|
|||||||
@@ -80,9 +80,21 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
|||||||
if goroot == "" {
|
if goroot == "" {
|
||||||
return errors.New("cannot locate $GOROOT, please set it manually")
|
return errors.New("cannot locate $GOROOT, please set it manually")
|
||||||
}
|
}
|
||||||
|
tags := spec.BuildTags
|
||||||
|
major, minor := getGorootVersion(goroot)
|
||||||
|
if major != 1 {
|
||||||
|
if major == 0 {
|
||||||
|
return errors.New("could not read version from GOROOT: " + goroot)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("expected major version 1, got go%d.%d", major, minor)
|
||||||
|
}
|
||||||
|
for i := 1; i <= minor; i++ {
|
||||||
|
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||||
|
}
|
||||||
compilerConfig := compiler.Config{
|
compilerConfig := compiler.Config{
|
||||||
Triple: spec.Triple,
|
Triple: spec.Triple,
|
||||||
CPU: spec.CPU,
|
CPU: spec.CPU,
|
||||||
|
Features: spec.Features,
|
||||||
GOOS: spec.GOOS,
|
GOOS: spec.GOOS,
|
||||||
GOARCH: spec.GOARCH,
|
GOARCH: spec.GOARCH,
|
||||||
GC: config.gc,
|
GC: config.gc,
|
||||||
@@ -94,7 +106,7 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act
|
|||||||
TINYGOROOT: root,
|
TINYGOROOT: root,
|
||||||
GOROOT: goroot,
|
GOROOT: goroot,
|
||||||
GOPATH: getGopath(),
|
GOPATH: getGopath(),
|
||||||
BuildTags: spec.BuildTags,
|
BuildTags: tags,
|
||||||
}
|
}
|
||||||
c, err := compiler.NewCompiler(pkgName, compilerConfig)
|
c, err := compiler.NewCompiler(pkgName, compilerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
.syntax unified
|
||||||
|
|
||||||
|
.section .text.HardFault_Handler
|
||||||
|
.global HardFault_Handler
|
||||||
|
.type HardFault_Handler, %function
|
||||||
|
HardFault_Handler:
|
||||||
|
// Put the old stack pointer in the first argument, for easy debugging. This
|
||||||
|
// is especially useful on Cortex-M0, which supports far fewer debug
|
||||||
|
// facilities.
|
||||||
|
mov r0, sp
|
||||||
|
|
||||||
|
// Load the default stack pointer from address 0 so that we can call normal
|
||||||
|
// functions again that expect a working stack. However, it will corrupt the
|
||||||
|
// old stack so the function below must not attempt to recover from this
|
||||||
|
// fault.
|
||||||
|
movs r3, #0
|
||||||
|
ldr r3, [r3]
|
||||||
|
mov sp, r3
|
||||||
|
|
||||||
|
// Continue handling this error in Go.
|
||||||
|
bl handleHardFault
|
||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
func main() {
|
func main() {
|
||||||
machine.InitADC()
|
machine.InitADC()
|
||||||
|
|
||||||
led := machine.GPIO{machine.LED}
|
led := machine.LED
|
||||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
|
||||||
sensor := machine.ADC{machine.ADC2}
|
sensor := machine.ADC{machine.ADC2}
|
||||||
sensor.Configure()
|
sensor.Configure()
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
led := machine.GPIO{machine.LED}
|
led := machine.LED
|
||||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
for {
|
for {
|
||||||
led.Low()
|
led.Low()
|
||||||
time.Sleep(time.Millisecond * 500)
|
time.Sleep(time.Millisecond * 500)
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func led1() {
|
func led1() {
|
||||||
led := machine.GPIO{machine.LED}
|
led := machine.LED1
|
||||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
for {
|
for {
|
||||||
println("+")
|
println("+")
|
||||||
led.Low()
|
led.Low()
|
||||||
@@ -30,8 +30,8 @@ func led1() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func led2() {
|
func led2() {
|
||||||
led := machine.GPIO{machine.LED2}
|
led := machine.LED2
|
||||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
for {
|
for {
|
||||||
println(" +")
|
println(" +")
|
||||||
led.Low()
|
led.Low()
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ import (
|
|||||||
|
|
||||||
// This example assumes that the button is connected to pin 8. Change the value
|
// This example assumes that the button is connected to pin 8. Change the value
|
||||||
// below to use a different pin.
|
// below to use a different pin.
|
||||||
const buttonPin = 8
|
const (
|
||||||
|
led = machine.LED
|
||||||
|
button = machine.Pin(8)
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
led := machine.GPIO{machine.LED}
|
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
led.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
button.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||||
|
|
||||||
button := machine.GPIO{buttonPin}
|
|
||||||
button.Configure(machine.GPIOConfig{Mode: machine.GPIO_INPUT})
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if button.Get() {
|
if button.Get() {
|
||||||
|
|||||||
@@ -8,29 +8,29 @@ import (
|
|||||||
// This example assumes that you are using the pca10040 board
|
// This example assumes that you are using the pca10040 board
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
led1 := machine.GPIO{machine.LED1}
|
led1 := machine.LED1
|
||||||
led1.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
led1.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
|
||||||
led2 := machine.GPIO{machine.LED2}
|
led2 := machine.LED2
|
||||||
led2.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
led2.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
|
||||||
led3 := machine.GPIO{machine.LED3}
|
led3 := machine.LED3
|
||||||
led3.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
led3.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
|
||||||
led4 := machine.GPIO{machine.LED4}
|
led4 := machine.LED4
|
||||||
led4.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
led4.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
|
||||||
button1 := machine.GPIO{machine.BUTTON1}
|
button1 := machine.BUTTON1
|
||||||
button1.Configure(machine.GPIOConfig{Mode: machine.GPIO_INPUT_PULLUP})
|
button1.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
|
|
||||||
button2 := machine.GPIO{machine.BUTTON2}
|
button2 := machine.BUTTON2
|
||||||
button2.Configure(machine.GPIOConfig{Mode: machine.GPIO_INPUT_PULLUP})
|
button2.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
|
|
||||||
button3 := machine.GPIO{machine.BUTTON3}
|
button3 := machine.BUTTON3
|
||||||
button3.Configure(machine.GPIOConfig{Mode: machine.GPIO_INPUT_PULLUP})
|
button3.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
|
|
||||||
button4 := machine.GPIO{machine.BUTTON4}
|
button4 := machine.BUTTON4
|
||||||
button4.Configure(machine.GPIOConfig{Mode: machine.GPIO_INPUT_PULLUP})
|
button4.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
|
|
||||||
for {
|
for {
|
||||||
led1.Set(button1.Get())
|
led1.Set(button1.Get())
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import (
|
|||||||
|
|
||||||
// change these to test a different UART or pins if available
|
// change these to test a different UART or pins if available
|
||||||
var (
|
var (
|
||||||
uart = machine.UART0
|
uart = machine.UART0
|
||||||
tx uint8 = machine.UART_TX_PIN
|
tx = machine.UART_TX_PIN
|
||||||
rx uint8 = machine.UART_RX_PIN
|
rx = machine.UART_RX_PIN
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// Example using the i2s hardware interface on the Adafruit Circuit Playground Express
|
||||||
|
// to read data from the onboard MEMS microphone.
|
||||||
|
//
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
machine.I2S0.Configure(machine.I2SConfig{
|
||||||
|
Mode: machine.I2SModePDM,
|
||||||
|
ClockSource: machine.I2SClockSourceExternal,
|
||||||
|
Stereo: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
data := make([]uint32, 64)
|
||||||
|
|
||||||
|
for {
|
||||||
|
// get the next group of samples
|
||||||
|
machine.I2S0.Read(data)
|
||||||
|
|
||||||
|
println("data", data[0], data[1], data[2], data[4], "...")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,19 +8,17 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CS_PIN is the pin used for Chip Select (CS). Change to whatever is in use on your board.
|
// cs is the pin used for Chip Select (CS). Change to whatever is in use on your board.
|
||||||
const CS_PIN = 3
|
const cs = machine.Pin(3)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tx []byte
|
tx []byte
|
||||||
rx []byte
|
rx []byte
|
||||||
val, result uint16
|
val, result uint16
|
||||||
cs machine.GPIO
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cs = machine.GPIO{CS_PIN}
|
cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
cs.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
|
||||||
|
|
||||||
machine.SPI0.Configure(machine.SPIConfig{
|
machine.SPI0.Configure(machine.SPIConfig{
|
||||||
Frequency: 4000000,
|
Frequency: 4000000,
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import (
|
|||||||
// The LED matrix in the micro:bit is a multiplexed display: https://en.wikipedia.org/wiki/Multiplexed_display
|
// The LED matrix in the micro:bit is a multiplexed display: https://en.wikipedia.org/wiki/Multiplexed_display
|
||||||
// Driver for easier control: https://github.com/tinygo-org/drivers/tree/master/microbitmatrix
|
// Driver for easier control: https://github.com/tinygo-org/drivers/tree/master/microbitmatrix
|
||||||
func main() {
|
func main() {
|
||||||
ledrow := machine.GPIO{machine.LED_ROW_1}
|
ledrow := machine.LED_ROW_1
|
||||||
ledrow.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
ledrow.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
ledcol := machine.GPIO{machine.LED_COL_1}
|
ledcol := machine.LED_COL_1
|
||||||
ledcol.Configure(machine.GPIOConfig{Mode: machine.GPIO_OUTPUT})
|
ledcol.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
ledcol.Low()
|
ledcol.Low()
|
||||||
for {
|
for {
|
||||||
ledrow.Low()
|
ledrow.Low()
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
export: clean wasm_exec
|
export: clean wasm_exec
|
||||||
tinygo build -o ./html/wasm.wasm -target wasm ./export/wasm.go
|
tinygo build -o ./html/wasm.wasm -target wasm -no-debug ./export/wasm.go
|
||||||
cp ./export/wasm.js ./html/
|
cp ./export/wasm.js ./html/
|
||||||
cp ./export/index.html ./html/
|
cp ./export/index.html ./html/
|
||||||
|
|
||||||
|
callback: clean wasm_exec
|
||||||
|
tinygo build -o ./html/wasm.wasm -target wasm ./callback/wasm.go
|
||||||
|
cp ./callback/wasm.js ./html/
|
||||||
|
cp ./callback/index.html ./html/
|
||||||
|
|
||||||
main: clean wasm_exec
|
main: clean wasm_exec
|
||||||
tinygo build -o ./html/wasm.wasm -target wasm ./main/main.go
|
tinygo build -o ./html/wasm.wasm -target wasm -no-debug ./main/main.go
|
||||||
cp ./main/index.html ./html/
|
cp ./main/index.html ./html/
|
||||||
|
|
||||||
wasm_exec:
|
wasm_exec:
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Go WebAssembly</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<script src="wasm_exec.js" defer></script>
|
||||||
|
<script src="wasm.js" defer></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>WebAssembly</h1>
|
||||||
|
<p>Add two numbers, using WebAssembly:</p>
|
||||||
|
<input type="number" id="a" value="0" /> + <input type="number" id="b" value="0" /> = <input type="number" id="result" readonly />
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"syscall/js"
|
||||||
|
)
|
||||||
|
|
||||||
|
var a, b int
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
document := js.Global().Get("document")
|
||||||
|
document.Call("getElementById", "a").Set("oninput", updater(&a))
|
||||||
|
document.Call("getElementById", "b").Set("oninput", updater(&b))
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updater(n *int) js.Func {
|
||||||
|
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||||
|
*n, _ = strconv.Atoi(this.Get("value").String())
|
||||||
|
update()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func update() {
|
||||||
|
js.Global().Get("document").Call("getElementById", "result").Set("value", a+b)
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const WASM_URL = 'wasm.wasm';
|
||||||
|
|
||||||
|
var wasm;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
const go = new Go();
|
||||||
|
if ('instantiateStreaming' in WebAssembly) {
|
||||||
|
WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function (obj) {
|
||||||
|
wasm = obj.instance;
|
||||||
|
go.run(wasm);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
fetch(WASM_URL).then(resp =>
|
||||||
|
resp.arrayBuffer()
|
||||||
|
).then(bytes =>
|
||||||
|
WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
|
||||||
|
wasm = obj.instance;
|
||||||
|
go.run(wasm);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
@@ -5,20 +5,20 @@ package machine
|
|||||||
const CPU_FREQUENCY = 16000000
|
const CPU_FREQUENCY = 16000000
|
||||||
|
|
||||||
// LED on the Arduino
|
// LED on the Arduino
|
||||||
const LED = 13
|
const LED Pin = 13
|
||||||
|
|
||||||
// ADC on the Arduino
|
// ADC on the Arduino
|
||||||
const (
|
const (
|
||||||
ADC0 = 0
|
ADC0 Pin = 0
|
||||||
ADC1 = 1
|
ADC1 Pin = 1
|
||||||
ADC2 = 2
|
ADC2 Pin = 2
|
||||||
ADC3 = 3
|
ADC3 Pin = 3
|
||||||
ADC4 = 4 // Used by TWI for SDA
|
ADC4 Pin = 4 // Used by TWI for SDA
|
||||||
ADC5 = 5 // Used by TWI for SCL
|
ADC5 Pin = 5 // Used by TWI for SCL
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART pins
|
// UART pins
|
||||||
const (
|
const (
|
||||||
UART_TX_PIN = 1
|
UART_TX_PIN Pin = 1
|
||||||
UART_RX_PIN = 0
|
UART_RX_PIN Pin = 0
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const (
|
|||||||
D8 = PB23
|
D8 = PB23
|
||||||
D9 = PA06
|
D9 = PA06
|
||||||
D10 = PA07
|
D10 = PA07
|
||||||
D11 = 0xff // does not seem to exist
|
D11 = NoPin // does not seem to exist
|
||||||
D12 = PA02
|
D12 = PA02
|
||||||
D13 = PA17 // PWM available
|
D13 = PA17 // PWM available
|
||||||
)
|
)
|
||||||
@@ -80,12 +80,12 @@ var (
|
|||||||
I2C0 = I2C{Bus: sam.SERCOM5_I2CM,
|
I2C0 = I2C{Bus: sam.SERCOM5_I2CM,
|
||||||
SDA: SDA_PIN,
|
SDA: SDA_PIN,
|
||||||
SCL: SCL_PIN,
|
SCL: SCL_PIN,
|
||||||
PinMode: GPIO_SERCOM}
|
PinMode: PinSERCOM}
|
||||||
// internal device
|
// internal device
|
||||||
I2C1 = I2C{Bus: sam.SERCOM1_I2CM,
|
I2C1 = I2C{Bus: sam.SERCOM1_I2CM,
|
||||||
SDA: SDA1_PIN,
|
SDA: SDA1_PIN,
|
||||||
SCL: SCL1_PIN,
|
SCL: SCL1_PIN,
|
||||||
PinMode: GPIO_SERCOM_ALT}
|
PinMode: PinSERCOMAlt}
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins (internal flash)
|
// SPI pins (internal flash)
|
||||||
@@ -99,3 +99,15 @@ const (
|
|||||||
var (
|
var (
|
||||||
SPI0 = SPI{Bus: sam.SERCOM3_SPI}
|
SPI0 = SPI{Bus: sam.SERCOM3_SPI}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// I2S pins
|
||||||
|
const (
|
||||||
|
I2S_SCK_PIN = PA10
|
||||||
|
I2S_SD_PIN = PA08
|
||||||
|
I2S_WS_PIN = NoPin // no WS, instead uses SCK to sync
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2S on the Circuit Playground Express.
|
||||||
|
var (
|
||||||
|
I2S0 = I2S{Bus: sam.I2S}
|
||||||
|
)
|
||||||
|
|||||||
@@ -3,5 +3,5 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LED = 1
|
LED Pin = 1
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
// +build sam,atsamd21,feather_m0
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import "device/sam"
|
||||||
|
|
||||||
|
// GPIO Pins
|
||||||
|
const (
|
||||||
|
D0 = PA11 // UART0 RX
|
||||||
|
D1 = PA10 // UART0 TX
|
||||||
|
D2 = NoPin // does not seem to exist
|
||||||
|
D3 = PA09
|
||||||
|
D4 = PA08
|
||||||
|
D5 = PA15 // PWM available
|
||||||
|
D6 = PA20 // PWM available
|
||||||
|
D7 = NoPin // does not seem to exist
|
||||||
|
D8 = PA06
|
||||||
|
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 = 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 aka USBCDC pins
|
||||||
|
const (
|
||||||
|
USBCDC_DM_PIN = PA24
|
||||||
|
USBCDC_DP_PIN = PA25
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART1 pins
|
||||||
|
const (
|
||||||
|
UART_TX_PIN = D10
|
||||||
|
UART_RX_PIN = D11
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C pins
|
||||||
|
const (
|
||||||
|
SDA_PIN = PA22 // SDA: SERCOM3/PAD[0]
|
||||||
|
SCL_PIN = PA23 // SCL: SERCOM3/PAD[1]
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C on the Feather M0.
|
||||||
|
var (
|
||||||
|
I2C0 = I2C{Bus: sam.SERCOM3_I2CM,
|
||||||
|
SDA: SDA_PIN,
|
||||||
|
SCL: SCL_PIN,
|
||||||
|
PinMode: PinSERCOM}
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI pins
|
||||||
|
const (
|
||||||
|
SPI0_SCK_PIN = PB11 // SCK: SERCOM4/PAD[3]
|
||||||
|
SPI0_MOSI_PIN = PB10 // MOSI: SERCOM4/PAD[2]
|
||||||
|
SPI0_MISO_PIN = PA12 // MISO: SERCOM4/PAD[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI on the Feather M0.
|
||||||
|
var (
|
||||||
|
SPI0 = SPI{Bus: sam.SERCOM4_SPI}
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2S pins
|
||||||
|
const (
|
||||||
|
I2S_SCK_PIN = PA10
|
||||||
|
I2S_SD_PIN = PA08
|
||||||
|
I2S_WS_PIN = NoPin // TODO: figure out what this is on Feather M0.
|
||||||
|
)
|
||||||
@@ -59,7 +59,7 @@ var (
|
|||||||
I2C0 = I2C{Bus: sam.SERCOM3_I2CM,
|
I2C0 = I2C{Bus: sam.SERCOM3_I2CM,
|
||||||
SDA: SDA_PIN,
|
SDA: SDA_PIN,
|
||||||
SCL: SCL_PIN,
|
SCL: SCL_PIN,
|
||||||
PinMode: GPIO_SERCOM}
|
PinMode: PinSERCOM}
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
@@ -73,3 +73,15 @@ const (
|
|||||||
var (
|
var (
|
||||||
SPI0 = SPI{Bus: sam.SERCOM4_SPI}
|
SPI0 = SPI{Bus: sam.SERCOM4_SPI}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// I2S pins
|
||||||
|
const (
|
||||||
|
I2S_SCK_PIN = PA10
|
||||||
|
I2S_SD_PIN = PA08
|
||||||
|
I2S_WS_PIN = NoPin // TODO: figure out what this is on ItsyBitsy M0.
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2S on the ItsyBitsy M0.
|
||||||
|
var (
|
||||||
|
I2S0 = I2S{Bus: sam.I2S}
|
||||||
|
)
|
||||||
|
|||||||
@@ -7,70 +7,70 @@ const HasLowFrequencyCrystal = false
|
|||||||
|
|
||||||
// Buttons on the micro:bit (A and B)
|
// Buttons on the micro:bit (A and B)
|
||||||
const (
|
const (
|
||||||
BUTTON = BUTTONA
|
BUTTON Pin = BUTTONA
|
||||||
BUTTONA = 17
|
BUTTONA Pin = 17
|
||||||
BUTTONB = 26
|
BUTTONB Pin = 26
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART pins
|
// UART pins
|
||||||
const (
|
const (
|
||||||
UART_TX_PIN = 24
|
UART_TX_PIN Pin = 24
|
||||||
UART_RX_PIN = 25
|
UART_RX_PIN Pin = 25
|
||||||
)
|
)
|
||||||
|
|
||||||
// ADC pins
|
// ADC pins
|
||||||
const (
|
const (
|
||||||
ADC0 = 3 // P0 on the board
|
ADC0 Pin = 3 // P0 on the board
|
||||||
ADC1 = 2 // P1 on the board
|
ADC1 Pin = 2 // P1 on the board
|
||||||
ADC2 = 1 // P2 on the board
|
ADC2 Pin = 1 // P2 on the board
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN = 30 // P20 on the board
|
SDA_PIN Pin = 30 // P20 on the board
|
||||||
SCL_PIN = 0 // P19 on the board
|
SCL_PIN Pin = 0 // P19 on the board
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
const (
|
const (
|
||||||
SPI0_SCK_PIN = 23 // P13 on the board
|
SPI0_SCK_PIN Pin = 23 // P13 on the board
|
||||||
SPI0_MOSI_PIN = 21 // P15 on the board
|
SPI0_MOSI_PIN Pin = 21 // P15 on the board
|
||||||
SPI0_MISO_PIN = 22 // P14 on the board
|
SPI0_MISO_PIN Pin = 22 // P14 on the board
|
||||||
)
|
)
|
||||||
|
|
||||||
// GPIO/Analog pins
|
// GPIO/Analog pins
|
||||||
const (
|
const (
|
||||||
P0 = 3
|
P0 Pin = 3
|
||||||
P1 = 2
|
P1 Pin = 2
|
||||||
P2 = 1
|
P2 Pin = 1
|
||||||
P3 = 4
|
P3 Pin = 4
|
||||||
P4 = 5
|
P4 Pin = 5
|
||||||
P5 = 17
|
P5 Pin = 17
|
||||||
P6 = 12
|
P6 Pin = 12
|
||||||
P7 = 11
|
P7 Pin = 11
|
||||||
P8 = 18
|
P8 Pin = 18
|
||||||
P9 = 10
|
P9 Pin = 10
|
||||||
P10 = 6
|
P10 Pin = 6
|
||||||
P11 = 26
|
P11 Pin = 26
|
||||||
P12 = 20
|
P12 Pin = 20
|
||||||
P13 = 23
|
P13 Pin = 23
|
||||||
P14 = 22
|
P14 Pin = 22
|
||||||
P15 = 21
|
P15 Pin = 21
|
||||||
P16 = 16
|
P16 Pin = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
// LED matrix pins
|
// LED matrix pins
|
||||||
const (
|
const (
|
||||||
LED_COL_1 = 4
|
LED_COL_1 Pin = 4
|
||||||
LED_COL_2 = 5
|
LED_COL_2 Pin = 5
|
||||||
LED_COL_3 = 6
|
LED_COL_3 Pin = 6
|
||||||
LED_COL_4 = 7
|
LED_COL_4 Pin = 7
|
||||||
LED_COL_5 = 8
|
LED_COL_5 Pin = 8
|
||||||
LED_COL_6 = 9
|
LED_COL_6 Pin = 9
|
||||||
LED_COL_7 = 10
|
LED_COL_7 Pin = 10
|
||||||
LED_COL_8 = 11
|
LED_COL_8 Pin = 11
|
||||||
LED_COL_9 = 12
|
LED_COL_9 Pin = 12
|
||||||
LED_ROW_1 = 13
|
LED_ROW_1 Pin = 13
|
||||||
LED_ROW_2 = 14
|
LED_ROW_2 Pin = 14
|
||||||
LED_ROW_3 = 15
|
LED_ROW_3 Pin = 15
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,27 +6,27 @@ const HasLowFrequencyCrystal = true
|
|||||||
|
|
||||||
// LEDs on the nrf52840-mdk (nRF52840 dev board)
|
// LEDs on the nrf52840-mdk (nRF52840 dev board)
|
||||||
const (
|
const (
|
||||||
LED = LED_GREEN
|
LED Pin = LED_GREEN
|
||||||
LED_GREEN = 22
|
LED_GREEN Pin = 22
|
||||||
LED_RED = 23
|
LED_RED Pin = 23
|
||||||
LED_BLUE = 24
|
LED_BLUE Pin = 24
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART pins
|
// UART pins
|
||||||
const (
|
const (
|
||||||
UART_TX_PIN = 20
|
UART_TX_PIN Pin = 20
|
||||||
UART_RX_PIN = 19
|
UART_RX_PIN Pin = 19
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2C pins (unused)
|
// I2C pins (unused)
|
||||||
const (
|
const (
|
||||||
SDA_PIN = 0xff
|
SDA_PIN = NoPin
|
||||||
SCL_PIN = 0xff
|
SCL_PIN = NoPin
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins (unused)
|
// SPI pins (unused)
|
||||||
const (
|
const (
|
||||||
SPI0_SCK_PIN = 0
|
SPI0_SCK_PIN = NoPin
|
||||||
SPI0_MOSI_PIN = 0
|
SPI0_MOSI_PIN = NoPin
|
||||||
SPI0_MISO_PIN = 0
|
SPI0_MISO_PIN = NoPin
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,30 +10,30 @@ const HasLowFrequencyCrystal = true
|
|||||||
|
|
||||||
// LED on the pca10031
|
// LED on the pca10031
|
||||||
const (
|
const (
|
||||||
LED = LED_RED
|
LED Pin = LED_RED
|
||||||
LED1 = LED_RED
|
LED1 Pin = LED_RED
|
||||||
LED2 = LED_GREEN
|
LED2 Pin = LED_GREEN
|
||||||
LED3 = LED_BLUE
|
LED3 Pin = LED_BLUE
|
||||||
LED_RED = 21
|
LED_RED Pin = 21
|
||||||
LED_GREEN = 22
|
LED_GREEN Pin = 22
|
||||||
LED_BLUE = 23
|
LED_BLUE Pin = 23
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART pins
|
// UART pins
|
||||||
const (
|
const (
|
||||||
UART_TX_PIN = 9
|
UART_TX_PIN Pin = 9
|
||||||
UART_RX_PIN = 11
|
UART_RX_PIN Pin = 11
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2C pins (disabled)
|
// I2C pins (disabled)
|
||||||
const (
|
const (
|
||||||
SDA_PIN = 0xff
|
SDA_PIN = NoPin
|
||||||
SCL_PIN = 0xff
|
SCL_PIN = NoPin
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins (unused)
|
// SPI pins (unused)
|
||||||
const (
|
const (
|
||||||
SPI0_SCK_PIN = 0
|
SPI0_SCK_PIN = NoPin
|
||||||
SPI0_MOSI_PIN = 0
|
SPI0_MOSI_PIN = NoPin
|
||||||
SPI0_MISO_PIN = 0
|
SPI0_MISO_PIN = NoPin
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,47 +7,47 @@ const HasLowFrequencyCrystal = true
|
|||||||
|
|
||||||
// LEDs on the PCA10040 (nRF52832 dev board)
|
// LEDs on the PCA10040 (nRF52832 dev board)
|
||||||
const (
|
const (
|
||||||
LED = LED1
|
LED Pin = LED1
|
||||||
LED1 = 17
|
LED1 Pin = 17
|
||||||
LED2 = 18
|
LED2 Pin = 18
|
||||||
LED3 = 19
|
LED3 Pin = 19
|
||||||
LED4 = 20
|
LED4 Pin = 20
|
||||||
)
|
)
|
||||||
|
|
||||||
// Buttons on the PCA10040 (nRF52832 dev board)
|
// Buttons on the PCA10040 (nRF52832 dev board)
|
||||||
const (
|
const (
|
||||||
BUTTON = BUTTON1
|
BUTTON Pin = BUTTON1
|
||||||
BUTTON1 = 13
|
BUTTON1 Pin = 13
|
||||||
BUTTON2 = 14
|
BUTTON2 Pin = 14
|
||||||
BUTTON3 = 15
|
BUTTON3 Pin = 15
|
||||||
BUTTON4 = 16
|
BUTTON4 Pin = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART pins for NRF52840-DK
|
// UART pins for NRF52840-DK
|
||||||
const (
|
const (
|
||||||
UART_TX_PIN = 6
|
UART_TX_PIN Pin = 6
|
||||||
UART_RX_PIN = 8
|
UART_RX_PIN Pin = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
// ADC pins
|
// ADC pins
|
||||||
const (
|
const (
|
||||||
ADC0 = 3
|
ADC0 Pin = 3
|
||||||
ADC1 = 4
|
ADC1 Pin = 4
|
||||||
ADC2 = 28
|
ADC2 Pin = 28
|
||||||
ADC3 = 29
|
ADC3 Pin = 29
|
||||||
ADC4 = 30
|
ADC4 Pin = 30
|
||||||
ADC5 = 31
|
ADC5 Pin = 31
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN = 26
|
SDA_PIN Pin = 26
|
||||||
SCL_PIN = 27
|
SCL_PIN Pin = 27
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
const (
|
const (
|
||||||
SPI0_SCK_PIN = 25
|
SPI0_SCK_PIN Pin = 25
|
||||||
SPI0_MOSI_PIN = 23
|
SPI0_MOSI_PIN Pin = 23
|
||||||
SPI0_MISO_PIN = 24
|
SPI0_MISO_PIN Pin = 24
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,47 +6,47 @@ const HasLowFrequencyCrystal = true
|
|||||||
|
|
||||||
// LEDs on the pca10056
|
// LEDs on the pca10056
|
||||||
const (
|
const (
|
||||||
LED = LED1
|
LED Pin = LED1
|
||||||
LED1 = 13
|
LED1 Pin = 13
|
||||||
LED2 = 14
|
LED2 Pin = 14
|
||||||
LED3 = 15
|
LED3 Pin = 15
|
||||||
LED4 = 16
|
LED4 Pin = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
// Buttons on the pca10056
|
// Buttons on the pca10056
|
||||||
const (
|
const (
|
||||||
BUTTON = BUTTON1
|
BUTTON Pin = BUTTON1
|
||||||
BUTTON1 = 11
|
BUTTON1 Pin = 11
|
||||||
BUTTON2 = 12
|
BUTTON2 Pin = 12
|
||||||
BUTTON3 = 24
|
BUTTON3 Pin = 24
|
||||||
BUTTON4 = 25
|
BUTTON4 Pin = 25
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART pins
|
// UART pins
|
||||||
const (
|
const (
|
||||||
UART_TX_PIN = 6
|
UART_TX_PIN Pin = 6
|
||||||
UART_RX_PIN = 8
|
UART_RX_PIN Pin = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
// ADC pins
|
// ADC pins
|
||||||
const (
|
const (
|
||||||
ADC0 = 3
|
ADC0 Pin = 3
|
||||||
ADC1 = 4
|
ADC1 Pin = 4
|
||||||
ADC2 = 28
|
ADC2 Pin = 28
|
||||||
ADC3 = 29
|
ADC3 Pin = 29
|
||||||
ADC4 = 30
|
ADC4 Pin = 30
|
||||||
ADC5 = 31
|
ADC5 Pin = 31
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN = 26 // P0.26
|
SDA_PIN Pin = 26 // P0.26
|
||||||
SCL_PIN = 27 // P0.27
|
SCL_PIN Pin = 27 // P0.27
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
const (
|
const (
|
||||||
SPI0_SCK_PIN = 47 // P1.15
|
SPI0_SCK_PIN Pin = 47 // P1.15
|
||||||
SPI0_MOSI_PIN = 45 // P1.13
|
SPI0_MOSI_PIN Pin = 45 // P1.13
|
||||||
SPI0_MISO_PIN = 46 // P1.14
|
SPI0_MISO_PIN Pin = 46 // P1.14
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,37 +6,37 @@ const HasLowFrequencyCrystal = true
|
|||||||
|
|
||||||
// LEDs on the reel board
|
// LEDs on the reel board
|
||||||
const (
|
const (
|
||||||
LED = LED1
|
LED Pin = LED1
|
||||||
LED1 = LED_YELLOW
|
LED1 Pin = LED_YELLOW
|
||||||
LED2 = LED_RED
|
LED2 Pin = LED_RED
|
||||||
LED3 = LED_GREEN
|
LED3 Pin = LED_GREEN
|
||||||
LED4 = LED_BLUE
|
LED4 Pin = LED_BLUE
|
||||||
LED_RED = 11
|
LED_RED Pin = 11
|
||||||
LED_GREEN = 12
|
LED_GREEN Pin = 12
|
||||||
LED_BLUE = 41
|
LED_BLUE Pin = 41
|
||||||
LED_YELLOW = 13
|
LED_YELLOW Pin = 13
|
||||||
)
|
)
|
||||||
|
|
||||||
// User "a" button on the reel board
|
// User "a" button on the reel board
|
||||||
const (
|
const (
|
||||||
BUTTON = 7
|
BUTTON Pin = 7
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART pins
|
// UART pins
|
||||||
const (
|
const (
|
||||||
UART_TX_PIN = 6
|
UART_TX_PIN Pin = 6
|
||||||
UART_RX_PIN = 8
|
UART_RX_PIN Pin = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN = 26
|
SDA_PIN Pin = 26
|
||||||
SCL_PIN = 27
|
SCL_PIN Pin = 27
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
const (
|
const (
|
||||||
SPI0_SCK_PIN = 47
|
SPI0_SCK_PIN Pin = 47
|
||||||
SPI0_MOSI_PIN = 45
|
SPI0_MOSI_PIN Pin = 45
|
||||||
SPI0_MISO_PIN = 46
|
SPI0_MISO_PIN Pin = 46
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
// +build sam,atsamd21,trinket_m0
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import "device/sam"
|
||||||
|
|
||||||
|
// GPIO Pins
|
||||||
|
const (
|
||||||
|
D0 = PA08 // PWM available
|
||||||
|
D1 = PA02
|
||||||
|
D2 = PA09 // PWM available
|
||||||
|
D3 = PA07 // PWM available / UART0 RX
|
||||||
|
D4 = PA06 // PWM available / UART0 TX
|
||||||
|
D13 = PA10 // LED
|
||||||
|
)
|
||||||
|
|
||||||
|
// Analog pins
|
||||||
|
const (
|
||||||
|
A0 = D1
|
||||||
|
A1 = D2
|
||||||
|
A2 = D0
|
||||||
|
A3 = D3
|
||||||
|
A4 = D4
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LED = D13
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART0 aka USBCDC pins
|
||||||
|
const (
|
||||||
|
USBCDC_DM_PIN = PA24
|
||||||
|
USBCDC_DP_PIN = PA25
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART1 pins
|
||||||
|
const (
|
||||||
|
UART_TX_PIN = D4
|
||||||
|
UART_RX_PIN = D3
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI pins
|
||||||
|
const (
|
||||||
|
SPI0_SCK_PIN = D3
|
||||||
|
SPI0_MOSI_PIN = D4
|
||||||
|
SPI0_MISO_PIN = D2
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI on the Trinket M0.
|
||||||
|
var (
|
||||||
|
SPI0 = SPI{Bus: sam.SERCOM0_SPI}
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C pins
|
||||||
|
const (
|
||||||
|
SDA_PIN = D0 // SDA
|
||||||
|
SCL_PIN = D2 // SCL
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C on the Trinket M0.
|
||||||
|
var (
|
||||||
|
I2C0 = I2C{Bus: sam.SERCOM2_I2CM,
|
||||||
|
SDA: SDA_PIN,
|
||||||
|
SCL: SCL_PIN,
|
||||||
|
PinMode: PinSERCOMAlt}
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2S pins
|
||||||
|
const (
|
||||||
|
I2S_SCK_PIN = PA10
|
||||||
|
I2S_SD_PIN = PA08
|
||||||
|
I2S_WS_PIN = NoPin // TODO: figure out what this is on Trinket M0.
|
||||||
|
)
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// +build sam
|
||||||
|
|
||||||
|
// This is the definition for I2S bus functions.
|
||||||
|
// Actual implementations if available for any given hardware
|
||||||
|
// are to be found in its the board definition.
|
||||||
|
//
|
||||||
|
// For more info about I2S, see: https://en.wikipedia.org/wiki/I%C2%B2S
|
||||||
|
//
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
type I2SMode uint8
|
||||||
|
type I2SStandard uint8
|
||||||
|
type I2SClockSource uint8
|
||||||
|
type I2SDataFormat uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
I2SModeMaster I2SMode = iota
|
||||||
|
I2SModeSlave
|
||||||
|
I2SModePDM
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
I2StandardPhilips I2SStandard = iota
|
||||||
|
I2SStandardMSB
|
||||||
|
I2SStandardLSB
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
I2SClockSourceInternal I2SClockSource = iota
|
||||||
|
I2SClockSourceExternal
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
I2SDataFormatDefault I2SDataFormat = 0
|
||||||
|
I2SDataFormat8bit = 8
|
||||||
|
I2SDataFormat16bit = 16
|
||||||
|
I2SDataFormat24bit = 24
|
||||||
|
I2SDataFormat32bit = 32
|
||||||
|
)
|
||||||
|
|
||||||
|
// All fields are optional and may not be required or used on a particular platform.
|
||||||
|
type I2SConfig struct {
|
||||||
|
SCK Pin
|
||||||
|
WS Pin
|
||||||
|
SD Pin
|
||||||
|
Mode I2SMode
|
||||||
|
Standard I2SStandard
|
||||||
|
ClockSource I2SClockSource
|
||||||
|
DataFormat I2SDataFormat
|
||||||
|
AudioFrequency uint32
|
||||||
|
MasterClockOutput bool
|
||||||
|
Stereo bool
|
||||||
|
}
|
||||||
+20
-9
@@ -1,25 +1,36 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
type GPIOConfig struct {
|
type PinConfig struct {
|
||||||
Mode GPIOMode
|
Mode PinMode
|
||||||
}
|
}
|
||||||
|
|
||||||
type GPIO struct {
|
// Pin is a single pin on a chip, which may be connected to other hardware
|
||||||
Pin uint8
|
// devices. It can either be used directly as GPIO pin or it can be used in
|
||||||
}
|
// other peripherals like ADC, I2C, etc.
|
||||||
|
type Pin int8
|
||||||
|
|
||||||
func (p GPIO) High() {
|
// NoPin explicitly indicates "not a pin". Use this pin if you want to leave one
|
||||||
|
// of the pins in a peripheral unconfigured (if supported by the hardware).
|
||||||
|
const NoPin = Pin(-1)
|
||||||
|
|
||||||
|
// High sets this GPIO pin to high, assuming it has been configured as an output
|
||||||
|
// pin. It is hardware dependent (and often undefined) what happens if you set a
|
||||||
|
// pin to high that is not configured as an output pin.
|
||||||
|
func (p Pin) High() {
|
||||||
p.Set(true)
|
p.Set(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p GPIO) Low() {
|
// Low sets this GPIO pin to low, assuming it has been configured as an output
|
||||||
|
// pin. It is hardware dependent (and often undefined) what happens if you set a
|
||||||
|
// pin to low that is not configured as an output pin.
|
||||||
|
func (p Pin) Low() {
|
||||||
p.Set(false)
|
p.Set(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PWM struct {
|
type PWM struct {
|
||||||
Pin uint8
|
Pin Pin
|
||||||
}
|
}
|
||||||
|
|
||||||
type ADC struct {
|
type ADC struct {
|
||||||
Pin uint8
|
Pin Pin
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,102 +7,102 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Configure sets the pin to input or output.
|
// Configure sets the pin to input or output.
|
||||||
func (p GPIO) Configure(config GPIOConfig) {
|
func (p Pin) Configure(config PinConfig) {
|
||||||
if config.Mode == GPIO_OUTPUT { // set output bit
|
if config.Mode == PinOutput { // set output bit
|
||||||
if p.Pin < 8 {
|
if p < 8 {
|
||||||
*avr.DDRD |= 1 << p.Pin
|
avr.DDRD.SetBits(1 << uint8(p))
|
||||||
} else {
|
} else {
|
||||||
*avr.DDRB |= 1 << (p.Pin - 8)
|
avr.DDRB.SetBits(1 << uint8(p-8))
|
||||||
}
|
}
|
||||||
} else { // configure input: clear output bit
|
} else { // configure input: clear output bit
|
||||||
if p.Pin < 8 {
|
if p < 8 {
|
||||||
*avr.DDRD &^= 1 << p.Pin
|
avr.DDRD.ClearBits(1 << uint8(p))
|
||||||
} else {
|
} else {
|
||||||
*avr.DDRB &^= 1 << (p.Pin - 8)
|
avr.DDRB.ClearBits(1 << uint8(p-8))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the current value of a GPIO pin.
|
// Get returns the current value of a GPIO pin.
|
||||||
func (p GPIO) Get() bool {
|
func (p Pin) Get() bool {
|
||||||
if p.Pin < 8 {
|
if p < 8 {
|
||||||
val := *avr.PIND & (1 << p.Pin)
|
val := avr.PIND.Get() & (1 << uint8(p))
|
||||||
return (val > 0)
|
return (val > 0)
|
||||||
} else {
|
} else {
|
||||||
val := *avr.PINB & (1 << (p.Pin - 8))
|
val := avr.PINB.Get() & (1 << uint8(p-8))
|
||||||
return (val > 0)
|
return (val > 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p GPIO) getPortMask() (*avr.RegValue, uint8) {
|
func (p Pin) getPortMask() (*avr.Register8, uint8) {
|
||||||
if p.Pin < 8 {
|
if p < 8 {
|
||||||
return avr.PORTD, 1 << p.Pin
|
return avr.PORTD, 1 << uint8(p)
|
||||||
} else {
|
} else {
|
||||||
return avr.PORTB, 1 << (p.Pin - 8)
|
return avr.PORTB, 1 << uint8(p-8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitPWM initializes the registers needed for PWM.
|
// InitPWM initializes the registers needed for PWM.
|
||||||
func InitPWM() {
|
func InitPWM() {
|
||||||
// use waveform generation
|
// use waveform generation
|
||||||
*avr.TCCR0A |= avr.TCCR0A_WGM00
|
avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
|
||||||
|
|
||||||
// set timer 0 prescale factor to 64
|
// set timer 0 prescale factor to 64
|
||||||
*avr.TCCR0B |= avr.TCCR0B_CS01 | avr.TCCR0B_CS00
|
avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
|
||||||
|
|
||||||
// set timer 1 prescale factor to 64
|
// set timer 1 prescale factor to 64
|
||||||
*avr.TCCR1B |= avr.TCCR1B_CS11
|
avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
|
||||||
|
|
||||||
// put timer 1 in 8-bit phase correct pwm mode
|
// put timer 1 in 8-bit phase correct pwm mode
|
||||||
*avr.TCCR1A |= avr.TCCR1A_WGM10
|
avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
|
||||||
|
|
||||||
// set timer 2 prescale factor to 64
|
// set timer 2 prescale factor to 64
|
||||||
*avr.TCCR2B |= avr.TCCR2B_CS22
|
avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
|
||||||
|
|
||||||
// configure timer 2 for phase correct pwm (8-bit)
|
// configure timer 2 for phase correct pwm (8-bit)
|
||||||
*avr.TCCR2A |= avr.TCCR2A_WGM20
|
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure configures a PWM pin for output.
|
// Configure configures a PWM pin for output.
|
||||||
func (pwm PWM) Configure() {
|
func (pwm PWM) Configure() {
|
||||||
if pwm.Pin < 8 {
|
if pwm.Pin < 8 {
|
||||||
*avr.DDRD |= 1 << pwm.Pin
|
avr.DDRD.SetBits(1 << uint8(pwm.Pin))
|
||||||
} else {
|
} else {
|
||||||
*avr.DDRB |= 1 << (pwm.Pin - 8)
|
avr.DDRB.SetBits(1 << uint8(pwm.Pin-8))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
|
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
|
||||||
// 8-bit value ranging from 0 to 255.
|
// 8-bit value ranging from 0 to 255.
|
||||||
func (pwm PWM) Set(value uint16) {
|
func (pwm PWM) Set(value uint16) {
|
||||||
value8 := value >> 8
|
value8 := uint8(value >> 8)
|
||||||
switch pwm.Pin {
|
switch pwm.Pin {
|
||||||
case 3:
|
case 3:
|
||||||
// connect pwm to pin on timer 2, channel B
|
// connect pwm to pin on timer 2, channel B
|
||||||
*avr.TCCR2A |= avr.TCCR2A_COM2B1
|
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||||
*avr.OCR2B = avr.RegValue(value8) // set pwm duty
|
avr.OCR2B.Set(value8) // set pwm duty
|
||||||
case 5:
|
case 5:
|
||||||
// connect pwm to pin on timer 0, channel B
|
// connect pwm to pin on timer 0, channel B
|
||||||
*avr.TCCR0A |= avr.TCCR0A_COM0B1
|
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||||
*avr.OCR0B = avr.RegValue(value8) // set pwm duty
|
avr.OCR0B.Set(value8) // set pwm duty
|
||||||
case 6:
|
case 6:
|
||||||
// connect pwm to pin on timer 0, channel A
|
// connect pwm to pin on timer 0, channel A
|
||||||
*avr.TCCR0A |= avr.TCCR0A_COM0A1
|
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||||
*avr.OCR0A = avr.RegValue(value8) // set pwm duty
|
avr.OCR0A.Set(value8) // set pwm duty
|
||||||
case 9:
|
case 9:
|
||||||
// connect pwm to pin on timer 1, channel A
|
// connect pwm to pin on timer 1, channel A
|
||||||
*avr.TCCR1A |= avr.TCCR1A_COM1A1
|
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||||
*avr.OCR1AL = avr.RegValue(value8) // set pwm duty
|
avr.OCR1AL.Set(value8) // set pwm duty
|
||||||
case 10:
|
case 10:
|
||||||
// connect pwm to pin on timer 1, channel B
|
// connect pwm to pin on timer 1, channel B
|
||||||
*avr.TCCR1A |= avr.TCCR1A_COM1B1
|
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||||
*avr.OCR1BL = avr.RegValue(value8) // set pwm duty
|
avr.OCR1BL.Set(value8) // set pwm duty
|
||||||
case 11:
|
case 11:
|
||||||
// connect pwm to pin on timer 2, channel A
|
// connect pwm to pin on timer 2, channel A
|
||||||
*avr.TCCR2A |= avr.TCCR2A_COM2A1
|
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||||
*avr.OCR2A = avr.RegValue(value8) // set pwm duty
|
avr.OCR2A.Set(value8) // set pwm duty
|
||||||
default:
|
default:
|
||||||
panic("Invalid PWM pin")
|
panic("Invalid PWM pin")
|
||||||
}
|
}
|
||||||
@@ -121,19 +121,19 @@ func (i2c I2C) Configure(config I2CConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Activate internal pullups for twi.
|
// Activate internal pullups for twi.
|
||||||
*avr.PORTC |= (avr.DIDR0_ADC4D | avr.DIDR0_ADC5D)
|
avr.PORTC.SetBits((avr.DIDR0_ADC4D | avr.DIDR0_ADC5D))
|
||||||
|
|
||||||
// Initialize twi prescaler and bit rate.
|
// Initialize twi prescaler and bit rate.
|
||||||
*avr.TWSR |= (avr.TWSR_TWPS0 | avr.TWSR_TWPS1)
|
avr.TWSR.SetBits((avr.TWSR_TWPS0 | avr.TWSR_TWPS1))
|
||||||
|
|
||||||
// twi bit rate formula from atmega128 manual pg. 204:
|
// twi bit rate formula from atmega128 manual pg. 204:
|
||||||
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
||||||
// NOTE: TWBR should be 10 or higher for master mode.
|
// NOTE: TWBR should be 10 or higher for master mode.
|
||||||
// It is 72 for a 16mhz board with 100kHz TWI
|
// It is 72 for a 16mhz board with 100kHz TWI
|
||||||
*avr.TWBR = avr.RegValue(((CPU_FREQUENCY / config.Frequency) - 16) / 2)
|
avr.TWBR.Set(uint8(((CPU_FREQUENCY / config.Frequency) - 16) / 2))
|
||||||
|
|
||||||
// Enable twi module.
|
// Enable twi module.
|
||||||
*avr.TWCR = avr.TWCR_TWEN
|
avr.TWCR.Set(avr.TWCR_TWEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tx does a single I2C transaction at the specified address.
|
// Tx does a single I2C transaction at the specified address.
|
||||||
@@ -162,10 +162,10 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
// start starts an I2C communication session.
|
// start starts an I2C communication session.
|
||||||
func (i2c I2C) start(address uint8, write bool) {
|
func (i2c I2C) start(address uint8, write bool) {
|
||||||
// Clear TWI interrupt flag, put start condition on SDA, and enable TWI.
|
// Clear TWI interrupt flag, put start condition on SDA, and enable TWI.
|
||||||
*avr.TWCR = (avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN)
|
avr.TWCR.Set((avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN))
|
||||||
|
|
||||||
// Wait till start condition is transmitted.
|
// Wait till start condition is transmitted.
|
||||||
for (*avr.TWCR & avr.TWCR_TWINT) == 0 {
|
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write 7-bit shifted peripheral address.
|
// Write 7-bit shifted peripheral address.
|
||||||
@@ -179,36 +179,36 @@ func (i2c I2C) start(address uint8, write bool) {
|
|||||||
// stop ends an I2C communication session.
|
// stop ends an I2C communication session.
|
||||||
func (i2c I2C) stop() {
|
func (i2c I2C) stop() {
|
||||||
// Send stop condition.
|
// Send stop condition.
|
||||||
*avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO)
|
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO)
|
||||||
|
|
||||||
// Wait for stop condition to be executed on bus.
|
// Wait for stop condition to be executed on bus.
|
||||||
for (*avr.TWCR & avr.TWCR_TWSTO) == 0 {
|
for !avr.TWCR.HasBits(avr.TWCR_TWSTO) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeByte writes a single byte to the I2C bus.
|
// writeByte writes a single byte to the I2C bus.
|
||||||
func (i2c I2C) writeByte(data byte) {
|
func (i2c I2C) writeByte(data byte) {
|
||||||
// Write data to register.
|
// Write data to register.
|
||||||
*avr.TWDR = avr.RegValue(data)
|
avr.TWDR.Set(data)
|
||||||
|
|
||||||
// Clear TWI interrupt flag and enable TWI.
|
// Clear TWI interrupt flag and enable TWI.
|
||||||
*avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT)
|
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT)
|
||||||
|
|
||||||
// Wait till data is transmitted.
|
// Wait till data is transmitted.
|
||||||
for (*avr.TWCR & avr.TWCR_TWINT) == 0 {
|
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// readByte reads a single byte from the I2C bus.
|
// readByte reads a single byte from the I2C bus.
|
||||||
func (i2c I2C) readByte() byte {
|
func (i2c I2C) readByte() byte {
|
||||||
// Clear TWI interrupt flag and enable TWI.
|
// Clear TWI interrupt flag and enable TWI.
|
||||||
*avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA)
|
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA)
|
||||||
|
|
||||||
// Wait till read request is transmitted.
|
// Wait till read request is transmitted.
|
||||||
for (*avr.TWCR & avr.TWCR_TWINT) == 0 {
|
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return byte(*avr.TWDR)
|
return byte(avr.TWDR.Get())
|
||||||
}
|
}
|
||||||
|
|
||||||
// UART on the AVR.
|
// UART on the AVR.
|
||||||
@@ -226,32 +226,32 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
||||||
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
||||||
ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
||||||
*avr.UBRR0H = avr.RegValue(ps >> 8)
|
avr.UBRR0H.Set(uint8(ps >> 8))
|
||||||
*avr.UBRR0L = avr.RegValue(ps & 0xff)
|
avr.UBRR0L.Set(uint8(ps & 0xff))
|
||||||
|
|
||||||
// enable RX, TX and RX interrupt
|
// enable RX, TX and RX interrupt
|
||||||
*avr.UCSR0B = avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 | avr.UCSR0B_RXCIE0
|
avr.UCSR0B.Set(avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 | avr.UCSR0B_RXCIE0)
|
||||||
|
|
||||||
// 8-bits data
|
// 8-bits data
|
||||||
*avr.UCSR0C = avr.UCSR0C_UCSZ01 | avr.UCSR0C_UCSZ00
|
avr.UCSR0C.Set(avr.UCSR0C_UCSZ01 | avr.UCSR0C_UCSZ00)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteByte writes a byte of data to the UART.
|
// WriteByte writes a byte of data to the UART.
|
||||||
func (uart UART) WriteByte(c byte) error {
|
func (uart UART) WriteByte(c byte) error {
|
||||||
// Wait until UART buffer is not busy.
|
// Wait until UART buffer is not busy.
|
||||||
for (*avr.UCSR0A & avr.UCSR0A_UDRE0) == 0 {
|
for !avr.UCSR0A.HasBits(avr.UCSR0A_UDRE0) {
|
||||||
}
|
}
|
||||||
*avr.UDR0 = avr.RegValue(c) // send char
|
avr.UDR0.Set(c) // send char
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:interrupt USART_RX_vect
|
//go:interrupt USART_RX_vect
|
||||||
func handleUSART_RX() {
|
func handleUSART_RX() {
|
||||||
// Read register to clear it.
|
// Read register to clear it.
|
||||||
data := *avr.UDR0
|
data := avr.UDR0.Get()
|
||||||
|
|
||||||
// Ensure no error.
|
// Ensure no error.
|
||||||
if (*avr.UCSR0A & (avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0)) == 0 {
|
if !avr.UCSR0A.HasBits(avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0) {
|
||||||
// Put data from UDR register into buffer.
|
// Put data from UDR register into buffer.
|
||||||
UART0.Receive(byte(data))
|
UART0.Receive(byte(data))
|
||||||
}
|
}
|
||||||
|
|||||||
+605
-978
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,334 @@
|
|||||||
|
// +build sam,atsamd21,atsamd21e18
|
||||||
|
|
||||||
|
// Peripheral abstraction layer for the atsamd21.
|
||||||
|
//
|
||||||
|
// Datasheet:
|
||||||
|
// http://ww1.microchip.com/downloads/en/DeviceDoc/SAMD21-Family-DataSheet-DS40001882D.pdf
|
||||||
|
//
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/sam"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Return the register and mask to enable a given GPIO pin. This can be used to
|
||||||
|
// implement bit-banged drivers.
|
||||||
|
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||||
|
return &sam.PORT.OUTSET0.Reg, 1 << uint8(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the register and mask to disable a given port. This can be used to
|
||||||
|
// implement bit-banged drivers.
|
||||||
|
func (p Pin) PortMaskClear() (*uint32, uint32) {
|
||||||
|
return &sam.PORT.OUTCLR0.Reg, 1 << uint8(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the pin to high or low.
|
||||||
|
// Warning: only use this on an output pin!
|
||||||
|
func (p Pin) Set(high bool) {
|
||||||
|
if high {
|
||||||
|
sam.PORT.OUTSET0.Set(1 << uint8(p))
|
||||||
|
} else {
|
||||||
|
sam.PORT.OUTCLR0.Set(1 << uint8(p))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the current value of a GPIO pin.
|
||||||
|
func (p Pin) Get() bool {
|
||||||
|
return (sam.PORT.IN0.Get()>>uint8(p))&1 > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure this pin with the given configuration.
|
||||||
|
func (p Pin) Configure(config PinConfig) {
|
||||||
|
switch config.Mode {
|
||||||
|
case PinOutput:
|
||||||
|
sam.PORT.DIRSET0.Set(1 << uint8(p))
|
||||||
|
// output is also set to input enable so pin can read back its own value
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||||
|
|
||||||
|
case PinInput:
|
||||||
|
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||||
|
|
||||||
|
case PinInputPulldown:
|
||||||
|
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||||
|
sam.PORT.OUTCLR0.Set(1 << uint8(p))
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||||
|
|
||||||
|
case PinInputPullup:
|
||||||
|
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||||
|
sam.PORT.OUTSET0.Set(1 << uint8(p))
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||||
|
|
||||||
|
case PinSERCOM:
|
||||||
|
if uint8(p)&1 > 0 {
|
||||||
|
// odd pin, so save the even pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||||
|
p.setPMux(val | (uint8(PinSERCOM) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||||
|
} else {
|
||||||
|
// even pin, so save the odd pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||||
|
p.setPMux(val | (uint8(PinSERCOM) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||||
|
}
|
||||||
|
// enable port config
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR | sam.PORT_PINCFG0_INEN)
|
||||||
|
|
||||||
|
case PinSERCOMAlt:
|
||||||
|
if uint8(p)&1 > 0 {
|
||||||
|
// odd pin, so save the even pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||||
|
p.setPMux(val | (uint8(PinSERCOMAlt) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||||
|
} else {
|
||||||
|
// even pin, so save the odd pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||||
|
p.setPMux(val | (uint8(PinSERCOMAlt) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||||
|
}
|
||||||
|
// enable port config
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)
|
||||||
|
|
||||||
|
case PinCom:
|
||||||
|
if uint8(p)&1 > 0 {
|
||||||
|
// odd pin, so save the even pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||||
|
p.setPMux(val | (uint8(PinCom) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||||
|
} else {
|
||||||
|
// even pin, so save the odd pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||||
|
p.setPMux(val | (uint8(PinCom) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||||
|
}
|
||||||
|
// enable port config
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
||||||
|
case PinAnalog:
|
||||||
|
if uint8(p)&1 > 0 {
|
||||||
|
// odd pin, so save the even pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||||
|
p.setPMux(val | (uint8(PinAnalog) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||||
|
} else {
|
||||||
|
// even pin, so save the odd pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||||
|
p.setPMux(val | (uint8(PinAnalog) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||||
|
}
|
||||||
|
// enable port config
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPMux returns the value for the correct PMUX register for this pin.
|
||||||
|
func (p Pin) getPMux() uint8 {
|
||||||
|
switch p >> 1 {
|
||||||
|
case 0:
|
||||||
|
return sam.PORT.PMUX0_0.Get()
|
||||||
|
case 1:
|
||||||
|
return sam.PORT.PMUX0_1.Get()
|
||||||
|
case 2:
|
||||||
|
return sam.PORT.PMUX0_2.Get()
|
||||||
|
case 3:
|
||||||
|
return sam.PORT.PMUX0_3.Get()
|
||||||
|
case 4:
|
||||||
|
return sam.PORT.PMUX0_4.Get()
|
||||||
|
case 5:
|
||||||
|
return sam.PORT.PMUX0_5.Get()
|
||||||
|
case 6:
|
||||||
|
return sam.PORT.PMUX0_6.Get()
|
||||||
|
case 7:
|
||||||
|
return sam.PORT.PMUX0_7.Get()
|
||||||
|
case 8:
|
||||||
|
return sam.PORT.PMUX0_8.Get()
|
||||||
|
case 9:
|
||||||
|
return sam.PORT.PMUX0_9.Get()
|
||||||
|
case 10:
|
||||||
|
return sam.PORT.PMUX0_10.Get()
|
||||||
|
case 11:
|
||||||
|
return sam.PORT.PMUX0_11.Get()
|
||||||
|
case 12:
|
||||||
|
return sam.PORT.PMUX0_12.Get()
|
||||||
|
case 13:
|
||||||
|
return sam.PORT.PMUX0_13.Get()
|
||||||
|
case 14:
|
||||||
|
return sam.PORT.PMUX0_14.Get()
|
||||||
|
case 15:
|
||||||
|
return sam.PORT.PMUX0_15.Get()
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPMux sets the value for the correct PMUX register for this pin.
|
||||||
|
func (p Pin) setPMux(val uint8) {
|
||||||
|
switch p >> 1 {
|
||||||
|
case 0:
|
||||||
|
sam.PORT.PMUX0_0.Set(val)
|
||||||
|
case 1:
|
||||||
|
sam.PORT.PMUX0_1.Set(val)
|
||||||
|
case 2:
|
||||||
|
sam.PORT.PMUX0_2.Set(val)
|
||||||
|
case 3:
|
||||||
|
sam.PORT.PMUX0_3.Set(val)
|
||||||
|
case 4:
|
||||||
|
sam.PORT.PMUX0_4.Set(val)
|
||||||
|
case 5:
|
||||||
|
sam.PORT.PMUX0_5.Set(val)
|
||||||
|
case 6:
|
||||||
|
sam.PORT.PMUX0_6.Set(val)
|
||||||
|
case 7:
|
||||||
|
sam.PORT.PMUX0_7.Set(val)
|
||||||
|
case 8:
|
||||||
|
sam.PORT.PMUX0_8.Set(val)
|
||||||
|
case 9:
|
||||||
|
sam.PORT.PMUX0_9.Set(val)
|
||||||
|
case 10:
|
||||||
|
sam.PORT.PMUX0_10.Set(val)
|
||||||
|
case 11:
|
||||||
|
sam.PORT.PMUX0_11.Set(val)
|
||||||
|
case 12:
|
||||||
|
sam.PORT.PMUX0_12.Set(val)
|
||||||
|
case 13:
|
||||||
|
sam.PORT.PMUX0_13.Set(val)
|
||||||
|
case 14:
|
||||||
|
sam.PORT.PMUX0_14.Set(val)
|
||||||
|
case 15:
|
||||||
|
sam.PORT.PMUX0_15.Set(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||||
|
func (p Pin) getPinCfg() uint8 {
|
||||||
|
switch p {
|
||||||
|
case 0:
|
||||||
|
return sam.PORT.PINCFG0_0.Get()
|
||||||
|
case 1:
|
||||||
|
return sam.PORT.PINCFG0_1.Get()
|
||||||
|
case 2:
|
||||||
|
return sam.PORT.PINCFG0_2.Get()
|
||||||
|
case 3:
|
||||||
|
return sam.PORT.PINCFG0_3.Get()
|
||||||
|
case 4:
|
||||||
|
return sam.PORT.PINCFG0_4.Get()
|
||||||
|
case 5:
|
||||||
|
return sam.PORT.PINCFG0_5.Get()
|
||||||
|
case 6:
|
||||||
|
return sam.PORT.PINCFG0_6.Get()
|
||||||
|
case 7:
|
||||||
|
return sam.PORT.PINCFG0_7.Get()
|
||||||
|
case 8:
|
||||||
|
return sam.PORT.PINCFG0_8.Get()
|
||||||
|
case 9:
|
||||||
|
return sam.PORT.PINCFG0_9.Get()
|
||||||
|
case 10:
|
||||||
|
return sam.PORT.PINCFG0_10.Get()
|
||||||
|
case 11:
|
||||||
|
return sam.PORT.PINCFG0_11.Get()
|
||||||
|
case 12:
|
||||||
|
return sam.PORT.PINCFG0_12.Get()
|
||||||
|
case 13:
|
||||||
|
return sam.PORT.PINCFG0_13.Get()
|
||||||
|
case 14:
|
||||||
|
return sam.PORT.PINCFG0_14.Get()
|
||||||
|
case 15:
|
||||||
|
return sam.PORT.PINCFG0_15.Get()
|
||||||
|
case 16:
|
||||||
|
return sam.PORT.PINCFG0_16.Get()
|
||||||
|
case 17:
|
||||||
|
return sam.PORT.PINCFG0_17.Get()
|
||||||
|
case 18:
|
||||||
|
return sam.PORT.PINCFG0_18.Get()
|
||||||
|
case 19:
|
||||||
|
return sam.PORT.PINCFG0_19.Get()
|
||||||
|
case 20:
|
||||||
|
return sam.PORT.PINCFG0_20.Get()
|
||||||
|
case 21:
|
||||||
|
return sam.PORT.PINCFG0_21.Get()
|
||||||
|
case 22:
|
||||||
|
return sam.PORT.PINCFG0_22.Get()
|
||||||
|
case 23:
|
||||||
|
return sam.PORT.PINCFG0_23.Get()
|
||||||
|
case 24:
|
||||||
|
return sam.PORT.PINCFG0_24.Get()
|
||||||
|
case 25:
|
||||||
|
return sam.PORT.PINCFG0_25.Get()
|
||||||
|
case 26:
|
||||||
|
return sam.PORT.PINCFG0_26.Get()
|
||||||
|
case 27:
|
||||||
|
return sam.PORT.PINCFG0_27.Get()
|
||||||
|
case 28:
|
||||||
|
return sam.PORT.PINCFG0_28.Get()
|
||||||
|
case 29:
|
||||||
|
return sam.PORT.PINCFG0_29.Get()
|
||||||
|
case 30:
|
||||||
|
return sam.PORT.PINCFG0_30.Get()
|
||||||
|
case 31:
|
||||||
|
return sam.PORT.PINCFG0_31.Get()
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||||
|
func (p Pin) setPinCfg(val uint8) {
|
||||||
|
switch p {
|
||||||
|
case 0:
|
||||||
|
sam.PORT.PINCFG0_0.Set(val)
|
||||||
|
case 1:
|
||||||
|
sam.PORT.PINCFG0_1.Set(val)
|
||||||
|
case 2:
|
||||||
|
sam.PORT.PINCFG0_2.Set(val)
|
||||||
|
case 3:
|
||||||
|
sam.PORT.PINCFG0_3.Set(val)
|
||||||
|
case 4:
|
||||||
|
sam.PORT.PINCFG0_4.Set(val)
|
||||||
|
case 5:
|
||||||
|
sam.PORT.PINCFG0_5.Set(val)
|
||||||
|
case 6:
|
||||||
|
sam.PORT.PINCFG0_6.Set(val)
|
||||||
|
case 7:
|
||||||
|
sam.PORT.PINCFG0_7.Set(val)
|
||||||
|
case 8:
|
||||||
|
sam.PORT.PINCFG0_8.Set(val)
|
||||||
|
case 9:
|
||||||
|
sam.PORT.PINCFG0_9.Set(val)
|
||||||
|
case 10:
|
||||||
|
sam.PORT.PINCFG0_10.Set(val)
|
||||||
|
case 11:
|
||||||
|
sam.PORT.PINCFG0_11.Set(val)
|
||||||
|
case 12:
|
||||||
|
sam.PORT.PINCFG0_12.Set(val)
|
||||||
|
case 13:
|
||||||
|
sam.PORT.PINCFG0_13.Set(val)
|
||||||
|
case 14:
|
||||||
|
sam.PORT.PINCFG0_14.Set(val)
|
||||||
|
case 15:
|
||||||
|
sam.PORT.PINCFG0_15.Set(val)
|
||||||
|
case 16:
|
||||||
|
sam.PORT.PINCFG0_16.Set(val)
|
||||||
|
case 17:
|
||||||
|
sam.PORT.PINCFG0_17.Set(val)
|
||||||
|
case 18:
|
||||||
|
sam.PORT.PINCFG0_18.Set(val)
|
||||||
|
case 19:
|
||||||
|
sam.PORT.PINCFG0_19.Set(val)
|
||||||
|
case 20:
|
||||||
|
sam.PORT.PINCFG0_20.Set(val)
|
||||||
|
case 21:
|
||||||
|
sam.PORT.PINCFG0_21.Set(val)
|
||||||
|
case 22:
|
||||||
|
sam.PORT.PINCFG0_22.Set(val)
|
||||||
|
case 23:
|
||||||
|
sam.PORT.PINCFG0_23.Set(val)
|
||||||
|
case 24:
|
||||||
|
sam.PORT.PINCFG0_24.Set(val)
|
||||||
|
case 25:
|
||||||
|
sam.PORT.PINCFG0_25.Set(val)
|
||||||
|
case 26:
|
||||||
|
sam.PORT.PINCFG0_26.Set(val)
|
||||||
|
case 27:
|
||||||
|
sam.PORT.PINCFG0_27.Set(val)
|
||||||
|
case 28:
|
||||||
|
sam.PORT.PINCFG0_28.Set(val)
|
||||||
|
case 29:
|
||||||
|
sam.PORT.PINCFG0_29.Set(val)
|
||||||
|
case 30:
|
||||||
|
sam.PORT.PINCFG0_30.Set(val)
|
||||||
|
case 31:
|
||||||
|
sam.PORT.PINCFG0_31.Set(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,569 @@
|
|||||||
|
// +build sam,atsamd21,atsamd21g18
|
||||||
|
|
||||||
|
// Peripheral abstraction layer for the atsamd21.
|
||||||
|
//
|
||||||
|
// Datasheet:
|
||||||
|
// http://ww1.microchip.com/downloads/en/DeviceDoc/SAMD21-Family-DataSheet-DS40001882D.pdf
|
||||||
|
//
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/sam"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Return the register and mask to enable a given GPIO pin. This can be used to
|
||||||
|
// implement bit-banged drivers.
|
||||||
|
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||||
|
if p < 32 {
|
||||||
|
return &sam.PORT.OUTSET0.Reg, 1 << uint8(p)
|
||||||
|
} else {
|
||||||
|
return &sam.PORT.OUTSET1.Reg, 1 << uint8(p-32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the register and mask to disable a given port. This can be used to
|
||||||
|
// implement bit-banged drivers.
|
||||||
|
func (p Pin) PortMaskClear() (*uint32, uint32) {
|
||||||
|
if p < 32 {
|
||||||
|
return &sam.PORT.OUTCLR0.Reg, 1 << uint8(p)
|
||||||
|
} else {
|
||||||
|
return &sam.PORT.OUTCLR1.Reg, 1 << uint8(p-32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the pin to high or low.
|
||||||
|
// Warning: only use this on an output pin!
|
||||||
|
func (p Pin) Set(high bool) {
|
||||||
|
if p < 32 {
|
||||||
|
if high {
|
||||||
|
sam.PORT.OUTSET0.Set(1 << uint8(p))
|
||||||
|
} else {
|
||||||
|
sam.PORT.OUTCLR0.Set(1 << uint8(p))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if high {
|
||||||
|
sam.PORT.OUTSET1.Set(1 << uint8(p-32))
|
||||||
|
} else {
|
||||||
|
sam.PORT.OUTCLR1.Set(1 << uint8(p-32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the current value of a GPIO pin.
|
||||||
|
func (p Pin) Get() bool {
|
||||||
|
if p < 32 {
|
||||||
|
return (sam.PORT.IN0.Get()>>uint8(p))&1 > 0
|
||||||
|
} else {
|
||||||
|
return (sam.PORT.IN1.Get()>>(uint8(p)-32))&1 > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure this pin with the given configuration.
|
||||||
|
func (p Pin) Configure(config PinConfig) {
|
||||||
|
switch config.Mode {
|
||||||
|
case PinOutput:
|
||||||
|
if p < 32 {
|
||||||
|
sam.PORT.DIRSET0.Set(1 << uint8(p))
|
||||||
|
// output is also set to input enable so pin can read back its own value
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||||
|
} else {
|
||||||
|
sam.PORT.DIRSET1.Set(1 << uint8(p-32))
|
||||||
|
// output is also set to input enable so pin can read back its own value
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||||
|
}
|
||||||
|
|
||||||
|
case PinInput:
|
||||||
|
if p < 32 {
|
||||||
|
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||||
|
} else {
|
||||||
|
sam.PORT.DIRCLR1.Set(1<<uint8(p) - 32)
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_INEN)
|
||||||
|
}
|
||||||
|
|
||||||
|
case PinInputPulldown:
|
||||||
|
if p < 32 {
|
||||||
|
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||||
|
sam.PORT.OUTCLR0.Set(1 << uint8(p))
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||||
|
} else {
|
||||||
|
sam.PORT.DIRCLR1.Set(1<<uint8(p) - 32)
|
||||||
|
sam.PORT.OUTCLR1.Set(1<<uint8(p) - 32)
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||||
|
}
|
||||||
|
|
||||||
|
case PinInputPullup:
|
||||||
|
if p < 32 {
|
||||||
|
sam.PORT.DIRCLR0.Set(1 << uint8(p))
|
||||||
|
sam.PORT.OUTSET0.Set(1 << uint8(p))
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||||
|
} else {
|
||||||
|
sam.PORT.DIRCLR1.Set(1<<uint8(p) - 32)
|
||||||
|
sam.PORT.OUTSET1.Set(1<<uint8(p) - 32)
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)
|
||||||
|
}
|
||||||
|
|
||||||
|
case PinSERCOM:
|
||||||
|
if p&1 > 0 {
|
||||||
|
// odd pin, so save the even pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||||
|
p.setPMux(val | (uint8(PinSERCOM) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||||
|
} else {
|
||||||
|
// even pin, so save the odd pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||||
|
p.setPMux(val | (uint8(PinSERCOM) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||||
|
}
|
||||||
|
// enable port config
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR | sam.PORT_PINCFG0_INEN)
|
||||||
|
|
||||||
|
case PinSERCOMAlt:
|
||||||
|
if p&1 > 0 {
|
||||||
|
// odd pin, so save the even pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||||
|
p.setPMux(val | (uint8(PinSERCOMAlt) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||||
|
} else {
|
||||||
|
// even pin, so save the odd pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||||
|
p.setPMux(val | (uint8(PinSERCOMAlt) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||||
|
}
|
||||||
|
// enable port config
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)
|
||||||
|
|
||||||
|
case PinCom:
|
||||||
|
if p&1 > 0 {
|
||||||
|
// odd pin, so save the even pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||||
|
p.setPMux(val | (uint8(PinCom) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||||
|
} else {
|
||||||
|
// even pin, so save the odd pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||||
|
p.setPMux(val | (uint8(PinCom) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||||
|
}
|
||||||
|
// enable port config
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN)
|
||||||
|
case PinAnalog:
|
||||||
|
if p&1 > 0 {
|
||||||
|
// odd pin, so save the even pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
|
||||||
|
p.setPMux(val | (uint8(PinAnalog) << sam.PORT_PMUX0_PMUXO_Pos))
|
||||||
|
} else {
|
||||||
|
// even pin, so save the odd pins
|
||||||
|
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
|
||||||
|
p.setPMux(val | (uint8(PinAnalog) << sam.PORT_PMUX0_PMUXE_Pos))
|
||||||
|
}
|
||||||
|
// enable port config
|
||||||
|
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPMux returns the value for the correct PMUX register for this pin.
|
||||||
|
func (p Pin) getPMux() uint8 {
|
||||||
|
switch uint8(p) >> 1 {
|
||||||
|
case 0:
|
||||||
|
return sam.PORT.PMUX0_0.Get()
|
||||||
|
case 1:
|
||||||
|
return sam.PORT.PMUX0_1.Get()
|
||||||
|
case 2:
|
||||||
|
return sam.PORT.PMUX0_2.Get()
|
||||||
|
case 3:
|
||||||
|
return sam.PORT.PMUX0_3.Get()
|
||||||
|
case 4:
|
||||||
|
return sam.PORT.PMUX0_4.Get()
|
||||||
|
case 5:
|
||||||
|
return sam.PORT.PMUX0_5.Get()
|
||||||
|
case 6:
|
||||||
|
return sam.PORT.PMUX0_6.Get()
|
||||||
|
case 7:
|
||||||
|
return sam.PORT.PMUX0_7.Get()
|
||||||
|
case 8:
|
||||||
|
return sam.PORT.PMUX0_8.Get()
|
||||||
|
case 9:
|
||||||
|
return sam.PORT.PMUX0_9.Get()
|
||||||
|
case 10:
|
||||||
|
return sam.PORT.PMUX0_10.Get()
|
||||||
|
case 11:
|
||||||
|
return sam.PORT.PMUX0_11.Get()
|
||||||
|
case 12:
|
||||||
|
return sam.PORT.PMUX0_12.Get()
|
||||||
|
case 13:
|
||||||
|
return sam.PORT.PMUX0_13.Get()
|
||||||
|
case 14:
|
||||||
|
return sam.PORT.PMUX0_14.Get()
|
||||||
|
case 15:
|
||||||
|
return sam.PORT.PMUX0_15.Get()
|
||||||
|
case 16:
|
||||||
|
return uint8(sam.PORT.PMUX1_0.Get()>>0) & 0xff
|
||||||
|
case 17:
|
||||||
|
return uint8(sam.PORT.PMUX1_0.Get()>>8) & 0xff
|
||||||
|
case 18:
|
||||||
|
return uint8(sam.PORT.PMUX1_0.Get()>>16) & 0xff
|
||||||
|
case 19:
|
||||||
|
return uint8(sam.PORT.PMUX1_0.Get()>>24) & 0xff
|
||||||
|
case 20:
|
||||||
|
return uint8(sam.PORT.PMUX1_4.Get()>>0) & 0xff
|
||||||
|
case 21:
|
||||||
|
return uint8(sam.PORT.PMUX1_4.Get()>>8) & 0xff
|
||||||
|
case 22:
|
||||||
|
return uint8(sam.PORT.PMUX1_4.Get()>>16) & 0xff
|
||||||
|
case 23:
|
||||||
|
return uint8(sam.PORT.PMUX1_4.Get()>>24) & 0xff
|
||||||
|
case 24:
|
||||||
|
return uint8(sam.PORT.PMUX1_8.Get()>>0) & 0xff
|
||||||
|
case 25:
|
||||||
|
return uint8(sam.PORT.PMUX1_8.Get()>>8) & 0xff
|
||||||
|
case 26:
|
||||||
|
return uint8(sam.PORT.PMUX1_8.Get()>>16) & 0xff
|
||||||
|
case 27:
|
||||||
|
return uint8(sam.PORT.PMUX1_8.Get()>>24) & 0xff
|
||||||
|
case 28:
|
||||||
|
return uint8(sam.PORT.PMUX1_12.Get()>>0) & 0xff
|
||||||
|
case 29:
|
||||||
|
return uint8(sam.PORT.PMUX1_12.Get()>>8) & 0xff
|
||||||
|
case 30:
|
||||||
|
return uint8(sam.PORT.PMUX1_12.Get()>>16) & 0xff
|
||||||
|
case 31:
|
||||||
|
return uint8(sam.PORT.PMUX1_12.Get()>>24) & 0xff
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPMux sets the value for the correct PMUX register for this pin.
|
||||||
|
func (p Pin) setPMux(val uint8) {
|
||||||
|
switch uint8(p) >> 1 {
|
||||||
|
case 0:
|
||||||
|
sam.PORT.PMUX0_0.Set(val)
|
||||||
|
case 1:
|
||||||
|
sam.PORT.PMUX0_1.Set(val)
|
||||||
|
case 2:
|
||||||
|
sam.PORT.PMUX0_2.Set(val)
|
||||||
|
case 3:
|
||||||
|
sam.PORT.PMUX0_3.Set(val)
|
||||||
|
case 4:
|
||||||
|
sam.PORT.PMUX0_4.Set(val)
|
||||||
|
case 5:
|
||||||
|
sam.PORT.PMUX0_5.Set(val)
|
||||||
|
case 6:
|
||||||
|
sam.PORT.PMUX0_6.Set(val)
|
||||||
|
case 7:
|
||||||
|
sam.PORT.PMUX0_7.Set(val)
|
||||||
|
case 8:
|
||||||
|
sam.PORT.PMUX0_8.Set(val)
|
||||||
|
case 9:
|
||||||
|
sam.PORT.PMUX0_9.Set(val)
|
||||||
|
case 10:
|
||||||
|
sam.PORT.PMUX0_10.Set(val)
|
||||||
|
case 11:
|
||||||
|
sam.PORT.PMUX0_11.Set(val)
|
||||||
|
case 12:
|
||||||
|
sam.PORT.PMUX0_12.Set(val)
|
||||||
|
case 13:
|
||||||
|
sam.PORT.PMUX0_13.Set(val)
|
||||||
|
case 14:
|
||||||
|
sam.PORT.PMUX0_14.Set(val)
|
||||||
|
case 15:
|
||||||
|
sam.PORT.PMUX0_15.Set(val)
|
||||||
|
case 16:
|
||||||
|
sam.PORT.PMUX1_0.Set(sam.PORT.PMUX1_0.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||||
|
case 17:
|
||||||
|
sam.PORT.PMUX1_0.Set(sam.PORT.PMUX1_0.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||||
|
case 18:
|
||||||
|
sam.PORT.PMUX1_0.Set(sam.PORT.PMUX1_0.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||||
|
case 19:
|
||||||
|
sam.PORT.PMUX1_0.Set(sam.PORT.PMUX1_0.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||||
|
case 20:
|
||||||
|
sam.PORT.PMUX1_4.Set(sam.PORT.PMUX1_4.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||||
|
case 21:
|
||||||
|
sam.PORT.PMUX1_4.Set(sam.PORT.PMUX1_4.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||||
|
case 22:
|
||||||
|
sam.PORT.PMUX1_4.Set(sam.PORT.PMUX1_4.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||||
|
case 23:
|
||||||
|
sam.PORT.PMUX1_4.Set(sam.PORT.PMUX1_4.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||||
|
case 24:
|
||||||
|
sam.PORT.PMUX1_8.Set(sam.PORT.PMUX1_8.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||||
|
case 25:
|
||||||
|
sam.PORT.PMUX1_8.Set(sam.PORT.PMUX1_8.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||||
|
case 26:
|
||||||
|
sam.PORT.PMUX1_8.Set(sam.PORT.PMUX1_8.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||||
|
case 27:
|
||||||
|
sam.PORT.PMUX1_8.Set(sam.PORT.PMUX1_8.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||||
|
case 28:
|
||||||
|
sam.PORT.PMUX1_12.Set(sam.PORT.PMUX1_12.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||||
|
case 29:
|
||||||
|
sam.PORT.PMUX1_12.Set(sam.PORT.PMUX1_12.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||||
|
case 30:
|
||||||
|
sam.PORT.PMUX1_12.Set(sam.PORT.PMUX1_12.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||||
|
case 31:
|
||||||
|
sam.PORT.PMUX1_12.Set(sam.PORT.PMUX1_12.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPinCfg returns the value for the correct PINCFG register for this pin.
|
||||||
|
func (p Pin) getPinCfg() uint8 {
|
||||||
|
switch p {
|
||||||
|
case 0:
|
||||||
|
return sam.PORT.PINCFG0_0.Get()
|
||||||
|
case 1:
|
||||||
|
return sam.PORT.PINCFG0_1.Get()
|
||||||
|
case 2:
|
||||||
|
return sam.PORT.PINCFG0_2.Get()
|
||||||
|
case 3:
|
||||||
|
return sam.PORT.PINCFG0_3.Get()
|
||||||
|
case 4:
|
||||||
|
return sam.PORT.PINCFG0_4.Get()
|
||||||
|
case 5:
|
||||||
|
return sam.PORT.PINCFG0_5.Get()
|
||||||
|
case 6:
|
||||||
|
return sam.PORT.PINCFG0_6.Get()
|
||||||
|
case 7:
|
||||||
|
return sam.PORT.PINCFG0_7.Get()
|
||||||
|
case 8:
|
||||||
|
return sam.PORT.PINCFG0_8.Get()
|
||||||
|
case 9:
|
||||||
|
return sam.PORT.PINCFG0_9.Get()
|
||||||
|
case 10:
|
||||||
|
return sam.PORT.PINCFG0_10.Get()
|
||||||
|
case 11:
|
||||||
|
return sam.PORT.PINCFG0_11.Get()
|
||||||
|
case 12:
|
||||||
|
return sam.PORT.PINCFG0_12.Get()
|
||||||
|
case 13:
|
||||||
|
return sam.PORT.PINCFG0_13.Get()
|
||||||
|
case 14:
|
||||||
|
return sam.PORT.PINCFG0_14.Get()
|
||||||
|
case 15:
|
||||||
|
return sam.PORT.PINCFG0_15.Get()
|
||||||
|
case 16:
|
||||||
|
return sam.PORT.PINCFG0_16.Get()
|
||||||
|
case 17:
|
||||||
|
return sam.PORT.PINCFG0_17.Get()
|
||||||
|
case 18:
|
||||||
|
return sam.PORT.PINCFG0_18.Get()
|
||||||
|
case 19:
|
||||||
|
return sam.PORT.PINCFG0_19.Get()
|
||||||
|
case 20:
|
||||||
|
return sam.PORT.PINCFG0_20.Get()
|
||||||
|
case 21:
|
||||||
|
return sam.PORT.PINCFG0_21.Get()
|
||||||
|
case 22:
|
||||||
|
return sam.PORT.PINCFG0_22.Get()
|
||||||
|
case 23:
|
||||||
|
return sam.PORT.PINCFG0_23.Get()
|
||||||
|
case 24:
|
||||||
|
return sam.PORT.PINCFG0_24.Get()
|
||||||
|
case 25:
|
||||||
|
return sam.PORT.PINCFG0_25.Get()
|
||||||
|
case 26:
|
||||||
|
return sam.PORT.PINCFG0_26.Get()
|
||||||
|
case 27:
|
||||||
|
return sam.PORT.PINCFG0_27.Get()
|
||||||
|
case 28:
|
||||||
|
return sam.PORT.PINCFG0_28.Get()
|
||||||
|
case 29:
|
||||||
|
return sam.PORT.PINCFG0_29.Get()
|
||||||
|
case 30:
|
||||||
|
return sam.PORT.PINCFG0_30.Get()
|
||||||
|
case 31:
|
||||||
|
return sam.PORT.PINCFG0_31.Get()
|
||||||
|
case 32: // PB00
|
||||||
|
return uint8(sam.PORT.PINCFG1_0.Get()>>0) & 0xff
|
||||||
|
case 33: // PB01
|
||||||
|
return uint8(sam.PORT.PINCFG1_0.Get()>>8) & 0xff
|
||||||
|
case 34: // PB02
|
||||||
|
return uint8(sam.PORT.PINCFG1_0.Get()>>16) & 0xff
|
||||||
|
case 35: // PB03
|
||||||
|
return uint8(sam.PORT.PINCFG1_0.Get()>>24) & 0xff
|
||||||
|
case 37: // PB04
|
||||||
|
return uint8(sam.PORT.PINCFG1_4.Get()>>0) & 0xff
|
||||||
|
case 38: // PB05
|
||||||
|
return uint8(sam.PORT.PINCFG1_4.Get()>>8) & 0xff
|
||||||
|
case 39: // PB06
|
||||||
|
return uint8(sam.PORT.PINCFG1_4.Get()>>16) & 0xff
|
||||||
|
case 40: // PB07
|
||||||
|
return uint8(sam.PORT.PINCFG1_4.Get()>>24) & 0xff
|
||||||
|
case 41: // PB08
|
||||||
|
return uint8(sam.PORT.PINCFG1_8.Get()>>0) & 0xff
|
||||||
|
case 42: // PB09
|
||||||
|
return uint8(sam.PORT.PINCFG1_8.Get()>>8) & 0xff
|
||||||
|
case 43: // PB10
|
||||||
|
return uint8(sam.PORT.PINCFG1_8.Get()>>16) & 0xff
|
||||||
|
case 44: // PB11
|
||||||
|
return uint8(sam.PORT.PINCFG1_8.Get()>>24) & 0xff
|
||||||
|
case 45: // PB12
|
||||||
|
return uint8(sam.PORT.PINCFG1_12.Get()>>0) & 0xff
|
||||||
|
case 46: // PB13
|
||||||
|
return uint8(sam.PORT.PINCFG1_12.Get()>>8) & 0xff
|
||||||
|
case 47: // PB14
|
||||||
|
return uint8(sam.PORT.PINCFG1_12.Get()>>16) & 0xff
|
||||||
|
case 48: // PB15
|
||||||
|
return uint8(sam.PORT.PINCFG1_12.Get()>>24) & 0xff
|
||||||
|
case 49: // PB16
|
||||||
|
return uint8(sam.PORT.PINCFG1_16.Get()>>0) & 0xff
|
||||||
|
case 50: // PB17
|
||||||
|
return uint8(sam.PORT.PINCFG1_16.Get()>>8) & 0xff
|
||||||
|
case 51: // PB18
|
||||||
|
return uint8(sam.PORT.PINCFG1_16.Get()>>16) & 0xff
|
||||||
|
case 52: // PB19
|
||||||
|
return uint8(sam.PORT.PINCFG1_16.Get()>>24) & 0xff
|
||||||
|
case 53: // PB20
|
||||||
|
return uint8(sam.PORT.PINCFG1_20.Get()>>0) & 0xff
|
||||||
|
case 54: // PB21
|
||||||
|
return uint8(sam.PORT.PINCFG1_20.Get()>>8) & 0xff
|
||||||
|
case 55: // PB22
|
||||||
|
return uint8(sam.PORT.PINCFG1_20.Get()>>16) & 0xff
|
||||||
|
case 56: // PB23
|
||||||
|
return uint8(sam.PORT.PINCFG1_20.Get()>>24) & 0xff
|
||||||
|
case 57: // PB24
|
||||||
|
return uint8(sam.PORT.PINCFG1_24.Get()>>0) & 0xff
|
||||||
|
case 58: // PB25
|
||||||
|
return uint8(sam.PORT.PINCFG1_24.Get()>>8) & 0xff
|
||||||
|
case 59: // PB26
|
||||||
|
return uint8(sam.PORT.PINCFG1_24.Get()>>16) & 0xff
|
||||||
|
case 60: // PB27
|
||||||
|
return uint8(sam.PORT.PINCFG1_24.Get()>>24) & 0xff
|
||||||
|
case 61: // PB28
|
||||||
|
return uint8(sam.PORT.PINCFG1_28.Get()>>0) & 0xff
|
||||||
|
case 62: // PB29
|
||||||
|
return uint8(sam.PORT.PINCFG1_28.Get()>>8) & 0xff
|
||||||
|
case 63: // PB30
|
||||||
|
return uint8(sam.PORT.PINCFG1_28.Get()>>16) & 0xff
|
||||||
|
case 64: // PB31
|
||||||
|
return uint8(sam.PORT.PINCFG1_28.Get()>>24) & 0xff
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPinCfg sets the value for the correct PINCFG register for this pin.
|
||||||
|
func (p Pin) setPinCfg(val uint8) {
|
||||||
|
switch p {
|
||||||
|
case 0:
|
||||||
|
sam.PORT.PINCFG0_0.Set(val)
|
||||||
|
case 1:
|
||||||
|
sam.PORT.PINCFG0_1.Set(val)
|
||||||
|
case 2:
|
||||||
|
sam.PORT.PINCFG0_2.Set(val)
|
||||||
|
case 3:
|
||||||
|
sam.PORT.PINCFG0_3.Set(val)
|
||||||
|
case 4:
|
||||||
|
sam.PORT.PINCFG0_4.Set(val)
|
||||||
|
case 5:
|
||||||
|
sam.PORT.PINCFG0_5.Set(val)
|
||||||
|
case 6:
|
||||||
|
sam.PORT.PINCFG0_6.Set(val)
|
||||||
|
case 7:
|
||||||
|
sam.PORT.PINCFG0_7.Set(val)
|
||||||
|
case 8:
|
||||||
|
sam.PORT.PINCFG0_8.Set(val)
|
||||||
|
case 9:
|
||||||
|
sam.PORT.PINCFG0_9.Set(val)
|
||||||
|
case 10:
|
||||||
|
sam.PORT.PINCFG0_10.Set(val)
|
||||||
|
case 11:
|
||||||
|
sam.PORT.PINCFG0_11.Set(val)
|
||||||
|
case 12:
|
||||||
|
sam.PORT.PINCFG0_12.Set(val)
|
||||||
|
case 13:
|
||||||
|
sam.PORT.PINCFG0_13.Set(val)
|
||||||
|
case 14:
|
||||||
|
sam.PORT.PINCFG0_14.Set(val)
|
||||||
|
case 15:
|
||||||
|
sam.PORT.PINCFG0_15.Set(val)
|
||||||
|
case 16:
|
||||||
|
sam.PORT.PINCFG0_16.Set(val)
|
||||||
|
case 17:
|
||||||
|
sam.PORT.PINCFG0_17.Set(val)
|
||||||
|
case 18:
|
||||||
|
sam.PORT.PINCFG0_18.Set(val)
|
||||||
|
case 19:
|
||||||
|
sam.PORT.PINCFG0_19.Set(val)
|
||||||
|
case 20:
|
||||||
|
sam.PORT.PINCFG0_20.Set(val)
|
||||||
|
case 21:
|
||||||
|
sam.PORT.PINCFG0_21.Set(val)
|
||||||
|
case 22:
|
||||||
|
sam.PORT.PINCFG0_22.Set(val)
|
||||||
|
case 23:
|
||||||
|
sam.PORT.PINCFG0_23.Set(val)
|
||||||
|
case 24:
|
||||||
|
sam.PORT.PINCFG0_24.Set(val)
|
||||||
|
case 25:
|
||||||
|
sam.PORT.PINCFG0_25.Set(val)
|
||||||
|
case 26:
|
||||||
|
sam.PORT.PINCFG0_26.Set(val)
|
||||||
|
case 27:
|
||||||
|
sam.PORT.PINCFG0_27.Set(val)
|
||||||
|
case 28:
|
||||||
|
sam.PORT.PINCFG0_28.Set(val)
|
||||||
|
case 29:
|
||||||
|
sam.PORT.PINCFG0_29.Set(val)
|
||||||
|
case 30:
|
||||||
|
sam.PORT.PINCFG0_30.Set(val)
|
||||||
|
case 31:
|
||||||
|
sam.PORT.PINCFG0_31.Set(val)
|
||||||
|
case 32: // PB00
|
||||||
|
sam.PORT.PINCFG1_0.Set(sam.PORT.PINCFG1_0.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||||
|
case 33: // PB01
|
||||||
|
sam.PORT.PINCFG1_0.Set(sam.PORT.PINCFG1_0.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||||
|
case 34: // PB02
|
||||||
|
sam.PORT.PINCFG1_0.Set(sam.PORT.PINCFG1_0.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||||
|
case 35: // PB03
|
||||||
|
sam.PORT.PINCFG1_0.Set(sam.PORT.PINCFG1_0.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||||
|
case 36: // PB04
|
||||||
|
sam.PORT.PINCFG1_4.Set(sam.PORT.PINCFG1_4.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||||
|
case 37: // PB05
|
||||||
|
sam.PORT.PINCFG1_4.Set(sam.PORT.PINCFG1_4.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||||
|
case 38: // PB06
|
||||||
|
sam.PORT.PINCFG1_4.Set(sam.PORT.PINCFG1_4.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||||
|
case 39: // PB07
|
||||||
|
sam.PORT.PINCFG1_4.Set(sam.PORT.PINCFG1_4.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||||
|
case 40: // PB08
|
||||||
|
sam.PORT.PINCFG1_8.Set(sam.PORT.PINCFG1_8.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||||
|
case 41: // PB09
|
||||||
|
sam.PORT.PINCFG1_8.Set(sam.PORT.PINCFG1_8.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||||
|
case 42: // PB10
|
||||||
|
sam.PORT.PINCFG1_8.Set(sam.PORT.PINCFG1_8.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||||
|
case 43: // PB11
|
||||||
|
sam.PORT.PINCFG1_8.Set(sam.PORT.PINCFG1_8.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||||
|
case 44: // PB12
|
||||||
|
sam.PORT.PINCFG1_12.Set(sam.PORT.PINCFG1_12.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||||
|
case 45: // PB13
|
||||||
|
sam.PORT.PINCFG1_12.Set(sam.PORT.PINCFG1_12.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||||
|
case 46: // PB14
|
||||||
|
sam.PORT.PINCFG1_12.Set(sam.PORT.PINCFG1_12.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||||
|
case 47: // PB15
|
||||||
|
sam.PORT.PINCFG1_12.Set(sam.PORT.PINCFG1_12.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||||
|
case 48: // PB16
|
||||||
|
sam.PORT.PINCFG1_16.Set(sam.PORT.PINCFG1_16.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||||
|
case 49: // PB17
|
||||||
|
sam.PORT.PINCFG1_16.Set(sam.PORT.PINCFG1_16.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||||
|
case 50: // PB18
|
||||||
|
sam.PORT.PINCFG1_16.Set(sam.PORT.PINCFG1_16.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||||
|
case 51: // PB19
|
||||||
|
sam.PORT.PINCFG1_16.Set(sam.PORT.PINCFG1_16.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||||
|
case 52: // PB20
|
||||||
|
sam.PORT.PINCFG1_20.Set(sam.PORT.PINCFG1_20.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||||
|
case 53: // PB21
|
||||||
|
sam.PORT.PINCFG1_20.Set(sam.PORT.PINCFG1_20.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||||
|
case 54: // PB22
|
||||||
|
sam.PORT.PINCFG1_20.Set(sam.PORT.PINCFG1_20.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||||
|
case 55: // PB23
|
||||||
|
sam.PORT.PINCFG1_20.Set(sam.PORT.PINCFG1_20.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||||
|
case 56: // PB24
|
||||||
|
sam.PORT.PINCFG1_24.Set(sam.PORT.PINCFG1_24.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||||
|
case 57: // PB25
|
||||||
|
sam.PORT.PINCFG1_24.Set(sam.PORT.PINCFG1_24.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||||
|
case 58: // PB26
|
||||||
|
sam.PORT.PINCFG1_24.Set(sam.PORT.PINCFG1_24.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||||
|
case 59: // PB27
|
||||||
|
sam.PORT.PINCFG1_24.Set(sam.PORT.PINCFG1_24.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||||
|
case 60: // PB28
|
||||||
|
sam.PORT.PINCFG1_28.Set(sam.PORT.PINCFG1_28.Get()&^(0xff<<0) | (uint32(val) << 0))
|
||||||
|
case 61: // PB29
|
||||||
|
sam.PORT.PINCFG1_28.Set(sam.PORT.PINCFG1_28.Get()&^(0xff<<8) | (uint32(val) << 8))
|
||||||
|
case 62: // PB30
|
||||||
|
sam.PORT.PINCFG1_28.Set(sam.PORT.PINCFG1_28.Get()&^(0xff<<16) | (uint32(val) << 16))
|
||||||
|
case 63: // PB31
|
||||||
|
sam.PORT.PINCFG1_28.Set(sam.PORT.PINCFG1_28.Get()&^(0xff<<24) | (uint32(val) << 24))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,21 +7,21 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Configure sets the pin to input or output.
|
// Configure sets the pin to input or output.
|
||||||
func (p GPIO) Configure(config GPIOConfig) {
|
func (p Pin) Configure(config PinConfig) {
|
||||||
if config.Mode == GPIO_OUTPUT { // set output bit
|
if config.Mode == PinOutput { // set output bit
|
||||||
*avr.DDRB |= 1 << p.Pin
|
avr.DDRB.SetBits(1 << uint8(p))
|
||||||
} else { // configure input: clear output bit
|
} else { // configure input: clear output bit
|
||||||
*avr.DDRB &^= 1 << p.Pin
|
avr.DDRB.ClearBits(1 << uint8(p))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p GPIO) getPortMask() (*avr.RegValue, uint8) {
|
func (p Pin) getPortMask() (*avr.Register8, uint8) {
|
||||||
return avr.PORTB, 1 << p.Pin
|
return avr.PORTB, 1 << uint8(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the current value of a GPIO pin.
|
// Get returns the current value of a GPIO pin.
|
||||||
func (p GPIO) Get() bool {
|
func (p Pin) Get() bool {
|
||||||
val := *avr.PINB & (1 << p.Pin)
|
val := avr.PINB.Get() & (1 << uint8(p))
|
||||||
return (val > 0)
|
return (val > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-18
@@ -6,21 +6,21 @@ import (
|
|||||||
"device/avr"
|
"device/avr"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GPIOMode uint8
|
type PinMode uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
GPIO_INPUT = iota
|
PinInput PinMode = iota
|
||||||
GPIO_OUTPUT
|
PinOutput
|
||||||
)
|
)
|
||||||
|
|
||||||
// Set changes the value of the GPIO pin. The pin must be configured as output.
|
// Set changes the value of the GPIO pin. The pin must be configured as output.
|
||||||
func (p GPIO) Set(value bool) {
|
func (p Pin) Set(value bool) {
|
||||||
if value { // set bits
|
if value { // set bits
|
||||||
port, mask := p.PortMaskSet()
|
port, mask := p.PortMaskSet()
|
||||||
*port = mask
|
port.Set(mask)
|
||||||
} else { // clear bits
|
} else { // clear bits
|
||||||
port, mask := p.PortMaskClear()
|
port, mask := p.PortMaskClear()
|
||||||
*port = mask
|
port.Set(mask)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,9 +30,9 @@ func (p GPIO) Set(value bool) {
|
|||||||
// Warning: there are no separate pin set/clear registers on the AVR. The
|
// Warning: there are no separate pin set/clear registers on the AVR. The
|
||||||
// returned mask is only valid as long as no other pin in the same port has been
|
// returned mask is only valid as long as no other pin in the same port has been
|
||||||
// changed.
|
// changed.
|
||||||
func (p GPIO) PortMaskSet() (*avr.RegValue, avr.RegValue) {
|
func (p Pin) PortMaskSet() (*avr.Register8, uint8) {
|
||||||
port, mask := p.getPortMask()
|
port, mask := p.getPortMask()
|
||||||
return port, *port | avr.RegValue(mask)
|
return port, port.Get() | mask
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the register and mask to disable a given port. This can be used to
|
// Return the register and mask to disable a given port. This can be used to
|
||||||
@@ -41,18 +41,18 @@ func (p GPIO) PortMaskSet() (*avr.RegValue, avr.RegValue) {
|
|||||||
// Warning: there are no separate pin set/clear registers on the AVR. The
|
// Warning: there are no separate pin set/clear registers on the AVR. The
|
||||||
// returned mask is only valid as long as no other pin in the same port has been
|
// returned mask is only valid as long as no other pin in the same port has been
|
||||||
// changed.
|
// changed.
|
||||||
func (p GPIO) PortMaskClear() (*avr.RegValue, avr.RegValue) {
|
func (p Pin) PortMaskClear() (*avr.Register8, uint8) {
|
||||||
port, mask := p.getPortMask()
|
port, mask := p.getPortMask()
|
||||||
return port, *port &^ avr.RegValue(mask)
|
return port, port.Get() &^ mask
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitADC initializes the registers needed for ADC.
|
// InitADC initializes the registers needed for ADC.
|
||||||
func InitADC() {
|
func InitADC() {
|
||||||
// set a2d prescaler so we are inside the desired 50-200 KHz range at 16MHz.
|
// set a2d prescaler so we are inside the desired 50-200 KHz range at 16MHz.
|
||||||
*avr.ADCSRA |= (avr.ADCSRA_ADPS2 | avr.ADCSRA_ADPS1 | avr.ADCSRA_ADPS0)
|
avr.ADCSRA.SetBits(avr.ADCSRA_ADPS2 | avr.ADCSRA_ADPS1 | avr.ADCSRA_ADPS0)
|
||||||
|
|
||||||
// enable a2d conversions
|
// enable a2d conversions
|
||||||
*avr.ADCSRA |= avr.ADCSRA_ADEN
|
avr.ADCSRA.SetBits(avr.ADCSRA_ADEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure configures a ADCPin to be able to be used to read data.
|
// Configure configures a ADCPin to be able to be used to read data.
|
||||||
@@ -68,18 +68,16 @@ func (a ADC) Get() uint16 {
|
|||||||
// set the ADLAR bit (left-adjusted result) to get a value scaled to 16
|
// set the ADLAR bit (left-adjusted result) to get a value scaled to 16
|
||||||
// bits. This has the same effect as shifting the return value left by 6
|
// bits. This has the same effect as shifting the return value left by 6
|
||||||
// bits.
|
// bits.
|
||||||
*avr.ADMUX = avr.RegValue(avr.ADMUX_REFS0 | avr.ADMUX_ADLAR | (a.Pin & 0x07))
|
avr.ADMUX.Set(avr.ADMUX_REFS0 | avr.ADMUX_ADLAR | (uint8(a.Pin) & 0x07))
|
||||||
|
|
||||||
// start the conversion
|
// start the conversion
|
||||||
*avr.ADCSRA |= avr.ADCSRA_ADSC
|
avr.ADCSRA.SetBits(avr.ADCSRA_ADSC)
|
||||||
|
|
||||||
// ADSC is cleared when the conversion finishes
|
// ADSC is cleared when the conversion finishes
|
||||||
for ok := true; ok; ok = (*avr.ADCSRA & avr.ADCSRA_ADSC) > 0 {
|
for ok := true; ok; ok = avr.ADCSRA.HasBits(avr.ADCSRA_ADSC) {
|
||||||
}
|
}
|
||||||
|
|
||||||
low := uint16(*avr.ADCL)
|
return uint16(avr.ADCL.Get()) | uint16(avr.ADCH.Get())<<8
|
||||||
high := uint16(*avr.ADCH)
|
|
||||||
return uint16(low) | uint16(high<<8)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C on AVR.
|
// I2C on AVR.
|
||||||
|
|||||||
@@ -4,37 +4,37 @@ package machine
|
|||||||
|
|
||||||
// Dummy machine package, filled with no-ops.
|
// Dummy machine package, filled with no-ops.
|
||||||
|
|
||||||
type GPIOMode uint8
|
type PinMode uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
GPIO_INPUT = iota
|
PinInput PinMode = iota
|
||||||
GPIO_OUTPUT
|
PinOutput
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fake LED numbers, for testing.
|
// Fake LED numbers, for testing.
|
||||||
const (
|
const (
|
||||||
LED = LED1
|
LED Pin = LED1
|
||||||
LED1 = 0
|
LED1 Pin = 0
|
||||||
LED2 = 0
|
LED2 Pin = 0
|
||||||
LED3 = 0
|
LED3 Pin = 0
|
||||||
LED4 = 0
|
LED4 Pin = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fake button numbers, for testing.
|
// Fake button numbers, for testing.
|
||||||
const (
|
const (
|
||||||
BUTTON = BUTTON1
|
BUTTON Pin = BUTTON1
|
||||||
BUTTON1 = 0
|
BUTTON1 Pin = 0
|
||||||
BUTTON2 = 0
|
BUTTON2 Pin = 0
|
||||||
BUTTON3 = 0
|
BUTTON3 Pin = 0
|
||||||
BUTTON4 = 0
|
BUTTON4 Pin = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p GPIO) Configure(config GPIOConfig) {
|
func (p Pin) Configure(config PinConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p GPIO) Set(value bool) {
|
func (p Pin) Set(value bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p GPIO) Get() bool {
|
func (p Pin) Get() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
+70
-81
@@ -7,51 +7,51 @@ import (
|
|||||||
"device/nrf"
|
"device/nrf"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GPIOMode uint8
|
type PinMode uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
GPIO_INPUT = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos)
|
PinInput PinMode = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos)
|
||||||
GPIO_INPUT_PULLUP = GPIO_INPUT | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos)
|
PinInputPullup PinMode = PinInput | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos)
|
||||||
GPIO_INPUT_PULLDOWN = GPIO_INPUT | (nrf.GPIO_PIN_CNF_PULL_Pulldown << nrf.GPIO_PIN_CNF_PULL_Pos)
|
PinInputPulldown PinMode = PinOutput | (nrf.GPIO_PIN_CNF_PULL_Pulldown << nrf.GPIO_PIN_CNF_PULL_Pos)
|
||||||
GPIO_OUTPUT = (nrf.GPIO_PIN_CNF_DIR_Output << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Disconnect << nrf.GPIO_PIN_CNF_INPUT_Pos)
|
PinOutput PinMode = (nrf.GPIO_PIN_CNF_DIR_Output << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Disconnect << nrf.GPIO_PIN_CNF_INPUT_Pos)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Configure this pin with the given configuration.
|
// Configure this pin with the given configuration.
|
||||||
func (p GPIO) Configure(config GPIOConfig) {
|
func (p Pin) Configure(config PinConfig) {
|
||||||
cfg := config.Mode | nrf.GPIO_PIN_CNF_DRIVE_S0S1 | nrf.GPIO_PIN_CNF_SENSE_Disabled
|
cfg := config.Mode | nrf.GPIO_PIN_CNF_DRIVE_S0S1 | nrf.GPIO_PIN_CNF_SENSE_Disabled
|
||||||
port, pin := p.getPortPin()
|
port, pin := p.getPortPin()
|
||||||
port.PIN_CNF[pin] = nrf.RegValue(cfg)
|
port.PIN_CNF[pin].Set(uint32(cfg))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the pin to high or low.
|
// Set the pin to high or low.
|
||||||
// Warning: only use this on an output pin!
|
// Warning: only use this on an output pin!
|
||||||
func (p GPIO) Set(high bool) {
|
func (p Pin) Set(high bool) {
|
||||||
port, pin := p.getPortPin()
|
port, pin := p.getPortPin()
|
||||||
if high {
|
if high {
|
||||||
port.OUTSET = 1 << pin
|
port.OUTSET.Set(1 << pin)
|
||||||
} else {
|
} else {
|
||||||
port.OUTCLR = 1 << pin
|
port.OUTCLR.Set(1 << pin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the register and mask to enable a given GPIO pin. This can be used to
|
// Return the register and mask to enable a given GPIO pin. This can be used to
|
||||||
// implement bit-banged drivers.
|
// implement bit-banged drivers.
|
||||||
func (p GPIO) PortMaskSet() (*uint32, uint32) {
|
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||||
port, pin := p.getPortPin()
|
port, pin := p.getPortPin()
|
||||||
return (*uint32)(&port.OUTSET), 1 << pin
|
return &port.OUTSET.Reg, 1 << pin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the register and mask to disable a given port. This can be used to
|
// Return the register and mask to disable a given port. This can be used to
|
||||||
// implement bit-banged drivers.
|
// implement bit-banged drivers.
|
||||||
func (p GPIO) PortMaskClear() (*uint32, uint32) {
|
func (p Pin) PortMaskClear() (*uint32, uint32) {
|
||||||
port, pin := p.getPortPin()
|
port, pin := p.getPortPin()
|
||||||
return (*uint32)(&port.OUTCLR), 1 << pin
|
return &port.OUTCLR.Reg, 1 << pin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the current value of a GPIO pin.
|
// Get returns the current value of a GPIO pin.
|
||||||
func (p GPIO) Get() bool {
|
func (p Pin) Get() bool {
|
||||||
port, pin := p.getPortPin()
|
port, pin := p.getPortPin()
|
||||||
return (port.IN>>pin)&1 != 0
|
return (port.IN.Get()>>pin)&1 != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// UART on the NRF.
|
// UART on the NRF.
|
||||||
@@ -77,10 +77,10 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
// Set TX and RX pins from board.
|
// Set TX and RX pins from board.
|
||||||
uart.setPins(UART_TX_PIN, UART_RX_PIN)
|
uart.setPins(UART_TX_PIN, UART_RX_PIN)
|
||||||
|
|
||||||
nrf.UART0.ENABLE = nrf.UART_ENABLE_ENABLE_Enabled
|
nrf.UART0.ENABLE.Set(nrf.UART_ENABLE_ENABLE_Enabled)
|
||||||
nrf.UART0.TASKS_STARTTX = 1
|
nrf.UART0.TASKS_STARTTX.Set(1)
|
||||||
nrf.UART0.TASKS_STARTRX = 1
|
nrf.UART0.TASKS_STARTRX.Set(1)
|
||||||
nrf.UART0.INTENSET = nrf.UART_INTENSET_RXDRDY_Msk
|
nrf.UART0.INTENSET.Set(nrf.UART_INTENSET_RXDRDY_Msk)
|
||||||
|
|
||||||
// Enable RX IRQ.
|
// Enable RX IRQ.
|
||||||
arm.SetPriority(nrf.IRQ_UART0, 0xc0) // low priority
|
arm.SetPriority(nrf.IRQ_UART0, 0xc0) // low priority
|
||||||
@@ -99,22 +99,22 @@ func (uart UART) SetBaudRate(br uint32) {
|
|||||||
// https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values/2046#2046
|
// https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values/2046#2046
|
||||||
rate := uint32((uint64(br/400)*uint64(400*0xffffffff/16000000) + 0x800) & 0xffffff000)
|
rate := uint32((uint64(br/400)*uint64(400*0xffffffff/16000000) + 0x800) & 0xffffff000)
|
||||||
|
|
||||||
nrf.UART0.BAUDRATE = nrf.RegValue(rate)
|
nrf.UART0.BAUDRATE.Set(rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteByte writes a byte of data to the UART.
|
// WriteByte writes a byte of data to the UART.
|
||||||
func (uart UART) WriteByte(c byte) error {
|
func (uart UART) WriteByte(c byte) error {
|
||||||
nrf.UART0.EVENTS_TXDRDY = 0
|
nrf.UART0.EVENTS_TXDRDY.Set(0)
|
||||||
nrf.UART0.TXD = nrf.RegValue(c)
|
nrf.UART0.TXD.Set(uint32(c))
|
||||||
for nrf.UART0.EVENTS_TXDRDY == 0 {
|
for nrf.UART0.EVENTS_TXDRDY.Get() == 0 {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uart UART) handleInterrupt() {
|
func (uart UART) handleInterrupt() {
|
||||||
if nrf.UART0.EVENTS_RXDRDY != 0 {
|
if nrf.UART0.EVENTS_RXDRDY.Get() != 0 {
|
||||||
uart.Receive(byte(nrf.UART0.RXD))
|
uart.Receive(byte(nrf.UART0.RXD.Get()))
|
||||||
nrf.UART0.EVENTS_RXDRDY = 0x0
|
nrf.UART0.EVENTS_RXDRDY.Set(0x0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,8 +132,8 @@ var (
|
|||||||
// I2CConfig is used to store config info for I2C.
|
// I2CConfig is used to store config info for I2C.
|
||||||
type I2CConfig struct {
|
type I2CConfig struct {
|
||||||
Frequency uint32
|
Frequency uint32
|
||||||
SCL uint8
|
SCL Pin
|
||||||
SDA uint8
|
SDA Pin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure is intended to setup the I2C interface.
|
// Configure is intended to setup the I2C interface.
|
||||||
@@ -149,27 +149,27 @@ func (i2c I2C) Configure(config I2CConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do config
|
// do config
|
||||||
sclPort, sclPin := GPIO{config.SCL}.getPortPin()
|
sclPort, sclPin := config.SCL.getPortPin()
|
||||||
sclPort.PIN_CNF[sclPin] = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) |
|
sclPort.PIN_CNF[sclPin].Set((nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) |
|
||||||
(nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos) |
|
(nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos) |
|
||||||
(nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos) |
|
(nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos) |
|
||||||
(nrf.GPIO_PIN_CNF_DRIVE_S0D1 << nrf.GPIO_PIN_CNF_DRIVE_Pos) |
|
(nrf.GPIO_PIN_CNF_DRIVE_S0D1 << nrf.GPIO_PIN_CNF_DRIVE_Pos) |
|
||||||
(nrf.GPIO_PIN_CNF_SENSE_Disabled << nrf.GPIO_PIN_CNF_SENSE_Pos)
|
(nrf.GPIO_PIN_CNF_SENSE_Disabled << nrf.GPIO_PIN_CNF_SENSE_Pos))
|
||||||
|
|
||||||
sdaPort, sdaPin := GPIO{config.SDA}.getPortPin()
|
sdaPort, sdaPin := config.SDA.getPortPin()
|
||||||
sdaPort.PIN_CNF[sdaPin] = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) |
|
sdaPort.PIN_CNF[sdaPin].Set((nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) |
|
||||||
(nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos) |
|
(nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos) |
|
||||||
(nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos) |
|
(nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos) |
|
||||||
(nrf.GPIO_PIN_CNF_DRIVE_S0D1 << nrf.GPIO_PIN_CNF_DRIVE_Pos) |
|
(nrf.GPIO_PIN_CNF_DRIVE_S0D1 << nrf.GPIO_PIN_CNF_DRIVE_Pos) |
|
||||||
(nrf.GPIO_PIN_CNF_SENSE_Disabled << nrf.GPIO_PIN_CNF_SENSE_Pos)
|
(nrf.GPIO_PIN_CNF_SENSE_Disabled << nrf.GPIO_PIN_CNF_SENSE_Pos))
|
||||||
|
|
||||||
if config.Frequency == TWI_FREQ_400KHZ {
|
if config.Frequency == TWI_FREQ_400KHZ {
|
||||||
i2c.Bus.FREQUENCY = nrf.TWI_FREQUENCY_FREQUENCY_K400
|
i2c.Bus.FREQUENCY.Set(nrf.TWI_FREQUENCY_FREQUENCY_K400)
|
||||||
} else {
|
} else {
|
||||||
i2c.Bus.FREQUENCY = nrf.TWI_FREQUENCY_FREQUENCY_K100
|
i2c.Bus.FREQUENCY.Set(nrf.TWI_FREQUENCY_FREQUENCY_K100)
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c.Bus.ENABLE = nrf.TWI_ENABLE_ENABLE_Enabled
|
i2c.Bus.ENABLE.Set(nrf.TWI_ENABLE_ENABLE_Enabled)
|
||||||
i2c.setPins(config.SCL, config.SDA)
|
i2c.setPins(config.SCL, config.SDA)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,29 +177,28 @@ func (i2c I2C) Configure(config I2CConfig) {
|
|||||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
// 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.
|
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||||
i2c.Bus.ADDRESS = nrf.RegValue(addr)
|
i2c.Bus.ADDRESS.Set(uint32(addr))
|
||||||
if len(w) != 0 {
|
if len(w) != 0 {
|
||||||
i2c.Bus.TASKS_STARTTX = 1 // start transmission for writing
|
i2c.Bus.TASKS_STARTTX.Set(1) // start transmission for writing
|
||||||
for _, b := range w {
|
for _, b := range w {
|
||||||
i2c.writeByte(b)
|
i2c.writeByte(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(r) != 0 {
|
if len(r) != 0 {
|
||||||
i2c.Bus.TASKS_STARTRX = 1 // re-start transmission for reading
|
// To trigger suspend task when a byte is received
|
||||||
for i := range r { // read each char
|
i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_SUSPEND)
|
||||||
|
i2c.Bus.TASKS_STARTRX.Set(1) // re-start transmission for reading
|
||||||
|
for i := range r { // read each char
|
||||||
if i+1 == len(r) {
|
if i+1 == len(r) {
|
||||||
// The 'stop' signal must be sent before reading back the last
|
// To trigger stop task when last byte is received, set before resume task.
|
||||||
// byte, so that it will be sent by the I2C peripheral right
|
i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_STOP)
|
||||||
// after the last byte has been read.
|
|
||||||
r[i] = i2c.readLastByte()
|
|
||||||
} else {
|
|
||||||
r[i] = i2c.readByte()
|
|
||||||
}
|
}
|
||||||
|
i2c.Bus.TASKS_RESUME.Set(1) // re-start transmission for reading
|
||||||
|
r[i] = i2c.readByte()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Nothing to read back. Stop the transmission.
|
|
||||||
i2c.signalStop()
|
|
||||||
}
|
}
|
||||||
|
i2c.signalStop()
|
||||||
|
i2c.Bus.SHORTS.Set(nrf.TWI_SHORTS_BB_SUSPEND_Disabled)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,36 +206,26 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
// it must generate a stop condition after the next character is retrieved when
|
// it must generate a stop condition after the next character is retrieved when
|
||||||
// reading.
|
// reading.
|
||||||
func (i2c I2C) signalStop() {
|
func (i2c I2C) signalStop() {
|
||||||
i2c.Bus.TASKS_STOP = 1
|
i2c.Bus.TASKS_STOP.Set(1)
|
||||||
for i2c.Bus.EVENTS_STOPPED == 0 {
|
for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
|
||||||
}
|
}
|
||||||
i2c.Bus.EVENTS_STOPPED = 0
|
i2c.Bus.EVENTS_STOPPED.Set(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeByte writes a single byte to the I2C bus.
|
// writeByte writes a single byte to the I2C bus.
|
||||||
func (i2c I2C) writeByte(data byte) {
|
func (i2c I2C) writeByte(data byte) {
|
||||||
i2c.Bus.TXD = nrf.RegValue(data)
|
i2c.Bus.TXD.Set(uint32(data))
|
||||||
for i2c.Bus.EVENTS_TXDSENT == 0 {
|
for i2c.Bus.EVENTS_TXDSENT.Get() == 0 {
|
||||||
}
|
}
|
||||||
i2c.Bus.EVENTS_TXDSENT = 0
|
i2c.Bus.EVENTS_TXDSENT.Set(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// readByte reads a single byte from the I2C bus.
|
// readByte reads a single byte from the I2C bus.
|
||||||
func (i2c I2C) readByte() byte {
|
func (i2c I2C) readByte() byte {
|
||||||
for i2c.Bus.EVENTS_RXDREADY == 0 {
|
for i2c.Bus.EVENTS_RXDREADY.Get() == 0 {
|
||||||
}
|
}
|
||||||
i2c.Bus.EVENTS_RXDREADY = 0
|
i2c.Bus.EVENTS_RXDREADY.Set(0)
|
||||||
return byte(i2c.Bus.RXD)
|
return byte(i2c.Bus.RXD.Get())
|
||||||
}
|
|
||||||
|
|
||||||
// readLastByte reads a single byte from the I2C bus, sending a stop signal
|
|
||||||
// after it has been read.
|
|
||||||
func (i2c I2C) readLastByte() byte {
|
|
||||||
for i2c.Bus.EVENTS_RXDREADY == 0 {
|
|
||||||
}
|
|
||||||
i2c.Bus.EVENTS_RXDREADY = 0
|
|
||||||
i2c.signalStop() // signal 'stop' now, so it is sent when reading RXD
|
|
||||||
return byte(i2c.Bus.RXD)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPI on the NRF.
|
// SPI on the NRF.
|
||||||
@@ -253,9 +242,9 @@ var (
|
|||||||
// SPIConfig is used to store config info for SPI.
|
// SPIConfig is used to store config info for SPI.
|
||||||
type SPIConfig struct {
|
type SPIConfig struct {
|
||||||
Frequency uint32
|
Frequency uint32
|
||||||
SCK uint8
|
SCK Pin
|
||||||
MOSI uint8
|
MOSI Pin
|
||||||
MISO uint8
|
MISO Pin
|
||||||
LSBFirst bool
|
LSBFirst bool
|
||||||
Mode uint8
|
Mode uint8
|
||||||
}
|
}
|
||||||
@@ -263,7 +252,7 @@ type SPIConfig struct {
|
|||||||
// Configure is intended to setup the SPI interface.
|
// Configure is intended to setup the SPI interface.
|
||||||
func (spi SPI) Configure(config SPIConfig) {
|
func (spi SPI) Configure(config SPIConfig) {
|
||||||
// Disable bus to configure it
|
// Disable bus to configure it
|
||||||
spi.Bus.ENABLE = nrf.SPI_ENABLE_ENABLE_Disabled
|
spi.Bus.ENABLE.Set(nrf.SPI_ENABLE_ENABLE_Disabled)
|
||||||
|
|
||||||
// set frequency
|
// set frequency
|
||||||
var freq uint32
|
var freq uint32
|
||||||
@@ -286,7 +275,7 @@ func (spi SPI) Configure(config SPIConfig) {
|
|||||||
default:
|
default:
|
||||||
freq = nrf.SPI_FREQUENCY_FREQUENCY_K500
|
freq = nrf.SPI_FREQUENCY_FREQUENCY_K500
|
||||||
}
|
}
|
||||||
spi.Bus.FREQUENCY = nrf.RegValue(freq)
|
spi.Bus.FREQUENCY.Set(freq)
|
||||||
|
|
||||||
var conf uint32
|
var conf uint32
|
||||||
|
|
||||||
@@ -313,22 +302,22 @@ func (spi SPI) Configure(config SPIConfig) {
|
|||||||
conf &^= (nrf.SPI_CONFIG_CPOL_ActiveHigh << nrf.SPI_CONFIG_CPOL_Pos)
|
conf &^= (nrf.SPI_CONFIG_CPOL_ActiveHigh << nrf.SPI_CONFIG_CPOL_Pos)
|
||||||
conf &^= (nrf.SPI_CONFIG_CPHA_Leading << nrf.SPI_CONFIG_CPHA_Pos)
|
conf &^= (nrf.SPI_CONFIG_CPHA_Leading << nrf.SPI_CONFIG_CPHA_Pos)
|
||||||
}
|
}
|
||||||
spi.Bus.CONFIG = nrf.RegValue(conf)
|
spi.Bus.CONFIG.Set(conf)
|
||||||
|
|
||||||
// set pins
|
// set pins
|
||||||
spi.setPins(config.SCK, config.MOSI, config.MISO)
|
spi.setPins(config.SCK, config.MOSI, config.MISO)
|
||||||
|
|
||||||
// Re-enable bus now that it is configured.
|
// Re-enable bus now that it is configured.
|
||||||
spi.Bus.ENABLE = nrf.SPI_ENABLE_ENABLE_Enabled
|
spi.Bus.ENABLE.Set(nrf.SPI_ENABLE_ENABLE_Enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer writes/reads a single byte using the SPI interface.
|
// Transfer writes/reads a single byte using the SPI interface.
|
||||||
func (spi SPI) Transfer(w byte) (byte, error) {
|
func (spi SPI) Transfer(w byte) (byte, error) {
|
||||||
spi.Bus.TXD = nrf.RegValue(w)
|
spi.Bus.TXD.Set(uint32(w))
|
||||||
for spi.Bus.EVENTS_READY == 0 {
|
for spi.Bus.EVENTS_READY.Get() == 0 {
|
||||||
}
|
}
|
||||||
r := spi.Bus.RXD
|
r := spi.Bus.RXD.Get()
|
||||||
spi.Bus.EVENTS_READY = 0
|
spi.Bus.EVENTS_READY.Set(0)
|
||||||
|
|
||||||
// TODO: handle SPI errors
|
// TODO: handle SPI errors
|
||||||
return byte(r), nil
|
return byte(r), nil
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import (
|
|||||||
const CPU_FREQUENCY = 16000000
|
const CPU_FREQUENCY = 16000000
|
||||||
|
|
||||||
// Get peripheral and pin number for this GPIO pin.
|
// Get peripheral and pin number for this GPIO pin.
|
||||||
func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) {
|
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||||
return nrf.GPIO, p.Pin
|
return nrf.GPIO, uint32(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uart UART) setPins(tx, rx uint32) {
|
func (uart UART) setPins(tx, rx Pin) {
|
||||||
nrf.UART0.PSELTXD = nrf.RegValue(tx)
|
nrf.UART0.PSELTXD.Set(uint32(tx))
|
||||||
nrf.UART0.PSELRXD = nrf.RegValue(rx)
|
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export UART0_IRQHandler
|
//go:export UART0_IRQHandler
|
||||||
@@ -23,13 +23,13 @@ func handleUART0() {
|
|||||||
UART0.handleInterrupt()
|
UART0.handleInterrupt()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i2c I2C) setPins(scl, sda uint8) {
|
func (i2c I2C) setPins(scl, sda Pin) {
|
||||||
i2c.Bus.PSELSCL = nrf.RegValue(scl)
|
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||||
i2c.Bus.PSELSDA = nrf.RegValue(sda)
|
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPI
|
// SPI
|
||||||
func (spi SPI) setPins(sck, mosi, miso uint8) {
|
func (spi SPI) setPins(sck, mosi, miso Pin) {
|
||||||
if sck == 0 {
|
if sck == 0 {
|
||||||
sck = SPI0_SCK_PIN
|
sck = SPI0_SCK_PIN
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ func (spi SPI) setPins(sck, mosi, miso uint8) {
|
|||||||
if miso == 0 {
|
if miso == 0 {
|
||||||
miso = SPI0_MISO_PIN
|
miso = SPI0_MISO_PIN
|
||||||
}
|
}
|
||||||
spi.Bus.PSELSCK = nrf.RegValue(sck)
|
spi.Bus.PSELSCK.Set(uint32(sck))
|
||||||
spi.Bus.PSELMOSI = nrf.RegValue(mosi)
|
spi.Bus.PSELMOSI.Set(uint32(mosi))
|
||||||
spi.Bus.PSELMISO = nrf.RegValue(miso)
|
spi.Bus.PSELMISO.Set(uint32(miso))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ import (
|
|||||||
const CPU_FREQUENCY = 64000000
|
const CPU_FREQUENCY = 64000000
|
||||||
|
|
||||||
// Get peripheral and pin number for this GPIO pin.
|
// Get peripheral and pin number for this GPIO pin.
|
||||||
func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) {
|
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||||
return nrf.P0, p.Pin
|
return nrf.P0, uint32(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uart UART) setPins(tx, rx uint32) {
|
func (uart UART) setPins(tx, rx Pin) {
|
||||||
nrf.UART0.PSELTXD = nrf.RegValue(tx)
|
nrf.UART0.PSELTXD.Set(uint32(tx))
|
||||||
nrf.UART0.PSELRXD = nrf.RegValue(rx)
|
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export UARTE0_UART0_IRQHandler
|
//go:export UARTE0_UART0_IRQHandler
|
||||||
@@ -24,13 +24,13 @@ func handleUART0() {
|
|||||||
UART0.handleInterrupt()
|
UART0.handleInterrupt()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i2c I2C) setPins(scl, sda uint8) {
|
func (i2c I2C) setPins(scl, sda Pin) {
|
||||||
i2c.Bus.PSELSCL = nrf.RegValue(scl)
|
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||||
i2c.Bus.PSELSDA = nrf.RegValue(sda)
|
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPI
|
// SPI
|
||||||
func (spi SPI) setPins(sck, mosi, miso uint8) {
|
func (spi SPI) setPins(sck, mosi, miso Pin) {
|
||||||
if sck == 0 {
|
if sck == 0 {
|
||||||
sck = SPI0_SCK_PIN
|
sck = SPI0_SCK_PIN
|
||||||
}
|
}
|
||||||
@@ -40,9 +40,9 @@ func (spi SPI) setPins(sck, mosi, miso uint8) {
|
|||||||
if miso == 0 {
|
if miso == 0 {
|
||||||
miso = SPI0_MISO_PIN
|
miso = SPI0_MISO_PIN
|
||||||
}
|
}
|
||||||
spi.Bus.PSEL.SCK = nrf.RegValue(sck)
|
spi.Bus.PSEL.SCK.Set(uint32(sck))
|
||||||
spi.Bus.PSEL.MOSI = nrf.RegValue(mosi)
|
spi.Bus.PSEL.MOSI.Set(uint32(mosi))
|
||||||
spi.Bus.PSEL.MISO = nrf.RegValue(miso)
|
spi.Bus.PSEL.MISO.Set(uint32(miso))
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitADC initializes the registers needed for ADC.
|
// InitADC initializes the registers needed for ADC.
|
||||||
@@ -89,53 +89,53 @@ func (a ADC) Get() uint16 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
nrf.SAADC.RESOLUTION = nrf.SAADC_RESOLUTION_VAL_12bit
|
nrf.SAADC.RESOLUTION.Set(nrf.SAADC_RESOLUTION_VAL_12bit)
|
||||||
|
|
||||||
// Enable ADC.
|
// Enable ADC.
|
||||||
nrf.SAADC.ENABLE = (nrf.SAADC_ENABLE_ENABLE_Enabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Enabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
||||||
for i := 0; i < 8; i++ {
|
for i := 0; i < 8; i++ {
|
||||||
nrf.SAADC.CH[i].PSELN = nrf.SAADC_CH_PSELP_PSELP_NC
|
nrf.SAADC.CH[i].PSELN.Set(nrf.SAADC_CH_PSELP_PSELP_NC)
|
||||||
nrf.SAADC.CH[i].PSELP = nrf.SAADC_CH_PSELP_PSELP_NC
|
nrf.SAADC.CH[i].PSELP.Set(nrf.SAADC_CH_PSELP_PSELP_NC)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure ADC.
|
// Configure ADC.
|
||||||
nrf.SAADC.CH[0].CONFIG = ((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESP_Pos) & nrf.SAADC_CH_CONFIG_RESP_Msk) |
|
nrf.SAADC.CH[0].CONFIG.Set(((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESP_Pos) & nrf.SAADC_CH_CONFIG_RESP_Msk) |
|
||||||
((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESN_Pos) & nrf.SAADC_CH_CONFIG_RESN_Msk) |
|
((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESN_Pos) & nrf.SAADC_CH_CONFIG_RESN_Msk) |
|
||||||
((nrf.SAADC_CH_CONFIG_GAIN_Gain1_5 << nrf.SAADC_CH_CONFIG_GAIN_Pos) & nrf.SAADC_CH_CONFIG_GAIN_Msk) |
|
((nrf.SAADC_CH_CONFIG_GAIN_Gain1_5 << nrf.SAADC_CH_CONFIG_GAIN_Pos) & nrf.SAADC_CH_CONFIG_GAIN_Msk) |
|
||||||
((nrf.SAADC_CH_CONFIG_REFSEL_Internal << nrf.SAADC_CH_CONFIG_REFSEL_Pos) & nrf.SAADC_CH_CONFIG_REFSEL_Msk) |
|
((nrf.SAADC_CH_CONFIG_REFSEL_Internal << nrf.SAADC_CH_CONFIG_REFSEL_Pos) & nrf.SAADC_CH_CONFIG_REFSEL_Msk) |
|
||||||
((nrf.SAADC_CH_CONFIG_TACQ_3us << nrf.SAADC_CH_CONFIG_TACQ_Pos) & nrf.SAADC_CH_CONFIG_TACQ_Msk) |
|
((nrf.SAADC_CH_CONFIG_TACQ_3us << nrf.SAADC_CH_CONFIG_TACQ_Pos) & nrf.SAADC_CH_CONFIG_TACQ_Msk) |
|
||||||
((nrf.SAADC_CH_CONFIG_MODE_SE << nrf.SAADC_CH_CONFIG_MODE_Pos) & nrf.SAADC_CH_CONFIG_MODE_Msk)
|
((nrf.SAADC_CH_CONFIG_MODE_SE << nrf.SAADC_CH_CONFIG_MODE_Pos) & nrf.SAADC_CH_CONFIG_MODE_Msk))
|
||||||
|
|
||||||
// Set pin to read.
|
// Set pin to read.
|
||||||
nrf.SAADC.CH[0].PSELN = nrf.RegValue(pwmPin)
|
nrf.SAADC.CH[0].PSELN.Set(pwmPin)
|
||||||
nrf.SAADC.CH[0].PSELP = nrf.RegValue(pwmPin)
|
nrf.SAADC.CH[0].PSELP.Set(pwmPin)
|
||||||
|
|
||||||
// Destination for sample result.
|
// Destination for sample result.
|
||||||
nrf.SAADC.RESULT.PTR = nrf.RegValue(uintptr(unsafe.Pointer(&value)))
|
nrf.SAADC.RESULT.PTR.Set(uint32(uintptr(unsafe.Pointer(&value))))
|
||||||
nrf.SAADC.RESULT.MAXCNT = 1 // One sample
|
nrf.SAADC.RESULT.MAXCNT.Set(1) // One sample
|
||||||
|
|
||||||
// Start tasks.
|
// Start tasks.
|
||||||
nrf.SAADC.TASKS_START = 1
|
nrf.SAADC.TASKS_START.Set(1)
|
||||||
for nrf.SAADC.EVENTS_STARTED == 0 {
|
for nrf.SAADC.EVENTS_STARTED.Get() == 0 {
|
||||||
}
|
}
|
||||||
nrf.SAADC.EVENTS_STARTED = 0x00
|
nrf.SAADC.EVENTS_STARTED.Set(0x00)
|
||||||
|
|
||||||
// Start the sample task.
|
// Start the sample task.
|
||||||
nrf.SAADC.TASKS_SAMPLE = 1
|
nrf.SAADC.TASKS_SAMPLE.Set(1)
|
||||||
|
|
||||||
// Wait until the sample task is done.
|
// Wait until the sample task is done.
|
||||||
for nrf.SAADC.EVENTS_END == 0 {
|
for nrf.SAADC.EVENTS_END.Get() == 0 {
|
||||||
}
|
}
|
||||||
nrf.SAADC.EVENTS_END = 0x00
|
nrf.SAADC.EVENTS_END.Set(0x00)
|
||||||
|
|
||||||
// Stop the ADC
|
// Stop the ADC
|
||||||
nrf.SAADC.TASKS_STOP = 1
|
nrf.SAADC.TASKS_STOP.Set(1)
|
||||||
for nrf.SAADC.EVENTS_STOPPED == 0 {
|
for nrf.SAADC.EVENTS_STOPPED.Get() == 0 {
|
||||||
}
|
}
|
||||||
nrf.SAADC.EVENTS_STOPPED = 0
|
nrf.SAADC.EVENTS_STOPPED.Set(0)
|
||||||
|
|
||||||
// Disable the ADC.
|
// Disable the ADC.
|
||||||
nrf.SAADC.ENABLE = (nrf.SAADC_ENABLE_ENABLE_Disabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Disabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
||||||
|
|
||||||
if value < 0 {
|
if value < 0 {
|
||||||
value = 0
|
value = 0
|
||||||
@@ -170,21 +170,21 @@ func (pwm PWM) Set(value uint16) {
|
|||||||
|
|
||||||
p := pwms[i]
|
p := pwms[i]
|
||||||
|
|
||||||
p.PSEL.OUT[0] = nrf.RegValue(pwm.Pin)
|
p.PSEL.OUT[0].Set(uint32(pwm.Pin))
|
||||||
p.PSEL.OUT[1] = nrf.RegValue(pwm.Pin)
|
p.PSEL.OUT[1].Set(uint32(pwm.Pin))
|
||||||
p.PSEL.OUT[2] = nrf.RegValue(pwm.Pin)
|
p.PSEL.OUT[2].Set(uint32(pwm.Pin))
|
||||||
p.PSEL.OUT[3] = nrf.RegValue(pwm.Pin)
|
p.PSEL.OUT[3].Set(uint32(pwm.Pin))
|
||||||
p.ENABLE = (nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos)
|
p.ENABLE.Set(nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos)
|
||||||
p.PRESCALER = nrf.PWM_PRESCALER_PRESCALER_DIV_2
|
p.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_2)
|
||||||
p.MODE = nrf.PWM_MODE_UPDOWN_Up
|
p.MODE.Set(nrf.PWM_MODE_UPDOWN_Up)
|
||||||
p.COUNTERTOP = 16384 // frequency
|
p.COUNTERTOP.Set(16384) // frequency
|
||||||
p.LOOP = 0
|
p.LOOP.Set(0)
|
||||||
p.DECODER = (nrf.PWM_DECODER_LOAD_Common << nrf.PWM_DECODER_LOAD_Pos) | (nrf.PWM_DECODER_MODE_RefreshCount << nrf.PWM_DECODER_MODE_Pos)
|
p.DECODER.Set((nrf.PWM_DECODER_LOAD_Common << nrf.PWM_DECODER_LOAD_Pos) | (nrf.PWM_DECODER_MODE_RefreshCount << nrf.PWM_DECODER_MODE_Pos))
|
||||||
p.SEQ[0].PTR = nrf.RegValue(uintptr(unsafe.Pointer(&pwmChannelSequence[i])))
|
p.SEQ[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&pwmChannelSequence[i]))))
|
||||||
p.SEQ[0].CNT = 1
|
p.SEQ[0].CNT.Set(1)
|
||||||
p.SEQ[0].REFRESH = 1
|
p.SEQ[0].REFRESH.Set(1)
|
||||||
p.SEQ[0].ENDDELAY = 0
|
p.SEQ[0].ENDDELAY.Set(0)
|
||||||
p.TASKS_SEQSTART[0] = 1
|
p.TASKS_SEQSTART[0].Set(1)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,17 +10,17 @@ import (
|
|||||||
const CPU_FREQUENCY = 64000000
|
const CPU_FREQUENCY = 64000000
|
||||||
|
|
||||||
// Get peripheral and pin number for this GPIO pin.
|
// Get peripheral and pin number for this GPIO pin.
|
||||||
func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) {
|
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||||
if p.Pin >= 32 {
|
if p >= 32 {
|
||||||
return nrf.P1, p.Pin - 32
|
return nrf.P1, uint32(p - 32)
|
||||||
} else {
|
} else {
|
||||||
return nrf.P0, p.Pin
|
return nrf.P0, uint32(p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uart UART) setPins(tx, rx uint32) {
|
func (uart UART) setPins(tx, rx Pin) {
|
||||||
nrf.UART0.PSEL.TXD = nrf.RegValue(tx)
|
nrf.UART0.PSEL.TXD.Set(uint32(tx))
|
||||||
nrf.UART0.PSEL.RXD = nrf.RegValue(rx)
|
nrf.UART0.PSEL.RXD.Set(uint32(rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export UARTE0_UART0_IRQHandler
|
//go:export UARTE0_UART0_IRQHandler
|
||||||
@@ -28,13 +28,13 @@ func handleUART0() {
|
|||||||
UART0.handleInterrupt()
|
UART0.handleInterrupt()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i2c I2C) setPins(scl, sda uint8) {
|
func (i2c I2C) setPins(scl, sda Pin) {
|
||||||
i2c.Bus.PSEL.SCL = nrf.RegValue(scl)
|
i2c.Bus.PSEL.SCL.Set(uint32(scl))
|
||||||
i2c.Bus.PSEL.SDA = nrf.RegValue(sda)
|
i2c.Bus.PSEL.SDA.Set(uint32(sda))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPI
|
// SPI
|
||||||
func (spi SPI) setPins(sck, mosi, miso uint8) {
|
func (spi SPI) setPins(sck, mosi, miso Pin) {
|
||||||
if sck == 0 {
|
if sck == 0 {
|
||||||
sck = SPI0_SCK_PIN
|
sck = SPI0_SCK_PIN
|
||||||
}
|
}
|
||||||
@@ -44,9 +44,9 @@ func (spi SPI) setPins(sck, mosi, miso uint8) {
|
|||||||
if miso == 0 {
|
if miso == 0 {
|
||||||
miso = SPI0_MISO_PIN
|
miso = SPI0_MISO_PIN
|
||||||
}
|
}
|
||||||
spi.Bus.PSEL.SCK = nrf.RegValue(sck)
|
spi.Bus.PSEL.SCK.Set(uint32(sck))
|
||||||
spi.Bus.PSEL.MOSI = nrf.RegValue(mosi)
|
spi.Bus.PSEL.MOSI.Set(uint32(mosi))
|
||||||
spi.Bus.PSEL.MISO = nrf.RegValue(miso)
|
spi.Bus.PSEL.MISO.Set(uint32(miso))
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitADC initializes the registers needed for ADC.
|
// InitADC initializes the registers needed for ADC.
|
||||||
@@ -93,53 +93,53 @@ func (a ADC) Get() uint16 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
nrf.SAADC.RESOLUTION = nrf.SAADC_RESOLUTION_VAL_12bit
|
nrf.SAADC.RESOLUTION.Set(nrf.SAADC_RESOLUTION_VAL_12bit)
|
||||||
|
|
||||||
// Enable ADC.
|
// Enable ADC.
|
||||||
nrf.SAADC.ENABLE = (nrf.SAADC_ENABLE_ENABLE_Enabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Enabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
||||||
for i := 0; i < 8; i++ {
|
for i := 0; i < 8; i++ {
|
||||||
nrf.SAADC.CH[i].PSELN = nrf.SAADC_CH_PSELP_PSELP_NC
|
nrf.SAADC.CH[i].PSELN.Set(nrf.SAADC_CH_PSELP_PSELP_NC)
|
||||||
nrf.SAADC.CH[i].PSELP = nrf.SAADC_CH_PSELP_PSELP_NC
|
nrf.SAADC.CH[i].PSELP.Set(nrf.SAADC_CH_PSELP_PSELP_NC)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure ADC.
|
// Configure ADC.
|
||||||
nrf.SAADC.CH[0].CONFIG = ((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESP_Pos) & nrf.SAADC_CH_CONFIG_RESP_Msk) |
|
nrf.SAADC.CH[0].CONFIG.Set(((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESP_Pos) & nrf.SAADC_CH_CONFIG_RESP_Msk) |
|
||||||
((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESN_Pos) & nrf.SAADC_CH_CONFIG_RESN_Msk) |
|
((nrf.SAADC_CH_CONFIG_RESP_Bypass << nrf.SAADC_CH_CONFIG_RESN_Pos) & nrf.SAADC_CH_CONFIG_RESN_Msk) |
|
||||||
((nrf.SAADC_CH_CONFIG_GAIN_Gain1_5 << nrf.SAADC_CH_CONFIG_GAIN_Pos) & nrf.SAADC_CH_CONFIG_GAIN_Msk) |
|
((nrf.SAADC_CH_CONFIG_GAIN_Gain1_5 << nrf.SAADC_CH_CONFIG_GAIN_Pos) & nrf.SAADC_CH_CONFIG_GAIN_Msk) |
|
||||||
((nrf.SAADC_CH_CONFIG_REFSEL_Internal << nrf.SAADC_CH_CONFIG_REFSEL_Pos) & nrf.SAADC_CH_CONFIG_REFSEL_Msk) |
|
((nrf.SAADC_CH_CONFIG_REFSEL_Internal << nrf.SAADC_CH_CONFIG_REFSEL_Pos) & nrf.SAADC_CH_CONFIG_REFSEL_Msk) |
|
||||||
((nrf.SAADC_CH_CONFIG_TACQ_3us << nrf.SAADC_CH_CONFIG_TACQ_Pos) & nrf.SAADC_CH_CONFIG_TACQ_Msk) |
|
((nrf.SAADC_CH_CONFIG_TACQ_3us << nrf.SAADC_CH_CONFIG_TACQ_Pos) & nrf.SAADC_CH_CONFIG_TACQ_Msk) |
|
||||||
((nrf.SAADC_CH_CONFIG_MODE_SE << nrf.SAADC_CH_CONFIG_MODE_Pos) & nrf.SAADC_CH_CONFIG_MODE_Msk)
|
((nrf.SAADC_CH_CONFIG_MODE_SE << nrf.SAADC_CH_CONFIG_MODE_Pos) & nrf.SAADC_CH_CONFIG_MODE_Msk))
|
||||||
|
|
||||||
// Set pin to read.
|
// Set pin to read.
|
||||||
nrf.SAADC.CH[0].PSELN = nrf.RegValue(pwmPin)
|
nrf.SAADC.CH[0].PSELN.Set(pwmPin)
|
||||||
nrf.SAADC.CH[0].PSELP = nrf.RegValue(pwmPin)
|
nrf.SAADC.CH[0].PSELP.Set(pwmPin)
|
||||||
|
|
||||||
// Destination for sample result.
|
// Destination for sample result.
|
||||||
nrf.SAADC.RESULT.PTR = nrf.RegValue(uintptr(unsafe.Pointer(&value)))
|
nrf.SAADC.RESULT.PTR.Set(uint32(uintptr(unsafe.Pointer(&value))))
|
||||||
nrf.SAADC.RESULT.MAXCNT = 1 // One sample
|
nrf.SAADC.RESULT.MAXCNT.Set(1) // One sample
|
||||||
|
|
||||||
// Start tasks.
|
// Start tasks.
|
||||||
nrf.SAADC.TASKS_START = 1
|
nrf.SAADC.TASKS_START.Set(1)
|
||||||
for nrf.SAADC.EVENTS_STARTED == 0 {
|
for nrf.SAADC.EVENTS_STARTED.Get() == 0 {
|
||||||
}
|
}
|
||||||
nrf.SAADC.EVENTS_STARTED = 0x00
|
nrf.SAADC.EVENTS_STARTED.Set(0x00)
|
||||||
|
|
||||||
// Start the sample task.
|
// Start the sample task.
|
||||||
nrf.SAADC.TASKS_SAMPLE = 1
|
nrf.SAADC.TASKS_SAMPLE.Set(1)
|
||||||
|
|
||||||
// Wait until the sample task is done.
|
// Wait until the sample task is done.
|
||||||
for nrf.SAADC.EVENTS_END == 0 {
|
for nrf.SAADC.EVENTS_END.Get() == 0 {
|
||||||
}
|
}
|
||||||
nrf.SAADC.EVENTS_END = 0x00
|
nrf.SAADC.EVENTS_END.Set(0x00)
|
||||||
|
|
||||||
// Stop the ADC
|
// Stop the ADC
|
||||||
nrf.SAADC.TASKS_STOP = 1
|
nrf.SAADC.TASKS_STOP.Set(1)
|
||||||
for nrf.SAADC.EVENTS_STOPPED == 0 {
|
for nrf.SAADC.EVENTS_STOPPED.Get() == 0 {
|
||||||
}
|
}
|
||||||
nrf.SAADC.EVENTS_STOPPED = 0
|
nrf.SAADC.EVENTS_STOPPED.Set(0)
|
||||||
|
|
||||||
// Disable the ADC.
|
// Disable the ADC.
|
||||||
nrf.SAADC.ENABLE = (nrf.SAADC_ENABLE_ENABLE_Disabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Disabled << nrf.SAADC_ENABLE_ENABLE_Pos)
|
||||||
|
|
||||||
if value < 0 {
|
if value < 0 {
|
||||||
value = 0
|
value = 0
|
||||||
@@ -174,21 +174,21 @@ func (pwm PWM) Set(value uint16) {
|
|||||||
|
|
||||||
p := pwms[i]
|
p := pwms[i]
|
||||||
|
|
||||||
p.PSEL.OUT[0] = nrf.RegValue(pwm.Pin)
|
p.PSEL.OUT[0].Set(uint32(pwm.Pin))
|
||||||
p.PSEL.OUT[1] = nrf.RegValue(pwm.Pin)
|
p.PSEL.OUT[1].Set(uint32(pwm.Pin))
|
||||||
p.PSEL.OUT[2] = nrf.RegValue(pwm.Pin)
|
p.PSEL.OUT[2].Set(uint32(pwm.Pin))
|
||||||
p.PSEL.OUT[3] = nrf.RegValue(pwm.Pin)
|
p.PSEL.OUT[3].Set(uint32(pwm.Pin))
|
||||||
p.ENABLE = (nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos)
|
p.ENABLE.Set(nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos)
|
||||||
p.PRESCALER = nrf.PWM_PRESCALER_PRESCALER_DIV_2
|
p.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_2)
|
||||||
p.MODE = nrf.PWM_MODE_UPDOWN_Up
|
p.MODE.Set(nrf.PWM_MODE_UPDOWN_Up)
|
||||||
p.COUNTERTOP = 16384 // frequency
|
p.COUNTERTOP.Set(16384) // frequency
|
||||||
p.LOOP = 0
|
p.LOOP.Set(0)
|
||||||
p.DECODER = (nrf.PWM_DECODER_LOAD_Common << nrf.PWM_DECODER_LOAD_Pos) | (nrf.PWM_DECODER_MODE_RefreshCount << nrf.PWM_DECODER_MODE_Pos)
|
p.DECODER.Set((nrf.PWM_DECODER_LOAD_Common << nrf.PWM_DECODER_LOAD_Pos) | (nrf.PWM_DECODER_MODE_RefreshCount << nrf.PWM_DECODER_MODE_Pos))
|
||||||
p.SEQ[0].PTR = nrf.RegValue(uintptr(unsafe.Pointer(&pwmChannelSequence[i])))
|
p.SEQ[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&pwmChannelSequence[i]))))
|
||||||
p.SEQ[0].CNT = 1
|
p.SEQ[0].CNT.Set(1)
|
||||||
p.SEQ[0].REFRESH = 1
|
p.SEQ[0].REFRESH.Set(1)
|
||||||
p.SEQ[0].ENDDELAY = 0
|
p.SEQ[0].ENDDELAY.Set(0)
|
||||||
p.TASKS_SEQSTART[0] = 1
|
p.TASKS_SEQSTART[0].Set(1)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ package machine
|
|||||||
|
|
||||||
// Peripheral abstraction layer for the stm32.
|
// Peripheral abstraction layer for the stm32.
|
||||||
|
|
||||||
type GPIOMode uint8
|
type PinMode uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
portA = iota * 16
|
portA Pin = iota * 16
|
||||||
portB
|
portB
|
||||||
portC
|
portC
|
||||||
portD
|
portD
|
||||||
|
|||||||
+131
-126
@@ -13,25 +13,25 @@ import (
|
|||||||
const CPU_FREQUENCY = 72000000
|
const CPU_FREQUENCY = 72000000
|
||||||
|
|
||||||
const (
|
const (
|
||||||
GPIO_INPUT = 0 // Input mode
|
PinInput PinMode = 0 // Input mode
|
||||||
GPIO_OUTPUT_10MHz = 1 // Output mode, max speed 10MHz
|
PinOutput10MHz PinMode = 1 // Output mode, max speed 10MHz
|
||||||
GPIO_OUTPUT_2MHz = 2 // Output mode, max speed 2MHz
|
PinOutput2MHz PinMode = 2 // Output mode, max speed 2MHz
|
||||||
GPIO_OUTPUT_50MHz = 3 // Output mode, max speed 50MHz
|
PinOutput50MHz PinMode = 3 // Output mode, max speed 50MHz
|
||||||
GPIO_OUTPUT = GPIO_OUTPUT_2MHz
|
PinOutput PinMode = PinOutput2MHz
|
||||||
|
|
||||||
GPIO_INPUT_MODE_ANALOG = 0 // Input analog mode
|
PinInputModeAnalog PinMode = 0 // Input analog mode
|
||||||
GPIO_INPUT_MODE_FLOATING = 4 // Input floating mode
|
PinInputModeFloating PinMode = 4 // Input floating mode
|
||||||
GPIO_INPUT_MODE_PULL_UP_DOWN = 8 // Input pull up/down mode
|
PinInputModePullUpDown PinMode = 8 // Input pull up/down mode
|
||||||
GPIO_INPUT_MODE_RESERVED = 12 // Input mode (reserved)
|
PinInputModeReserved PinMode = 12 // Input mode (reserved)
|
||||||
|
|
||||||
GPIO_OUTPUT_MODE_GP_PUSH_PULL = 0 // Output mode general purpose push/pull
|
PinOutputModeGPPushPull PinMode = 0 // Output mode general purpose push/pull
|
||||||
GPIO_OUTPUT_MODE_GP_OPEN_DRAIN = 4 // Output mode general purpose open drain
|
PinOutputModeGPOpenDrain PinMode = 4 // Output mode general purpose open drain
|
||||||
GPIO_OUTPUT_MODE_ALT_PUSH_PULL = 8 // Output mode alt. purpose push/pull
|
PinOutputModeAltPushPull PinMode = 8 // Output mode alt. purpose push/pull
|
||||||
GPIO_OUTPUT_MODE_ALT_OPEN_DRAIN = 12 // Output mode alt. purpose open drain
|
PinOutputModeAltOpenDrain PinMode = 12 // Output mode alt. purpose open drain
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p GPIO) getPort() *stm32.GPIO_Type {
|
func (p Pin) getPort() *stm32.GPIO_Type {
|
||||||
switch p.Pin / 16 {
|
switch p / 16 {
|
||||||
case 0:
|
case 0:
|
||||||
return stm32.GPIOA
|
return stm32.GPIOA
|
||||||
case 1:
|
case 1:
|
||||||
@@ -52,50 +52,50 @@ func (p GPIO) getPort() *stm32.GPIO_Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// enableClock enables the clock for this desired GPIO port.
|
// enableClock enables the clock for this desired GPIO port.
|
||||||
func (p GPIO) enableClock() {
|
func (p Pin) enableClock() {
|
||||||
switch p.Pin / 16 {
|
switch p / 16 {
|
||||||
case 0:
|
case 0:
|
||||||
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPAEN
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPAEN)
|
||||||
case 1:
|
case 1:
|
||||||
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPBEN
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPBEN)
|
||||||
case 2:
|
case 2:
|
||||||
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPCEN
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPCEN)
|
||||||
case 3:
|
case 3:
|
||||||
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPDEN
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPDEN)
|
||||||
case 4:
|
case 4:
|
||||||
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPEEN
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPEEN)
|
||||||
case 5:
|
case 5:
|
||||||
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPFEN
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPFEN)
|
||||||
case 6:
|
case 6:
|
||||||
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_IOPGEN
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_IOPGEN)
|
||||||
default:
|
default:
|
||||||
panic("machine: unknown port")
|
panic("machine: unknown port")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure this pin with the given configuration.
|
// Configure this pin with the given configuration.
|
||||||
func (p GPIO) Configure(config GPIOConfig) {
|
func (p Pin) Configure(config PinConfig) {
|
||||||
// Configure the GPIO pin.
|
// Configure the GPIO pin.
|
||||||
p.enableClock()
|
p.enableClock()
|
||||||
port := p.getPort()
|
port := p.getPort()
|
||||||
pin := p.Pin % 16
|
pin := uint8(p) % 16
|
||||||
pos := p.Pin % 8 * 4
|
pos := uint8(p) % 8 * 4
|
||||||
if pin < 8 {
|
if pin < 8 {
|
||||||
port.CRL = stm32.RegValue((uint32(port.CRL) &^ (0xf << pos)) | (uint32(config.Mode) << pos))
|
port.CRL.Set((uint32(port.CRL.Get()) &^ (0xf << pos)) | (uint32(config.Mode) << pos))
|
||||||
} else {
|
} else {
|
||||||
port.CRH = stm32.RegValue((uint32(port.CRH) &^ (0xf << pos)) | (uint32(config.Mode) << pos))
|
port.CRH.Set((uint32(port.CRH.Get()) &^ (0xf << pos)) | (uint32(config.Mode) << pos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the pin to high or low.
|
// Set the pin to high or low.
|
||||||
// Warning: only use this on an output pin!
|
// Warning: only use this on an output pin!
|
||||||
func (p GPIO) Set(high bool) {
|
func (p Pin) Set(high bool) {
|
||||||
port := p.getPort()
|
port := p.getPort()
|
||||||
pin := p.Pin % 16
|
pin := uint8(p) % 16
|
||||||
if high {
|
if high {
|
||||||
port.BSRR = 1 << pin
|
port.BSRR.Set(1 << pin)
|
||||||
} else {
|
} else {
|
||||||
port.BSRR = 1 << (pin + 16)
|
port.BSRR.Set(1 << (pin + 16))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,24 +122,24 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
switch config.TX {
|
switch config.TX {
|
||||||
case PB6:
|
case PB6:
|
||||||
// use alternate TX/RX pins PB6/PB7 via AFIO mapping
|
// use alternate TX/RX pins PB6/PB7 via AFIO mapping
|
||||||
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_AFIOEN
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_AFIOEN)
|
||||||
stm32.AFIO.MAPR |= stm32.AFIO_MAPR_USART1_REMAP
|
stm32.AFIO.MAPR.SetBits(stm32.AFIO_MAPR_USART1_REMAP)
|
||||||
GPIO{PB6}.Configure(GPIOConfig{Mode: GPIO_OUTPUT_50MHz + GPIO_OUTPUT_MODE_ALT_PUSH_PULL})
|
PB6.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
||||||
GPIO{PB7}.Configure(GPIOConfig{Mode: GPIO_INPUT_MODE_FLOATING})
|
PB7.Configure(PinConfig{Mode: PinInputModeFloating})
|
||||||
default:
|
default:
|
||||||
// use standard TX/RX pins PA9 and PA10
|
// use standard TX/RX pins PA9 and PA10
|
||||||
GPIO{UART_TX_PIN}.Configure(GPIOConfig{Mode: GPIO_OUTPUT_50MHz + GPIO_OUTPUT_MODE_ALT_PUSH_PULL})
|
UART_TX_PIN.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
||||||
GPIO{UART_RX_PIN}.Configure(GPIOConfig{Mode: GPIO_INPUT_MODE_FLOATING})
|
UART_RX_PIN.Configure(PinConfig{Mode: PinInputModeFloating})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable USART1 clock
|
// Enable USART1 clock
|
||||||
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_USART1EN
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
||||||
|
|
||||||
// Set baud rate
|
// Set baud rate
|
||||||
uart.SetBaudRate(config.BaudRate)
|
uart.SetBaudRate(config.BaudRate)
|
||||||
|
|
||||||
// Enable USART1 port.
|
// Enable USART1 port.
|
||||||
stm32.USART1.CR1 = stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE
|
stm32.USART1.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
||||||
|
|
||||||
// Enable RX IRQ.
|
// Enable RX IRQ.
|
||||||
arm.SetPriority(stm32.IRQ_USART1, 0xc0)
|
arm.SetPriority(stm32.IRQ_USART1, 0xc0)
|
||||||
@@ -150,21 +150,21 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
func (uart UART) SetBaudRate(br uint32) {
|
func (uart UART) SetBaudRate(br uint32) {
|
||||||
// first divide by PCLK2 prescaler (div 1) and then desired baudrate
|
// first divide by PCLK2 prescaler (div 1) and then desired baudrate
|
||||||
divider := CPU_FREQUENCY / br
|
divider := CPU_FREQUENCY / br
|
||||||
stm32.USART1.BRR = stm32.RegValue(divider)
|
stm32.USART1.BRR.Set(divider)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteByte writes a byte of data to the UART.
|
// WriteByte writes a byte of data to the UART.
|
||||||
func (uart UART) WriteByte(c byte) error {
|
func (uart UART) WriteByte(c byte) error {
|
||||||
stm32.USART1.DR = stm32.RegValue(c)
|
stm32.USART1.DR.Set(uint32(c))
|
||||||
|
|
||||||
for (stm32.USART1.SR & stm32.USART_SR_TXE) == 0 {
|
for !stm32.USART1.SR.HasBits(stm32.USART_SR_TXE) {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export USART1_IRQHandler
|
//go:export USART1_IRQHandler
|
||||||
func handleUART1() {
|
func handleUART1() {
|
||||||
UART1.Receive(byte((stm32.USART1.DR & 0xFF)))
|
UART1.Receive(byte((stm32.USART1.DR.Get() & 0xFF)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPI on the STM32.
|
// SPI on the STM32.
|
||||||
@@ -183,9 +183,9 @@ var (
|
|||||||
// SPIConfig is used to store config info for SPI.
|
// SPIConfig is used to store config info for SPI.
|
||||||
type SPIConfig struct {
|
type SPIConfig struct {
|
||||||
Frequency uint32
|
Frequency uint32
|
||||||
SCK uint8
|
SCK Pin
|
||||||
MOSI uint8
|
MOSI Pin
|
||||||
MISO uint8
|
MISO Pin
|
||||||
LSBFirst bool
|
LSBFirst bool
|
||||||
Mode uint8
|
Mode uint8
|
||||||
}
|
}
|
||||||
@@ -198,9 +198,9 @@ type SPIConfig struct {
|
|||||||
// - hardware SS pin?
|
// - hardware SS pin?
|
||||||
func (spi SPI) Configure(config SPIConfig) {
|
func (spi SPI) Configure(config SPIConfig) {
|
||||||
// enable clock for SPI
|
// enable clock for SPI
|
||||||
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_SPI1EN
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
||||||
|
|
||||||
var conf uint16
|
var conf uint32
|
||||||
|
|
||||||
// set frequency
|
// set frequency
|
||||||
switch config.Frequency {
|
switch config.Frequency {
|
||||||
@@ -250,37 +250,37 @@ func (spi SPI) Configure(config SPIConfig) {
|
|||||||
conf |= stm32.SPI_Mode_Master
|
conf |= stm32.SPI_Mode_Master
|
||||||
|
|
||||||
// now set the configuration
|
// now set the configuration
|
||||||
spi.Bus.CR1 = stm32.RegValue(conf)
|
spi.Bus.CR1.Set(conf)
|
||||||
|
|
||||||
// init pins
|
// init pins
|
||||||
spi.setPins(config.SCK, config.MOSI, config.MISO)
|
spi.setPins(config.SCK, config.MOSI, config.MISO)
|
||||||
|
|
||||||
// enable SPI interface
|
// enable SPI interface
|
||||||
spi.Bus.CR1 |= stm32.SPI_CR1_SPE
|
spi.Bus.CR1.SetBits(stm32.SPI_CR1_SPE)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer writes/reads a single byte using the SPI interface.
|
// Transfer writes/reads a single byte using the SPI interface.
|
||||||
func (spi SPI) Transfer(w byte) (byte, error) {
|
func (spi SPI) Transfer(w byte) (byte, error) {
|
||||||
// Write data to be transmitted to the SPI data register
|
// Write data to be transmitted to the SPI data register
|
||||||
spi.Bus.DR = stm32.RegValue(w)
|
spi.Bus.DR.Set(uint32(w))
|
||||||
|
|
||||||
// Wait until transmit complete
|
// Wait until transmit complete
|
||||||
for (spi.Bus.SR & stm32.SPI_SR_TXE) == 0 {
|
for !spi.Bus.SR.HasBits(stm32.SPI_SR_TXE) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until receive complete
|
// Wait until receive complete
|
||||||
for (spi.Bus.SR & stm32.SPI_SR_RXNE) == 0 {
|
for !spi.Bus.SR.HasBits(stm32.SPI_SR_RXNE) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until SPI is not busy
|
// Wait until SPI is not busy
|
||||||
for (spi.Bus.SR & stm32.SPI_SR_BSY) > 0 {
|
for spi.Bus.SR.HasBits(stm32.SPI_SR_BSY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return received data from SPI data register
|
// Return received data from SPI data register
|
||||||
return byte(spi.Bus.DR), nil
|
return byte(spi.Bus.DR.Get()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (spi SPI) setPins(sck, mosi, miso uint8) {
|
func (spi SPI) setPins(sck, mosi, miso Pin) {
|
||||||
if sck == 0 {
|
if sck == 0 {
|
||||||
sck = SPI0_SCK_PIN
|
sck = SPI0_SCK_PIN
|
||||||
}
|
}
|
||||||
@@ -291,9 +291,9 @@ func (spi SPI) setPins(sck, mosi, miso uint8) {
|
|||||||
miso = SPI0_MISO_PIN
|
miso = SPI0_MISO_PIN
|
||||||
}
|
}
|
||||||
|
|
||||||
GPIO{sck}.Configure(GPIOConfig{Mode: GPIO_OUTPUT_50MHz + GPIO_OUTPUT_MODE_ALT_PUSH_PULL})
|
sck.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
||||||
GPIO{mosi}.Configure(GPIOConfig{Mode: GPIO_OUTPUT_50MHz + GPIO_OUTPUT_MODE_ALT_PUSH_PULL})
|
mosi.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltPushPull})
|
||||||
GPIO{miso}.Configure(GPIOConfig{Mode: GPIO_INPUT_MODE_FLOATING})
|
miso.Configure(PinConfig{Mode: PinInputModeFloating})
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C on the STM32F103xx.
|
// I2C on the STM32F103xx.
|
||||||
@@ -312,8 +312,8 @@ var (
|
|||||||
// I2CConfig is used to store config info for I2C.
|
// I2CConfig is used to store config info for I2C.
|
||||||
type I2CConfig struct {
|
type I2CConfig struct {
|
||||||
Frequency uint32
|
Frequency uint32
|
||||||
SCL uint8
|
SCL Pin
|
||||||
SDA uint8
|
SDA Pin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure is intended to setup the I2C interface.
|
// Configure is intended to setup the I2C interface.
|
||||||
@@ -324,26 +324,26 @@ func (i2c I2C) Configure(config I2CConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// enable clock for I2C
|
// enable clock for I2C
|
||||||
stm32.RCC.APB1ENR |= stm32.RCC_APB1ENR_I2C1EN
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
|
||||||
|
|
||||||
// I2C1 pins
|
// I2C1 pins
|
||||||
switch config.SDA {
|
switch config.SDA {
|
||||||
case PB9:
|
case PB9:
|
||||||
config.SCL = PB8
|
config.SCL = PB8
|
||||||
// use alternate I2C1 pins PB8/PB9 via AFIO mapping
|
// use alternate I2C1 pins PB8/PB9 via AFIO mapping
|
||||||
stm32.RCC.APB2ENR |= stm32.RCC_APB2ENR_AFIOEN
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_AFIOEN)
|
||||||
stm32.AFIO.MAPR |= stm32.AFIO_MAPR_I2C1_REMAP
|
stm32.AFIO.MAPR.SetBits(stm32.AFIO_MAPR_I2C1_REMAP)
|
||||||
default:
|
default:
|
||||||
// use default I2C1 pins PB6/PB7
|
// use default I2C1 pins PB6/PB7
|
||||||
config.SDA = SDA_PIN
|
config.SDA = SDA_PIN
|
||||||
config.SCL = SCL_PIN
|
config.SCL = SCL_PIN
|
||||||
}
|
}
|
||||||
|
|
||||||
GPIO{config.SDA}.Configure(GPIOConfig{Mode: GPIO_OUTPUT_50MHz + GPIO_OUTPUT_MODE_ALT_OPEN_DRAIN})
|
config.SDA.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltOpenDrain})
|
||||||
GPIO{config.SCL}.Configure(GPIOConfig{Mode: GPIO_OUTPUT_50MHz + GPIO_OUTPUT_MODE_ALT_OPEN_DRAIN})
|
config.SCL.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltOpenDrain})
|
||||||
|
|
||||||
// Disable the selected I2C peripheral to configure
|
// Disable the selected I2C peripheral to configure
|
||||||
i2c.Bus.CR1 &^= stm32.I2C_CR1_PE
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_PE)
|
||||||
|
|
||||||
// pclk1 clock speed is main frequency divided by PCK1 prescaler (div 2)
|
// pclk1 clock speed is main frequency divided by PCK1 prescaler (div 2)
|
||||||
pclk1 := uint32(CPU_FREQUENCY / 2)
|
pclk1 := uint32(CPU_FREQUENCY / 2)
|
||||||
@@ -351,40 +351,40 @@ func (i2c I2C) Configure(config I2CConfig) {
|
|||||||
// set freqency range to pclk1 clock speed in Mhz.
|
// set freqency range to pclk1 clock speed in Mhz.
|
||||||
// aka setting the value 36 means to use 36MhZ clock.
|
// aka setting the value 36 means to use 36MhZ clock.
|
||||||
pclk1Mhz := pclk1 / 1000000
|
pclk1Mhz := pclk1 / 1000000
|
||||||
i2c.Bus.CR2 |= stm32.RegValue(pclk1Mhz)
|
i2c.Bus.CR2.SetBits(pclk1Mhz)
|
||||||
|
|
||||||
switch config.Frequency {
|
switch config.Frequency {
|
||||||
case TWI_FREQ_100KHZ:
|
case TWI_FREQ_100KHZ:
|
||||||
// Normal mode speed calculation
|
// Normal mode speed calculation
|
||||||
ccr := pclk1 / (config.Frequency * 2)
|
ccr := pclk1 / (config.Frequency * 2)
|
||||||
i2c.Bus.CCR = stm32.RegValue(ccr)
|
i2c.Bus.CCR.Set(ccr)
|
||||||
|
|
||||||
// duty cycle 2
|
// duty cycle 2
|
||||||
i2c.Bus.CCR &^= stm32.I2C_CCR_DUTY
|
i2c.Bus.CCR.ClearBits(stm32.I2C_CCR_DUTY)
|
||||||
|
|
||||||
// frequency standard mode
|
// frequency standard mode
|
||||||
i2c.Bus.CCR &^= stm32.I2C_CCR_F_S
|
i2c.Bus.CCR.ClearBits(stm32.I2C_CCR_F_S)
|
||||||
|
|
||||||
// Set Maximum Rise Time for standard mode
|
// Set Maximum Rise Time for standard mode
|
||||||
i2c.Bus.TRISE = stm32.RegValue(pclk1Mhz)
|
i2c.Bus.TRISE.Set(pclk1Mhz)
|
||||||
|
|
||||||
case TWI_FREQ_400KHZ:
|
case TWI_FREQ_400KHZ:
|
||||||
// Fast mode speed calculation
|
// Fast mode speed calculation
|
||||||
ccr := pclk1 / (config.Frequency * 3)
|
ccr := pclk1 / (config.Frequency * 3)
|
||||||
i2c.Bus.CCR = stm32.RegValue(ccr)
|
i2c.Bus.CCR.Set(ccr)
|
||||||
|
|
||||||
// duty cycle 2
|
// duty cycle 2
|
||||||
i2c.Bus.CCR &^= stm32.I2C_CCR_DUTY
|
i2c.Bus.CCR.ClearBits(stm32.I2C_CCR_DUTY)
|
||||||
|
|
||||||
// frequency fast mode
|
// frequency fast mode
|
||||||
i2c.Bus.CCR |= stm32.I2C_CCR_F_S
|
i2c.Bus.CCR.SetBits(stm32.I2C_CCR_F_S)
|
||||||
|
|
||||||
// Set Maximum Rise Time for fast mode
|
// Set Maximum Rise Time for fast mode
|
||||||
i2c.Bus.TRISE = stm32.RegValue(((pclk1Mhz * 300) / 1000))
|
i2c.Bus.TRISE.Set(((pclk1Mhz * 300) / 1000))
|
||||||
}
|
}
|
||||||
|
|
||||||
// re-enable the selected I2C peripheral
|
// re-enable the selected I2C peripheral
|
||||||
i2c.Bus.CR1 |= stm32.I2C_CR1_PE
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_PE)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tx does a single I2C transaction at the specified address.
|
// Tx does a single I2C transaction at the specified address.
|
||||||
@@ -435,11 +435,11 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Disable ACK of received data
|
// Disable ACK of received data
|
||||||
i2c.Bus.CR1 &^= stm32.I2C_CR1_ACK
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
|
||||||
|
|
||||||
// clear timeout here
|
// clear timeout here
|
||||||
timeout := i2cTimeout
|
timeout := i2cTimeout
|
||||||
for i2c.Bus.SR2&(stm32.I2C_SR2_MSL|stm32.I2C_SR2_BUSY) == 0 {
|
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on read clear address")
|
return errors.New("I2C timeout on read clear address")
|
||||||
@@ -447,10 +447,10 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate stop condition
|
// Generate stop condition
|
||||||
i2c.Bus.CR1 |= stm32.I2C_CR1_STOP
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
||||||
|
|
||||||
timeout = i2cTimeout
|
timeout = i2cTimeout
|
||||||
for (i2c.Bus.SR1 & stm32.I2C_SR1_RxNE) == 0 {
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_RxNE) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on read 1 byte")
|
return errors.New("I2C timeout on read 1 byte")
|
||||||
@@ -458,17 +458,17 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read and return data byte from I2C data register
|
// Read and return data byte from I2C data register
|
||||||
r[0] = byte(i2c.Bus.DR)
|
r[0] = byte(i2c.Bus.DR.Get())
|
||||||
|
|
||||||
// wait for stop
|
// wait for stop
|
||||||
return i2c.waitForStop()
|
return i2c.waitForStop()
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
// enable pos
|
// enable pos
|
||||||
i2c.Bus.CR1 |= stm32.I2C_CR1_POS
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_POS)
|
||||||
|
|
||||||
// Enable ACK of received data
|
// Enable ACK of received data
|
||||||
i2c.Bus.CR1 |= stm32.I2C_CR1_ACK
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
|
||||||
|
|
||||||
// send address
|
// send address
|
||||||
err = i2c.sendAddress(uint8(addr), false)
|
err = i2c.sendAddress(uint8(addr), false)
|
||||||
@@ -478,7 +478,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
|
|
||||||
// clear address here
|
// clear address here
|
||||||
timeout := i2cTimeout
|
timeout := i2cTimeout
|
||||||
for i2c.Bus.SR2&(stm32.I2C_SR2_MSL|stm32.I2C_SR2_BUSY) == 0 {
|
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on read clear address")
|
return errors.New("I2C timeout on read clear address")
|
||||||
@@ -486,11 +486,11 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Disable ACK of received data
|
// Disable ACK of received data
|
||||||
i2c.Bus.CR1 &^= stm32.I2C_CR1_ACK
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
|
||||||
|
|
||||||
// wait for btf. we need a longer timeout here than normal.
|
// wait for btf. we need a longer timeout here than normal.
|
||||||
timeout = 1000
|
timeout = 1000
|
||||||
for (i2c.Bus.SR1 & stm32.I2C_SR1_BTF) == 0 {
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on read 2 bytes")
|
return errors.New("I2C timeout on read 2 bytes")
|
||||||
@@ -498,18 +498,23 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate stop condition
|
// Generate stop condition
|
||||||
i2c.Bus.CR1 |= stm32.I2C_CR1_STOP
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
||||||
|
|
||||||
// read the 2 bytes by reading twice.
|
// read the 2 bytes by reading twice.
|
||||||
r[0] = byte(i2c.Bus.DR)
|
r[0] = byte(i2c.Bus.DR.Get())
|
||||||
r[1] = byte(i2c.Bus.DR)
|
r[1] = byte(i2c.Bus.DR.Get())
|
||||||
|
|
||||||
// wait for stop
|
// wait for stop
|
||||||
return i2c.waitForStop()
|
err = i2c.waitForStop()
|
||||||
|
|
||||||
|
//disable pos
|
||||||
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_POS)
|
||||||
|
|
||||||
|
return err
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
// Enable ACK of received data
|
// Enable ACK of received data
|
||||||
i2c.Bus.CR1 |= stm32.I2C_CR1_ACK
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
|
||||||
|
|
||||||
// send address
|
// send address
|
||||||
err = i2c.sendAddress(uint8(addr), false)
|
err = i2c.sendAddress(uint8(addr), false)
|
||||||
@@ -519,7 +524,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
|
|
||||||
// clear address here
|
// clear address here
|
||||||
timeout := i2cTimeout
|
timeout := i2cTimeout
|
||||||
for i2c.Bus.SR2&(stm32.I2C_SR2_MSL|stm32.I2C_SR2_BUSY) == 0 {
|
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on read clear address")
|
return errors.New("I2C timeout on read clear address")
|
||||||
@@ -527,11 +532,11 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Enable ACK of received data
|
// Enable ACK of received data
|
||||||
i2c.Bus.CR1 |= stm32.I2C_CR1_ACK
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
|
||||||
|
|
||||||
// wait for btf. we need a longer timeout here than normal.
|
// wait for btf. we need a longer timeout here than normal.
|
||||||
timeout = 1000
|
timeout = 1000
|
||||||
for (i2c.Bus.SR1 & stm32.I2C_SR1_BTF) == 0 {
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
println("I2C timeout on read 3 bytes")
|
println("I2C timeout on read 3 bytes")
|
||||||
@@ -540,13 +545,13 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Disable ACK of received data
|
// Disable ACK of received data
|
||||||
i2c.Bus.CR1 &^= stm32.I2C_CR1_ACK
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
|
||||||
|
|
||||||
// read the first byte
|
// read the first byte
|
||||||
r[0] = byte(i2c.Bus.DR)
|
r[0] = byte(i2c.Bus.DR.Get())
|
||||||
|
|
||||||
timeout = 1000
|
timeout = 1000
|
||||||
for (i2c.Bus.SR1 & stm32.I2C_SR1_BTF) == 0 {
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on read 3 bytes")
|
return errors.New("I2C timeout on read 3 bytes")
|
||||||
@@ -554,11 +559,11 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate stop condition
|
// Generate stop condition
|
||||||
i2c.Bus.CR1 |= stm32.I2C_CR1_STOP
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
||||||
|
|
||||||
// read the last 2 bytes by reading twice.
|
// read the last 2 bytes by reading twice.
|
||||||
r[1] = byte(i2c.Bus.DR)
|
r[1] = byte(i2c.Bus.DR.Get())
|
||||||
r[2] = byte(i2c.Bus.DR)
|
r[2] = byte(i2c.Bus.DR.Get())
|
||||||
|
|
||||||
// wait for stop
|
// wait for stop
|
||||||
return i2c.waitForStop()
|
return i2c.waitForStop()
|
||||||
@@ -574,7 +579,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
|
|
||||||
// clear address here
|
// clear address here
|
||||||
timeout := i2cTimeout
|
timeout := i2cTimeout
|
||||||
for i2c.Bus.SR2&(stm32.I2C_SR2_MSL|stm32.I2C_SR2_BUSY) == 0 {
|
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on read clear address")
|
return errors.New("I2C timeout on read clear address")
|
||||||
@@ -583,11 +588,11 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
|
|
||||||
for i := 0; i < len(r)-3; i++ {
|
for i := 0; i < len(r)-3; i++ {
|
||||||
// Enable ACK of received data
|
// Enable ACK of received data
|
||||||
i2c.Bus.CR1 |= stm32.I2C_CR1_ACK
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
|
||||||
|
|
||||||
// wait for btf. we need a longer timeout here than normal.
|
// wait for btf. we need a longer timeout here than normal.
|
||||||
timeout = 1000
|
timeout = 1000
|
||||||
for (i2c.Bus.SR1 & stm32.I2C_SR1_BTF) == 0 {
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
println("I2C timeout on read 3 bytes")
|
println("I2C timeout on read 3 bytes")
|
||||||
@@ -596,12 +601,12 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read the next byte
|
// read the next byte
|
||||||
r[i] = byte(i2c.Bus.DR)
|
r[i] = byte(i2c.Bus.DR.Get())
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for btf. we need a longer timeout here than normal.
|
// wait for btf. we need a longer timeout here than normal.
|
||||||
timeout = 1000
|
timeout = 1000
|
||||||
for (i2c.Bus.SR1 & stm32.I2C_SR1_BTF) == 0 {
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on read more than 3 bytes")
|
return errors.New("I2C timeout on read more than 3 bytes")
|
||||||
@@ -609,19 +614,19 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Disable ACK of received data
|
// Disable ACK of received data
|
||||||
i2c.Bus.CR1 &^= stm32.I2C_CR1_ACK
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
|
||||||
|
|
||||||
// get third from last byte
|
// get third from last byte
|
||||||
r[len(r)-3] = byte(i2c.Bus.DR)
|
r[len(r)-3] = byte(i2c.Bus.DR.Get())
|
||||||
|
|
||||||
// Generate stop condition
|
// Generate stop condition
|
||||||
i2c.Bus.CR1 |= stm32.I2C_CR1_STOP
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
||||||
|
|
||||||
// get second from last byte
|
// get second from last byte
|
||||||
r[len(r)-2] = byte(i2c.Bus.DR)
|
r[len(r)-2] = byte(i2c.Bus.DR.Get())
|
||||||
|
|
||||||
timeout = i2cTimeout
|
timeout = i2cTimeout
|
||||||
for (i2c.Bus.SR1 & stm32.I2C_SR1_RxNE) == 0 {
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_RxNE) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on read last byte of more than 3")
|
return errors.New("I2C timeout on read last byte of more than 3")
|
||||||
@@ -629,7 +634,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get last byte
|
// get last byte
|
||||||
r[len(r)-1] = byte(i2c.Bus.DR)
|
r[len(r)-1] = byte(i2c.Bus.DR.Get())
|
||||||
|
|
||||||
// wait for stop
|
// wait for stop
|
||||||
return i2c.waitForStop()
|
return i2c.waitForStop()
|
||||||
@@ -645,7 +650,7 @@ const i2cTimeout = 500
|
|||||||
func (i2c I2C) signalStart() error {
|
func (i2c I2C) signalStart() error {
|
||||||
// Wait until I2C is not busy
|
// Wait until I2C is not busy
|
||||||
timeout := i2cTimeout
|
timeout := i2cTimeout
|
||||||
for (i2c.Bus.SR2 & stm32.I2C_SR2_BUSY) > 0 {
|
for i2c.Bus.SR2.HasBits(stm32.I2C_SR2_BUSY) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C busy on start")
|
return errors.New("I2C busy on start")
|
||||||
@@ -653,14 +658,14 @@ func (i2c I2C) signalStart() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// clear stop
|
// clear stop
|
||||||
i2c.Bus.CR1 &^= stm32.I2C_CR1_STOP
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_STOP)
|
||||||
|
|
||||||
// Generate start condition
|
// Generate start condition
|
||||||
i2c.Bus.CR1 |= stm32.I2C_CR1_START
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_START)
|
||||||
|
|
||||||
// Wait for I2C EV5 aka SB flag.
|
// Wait for I2C EV5 aka SB flag.
|
||||||
timeout = i2cTimeout
|
timeout = i2cTimeout
|
||||||
for (i2c.Bus.SR1 & stm32.I2C_SR1_SB) == 0 {
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_SB) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on start")
|
return errors.New("I2C timeout on start")
|
||||||
@@ -673,7 +678,7 @@ func (i2c I2C) signalStart() error {
|
|||||||
// signalStop sends a stop signal and waits for it to succeed.
|
// signalStop sends a stop signal and waits for it to succeed.
|
||||||
func (i2c I2C) signalStop() error {
|
func (i2c I2C) signalStop() error {
|
||||||
// Generate stop condition
|
// Generate stop condition
|
||||||
i2c.Bus.CR1 |= stm32.I2C_CR1_STOP
|
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
||||||
|
|
||||||
// wait for stop
|
// wait for stop
|
||||||
return i2c.waitForStop()
|
return i2c.waitForStop()
|
||||||
@@ -683,7 +688,7 @@ func (i2c I2C) signalStop() error {
|
|||||||
func (i2c I2C) waitForStop() error {
|
func (i2c I2C) waitForStop() error {
|
||||||
// Wait until I2C is stopped
|
// Wait until I2C is stopped
|
||||||
timeout := i2cTimeout
|
timeout := i2cTimeout
|
||||||
for (i2c.Bus.SR1 & stm32.I2C_SR1_STOPF) > 0 {
|
for i2c.Bus.SR1.HasBits(stm32.I2C_SR1_STOPF) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
println("I2C timeout on wait for stop signal")
|
println("I2C timeout on wait for stop signal")
|
||||||
@@ -701,14 +706,14 @@ func (i2c I2C) sendAddress(address uint8, write bool) error {
|
|||||||
data |= 1 // set read flag
|
data |= 1 // set read flag
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c.Bus.DR = stm32.RegValue(data)
|
i2c.Bus.DR.Set(uint32(data))
|
||||||
|
|
||||||
// Wait for I2C EV6 event.
|
// Wait for I2C EV6 event.
|
||||||
// Destination device acknowledges address
|
// Destination device acknowledges address
|
||||||
timeout := i2cTimeout
|
timeout := i2cTimeout
|
||||||
if write {
|
if write {
|
||||||
// EV6 which is ADDR flag.
|
// EV6 which is ADDR flag.
|
||||||
for i2c.Bus.SR1&stm32.I2C_SR1_ADDR == 0 {
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_ADDR) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on send write address")
|
return errors.New("I2C timeout on send write address")
|
||||||
@@ -716,7 +721,7 @@ func (i2c I2C) sendAddress(address uint8, write bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
timeout = i2cTimeout
|
timeout = i2cTimeout
|
||||||
for i2c.Bus.SR2&(stm32.I2C_SR2_MSL|stm32.I2C_SR2_BUSY|stm32.I2C_SR2_TRA) == 0 {
|
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY | stm32.I2C_SR2_TRA) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on send write address")
|
return errors.New("I2C timeout on send write address")
|
||||||
@@ -724,7 +729,7 @@ func (i2c I2C) sendAddress(address uint8, write bool) error {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED which is ADDR flag.
|
// I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED which is ADDR flag.
|
||||||
for (i2c.Bus.SR1 & stm32.I2C_SR1_ADDR) == 0 {
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_ADDR) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on send read address")
|
return errors.New("I2C timeout on send read address")
|
||||||
@@ -738,13 +743,13 @@ func (i2c I2C) sendAddress(address uint8, write bool) error {
|
|||||||
// WriteByte writes a single byte to the I2C bus.
|
// WriteByte writes a single byte to the I2C bus.
|
||||||
func (i2c I2C) WriteByte(data byte) error {
|
func (i2c I2C) WriteByte(data byte) error {
|
||||||
// Send data byte
|
// Send data byte
|
||||||
i2c.Bus.DR = stm32.RegValue(data)
|
i2c.Bus.DR.Set(uint32(data))
|
||||||
|
|
||||||
// Wait for I2C EV8_2 when data has been physically shifted out and
|
// Wait for I2C EV8_2 when data has been physically shifted out and
|
||||||
// output on the bus.
|
// output on the bus.
|
||||||
// I2C_EVENT_MASTER_BYTE_TRANSMITTED is TXE flag.
|
// I2C_EVENT_MASTER_BYTE_TRANSMITTED is TXE flag.
|
||||||
timeout := i2cTimeout
|
timeout := i2cTimeout
|
||||||
for i2c.Bus.SR1&stm32.I2C_SR1_TxE == 0 {
|
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_TxE) {
|
||||||
timeout--
|
timeout--
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
return errors.New("I2C timeout on write")
|
return errors.New("I2C timeout on write")
|
||||||
|
|||||||
@@ -13,15 +13,15 @@ const CPU_FREQUENCY = 168000000
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// Mode Flag
|
// Mode Flag
|
||||||
GPIO_OUTPUT = 0
|
PinOutput PinMode = 0
|
||||||
GPIO_INPUT = GPIO_INPUT_PULLDOWN
|
PinInput PinMode = PinInputFloating
|
||||||
GPIO_INPUT_FLOATING = 1
|
PinInputFloating PinMode = 1
|
||||||
GPIO_INPUT_PULLDOWN = 2
|
PinInputPulldown PinMode = 2
|
||||||
GPIO_INPUT_PULLUP = 3
|
PinInputPullup PinMode = 3
|
||||||
|
|
||||||
// for UART
|
// for UART
|
||||||
GPIO_UART_TX = 4
|
PinModeUartTX PinMode = 4
|
||||||
GPIO_UART_RX = 5
|
PinModeUartRX PinMode = 5
|
||||||
|
|
||||||
//GPIOx_MODER
|
//GPIOx_MODER
|
||||||
GPIO_MODE_INPUT = 0
|
GPIO_MODE_INPUT = 0
|
||||||
@@ -45,8 +45,8 @@ const (
|
|||||||
GPIO_PULL_DOWN = 2
|
GPIO_PULL_DOWN = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p GPIO) getPort() *stm32.GPIO_Type {
|
func (p Pin) getPort() *stm32.GPIO_Type {
|
||||||
switch p.Pin / 16 {
|
switch p / 16 {
|
||||||
case 0:
|
case 0:
|
||||||
return stm32.GPIOA
|
return stm32.GPIOA
|
||||||
case 1:
|
case 1:
|
||||||
@@ -71,83 +71,83 @@ func (p GPIO) getPort() *stm32.GPIO_Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// enableClock enables the clock for this desired GPIO port.
|
// enableClock enables the clock for this desired GPIO port.
|
||||||
func (p GPIO) enableClock() {
|
func (p Pin) enableClock() {
|
||||||
switch p.Pin / 16 {
|
switch p / 16 {
|
||||||
case 0:
|
case 0:
|
||||||
stm32.RCC.AHB1ENR |= stm32.RCC_AHB1ENR_GPIOAEN
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOAEN)
|
||||||
case 1:
|
case 1:
|
||||||
stm32.RCC.AHB1ENR |= stm32.RCC_AHB1ENR_GPIOBEN
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOBEN)
|
||||||
case 2:
|
case 2:
|
||||||
stm32.RCC.AHB1ENR |= stm32.RCC_AHB1ENR_GPIOCEN
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOCEN)
|
||||||
case 3:
|
case 3:
|
||||||
stm32.RCC.AHB1ENR |= stm32.RCC_AHB1ENR_GPIODEN
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIODEN)
|
||||||
case 4:
|
case 4:
|
||||||
stm32.RCC.AHB1ENR |= stm32.RCC_AHB1ENR_GPIOEEN
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOEEN)
|
||||||
case 5:
|
case 5:
|
||||||
stm32.RCC.AHB1ENR |= stm32.RCC_AHB1ENR_GPIOFEN
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOFEN)
|
||||||
case 6:
|
case 6:
|
||||||
stm32.RCC.AHB1ENR |= stm32.RCC_AHB1ENR_GPIOGEN
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOGEN)
|
||||||
case 7:
|
case 7:
|
||||||
stm32.RCC.AHB1ENR |= stm32.RCC_AHB1ENR_GPIOHEN
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOHEN)
|
||||||
case 8:
|
case 8:
|
||||||
stm32.RCC.AHB1ENR |= stm32.RCC_AHB1ENR_GPIOIEN
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOIEN)
|
||||||
default:
|
default:
|
||||||
panic("machine: unknown port")
|
panic("machine: unknown port")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure this pin with the given configuration.
|
// Configure this pin with the given configuration.
|
||||||
func (p GPIO) Configure(config GPIOConfig) {
|
func (p Pin) Configure(config PinConfig) {
|
||||||
// Configure the GPIO pin.
|
// Configure the GPIO pin.
|
||||||
p.enableClock()
|
p.enableClock()
|
||||||
port := p.getPort()
|
port := p.getPort()
|
||||||
pin := p.Pin % 16
|
pin := uint8(p) % 16
|
||||||
pos := pin * 2
|
pos := pin * 2
|
||||||
|
|
||||||
if config.Mode == GPIO_INPUT_FLOATING {
|
if config.Mode == PinInputFloating {
|
||||||
port.MODER = stm32.RegValue((uint32(port.MODER)&^(0x3<<pos) | (uint32(GPIO_MODE_INPUT) << pos)))
|
port.MODER.Set((uint32(port.MODER.Get())&^(0x3<<pos) | (uint32(GPIO_MODE_INPUT) << pos)))
|
||||||
port.PUPDR = stm32.RegValue((uint32(port.PUPDR)&^(0x3<<pos) | (uint32(GPIO_FLOATING) << pos)))
|
port.PUPDR.Set((uint32(port.PUPDR.Get())&^(0x3<<pos) | (uint32(GPIO_FLOATING) << pos)))
|
||||||
} else if config.Mode == GPIO_INPUT_PULLDOWN {
|
} else if config.Mode == PinInputPulldown {
|
||||||
port.MODER = stm32.RegValue((uint32(port.MODER)&^(0x3<<pos) | (uint32(GPIO_MODE_INPUT) << pos)))
|
port.MODER.Set((uint32(port.MODER.Get())&^(0x3<<pos) | (uint32(GPIO_MODE_INPUT) << pos)))
|
||||||
port.PUPDR = stm32.RegValue((uint32(port.PUPDR)&^(0x3<<pos) | (uint32(GPIO_PULL_DOWN) << pos)))
|
port.PUPDR.Set((uint32(port.PUPDR.Get())&^(0x3<<pos) | (uint32(GPIO_PULL_DOWN) << pos)))
|
||||||
} else if config.Mode == GPIO_INPUT_PULLUP {
|
} else if config.Mode == PinInputPullup {
|
||||||
port.MODER = stm32.RegValue((uint32(port.MODER)&^(0x3<<pos) | (uint32(GPIO_MODE_INPUT) << pos)))
|
port.MODER.Set((uint32(port.MODER.Get())&^(0x3<<pos) | (uint32(GPIO_MODE_INPUT) << pos)))
|
||||||
port.PUPDR = stm32.RegValue((uint32(port.PUPDR)&^(0x3<<pos) | (uint32(GPIO_PULL_UP) << pos)))
|
port.PUPDR.Set((uint32(port.PUPDR.Get())&^(0x3<<pos) | (uint32(GPIO_PULL_UP) << pos)))
|
||||||
} else if config.Mode == GPIO_OUTPUT {
|
} else if config.Mode == PinOutput {
|
||||||
port.MODER = stm32.RegValue((uint32(port.MODER)&^(0x3<<pos) | (uint32(GPIO_MODE_GENERAL_OUTPUT) << pos)))
|
port.MODER.Set((uint32(port.MODER.Get())&^(0x3<<pos) | (uint32(GPIO_MODE_GENERAL_OUTPUT) << pos)))
|
||||||
port.OSPEEDR = stm32.RegValue((uint32(port.OSPEEDR)&^(0x3<<pos) | (uint32(GPIO_SPEED_HI) << pos)))
|
port.OSPEEDR.Set((uint32(port.OSPEEDR.Get())&^(0x3<<pos) | (uint32(GPIO_SPEED_HI) << pos)))
|
||||||
} else if config.Mode == GPIO_UART_TX {
|
} else if config.Mode == PinModeUartTX {
|
||||||
port.MODER = stm32.RegValue((uint32(port.MODER)&^(0x3<<pos) | (uint32(GPIO_MODE_ALTERNABTIVE) << pos)))
|
port.MODER.Set((uint32(port.MODER.Get())&^(0x3<<pos) | (uint32(GPIO_MODE_ALTERNABTIVE) << pos)))
|
||||||
port.OSPEEDR = stm32.RegValue((uint32(port.OSPEEDR)&^(0x3<<pos) | (uint32(GPIO_SPEED_HI) << pos)))
|
port.OSPEEDR.Set((uint32(port.OSPEEDR.Get())&^(0x3<<pos) | (uint32(GPIO_SPEED_HI) << pos)))
|
||||||
port.PUPDR = stm32.RegValue((uint32(port.PUPDR)&^(0x3<<pos) | (uint32(GPIO_PULL_UP) << pos)))
|
port.PUPDR.Set((uint32(port.PUPDR.Get())&^(0x3<<pos) | (uint32(GPIO_PULL_UP) << pos)))
|
||||||
p.setAltFunc(0x7)
|
p.setAltFunc(0x7)
|
||||||
} else if config.Mode == GPIO_UART_RX {
|
} else if config.Mode == PinModeUartRX {
|
||||||
port.MODER = stm32.RegValue((uint32(port.MODER)&^(0x3<<pos) | (uint32(GPIO_MODE_ALTERNABTIVE) << pos)))
|
port.MODER.Set((uint32(port.MODER.Get())&^(0x3<<pos) | (uint32(GPIO_MODE_ALTERNABTIVE) << pos)))
|
||||||
port.PUPDR = stm32.RegValue((uint32(port.PUPDR)&^(0x3<<pos) | (uint32(GPIO_FLOATING) << pos)))
|
port.PUPDR.Set((uint32(port.PUPDR.Get())&^(0x3<<pos) | (uint32(GPIO_FLOATING) << pos)))
|
||||||
p.setAltFunc(0x7)
|
p.setAltFunc(0x7)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p GPIO) setAltFunc(af uint32) {
|
func (p Pin) setAltFunc(af uint32) {
|
||||||
port := p.getPort()
|
port := p.getPort()
|
||||||
pin := p.Pin % 16
|
pin := uint8(p) % 16
|
||||||
pos := pin * 4
|
pos := pin * 4
|
||||||
if pin >= 8 {
|
if pin >= 8 {
|
||||||
port.AFRH = stm32.RegValue(uint32(port.AFRH)&^(0xF<<pos) | ((af & 0xF) << pos))
|
port.AFRH.Set(uint32(port.AFRH.Get())&^(0xF<<pos) | ((af & 0xF) << pos))
|
||||||
} else {
|
} else {
|
||||||
port.AFRL = stm32.RegValue(uint32(port.AFRL)&^(0xF<<pos) | ((af & 0xF) << pos))
|
port.AFRL.Set(uint32(port.AFRL.Get())&^(0xF<<pos) | ((af & 0xF) << pos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the pin to high or low.
|
// Set the pin to high or low.
|
||||||
// Warning: only use this on an output pin!
|
// Warning: only use this on an output pin!
|
||||||
func (p GPIO) Set(high bool) {
|
func (p Pin) Set(high bool) {
|
||||||
port := p.getPort()
|
port := p.getPort()
|
||||||
pin := p.Pin % 16
|
pin := p % 16
|
||||||
if high {
|
if high {
|
||||||
port.BSRR = 1 << pin
|
port.BSRR.Set(1 << uint8(pin))
|
||||||
} else {
|
} else {
|
||||||
port.BSRR = 1 << (pin + 16)
|
port.BSRR.Set(1 << uint8(pin+16))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,12 +173,12 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
switch config.TX {
|
switch config.TX {
|
||||||
default:
|
default:
|
||||||
// use standard TX/RX pins PA2 and PA3
|
// use standard TX/RX pins PA2 and PA3
|
||||||
GPIO{UART_TX_PIN}.Configure(GPIOConfig{Mode: GPIO_UART_TX})
|
UART_TX_PIN.Configure(PinConfig{Mode: PinModeUartTX})
|
||||||
GPIO{UART_RX_PIN}.Configure(GPIOConfig{Mode: GPIO_UART_RX})
|
UART_RX_PIN.Configure(PinConfig{Mode: PinModeUartRX})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable USART2 clock
|
// Enable USART2 clock
|
||||||
stm32.RCC.APB1ENR |= stm32.RCC_APB1ENR_USART2EN
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Set baud rate(115200)
|
Set baud rate(115200)
|
||||||
@@ -195,10 +195,10 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
| 115200 | 0x16D |
|
| 115200 | 0x16D |
|
||||||
+----------+--------+
|
+----------+--------+
|
||||||
*/
|
*/
|
||||||
stm32.USART2.BRR = 0x16c
|
stm32.USART2.BRR.Set(0x16c)
|
||||||
|
|
||||||
// Enable USART2 port.
|
// Enable USART2 port.
|
||||||
stm32.USART2.CR1 = stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE
|
stm32.USART2.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
||||||
|
|
||||||
// Enable RX IRQ.
|
// Enable RX IRQ.
|
||||||
arm.SetPriority(stm32.IRQ_USART2, 0xc0)
|
arm.SetPriority(stm32.IRQ_USART2, 0xc0)
|
||||||
@@ -207,14 +207,14 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
|
|
||||||
// WriteByte writes a byte of data to the UART.
|
// WriteByte writes a byte of data to the UART.
|
||||||
func (uart UART) WriteByte(c byte) error {
|
func (uart UART) WriteByte(c byte) error {
|
||||||
stm32.USART2.DR = stm32.RegValue(c)
|
stm32.USART2.DR.Set(uint32(c))
|
||||||
|
|
||||||
for (stm32.USART2.SR & stm32.USART_SR_TXE) == 0 {
|
for !stm32.USART2.SR.HasBits(stm32.USART_SR_TXE) {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export USART2_IRQHandler
|
//go:export USART2_IRQHandler
|
||||||
func handleUSART2() {
|
func handleUSART2() {
|
||||||
UART1.Receive(byte((stm32.USART2.DR & 0xFF)))
|
UART1.Receive(byte((stm32.USART2.DR.Get() & 0xFF)))
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -6,8 +6,8 @@ import "errors"
|
|||||||
|
|
||||||
type UARTConfig struct {
|
type UARTConfig struct {
|
||||||
BaudRate uint32
|
BaudRate uint32
|
||||||
TX uint8
|
TX Pin
|
||||||
RX uint8
|
RX Pin
|
||||||
}
|
}
|
||||||
|
|
||||||
// To implement the UART interface for a board, you must declare a concrete type as follows:
|
// To implement the UART interface for a board, you must declare a concrete type as follows:
|
||||||
|
|||||||
+5
-5
@@ -484,11 +484,11 @@ const (
|
|||||||
// RoReg8 Reserved1[0x5];
|
// RoReg8 Reserved1[0x5];
|
||||||
// } UsbDeviceDescBank;
|
// } UsbDeviceDescBank;
|
||||||
type usbDeviceDescBank struct {
|
type usbDeviceDescBank struct {
|
||||||
ADDR sam.RegValue
|
ADDR sam.Register32
|
||||||
PCKSIZE sam.RegValue
|
PCKSIZE sam.Register32
|
||||||
EXTREG sam.RegValue16
|
EXTREG sam.Register16
|
||||||
STATUS_BK sam.RegValue8
|
STATUS_BK sam.Register8
|
||||||
_reserved [5]sam.RegValue8
|
_reserved [5]sam.Register8
|
||||||
}
|
}
|
||||||
|
|
||||||
type usbDeviceDescriptor struct {
|
type usbDeviceDescriptor struct {
|
||||||
|
|||||||
+49
-15
@@ -60,14 +60,20 @@ func hashmapTopHash(hash uint32) uint8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a new hashmap with the given keySize and valueSize.
|
// Create a new hashmap with the given keySize and valueSize.
|
||||||
func hashmapMake(keySize, valueSize uint8) *hashmap {
|
func hashmapMake(keySize, valueSize uint8, sizeHint uintptr) *hashmap {
|
||||||
|
numBuckets := sizeHint / 8
|
||||||
|
bucketBits := uint8(0)
|
||||||
|
for numBuckets != 0 {
|
||||||
|
numBuckets /= 2
|
||||||
|
bucketBits++
|
||||||
|
}
|
||||||
bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(keySize)*8 + uintptr(valueSize)*8
|
bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(keySize)*8 + uintptr(valueSize)*8
|
||||||
bucket := alloc(bucketBufSize)
|
buckets := alloc(bucketBufSize * (1 << bucketBits))
|
||||||
return &hashmap{
|
return &hashmap{
|
||||||
buckets: bucket,
|
buckets: buckets,
|
||||||
keySize: keySize,
|
keySize: keySize,
|
||||||
valueSize: valueSize,
|
valueSize: valueSize,
|
||||||
bucketBits: 0,
|
bucketBits: bucketBits,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,13 +89,20 @@ func hashmapLen(m *hashmap) int {
|
|||||||
// Set a specified key to a given value. Grow the map if necessary.
|
// Set a specified key to a given value. Grow the map if necessary.
|
||||||
//go:nobounds
|
//go:nobounds
|
||||||
func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
|
func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
|
||||||
|
tophash := hashmapTopHash(hash)
|
||||||
|
|
||||||
|
if m.buckets == nil {
|
||||||
|
// No bucket was allocated yet, do so now.
|
||||||
|
m.buckets = unsafe.Pointer(hashmapInsertIntoNewBucket(m, key, value, tophash))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
numBuckets := uintptr(1) << m.bucketBits
|
numBuckets := uintptr(1) << m.bucketBits
|
||||||
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
||||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||||
bucketAddr := uintptr(m.buckets) + bucketSize*bucketNumber
|
bucketAddr := uintptr(m.buckets) + bucketSize*bucketNumber
|
||||||
bucket := (*hashmapBucket)(unsafe.Pointer(bucketAddr))
|
bucket := (*hashmapBucket)(unsafe.Pointer(bucketAddr))
|
||||||
|
var lastBucket *hashmapBucket
|
||||||
tophash := hashmapTopHash(hash)
|
|
||||||
|
|
||||||
// See whether the key already exists somewhere.
|
// See whether the key already exists somewhere.
|
||||||
var emptySlotKey unsafe.Pointer
|
var emptySlotKey unsafe.Pointer
|
||||||
@@ -98,9 +111,9 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
|||||||
for bucket != nil {
|
for bucket != nil {
|
||||||
for i := uintptr(0); i < 8; i++ {
|
for i := uintptr(0); i < 8; i++ {
|
||||||
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(i)
|
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(i)
|
||||||
slotKey := unsafe.Pointer(bucketAddr + slotKeyOffset)
|
slotKey := unsafe.Pointer(uintptr(unsafe.Pointer(bucket)) + slotKeyOffset)
|
||||||
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i)
|
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i)
|
||||||
slotValue := unsafe.Pointer(bucketAddr + slotValueOffset)
|
slotValue := unsafe.Pointer(uintptr(unsafe.Pointer(bucket)) + slotValueOffset)
|
||||||
if bucket.tophash[i] == 0 && emptySlotKey == nil {
|
if bucket.tophash[i] == 0 && emptySlotKey == nil {
|
||||||
// Found an empty slot, store it for if we couldn't find an
|
// Found an empty slot, store it for if we couldn't find an
|
||||||
// existing slot.
|
// existing slot.
|
||||||
@@ -109,7 +122,7 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
|||||||
emptySlotTophash = &bucket.tophash[i]
|
emptySlotTophash = &bucket.tophash[i]
|
||||||
}
|
}
|
||||||
if bucket.tophash[i] == tophash {
|
if bucket.tophash[i] == tophash {
|
||||||
// Could be an existing value that's the same.
|
// Could be an existing key that's the same.
|
||||||
if keyEqual(key, slotKey, uintptr(m.keySize)) {
|
if keyEqual(key, slotKey, uintptr(m.keySize)) {
|
||||||
// found same key, replace it
|
// found same key, replace it
|
||||||
memcpy(slotValue, value, uintptr(m.valueSize))
|
memcpy(slotValue, value, uintptr(m.valueSize))
|
||||||
@@ -117,16 +130,37 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
lastBucket = bucket
|
||||||
bucket = bucket.next
|
bucket = bucket.next
|
||||||
}
|
}
|
||||||
if emptySlotKey != nil {
|
if emptySlotKey == nil {
|
||||||
m.count++
|
// Add a new bucket to the bucket chain.
|
||||||
memcpy(emptySlotKey, key, uintptr(m.keySize))
|
// TODO: rebalance if necessary to avoid O(n) insert and lookup time.
|
||||||
memcpy(emptySlotValue, value, uintptr(m.valueSize))
|
lastBucket.next = (*hashmapBucket)(hashmapInsertIntoNewBucket(m, key, value, tophash))
|
||||||
*emptySlotTophash = tophash
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
panic("todo: hashmap: grow bucket")
|
m.count++
|
||||||
|
memcpy(emptySlotKey, key, uintptr(m.keySize))
|
||||||
|
memcpy(emptySlotValue, value, uintptr(m.valueSize))
|
||||||
|
*emptySlotTophash = tophash
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashmapInsertIntoNewBucket creates a new bucket, inserts the given key and
|
||||||
|
// value into the bucket, and returns a pointer to this bucket.
|
||||||
|
func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket {
|
||||||
|
bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||||
|
bucketBuf := alloc(bucketBufSize)
|
||||||
|
// Insert into the first slot, which is empty as it has just been allocated.
|
||||||
|
slotKeyOffset := unsafe.Sizeof(hashmapBucket{})
|
||||||
|
slotKey := unsafe.Pointer(uintptr(bucketBuf) + slotKeyOffset)
|
||||||
|
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8
|
||||||
|
slotValue := unsafe.Pointer(uintptr(bucketBuf) + slotValueOffset)
|
||||||
|
m.count++
|
||||||
|
memcpy(slotKey, key, uintptr(m.keySize))
|
||||||
|
memcpy(slotValue, value, uintptr(m.valueSize))
|
||||||
|
bucket := (*hashmapBucket)(bucketBuf)
|
||||||
|
bucket.tophash[0] = tophash
|
||||||
|
return bucket
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the value of a specified key, or zero the value if not found.
|
// Get the value of a specified key, or zero the value if not found.
|
||||||
|
|||||||
@@ -36,16 +36,20 @@ func isnil(ptr *uint8) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Panic when trying to dereference a nil pointer.
|
// Panic when trying to dereference a nil pointer.
|
||||||
func nilpanic() {
|
func nilPanic() {
|
||||||
runtimePanic("nil pointer dereference")
|
runtimePanic("nil pointer dereference")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panic when trying to acces an array or slice out of bounds.
|
// Panic when trying to acces an array or slice out of bounds.
|
||||||
func lookuppanic() {
|
func lookupPanic() {
|
||||||
runtimePanic("index out of range")
|
runtimePanic("index out of range")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panic when trying to slice a slice out of bounds.
|
// Panic when trying to slice a slice out of bounds.
|
||||||
func slicepanic() {
|
func slicePanic() {
|
||||||
runtimePanic("slice out of range")
|
runtimePanic("slice out of range")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func blockingPanic() {
|
||||||
|
runtimePanic("trying to do blocking operation in exported function")
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const Compiler = "tinygo"
|
|||||||
// package.
|
// package.
|
||||||
func initAll()
|
func initAll()
|
||||||
|
|
||||||
// A function call to this function is replaced withone of the following,
|
// A function call to this function is replaced with one of the following,
|
||||||
// depending on whether the scheduler is necessary:
|
// depending on whether the scheduler is necessary:
|
||||||
//
|
//
|
||||||
// Without scheduler:
|
// Without scheduler:
|
||||||
|
|||||||
@@ -17,19 +17,19 @@ func sleepWDT(period uint8) {
|
|||||||
avr.Asm("cli")
|
avr.Asm("cli")
|
||||||
avr.Asm("wdr")
|
avr.Asm("wdr")
|
||||||
// Start timed sequence.
|
// Start timed sequence.
|
||||||
*avr.WDTCSR |= avr.WDTCSR_WDCE | avr.WDTCSR_WDE
|
avr.WDTCSR.SetBits(avr.WDTCSR_WDCE | avr.WDTCSR_WDE)
|
||||||
// Enable WDT and set new timeout
|
// Enable WDT and set new timeout
|
||||||
*avr.WDTCSR = avr.WDTCSR_WDIE | avr.RegValue(period)
|
avr.WDTCSR.SetBits(avr.WDTCSR_WDIE | period)
|
||||||
avr.Asm("sei")
|
avr.Asm("sei")
|
||||||
|
|
||||||
// Set sleep mode to idle and enable sleep mode.
|
// Set sleep mode to idle and enable sleep mode.
|
||||||
// Note: when using something other than idle, the UART won't work
|
// Note: when using something other than idle, the UART won't work
|
||||||
// correctly. This needs to be fixed, though, so we can truly sleep.
|
// correctly. This needs to be fixed, though, so we can truly sleep.
|
||||||
*avr.SMCR = (0 << 1) | avr.SMCR_SE
|
avr.SMCR.Set((0 << 1) | avr.SMCR_SE)
|
||||||
|
|
||||||
// go to sleep
|
// go to sleep
|
||||||
avr.Asm("sleep")
|
avr.Asm("sleep")
|
||||||
|
|
||||||
// disable sleep
|
// disable sleep
|
||||||
*avr.SMCR = 0
|
avr.SMCR.Set(0)
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-107
@@ -36,12 +36,12 @@ func putchar(c byte) {
|
|||||||
|
|
||||||
func initClocks() {
|
func initClocks() {
|
||||||
// Set 1 Flash Wait State for 48MHz, required for 3.3V operation according to SAMD21 Datasheet
|
// Set 1 Flash Wait State for 48MHz, required for 3.3V operation according to SAMD21 Datasheet
|
||||||
sam.NVMCTRL.CTRLB |= (sam.NVMCTRL_CTRLB_RWS_HALF << sam.NVMCTRL_CTRLB_RWS_Pos)
|
sam.NVMCTRL.CTRLB.SetBits(sam.NVMCTRL_CTRLB_RWS_HALF << sam.NVMCTRL_CTRLB_RWS_Pos)
|
||||||
|
|
||||||
// Turn on the digital interface clock
|
// Turn on the digital interface clock
|
||||||
sam.PM.APBAMASK |= sam.PM_APBAMASK_GCLK_
|
sam.PM.APBAMASK.SetBits(sam.PM_APBAMASK_GCLK_)
|
||||||
// turn off RTC
|
// turn off RTC
|
||||||
sam.PM.APBAMASK &^= sam.PM_APBAMASK_RTC_
|
sam.PM.APBAMASK.ClearBits(sam.PM_APBAMASK_RTC_)
|
||||||
|
|
||||||
// Enable OSC32K clock (Internal 32.768Hz oscillator).
|
// Enable OSC32K clock (Internal 32.768Hz oscillator).
|
||||||
// This requires registers that are not included in the SVD file.
|
// This requires registers that are not included in the SVD file.
|
||||||
@@ -61,42 +61,42 @@ func initClocks() {
|
|||||||
// SYSCTRL_OSC32K_CALIB(calib) |
|
// SYSCTRL_OSC32K_CALIB(calib) |
|
||||||
// SYSCTRL_OSC32K_STARTUP(0x6u) |
|
// SYSCTRL_OSC32K_STARTUP(0x6u) |
|
||||||
// SYSCTRL_OSC32K_EN32K | SYSCTRL_OSC32K_ENABLE;
|
// SYSCTRL_OSC32K_EN32K | SYSCTRL_OSC32K_ENABLE;
|
||||||
sam.SYSCTRL.OSC32K = sam.RegValue((calib << sam.SYSCTRL_OSC32K_CALIB_Pos) |
|
sam.SYSCTRL.OSC32K.Set((calib << sam.SYSCTRL_OSC32K_CALIB_Pos) |
|
||||||
(0x6 << sam.SYSCTRL_OSC32K_STARTUP_Pos) |
|
(0x6 << sam.SYSCTRL_OSC32K_STARTUP_Pos) |
|
||||||
sam.SYSCTRL_OSC32K_EN32K |
|
sam.SYSCTRL_OSC32K_EN32K |
|
||||||
sam.SYSCTRL_OSC32K_EN1K |
|
sam.SYSCTRL_OSC32K_EN1K |
|
||||||
sam.SYSCTRL_OSC32K_ENABLE)
|
sam.SYSCTRL_OSC32K_ENABLE)
|
||||||
// Wait for oscillator stabilization
|
// Wait for oscillator stabilization
|
||||||
for (sam.SYSCTRL.PCLKSR & sam.SYSCTRL_PCLKSR_OSC32KRDY) == 0 {
|
for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_OSC32KRDY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Software reset the module to ensure it is re-initialized correctly
|
// Software reset the module to ensure it is re-initialized correctly
|
||||||
sam.GCLK.CTRL = sam.GCLK_CTRL_SWRST
|
sam.GCLK.CTRL.Set(sam.GCLK_CTRL_SWRST)
|
||||||
// Wait for reset to complete
|
// Wait for reset to complete
|
||||||
for (sam.GCLK.CTRL&sam.GCLK_CTRL_SWRST) > 0 && (sam.GCLK.STATUS&sam.GCLK_STATUS_SYNCBUSY) > 0 {
|
for sam.GCLK.CTRL.HasBits(sam.GCLK_CTRL_SWRST) && sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put OSC32K as source of Generic Clock Generator 1
|
// Put OSC32K as source of Generic Clock Generator 1
|
||||||
sam.GCLK.GENDIV = sam.RegValue((1 << sam.GCLK_GENDIV_ID_Pos) |
|
sam.GCLK.GENDIV.Set((1 << sam.GCLK_GENDIV_ID_Pos) |
|
||||||
(0 << sam.GCLK_GENDIV_DIV_Pos))
|
(0 << sam.GCLK_GENDIV_DIV_Pos))
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
// GCLK_GENCTRL_ID(1) | GCLK_GENCTRL_SRC_OSC32K | GCLK_GENCTRL_GENEN;
|
// GCLK_GENCTRL_ID(1) | GCLK_GENCTRL_SRC_OSC32K | GCLK_GENCTRL_GENEN;
|
||||||
sam.GCLK.GENCTRL = sam.RegValue((1 << sam.GCLK_GENCTRL_ID_Pos) |
|
sam.GCLK.GENCTRL.Set((1 << sam.GCLK_GENCTRL_ID_Pos) |
|
||||||
(sam.GCLK_GENCTRL_SRC_OSC32K << sam.GCLK_GENCTRL_SRC_Pos) |
|
(sam.GCLK_GENCTRL_SRC_OSC32K << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||||
sam.GCLK_GENCTRL_GENEN)
|
sam.GCLK_GENCTRL_GENEN)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
// Use Generic Clock Generator 1 as source for Generic Clock Multiplexer 0 (DFLL48M reference)
|
// Use Generic Clock Generator 1 as source for Generic Clock Multiplexer 0 (DFLL48M reference)
|
||||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_DFLL48 << sam.GCLK_CLKCTRL_ID_Pos) |
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_DFLL48 << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
(sam.GCLK_CLKCTRL_GEN_GCLK1 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
(sam.GCLK_CLKCTRL_GEN_GCLK1 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
// Remove the OnDemand mode, Bug http://avr32.icgroup.norway.atmel.com/bugzilla/show_bug.cgi?id=9905
|
// Remove the OnDemand mode, Bug http://avr32.icgroup.norway.atmel.com/bugzilla/show_bug.cgi?id=9905
|
||||||
sam.SYSCTRL.DFLLCTRL = sam.SYSCTRL_DFLLCTRL_ENABLE
|
sam.SYSCTRL.DFLLCTRL.Set(sam.SYSCTRL_DFLLCTRL_ENABLE)
|
||||||
// Wait for ready
|
// Wait for ready
|
||||||
for (sam.SYSCTRL.PCLKSR & sam.SYSCTRL_PCLKSR_DFLLRDY) == 0 {
|
for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_DFLLRDY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle DFLL calibration based on info learned from Arduino SAMD implementation,
|
// Handle DFLL calibration based on info learned from Arduino SAMD implementation,
|
||||||
@@ -110,107 +110,107 @@ func initClocks() {
|
|||||||
coarse = 0x1f
|
coarse = 0x1f
|
||||||
}
|
}
|
||||||
|
|
||||||
sam.SYSCTRL.DFLLVAL |= sam.RegValue(coarse << sam.SYSCTRL_DFLLVAL_COARSE_Pos)
|
sam.SYSCTRL.DFLLVAL.SetBits(coarse << sam.SYSCTRL_DFLLVAL_COARSE_Pos)
|
||||||
sam.SYSCTRL.DFLLVAL |= (0x1ff << sam.SYSCTRL_DFLLVAL_FINE_Pos)
|
sam.SYSCTRL.DFLLVAL.SetBits(0x1ff << sam.SYSCTRL_DFLLVAL_FINE_Pos)
|
||||||
|
|
||||||
// Write full configuration to DFLL control register
|
// Write full configuration to DFLL control register
|
||||||
// SYSCTRL_DFLLMUL_CSTEP( 0x1f / 4 ) | // Coarse step is 31, half of the max value
|
// SYSCTRL_DFLLMUL_CSTEP( 0x1f / 4 ) | // Coarse step is 31, half of the max value
|
||||||
// SYSCTRL_DFLLMUL_FSTEP( 10 ) |
|
// SYSCTRL_DFLLMUL_FSTEP( 10 ) |
|
||||||
// SYSCTRL_DFLLMUL_MUL( (48000) ) ;
|
// SYSCTRL_DFLLMUL_MUL( (48000) ) ;
|
||||||
sam.SYSCTRL.DFLLMUL = sam.RegValue(((31 / 4) << sam.SYSCTRL_DFLLMUL_CSTEP_Pos) |
|
sam.SYSCTRL.DFLLMUL.Set(((31 / 4) << sam.SYSCTRL_DFLLMUL_CSTEP_Pos) |
|
||||||
(10 << sam.SYSCTRL_DFLLMUL_FSTEP_Pos) |
|
(10 << sam.SYSCTRL_DFLLMUL_FSTEP_Pos) |
|
||||||
(48000 << sam.SYSCTRL_DFLLMUL_MUL_Pos))
|
(48000 << sam.SYSCTRL_DFLLMUL_MUL_Pos))
|
||||||
|
|
||||||
// disable DFLL
|
// disable DFLL
|
||||||
sam.SYSCTRL.DFLLCTRL = 0
|
sam.SYSCTRL.DFLLCTRL.Set(0)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
sam.SYSCTRL.DFLLCTRL |= sam.SYSCTRL_DFLLCTRL_MODE |
|
sam.SYSCTRL.DFLLCTRL.SetBits(sam.SYSCTRL_DFLLCTRL_MODE |
|
||||||
sam.SYSCTRL_DFLLCTRL_CCDIS |
|
sam.SYSCTRL_DFLLCTRL_CCDIS |
|
||||||
sam.SYSCTRL_DFLLCTRL_USBCRM |
|
sam.SYSCTRL_DFLLCTRL_USBCRM |
|
||||||
sam.SYSCTRL_DFLLCTRL_BPLCKC
|
sam.SYSCTRL_DFLLCTRL_BPLCKC)
|
||||||
// Wait for ready
|
// Wait for ready
|
||||||
for (sam.SYSCTRL.PCLKSR & sam.SYSCTRL_PCLKSR_DFLLRDY) == 0 {
|
for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_DFLLRDY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-enable the DFLL
|
// Re-enable the DFLL
|
||||||
sam.SYSCTRL.DFLLCTRL |= sam.SYSCTRL_DFLLCTRL_ENABLE
|
sam.SYSCTRL.DFLLCTRL.SetBits(sam.SYSCTRL_DFLLCTRL_ENABLE)
|
||||||
// Wait for ready
|
// Wait for ready
|
||||||
for (sam.SYSCTRL.PCLKSR & sam.SYSCTRL_PCLKSR_DFLLRDY) == 0 {
|
for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_DFLLRDY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch Generic Clock Generator 0 to DFLL48M. CPU will run at 48MHz.
|
// Switch Generic Clock Generator 0 to DFLL48M. CPU will run at 48MHz.
|
||||||
sam.GCLK.GENDIV = sam.RegValue((0 << sam.GCLK_GENDIV_ID_Pos) |
|
sam.GCLK.GENDIV.Set((0 << sam.GCLK_GENDIV_ID_Pos) |
|
||||||
(0 << sam.GCLK_GENDIV_DIV_Pos))
|
(0 << sam.GCLK_GENDIV_DIV_Pos))
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
sam.GCLK.GENCTRL = sam.RegValue((0 << sam.GCLK_GENCTRL_ID_Pos) |
|
sam.GCLK.GENCTRL.Set((0 << sam.GCLK_GENCTRL_ID_Pos) |
|
||||||
(sam.GCLK_GENCTRL_SRC_DFLL48M << sam.GCLK_GENCTRL_SRC_Pos) |
|
(sam.GCLK_GENCTRL_SRC_DFLL48M << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||||
sam.GCLK_GENCTRL_IDC |
|
sam.GCLK_GENCTRL_IDC |
|
||||||
sam.GCLK_GENCTRL_GENEN)
|
sam.GCLK_GENCTRL_GENEN)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
// Modify PRESCaler value of OSC8M to have 8MHz
|
// Modify PRESCaler value of OSC8M to have 8MHz
|
||||||
sam.SYSCTRL.OSC8M |= (sam.SYSCTRL_OSC8M_PRESC_0 << sam.SYSCTRL_OSC8M_PRESC_Pos)
|
sam.SYSCTRL.OSC8M.SetBits(sam.SYSCTRL_OSC8M_PRESC_0 << sam.SYSCTRL_OSC8M_PRESC_Pos)
|
||||||
sam.SYSCTRL.OSC8M &^= (1 << sam.SYSCTRL_OSC8M_ONDEMAND_Pos)
|
sam.SYSCTRL.OSC8M.ClearBits(1 << sam.SYSCTRL_OSC8M_ONDEMAND_Pos)
|
||||||
// Wait for oscillator stabilization
|
// Wait for oscillator stabilization
|
||||||
for (sam.SYSCTRL.PCLKSR & sam.SYSCTRL_PCLKSR_OSC8MRDY) == 0 {
|
for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_OSC8MRDY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use OSC8M as source for Generic Clock Generator 3
|
// Use OSC8M as source for Generic Clock Generator 3
|
||||||
sam.GCLK.GENDIV = sam.RegValue((3 << sam.GCLK_GENDIV_ID_Pos))
|
sam.GCLK.GENDIV.Set((3 << sam.GCLK_GENDIV_ID_Pos))
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
sam.GCLK.GENCTRL = sam.RegValue((3 << sam.GCLK_GENCTRL_ID_Pos) |
|
sam.GCLK.GENCTRL.Set((3 << sam.GCLK_GENCTRL_ID_Pos) |
|
||||||
(sam.GCLK_GENCTRL_SRC_OSC8M << sam.GCLK_GENCTRL_SRC_Pos) |
|
(sam.GCLK_GENCTRL_SRC_OSC8M << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||||
sam.GCLK_GENCTRL_GENEN)
|
sam.GCLK_GENCTRL_GENEN)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
// Use OSC32K as source for Generic Clock Generator 2
|
// Use OSC32K as source for Generic Clock Generator 2
|
||||||
// OSC32K/1 -> GCLK2 at 32KHz
|
// OSC32K/1 -> GCLK2 at 32KHz
|
||||||
sam.GCLK.GENDIV = sam.RegValue(2 << sam.GCLK_GENDIV_ID_Pos)
|
sam.GCLK.GENDIV.Set(2 << sam.GCLK_GENDIV_ID_Pos)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
sam.GCLK.GENCTRL = sam.RegValue((2 << sam.GCLK_GENCTRL_ID_Pos) |
|
sam.GCLK.GENCTRL.Set((2 << sam.GCLK_GENCTRL_ID_Pos) |
|
||||||
(sam.GCLK_GENCTRL_SRC_OSC32K << sam.GCLK_GENCTRL_SRC_Pos) |
|
(sam.GCLK_GENCTRL_SRC_OSC32K << sam.GCLK_GENCTRL_SRC_Pos) |
|
||||||
sam.GCLK_GENCTRL_GENEN)
|
sam.GCLK_GENCTRL_GENEN)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
// Use GCLK2 for RTC
|
// Use GCLK2 for RTC
|
||||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_RTC << sam.GCLK_CLKCTRL_ID_Pos) |
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_RTC << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
(sam.GCLK_CLKCTRL_GEN_GCLK2 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
(sam.GCLK_CLKCTRL_GEN_GCLK2 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
// Set the CPU, APBA, B, and C dividers
|
// Set the CPU, APBA, B, and C dividers
|
||||||
sam.PM.CPUSEL = sam.PM_CPUSEL_CPUDIV_DIV1
|
sam.PM.CPUSEL.Set(sam.PM_CPUSEL_CPUDIV_DIV1)
|
||||||
sam.PM.APBASEL = sam.PM_APBASEL_APBADIV_DIV1
|
sam.PM.APBASEL.Set(sam.PM_APBASEL_APBADIV_DIV1)
|
||||||
sam.PM.APBBSEL = sam.PM_APBBSEL_APBBDIV_DIV1
|
sam.PM.APBBSEL.Set(sam.PM_APBBSEL_APBBDIV_DIV1)
|
||||||
sam.PM.APBCSEL = sam.PM_APBCSEL_APBCDIV_DIV1
|
sam.PM.APBCSEL.Set(sam.PM_APBCSEL_APBCDIV_DIV1)
|
||||||
|
|
||||||
// Disable automatic NVM write operations
|
// Disable automatic NVM write operations
|
||||||
sam.NVMCTRL.CTRLB |= sam.NVMCTRL_CTRLB_MANW
|
sam.NVMCTRL.CTRLB.SetBits(sam.NVMCTRL_CTRLB_MANW)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initRTC() {
|
func initRTC() {
|
||||||
// turn on digital interface clock
|
// turn on digital interface clock
|
||||||
sam.PM.APBAMASK |= sam.PM_APBAMASK_RTC_
|
sam.PM.APBAMASK.SetBits(sam.PM_APBAMASK_RTC_)
|
||||||
|
|
||||||
// disable RTC
|
// disable RTC
|
||||||
sam.RTC_MODE0.CTRL = 0
|
sam.RTC_MODE0.CTRL.Set(0)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
// reset RTC
|
// reset RTC
|
||||||
sam.RTC_MODE0.CTRL |= sam.RTC_MODE0_CTRL_SWRST
|
sam.RTC_MODE0.CTRL.SetBits(sam.RTC_MODE0_CTRL_SWRST)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
// set Mode0 to 32-bit counter (mode 0) with prescaler 1 and GCLK2 is 32KHz/1
|
// 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.Set((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_PRESCALER_DIV1 << sam.RTC_MODE0_CTRL_PRESCALER_Pos))
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
// re-enable RTC
|
// re-enable RTC
|
||||||
sam.RTC_MODE0.CTRL |= sam.RTC_MODE0_CTRL_ENABLE
|
sam.RTC_MODE0.CTRL.SetBits(sam.RTC_MODE0_CTRL_ENABLE)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
arm.SetPriority(sam.IRQ_RTC, 0xc0)
|
arm.SetPriority(sam.IRQ_RTC, 0xc0)
|
||||||
@@ -218,7 +218,7 @@ func initRTC() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func waitForSync() {
|
func waitForSync() {
|
||||||
for (sam.GCLK.STATUS & sam.GCLK_STATUS_SYNCBUSY) > 0 {
|
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,11 +250,11 @@ func sleepTicks(d timeUnit) {
|
|||||||
// ticks returns number of microseconds since start.
|
// ticks returns number of microseconds since start.
|
||||||
func ticks() timeUnit {
|
func ticks() timeUnit {
|
||||||
// request read of count
|
// request read of count
|
||||||
sam.RTC_MODE0.READREQ = sam.RTC_MODE0_READREQ_RREQ
|
sam.RTC_MODE0.READREQ.Set(sam.RTC_MODE0_READREQ_RREQ)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
rtcCounter := (uint64(sam.RTC_MODE0.COUNT) * 305) / 10 // each counter tick == 30.5us
|
rtcCounter := (uint64(sam.RTC_MODE0.COUNT.Get()) * 305) / 10 // each counter tick == 30.5us
|
||||||
offset := (rtcCounter - timerLastCounter) // change since last measurement
|
offset := (rtcCounter - timerLastCounter) // change since last measurement
|
||||||
timerLastCounter = rtcCounter
|
timerLastCounter = rtcCounter
|
||||||
timestamp += timeUnit(offset) // TODO: not precise
|
timestamp += timeUnit(offset) // TODO: not precise
|
||||||
return timestamp
|
return timestamp
|
||||||
@@ -269,16 +269,16 @@ func timerSleep(ticks uint32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// request read of count
|
// request read of count
|
||||||
sam.RTC_MODE0.READREQ = sam.RTC_MODE0_READREQ_RREQ
|
sam.RTC_MODE0.READREQ.Set(sam.RTC_MODE0_READREQ_RREQ)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
// set compare value
|
// set compare value
|
||||||
cnt := sam.RTC_MODE0.COUNT
|
cnt := sam.RTC_MODE0.COUNT.Get()
|
||||||
sam.RTC_MODE0.COMP0 = sam.RegValue(uint32(cnt) + (ticks * 10 / 305)) // each counter tick == 30.5us
|
sam.RTC_MODE0.COMP0.Set(uint32(cnt) + (ticks * 10 / 305)) // each counter tick == 30.5us
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
// enable IRQ for CMP0 compare
|
// enable IRQ for CMP0 compare
|
||||||
sam.RTC_MODE0.INTENSET |= sam.RTC_MODE0_INTENSET_CMP0
|
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
||||||
|
|
||||||
for !timerWakeup {
|
for !timerWakeup {
|
||||||
arm.Asm("wfi")
|
arm.Asm("wfi")
|
||||||
@@ -288,70 +288,17 @@ func timerSleep(ticks uint32) {
|
|||||||
//go:export RTC_IRQHandler
|
//go:export RTC_IRQHandler
|
||||||
func handleRTC() {
|
func handleRTC() {
|
||||||
// disable IRQ for CMP0 compare
|
// disable IRQ for CMP0 compare
|
||||||
sam.RTC_MODE0.INTFLAG = sam.RTC_MODE0_INTENSET_CMP0
|
sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0)
|
||||||
|
|
||||||
timerWakeup = true
|
timerWakeup = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func initSERCOMClocks() {
|
|
||||||
// Turn on clock to SERCOM0 for UART0
|
|
||||||
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM0_
|
|
||||||
|
|
||||||
// Use GCLK0 for SERCOM0 aka UART0
|
|
||||||
// GCLK_CLKCTRL_ID( clockId ) | // Generic Clock 0 (SERCOMx)
|
|
||||||
// GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source
|
|
||||||
// GCLK_CLKCTRL_CLKEN ;
|
|
||||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM0_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
|
||||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
|
||||||
waitForSync()
|
|
||||||
|
|
||||||
// Turn on clock to SERCOM1
|
|
||||||
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM1_
|
|
||||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM1_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
|
||||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
|
||||||
waitForSync()
|
|
||||||
|
|
||||||
// Turn on clock to SERCOM2
|
|
||||||
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM2_
|
|
||||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM2_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
|
||||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
|
||||||
waitForSync()
|
|
||||||
|
|
||||||
// Turn on clock to SERCOM3
|
|
||||||
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM3_
|
|
||||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM3_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
|
||||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
|
||||||
waitForSync()
|
|
||||||
|
|
||||||
// Turn on clock to SERCOM4
|
|
||||||
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM4_
|
|
||||||
|
|
||||||
// Use GCLK0 for SERCOM4
|
|
||||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM4_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
|
||||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
|
||||||
waitForSync()
|
|
||||||
|
|
||||||
// Turn on clock to SERCOM5
|
|
||||||
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM5_
|
|
||||||
|
|
||||||
// Use GCLK0 for SERCOM5
|
|
||||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM5_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
|
||||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
|
||||||
waitForSync()
|
|
||||||
}
|
|
||||||
|
|
||||||
func initUSBClock() {
|
func initUSBClock() {
|
||||||
// Turn on clock for USB
|
// Turn on clock for USB
|
||||||
sam.PM.APBBMASK |= sam.PM_APBBMASK_USB_
|
sam.PM.APBBMASK.SetBits(sam.PM_APBBMASK_USB_)
|
||||||
|
|
||||||
// Put Generic Clock Generator 0 as source for Generic Clock Multiplexer 6 (USB reference)
|
// 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.Set((sam.GCLK_CLKCTRL_ID_USB << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
@@ -359,10 +306,10 @@ func initUSBClock() {
|
|||||||
|
|
||||||
func initADCClock() {
|
func initADCClock() {
|
||||||
// Turn on clock for ADC
|
// Turn on clock for ADC
|
||||||
sam.PM.APBCMASK |= sam.PM_APBCMASK_ADC_
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_ADC_)
|
||||||
|
|
||||||
// Put Generic Clock Generator 0 as source for Generic Clock Multiplexer for ADC.
|
// Put Generic Clock Generator 0 as source for Generic Clock Multiplexer for ADC.
|
||||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_ADC << sam.GCLK_CLKCTRL_ID_Pos) |
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_ADC << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// +build sam,atsamd21,atsamd21e18
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/sam"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initSERCOMClocks() {
|
||||||
|
// Turn on clock to SERCOM0 for UART0
|
||||||
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_SERCOM0_)
|
||||||
|
|
||||||
|
// Use GCLK0 for SERCOM0 aka UART0
|
||||||
|
// GCLK_CLKCTRL_ID( clockId ) | // Generic Clock 0 (SERCOMx)
|
||||||
|
// GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source
|
||||||
|
// GCLK_CLKCTRL_CLKEN ;
|
||||||
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_SERCOM0_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
waitForSync()
|
||||||
|
|
||||||
|
// Turn on clock to SERCOM1
|
||||||
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_SERCOM1_)
|
||||||
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_SERCOM1_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
waitForSync()
|
||||||
|
|
||||||
|
// Turn on clock to SERCOM2
|
||||||
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_SERCOM2_)
|
||||||
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_SERCOM2_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
waitForSync()
|
||||||
|
|
||||||
|
// Turn on clock to SERCOM3
|
||||||
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_SERCOM3_)
|
||||||
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_SERCOM3_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
waitForSync()
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// +build sam,atsamd21,atsamd21g18
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/sam"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initSERCOMClocks() {
|
||||||
|
// Turn on clock to SERCOM0 for UART0
|
||||||
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_SERCOM0_)
|
||||||
|
|
||||||
|
// Use GCLK0 for SERCOM0 aka UART0
|
||||||
|
// GCLK_CLKCTRL_ID( clockId ) | // Generic Clock 0 (SERCOMx)
|
||||||
|
// GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source
|
||||||
|
// GCLK_CLKCTRL_CLKEN ;
|
||||||
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_SERCOM0_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
waitForSync()
|
||||||
|
|
||||||
|
// Turn on clock to SERCOM1
|
||||||
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_SERCOM1_)
|
||||||
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_SERCOM1_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
waitForSync()
|
||||||
|
|
||||||
|
// Turn on clock to SERCOM2
|
||||||
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_SERCOM2_)
|
||||||
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_SERCOM2_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
waitForSync()
|
||||||
|
|
||||||
|
// Turn on clock to SERCOM3
|
||||||
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_SERCOM3_)
|
||||||
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_SERCOM3_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
waitForSync()
|
||||||
|
|
||||||
|
// Turn on clock to SERCOM4
|
||||||
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_SERCOM4_)
|
||||||
|
|
||||||
|
// Use GCLK0 for SERCOM4
|
||||||
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_SERCOM4_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
waitForSync()
|
||||||
|
|
||||||
|
// Turn on clock to SERCOM5
|
||||||
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_SERCOM5_)
|
||||||
|
|
||||||
|
// Use GCLK0 for SERCOM5
|
||||||
|
sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_SERCOM5_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||||
|
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||||
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
|
waitForSync()
|
||||||
|
}
|
||||||
@@ -41,11 +41,61 @@ func preinit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func abort() {
|
func abort() {
|
||||||
|
// disable all interrupts
|
||||||
|
arm.DisableInterrupts()
|
||||||
|
|
||||||
|
// lock up forever
|
||||||
for {
|
for {
|
||||||
arm.Asm("wfi")
|
arm.Asm("wfi")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The stack layout at the moment an interrupt occurs.
|
||||||
|
// Registers can be accessed if the stack pointer is cast to a pointer to this
|
||||||
|
// struct.
|
||||||
|
type interruptStack struct {
|
||||||
|
R0 uintptr
|
||||||
|
R1 uintptr
|
||||||
|
R2 uintptr
|
||||||
|
R3 uintptr
|
||||||
|
R12 uintptr
|
||||||
|
LR uintptr
|
||||||
|
PC uintptr
|
||||||
|
PSR uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called at HardFault.
|
||||||
|
// Before this function is called, the stack pointer is reset to the initial
|
||||||
|
// stack pointer (loaded from addres 0x0) and the previous stack pointer is
|
||||||
|
// passed as an argument to this function. This allows for easy inspection of
|
||||||
|
// the stack the moment a HardFault occurs, but it means that the stack will be
|
||||||
|
// corrupted by this function and thus this handler must not attempt to recover.
|
||||||
|
//
|
||||||
|
// For details, see:
|
||||||
|
// https://community.arm.com/developer/ip-products/system/f/embedded-forum/3257/debugging-a-cortex-m0-hard-fault
|
||||||
|
// https://blog.feabhas.com/2013/02/developing-a-generic-hard-fault-handler-for-arm-cortex-m3cortex-m4/
|
||||||
|
//go:export handleHardFault
|
||||||
|
func handleHardFault(sp *interruptStack) {
|
||||||
|
print("fatal error: ")
|
||||||
|
if uintptr(unsafe.Pointer(sp)) < 0x20000000 {
|
||||||
|
print("stack overflow")
|
||||||
|
} else {
|
||||||
|
// TODO: try to find the cause of the hard fault. Especially on
|
||||||
|
// Cortex-M3 and higher it is possible to find more detailed information
|
||||||
|
// in special status registers.
|
||||||
|
print("HardFault")
|
||||||
|
}
|
||||||
|
print(" with sp=", sp)
|
||||||
|
if uintptr(unsafe.Pointer(&sp.PC)) >= 0x20000000 {
|
||||||
|
// Only print the PC if it points into memory.
|
||||||
|
// It may not point into memory during a stack overflow, so check that
|
||||||
|
// first before accessing the stack.
|
||||||
|
print(" pc=", sp.PC)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
abort()
|
||||||
|
}
|
||||||
|
|
||||||
// Implement memset for LLVM and compiler-rt.
|
// Implement memset for LLVM and compiler-rt.
|
||||||
//go:export memset
|
//go:export memset
|
||||||
func libc_memset(ptr unsafe.Pointer, c byte, size uintptr) {
|
func libc_memset(ptr unsafe.Pointer, c byte, size uintptr) {
|
||||||
|
|||||||
+10
-10
@@ -32,16 +32,16 @@ func init() {
|
|||||||
|
|
||||||
func initLFCLK() {
|
func initLFCLK() {
|
||||||
if machine.HasLowFrequencyCrystal {
|
if machine.HasLowFrequencyCrystal {
|
||||||
nrf.CLOCK.LFCLKSRC = nrf.CLOCK_LFCLKSTAT_SRC_Xtal
|
nrf.CLOCK.LFCLKSRC.Set(nrf.CLOCK_LFCLKSTAT_SRC_Xtal)
|
||||||
}
|
}
|
||||||
nrf.CLOCK.TASKS_LFCLKSTART = 1
|
nrf.CLOCK.TASKS_LFCLKSTART.Set(1)
|
||||||
for nrf.CLOCK.EVENTS_LFCLKSTARTED == 0 {
|
for nrf.CLOCK.EVENTS_LFCLKSTARTED.Get() == 0 {
|
||||||
}
|
}
|
||||||
nrf.CLOCK.EVENTS_LFCLKSTARTED = 0
|
nrf.CLOCK.EVENTS_LFCLKSTARTED.Set(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initRTC() {
|
func initRTC() {
|
||||||
nrf.RTC1.TASKS_START = 1
|
nrf.RTC1.TASKS_START.Set(1)
|
||||||
arm.SetPriority(nrf.IRQ_RTC1, 0xc0) // low priority
|
arm.SetPriority(nrf.IRQ_RTC1, 0xc0) // low priority
|
||||||
arm.EnableIRQ(nrf.IRQ_RTC1)
|
arm.EnableIRQ(nrf.IRQ_RTC1)
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ var (
|
|||||||
// overflow the counter, leading to incorrect results. This might be fixed by
|
// overflow the counter, leading to incorrect results. This might be fixed by
|
||||||
// handling the overflow event.
|
// handling the overflow event.
|
||||||
func ticks() timeUnit {
|
func ticks() timeUnit {
|
||||||
rtcCounter := uint32(nrf.RTC1.COUNTER)
|
rtcCounter := uint32(nrf.RTC1.COUNTER.Get())
|
||||||
offset := (rtcCounter - rtcLastCounter) & 0xffffff // change since last measurement
|
offset := (rtcCounter - rtcLastCounter) & 0xffffff // change since last measurement
|
||||||
rtcLastCounter = rtcCounter
|
rtcLastCounter = rtcCounter
|
||||||
timestamp += timeUnit(offset) // TODO: not precise
|
timestamp += timeUnit(offset) // TODO: not precise
|
||||||
@@ -85,7 +85,7 @@ type isrFlag bool
|
|||||||
var rtc_wakeup isrFlag
|
var rtc_wakeup isrFlag
|
||||||
|
|
||||||
func rtc_sleep(ticks uint32) {
|
func rtc_sleep(ticks uint32) {
|
||||||
nrf.RTC1.INTENSET = nrf.RTC_INTENSET_COMPARE0
|
nrf.RTC1.INTENSET.Set(nrf.RTC_INTENSET_COMPARE0)
|
||||||
rtc_wakeup = false
|
rtc_wakeup = false
|
||||||
if ticks == 1 {
|
if ticks == 1 {
|
||||||
// Race condition (even in hardware) at ticks == 1.
|
// Race condition (even in hardware) at ticks == 1.
|
||||||
@@ -93,7 +93,7 @@ func rtc_sleep(ticks uint32) {
|
|||||||
// describes.
|
// describes.
|
||||||
ticks = 2
|
ticks = 2
|
||||||
}
|
}
|
||||||
nrf.RTC1.CC[0] = (nrf.RTC1.COUNTER + nrf.RegValue(ticks)) & 0x00ffffff
|
nrf.RTC1.CC[0].Set((nrf.RTC1.COUNTER.Get() + ticks) & 0x00ffffff)
|
||||||
for !rtc_wakeup {
|
for !rtc_wakeup {
|
||||||
arm.Asm("wfi")
|
arm.Asm("wfi")
|
||||||
}
|
}
|
||||||
@@ -101,7 +101,7 @@ func rtc_sleep(ticks uint32) {
|
|||||||
|
|
||||||
//go:export RTC1_IRQHandler
|
//go:export RTC1_IRQHandler
|
||||||
func handleRTC1() {
|
func handleRTC1() {
|
||||||
nrf.RTC1.INTENCLR = nrf.RTC_INTENSET_COMPARE0
|
nrf.RTC1.INTENCLR.Set(nrf.RTC_INTENSET_COMPARE0)
|
||||||
nrf.RTC1.EVENTS_COMPARE[0] = 0
|
nrf.RTC1.EVENTS_COMPARE[0].Set(0)
|
||||||
rtc_wakeup = true
|
rtc_wakeup = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,33 +21,33 @@ func putchar(c byte) {
|
|||||||
|
|
||||||
// initCLK sets clock to 72MHz using HSE 8MHz crystal w/ PLL X 9 (8MHz x 9 = 72MHz).
|
// initCLK sets clock to 72MHz using HSE 8MHz crystal w/ PLL X 9 (8MHz x 9 = 72MHz).
|
||||||
func initCLK() {
|
func initCLK() {
|
||||||
stm32.FLASH.ACR |= stm32.FLASH_ACR_LATENCY_2 // Two wait states, per datasheet
|
stm32.FLASH.ACR.SetBits(stm32.FLASH_ACR_LATENCY_2) // Two wait states, per datasheet
|
||||||
stm32.RCC.CFGR |= stm32.RCC_CFGR_PPRE1_DIV_2 // prescale PCLK1 = HCLK/2
|
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PPRE1_DIV_2) // prescale PCLK1 = HCLK/2
|
||||||
stm32.RCC.CFGR |= stm32.RCC_CFGR_PPRE2_DIV_NONE // prescale PCLK2 = HCLK/1
|
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PPRE2_DIV_NONE) // prescale PCLK2 = HCLK/1
|
||||||
stm32.RCC.CR |= stm32.RCC_CR_HSEON // enable HSE clock
|
stm32.RCC.CR.SetBits(stm32.RCC_CR_HSEON) // enable HSE clock
|
||||||
|
|
||||||
// wait for the HSEREADY flag
|
// wait for the HSEREADY flag
|
||||||
for (stm32.RCC.CR & stm32.RCC_CR_HSERDY) == 0 {
|
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_HSERDY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
stm32.RCC.CR |= stm32.RCC_CR_HSION // enable HSI clock
|
stm32.RCC.CR.SetBits(stm32.RCC_CR_HSION) // enable HSI clock
|
||||||
|
|
||||||
// wait for the HSIREADY flag
|
// wait for the HSIREADY flag
|
||||||
for (stm32.RCC.CR & stm32.RCC_CR_HSIRDY) == 0 {
|
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_HSIRDY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
stm32.RCC.CFGR |= stm32.RCC_CFGR_PLLSRC // set PLL source to HSE
|
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PLLSRC) // set PLL source to HSE
|
||||||
stm32.RCC.CFGR |= stm32.RCC_CFGR_PLLMUL_9 // multiply by 9
|
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PLLMUL_9) // multiply by 9
|
||||||
stm32.RCC.CR |= stm32.RCC_CR_PLLON // enable the PLL
|
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON) // enable the PLL
|
||||||
|
|
||||||
// wait for the PLLRDY flag
|
// wait for the PLLRDY flag
|
||||||
for (stm32.RCC.CR & stm32.RCC_CR_PLLRDY) == 0 {
|
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_PLLRDY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
stm32.RCC.CFGR |= stm32.RCC_CFGR_SW_PLL // set clock source to pll
|
stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_SW_PLL) // set clock source to pll
|
||||||
|
|
||||||
// wait for PLL to be CLK
|
// wait for PLL to be CLK
|
||||||
for (stm32.RCC.CFGR & stm32.RCC_CFGR_SWS_PLL) == 0 {
|
for !stm32.RCC.CFGR.HasBits(stm32.RCC_CFGR_SWS_PLL) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,43 +65,43 @@ var timerWakeup isrFlag
|
|||||||
|
|
||||||
func initRTC() {
|
func initRTC() {
|
||||||
// Enable the PWR and BKP.
|
// Enable the PWR and BKP.
|
||||||
stm32.RCC.APB1ENR |= stm32.RCC_APB1ENR_PWREN | stm32.RCC_APB1ENR_BKPEN
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN | stm32.RCC_APB1ENR_BKPEN)
|
||||||
|
|
||||||
// access to backup register
|
// access to backup register
|
||||||
stm32.PWR.CR |= stm32.PWR_CR_DBP
|
stm32.PWR.CR.SetBits(stm32.PWR_CR_DBP)
|
||||||
|
|
||||||
// Enable LSE
|
// Enable LSE
|
||||||
stm32.RCC.BDCR |= stm32.RCC_BDCR_LSEON
|
stm32.RCC.BDCR.SetBits(stm32.RCC_BDCR_LSEON)
|
||||||
|
|
||||||
// wait until LSE is ready
|
// wait until LSE is ready
|
||||||
for stm32.RCC.BDCR&stm32.RCC_BDCR_LSERDY == 0 {
|
for !stm32.RCC.BDCR.HasBits(stm32.RCC_BDCR_LSERDY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select LSE
|
// Select LSE
|
||||||
stm32.RCC.BDCR |= stm32.RCC_RTCCLKSource_LSE
|
stm32.RCC.BDCR.SetBits(stm32.RCC_RTCCLKSource_LSE)
|
||||||
|
|
||||||
// set prescaler to "max" per datasheet
|
// set prescaler to "max" per datasheet
|
||||||
stm32.RTC.PRLH = stm32.RTC_PRLH_PRLH_Msk
|
stm32.RTC.PRLH.Set(stm32.RTC_PRLH_PRLH_Msk)
|
||||||
stm32.RTC.PRLL = stm32.RTC_PRLL_PRLL_Msk
|
stm32.RTC.PRLL.Set(stm32.RTC_PRLL_PRLL_Msk)
|
||||||
|
|
||||||
// set count to zero
|
// set count to zero
|
||||||
stm32.RTC.CNTH = 0x0
|
stm32.RTC.CNTH.Set(0x0)
|
||||||
stm32.RTC.CNTL = 0x0
|
stm32.RTC.CNTL.Set(0x0)
|
||||||
|
|
||||||
// Enable RTC
|
// Enable RTC
|
||||||
stm32.RCC.BDCR |= stm32.RCC_BDCR_RTCEN
|
stm32.RCC.BDCR.SetBits(stm32.RCC_BDCR_RTCEN)
|
||||||
|
|
||||||
// Clear RSF
|
// Clear RSF
|
||||||
stm32.RTC.CRL &^= stm32.RTC_CRL_RSF
|
stm32.RTC.CRL.ClearBits(stm32.RTC_CRL_RSF)
|
||||||
|
|
||||||
// Wait till flag is set
|
// Wait till flag is set
|
||||||
for stm32.RTC.CRL&stm32.RTC_CRL_RSF == 0 {
|
for !stm32.RTC.CRL.HasBits(stm32.RTC_CRL_RSF) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable the TIM3 clock.
|
// Enable the TIM3 clock.
|
||||||
func initTIM() {
|
func initTIM() {
|
||||||
stm32.RCC.APB1ENR |= stm32.RCC_APB1ENR_TIM3EN
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||||
|
|
||||||
arm.SetPriority(stm32.IRQ_TIM3, 0xc3)
|
arm.SetPriority(stm32.IRQ_TIM3, 0xc3)
|
||||||
arm.EnableIRQ(stm32.IRQ_TIM3)
|
arm.EnableIRQ(stm32.IRQ_TIM3)
|
||||||
@@ -122,10 +122,10 @@ func sleepTicks(d timeUnit) {
|
|||||||
// number of ticks (microseconds) since start.
|
// number of ticks (microseconds) since start.
|
||||||
func ticks() timeUnit {
|
func ticks() timeUnit {
|
||||||
// convert RTC counter from seconds to microseconds
|
// convert RTC counter from seconds to microseconds
|
||||||
timerCounter := uint64(stm32.RTC.CNTH<<16|stm32.RTC.CNTL) * 1000 * 1000
|
timerCounter := uint64(stm32.RTC.CNTH.Get()<<16|stm32.RTC.CNTL.Get()) * 1000 * 1000
|
||||||
|
|
||||||
// add the fractional part of current time using DIV register
|
// add the fractional part of current time using DIV register
|
||||||
timerCounter += uint64(0x8000-stm32.RTC.DIVL) * 31
|
timerCounter += uint64(0x8000-stm32.RTC.DIVL.Get()) * 31
|
||||||
|
|
||||||
// change since last measurement
|
// change since last measurement
|
||||||
offset := (timerCounter - timerLastCounter)
|
offset := (timerCounter - timerLastCounter)
|
||||||
@@ -165,16 +165,16 @@ func timerSleep(ticks uint32) {
|
|||||||
// The current scaling only supports a range of 100 usec to 6553 msec.
|
// The current scaling only supports a range of 100 usec to 6553 msec.
|
||||||
|
|
||||||
// prescale counter down from 72mhz to 10khz aka 0.1 ms frequency.
|
// prescale counter down from 72mhz to 10khz aka 0.1 ms frequency.
|
||||||
stm32.TIM3.PSC = machine.CPU_FREQUENCY/10000 - 1 // 7199
|
stm32.TIM3.PSC.Set(machine.CPU_FREQUENCY/10000 - 1) // 7199
|
||||||
|
|
||||||
// set duty aka duration
|
// set duty aka duration
|
||||||
stm32.TIM3.ARR = stm32.RegValue(ticks/100) - 1 // convert from microseconds to 0.1 ms
|
stm32.TIM3.ARR.Set(ticks/100 - 1) // convert from microseconds to 0.1 ms
|
||||||
|
|
||||||
// Enable the hardware interrupt.
|
// Enable the hardware interrupt.
|
||||||
stm32.TIM3.DIER |= stm32.TIM_DIER_UIE
|
stm32.TIM3.DIER.SetBits(stm32.TIM_DIER_UIE)
|
||||||
|
|
||||||
// Enable the timer.
|
// Enable the timer.
|
||||||
stm32.TIM3.CR1 |= stm32.TIM_CR1_CEN
|
stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN)
|
||||||
|
|
||||||
// wait till timer wakes up
|
// wait till timer wakes up
|
||||||
for !timerWakeup {
|
for !timerWakeup {
|
||||||
@@ -184,12 +184,12 @@ func timerSleep(ticks uint32) {
|
|||||||
|
|
||||||
//go:export TIM3_IRQHandler
|
//go:export TIM3_IRQHandler
|
||||||
func handleTIM3() {
|
func handleTIM3() {
|
||||||
if (stm32.TIM3.SR & stm32.TIM_SR_UIF) > 0 {
|
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||||
// Disable the timer.
|
// Disable the timer.
|
||||||
stm32.TIM3.CR1 &^= stm32.TIM_CR1_CEN
|
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN)
|
||||||
|
|
||||||
// clear the update flag
|
// clear the update flag
|
||||||
stm32.TIM3.SR &^= stm32.TIM_SR_UIF
|
stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF)
|
||||||
|
|
||||||
// timer was triggered
|
// timer was triggered
|
||||||
timerWakeup = true
|
timerWakeup = true
|
||||||
|
|||||||
@@ -42,58 +42,59 @@ func initCLK() {
|
|||||||
|
|
||||||
// Reset clock registers
|
// Reset clock registers
|
||||||
// Set HSION
|
// Set HSION
|
||||||
stm32.RCC.CR |= stm32.RCC_CR_HSION
|
stm32.RCC.CR.SetBits(stm32.RCC_CR_HSION)
|
||||||
for (stm32.RCC.CR & stm32.RCC_CR_HSIRDY) == 0 {
|
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_HSIRDY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset CFGR
|
// Reset CFGR
|
||||||
stm32.RCC.CFGR = 0x00000000
|
stm32.RCC.CFGR.Set(0x00000000)
|
||||||
// Reset HSEON, CSSON and PLLON
|
// Reset HSEON, CSSON and PLLON
|
||||||
stm32.RCC.CR &= 0xFEF6FFFF
|
stm32.RCC.CR.ClearBits(stm32.RCC_CR_HSEON | stm32.RCC_CR_CSSON | stm32.RCC_CR_PLLON)
|
||||||
// Reset PLLCFGR
|
// Reset PLLCFGR
|
||||||
stm32.RCC.PLLCFGR = 0x24003010
|
stm32.RCC.PLLCFGR.Set(0x24003010)
|
||||||
// Reset HSEBYP
|
// Reset HSEBYP
|
||||||
stm32.RCC.CR &= 0xFFFBFFFF
|
stm32.RCC.CR.ClearBits(stm32.RCC_CR_HSEBYP)
|
||||||
// Disable all interrupts
|
// Disable all interrupts
|
||||||
stm32.RCC.CIR = 0x00000000
|
stm32.RCC.CIR.Set(0x00000000)
|
||||||
|
|
||||||
// Set up the clock
|
// Set up the clock
|
||||||
var startupCounter uint32 = 0
|
var startupCounter uint32 = 0
|
||||||
|
|
||||||
// Enable HSE
|
// Enable HSE
|
||||||
stm32.RCC.CR = stm32.RCC_CR_HSEON
|
stm32.RCC.CR.Set(stm32.RCC_CR_HSEON)
|
||||||
|
|
||||||
// Wait till HSE is ready and if timeout is reached exit
|
// Wait till HSE is ready and if timeout is reached exit
|
||||||
for {
|
for {
|
||||||
startupCounter++
|
startupCounter++
|
||||||
if (stm32.RCC.CR&stm32.RCC_CR_HSERDY != 0) || (startupCounter == HSE_STARTUP_TIMEOUT) {
|
if stm32.RCC.CR.HasBits(stm32.RCC_CR_HSERDY) || (startupCounter == HSE_STARTUP_TIMEOUT) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (stm32.RCC.CR & stm32.RCC_CR_HSERDY) != 0 {
|
if stm32.RCC.CR.HasBits(stm32.RCC_CR_HSERDY) {
|
||||||
// Enable high performance mode, System frequency up to 168MHz
|
// Enable high performance mode, System frequency up to 168MHz
|
||||||
stm32.RCC.APB1ENR |= stm32.RCC_APB1ENR_PWREN
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
|
||||||
stm32.PWR.CR |= 0x4000 // PWR_CR_VOS
|
stm32.PWR.CR.SetBits(0x4000) // PWR_CR_VOS
|
||||||
// HCLK = SYSCLK / 1
|
// HCLK = SYSCLK / 1
|
||||||
stm32.RCC.CFGR |= (0x0 << stm32.RCC_CFGR_HPRE_Pos)
|
stm32.RCC.CFGR.SetBits(0x0 << stm32.RCC_CFGR_HPRE_Pos)
|
||||||
// PCLK2 = HCLK / 2
|
// PCLK2 = HCLK / 2
|
||||||
stm32.RCC.CFGR |= (0x4 << stm32.RCC_CFGR_PPRE2_Pos)
|
stm32.RCC.CFGR.SetBits(0x4 << stm32.RCC_CFGR_PPRE2_Pos)
|
||||||
// PCLK1 = HCLK / 4
|
// PCLK1 = HCLK / 4
|
||||||
stm32.RCC.CFGR |= (0x5 << stm32.RCC_CFGR_PPRE1_Pos)
|
stm32.RCC.CFGR.SetBits(0x5 << stm32.RCC_CFGR_PPRE1_Pos)
|
||||||
// Configure the main PLL
|
// Configure the main PLL
|
||||||
// PLL Options - See RM0090 Reference Manual pg. 95
|
// PLL Options - See RM0090 Reference Manual pg. 95
|
||||||
stm32.RCC.PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) - 1) << 16) |
|
stm32.RCC.PLLCFGR.Set(PLL_M | (PLL_N << 6) | (((PLL_P >> 1) - 1) << 16) |
|
||||||
(1 << stm32.RCC_PLLCFGR_PLLSRC_Pos) | (PLL_Q << 24)
|
(1 << stm32.RCC_PLLCFGR_PLLSRC_Pos) | (PLL_Q << 24))
|
||||||
// Enable main PLL
|
// Enable main PLL
|
||||||
stm32.RCC.CR |= stm32.RCC_CR_PLLON
|
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON)
|
||||||
// Wait till the main PLL is ready
|
// Wait till the main PLL is ready
|
||||||
for (stm32.RCC.CR & stm32.RCC_CR_PLLRDY) == 0 {
|
for (stm32.RCC.CR.Get() & stm32.RCC_CR_PLLRDY) == 0 {
|
||||||
}
|
}
|
||||||
// Configure Flash prefetch, Instruction cache, Data cache and wait state
|
// Configure Flash prefetch, Instruction cache, Data cache and wait state
|
||||||
stm32.FLASH.ACR = stm32.FLASH_ACR_ICEN | stm32.FLASH_ACR_DCEN | (5 << stm32.FLASH_ACR_LATENCY_Pos)
|
stm32.FLASH.ACR.Set(stm32.FLASH_ACR_ICEN | stm32.FLASH_ACR_DCEN | (5 << stm32.FLASH_ACR_LATENCY_Pos))
|
||||||
// Select the main PLL as system clock source
|
// Select the main PLL as system clock source
|
||||||
stm32.RCC.CFGR &^= stm32.RCC_CFGR_SW0 | stm32.RCC_CFGR_SW1
|
stm32.RCC.CFGR.ClearBits(stm32.RCC_CFGR_SW0 | stm32.RCC_CFGR_SW1)
|
||||||
stm32.RCC.CFGR |= (0x2 << stm32.RCC_CFGR_SW0_Pos)
|
stm32.RCC.CFGR.SetBits(0x2 << stm32.RCC_CFGR_SW0_Pos)
|
||||||
for (stm32.RCC.CFGR & (0x3 << stm32.RCC_CFGR_SWS0_Pos)) != (0x2 << stm32.RCC_CFGR_SWS0_Pos) {
|
for (stm32.RCC.CFGR.Get() & (0x3 << stm32.RCC_CFGR_SWS0_Pos)) != (0x2 << stm32.RCC_CFGR_SWS0_Pos) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -102,7 +103,7 @@ func initCLK() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Enable the CCM RAM clock
|
// Enable the CCM RAM clock
|
||||||
stm32.RCC.AHB1ENR |= (1 << 20)
|
stm32.RCC.AHB1ENR.SetBits(1 << 20)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +121,7 @@ var timerWakeup isrFlag
|
|||||||
|
|
||||||
// Enable the TIM3 clock.(sleep count)
|
// Enable the TIM3 clock.(sleep count)
|
||||||
func initTIM3() {
|
func initTIM3() {
|
||||||
stm32.RCC.APB1ENR |= stm32.RCC_APB1ENR_TIM3EN
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||||
|
|
||||||
arm.SetPriority(stm32.IRQ_TIM3, 0xc3)
|
arm.SetPriority(stm32.IRQ_TIM3, 0xc3)
|
||||||
arm.EnableIRQ(stm32.IRQ_TIM3)
|
arm.EnableIRQ(stm32.IRQ_TIM3)
|
||||||
@@ -128,17 +129,17 @@ func initTIM3() {
|
|||||||
|
|
||||||
// Enable the TIM7 clock.(tick count)
|
// Enable the TIM7 clock.(tick count)
|
||||||
func initTIM7() {
|
func initTIM7() {
|
||||||
stm32.RCC.APB1ENR |= stm32.RCC_APB1ENR_TIM7EN
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN)
|
||||||
|
|
||||||
// CK_INT = APB1 x2 = 84mhz
|
// CK_INT = APB1 x2 = 84mhz
|
||||||
stm32.TIM7.PSC = 84000000/10000 - 1 // 84mhz to 10khz(0.1ms)
|
stm32.TIM7.PSC.Set(84000000/10000 - 1) // 84mhz to 10khz(0.1ms)
|
||||||
stm32.TIM7.ARR = stm32.RegValue(10) - 1 // interrupt per 1ms
|
stm32.TIM7.ARR.Set(10 - 1) // interrupt per 1ms
|
||||||
|
|
||||||
// Enable the hardware interrupt.
|
// Enable the hardware interrupt.
|
||||||
stm32.TIM7.DIER |= stm32.TIM_DIER_UIE
|
stm32.TIM7.DIER.SetBits(stm32.TIM_DIER_UIE)
|
||||||
|
|
||||||
// Enable the timer.
|
// Enable the timer.
|
||||||
stm32.TIM7.CR1 |= stm32.TIM_CR1_CEN
|
stm32.TIM7.CR1.SetBits(stm32.TIM_CR1_CEN)
|
||||||
|
|
||||||
arm.SetPriority(stm32.IRQ_TIM7, 0xc1)
|
arm.SetPriority(stm32.IRQ_TIM7, 0xc1)
|
||||||
arm.EnableIRQ(stm32.IRQ_TIM7)
|
arm.EnableIRQ(stm32.IRQ_TIM7)
|
||||||
@@ -163,20 +164,20 @@ func timerSleep(ticks uint32) {
|
|||||||
|
|
||||||
// CK_INT = APB1 x2 = 84mhz
|
// CK_INT = APB1 x2 = 84mhz
|
||||||
// prescale counter down from 84mhz to 10khz aka 0.1 ms frequency.
|
// prescale counter down from 84mhz to 10khz aka 0.1 ms frequency.
|
||||||
stm32.TIM3.PSC = 84000000/10000 - 1 // 8399
|
stm32.TIM3.PSC.Set(84000000/10000 - 1) // 8399
|
||||||
|
|
||||||
// set duty aka duration
|
// set duty aka duration
|
||||||
arr := (ticks / 100) - 1 // convert from microseconds to 0.1 ms
|
arr := (ticks / 100) - 1 // convert from microseconds to 0.1 ms
|
||||||
if arr == 0 {
|
if arr == 0 {
|
||||||
arr = 1 // avoid blocking
|
arr = 1 // avoid blocking
|
||||||
}
|
}
|
||||||
stm32.TIM3.ARR = stm32.RegValue(arr)
|
stm32.TIM3.ARR.Set(arr)
|
||||||
|
|
||||||
// Enable the hardware interrupt.
|
// Enable the hardware interrupt.
|
||||||
stm32.TIM3.DIER |= stm32.TIM_DIER_UIE
|
stm32.TIM3.DIER.SetBits(stm32.TIM_DIER_UIE)
|
||||||
|
|
||||||
// Enable the timer.
|
// Enable the timer.
|
||||||
stm32.TIM3.CR1 |= stm32.TIM_CR1_CEN
|
stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN)
|
||||||
|
|
||||||
// wait till timer wakes up
|
// wait till timer wakes up
|
||||||
for !timerWakeup {
|
for !timerWakeup {
|
||||||
@@ -186,12 +187,12 @@ func timerSleep(ticks uint32) {
|
|||||||
|
|
||||||
//go:export TIM3_IRQHandler
|
//go:export TIM3_IRQHandler
|
||||||
func handleTIM3() {
|
func handleTIM3() {
|
||||||
if (stm32.TIM3.SR & stm32.TIM_SR_UIF) > 0 {
|
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||||
// Disable the timer.
|
// Disable the timer.
|
||||||
stm32.TIM3.CR1 &^= stm32.TIM_CR1_CEN
|
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN)
|
||||||
|
|
||||||
// clear the update flag
|
// clear the update flag
|
||||||
stm32.TIM3.SR &^= stm32.TIM_SR_UIF
|
stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF)
|
||||||
|
|
||||||
// timer was triggered
|
// timer was triggered
|
||||||
timerWakeup = true
|
timerWakeup = true
|
||||||
@@ -200,9 +201,9 @@ func handleTIM3() {
|
|||||||
|
|
||||||
//go:export TIM7_IRQHandler
|
//go:export TIM7_IRQHandler
|
||||||
func handleTIM7() {
|
func handleTIM7() {
|
||||||
if (stm32.TIM7.SR & stm32.TIM_SR_UIF) > 0 {
|
if stm32.TIM7.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||||
// clear the update flag
|
// clear the update flag
|
||||||
stm32.TIM7.SR &^= stm32.TIM_SR_UIF
|
stm32.TIM7.SR.ClearBits(stm32.TIM_SR_UIF)
|
||||||
tickCount++
|
tickCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ func malloc(size uintptr) unsafe.Pointer
|
|||||||
//go:export abort
|
//go:export abort
|
||||||
func abort()
|
func abort()
|
||||||
|
|
||||||
|
//go:export exit
|
||||||
|
func exit(code int)
|
||||||
|
|
||||||
//go:export clock_gettime
|
//go:export clock_gettime
|
||||||
func clock_gettime(clk_id uint, ts *timespec)
|
func clock_gettime(clk_id uint, ts *timespec)
|
||||||
|
|
||||||
@@ -75,3 +78,8 @@ func monotime() uint64 {
|
|||||||
func ticks() timeUnit {
|
func ticks() timeUnit {
|
||||||
return timeUnit(monotime())
|
return timeUnit(monotime())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname syscall_Exit syscall.Exit
|
||||||
|
func syscall_Exit(code int) {
|
||||||
|
exit(code)
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,9 +37,16 @@ func putchar(c byte) {
|
|||||||
resource_write(stdout, &c, 1)
|
resource_write(stdout, &c, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var handleEvent func()
|
||||||
|
|
||||||
//go:linkname setEventHandler syscall/js.setEventHandler
|
//go:linkname setEventHandler syscall/js.setEventHandler
|
||||||
func setEventHandler(fn func()) {
|
func setEventHandler(fn func()) {
|
||||||
// TODO
|
handleEvent = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:export resume
|
||||||
|
func resume() {
|
||||||
|
handleEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export go_scheduler
|
//go:export go_scheduler
|
||||||
|
|||||||
@@ -120,6 +120,9 @@ func setTaskPromisePtr(task *coroutine, value unsafe.Pointer) {
|
|||||||
// getTaskPromisePtr is a helper function to get the current .ptr field from a
|
// getTaskPromisePtr is a helper function to get the current .ptr field from a
|
||||||
// coroutine promise.
|
// coroutine promise.
|
||||||
func getTaskPromisePtr(task *coroutine) unsafe.Pointer {
|
func getTaskPromisePtr(task *coroutine) unsafe.Pointer {
|
||||||
|
if task == nil {
|
||||||
|
blockingPanic()
|
||||||
|
}
|
||||||
return task.promise().ptr
|
return task.promise().ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -166,9 +166,10 @@ func decodeUTF8(s string, index uintptr) (rune, uintptr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// indexByte returns the index of the first instance of c in s, or -1 if c is not present in s.
|
// indexByteString returns the index of the first instance of c in s, or -1 if c
|
||||||
//go:linkname indexByte strings.IndexByte
|
// is not present in s.
|
||||||
func indexByte(s string, c byte) int {
|
//go:linkname indexByteString internal/bytealg.IndexByteString
|
||||||
|
func indexByteString(s string, c byte) int {
|
||||||
for i := 0; i < len(s); i++ {
|
for i := 0; i < len(s); i++ {
|
||||||
if s[i] == c {
|
if s[i] == c {
|
||||||
return i
|
return i
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// +build !go1.12
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
// indexByte provides compatibility with Go 1.11.
|
||||||
|
// See the following:
|
||||||
|
// https://github.com/tinygo-org/tinygo/issues/351
|
||||||
|
// https://github.com/golang/go/commit/ad4a58e31501bce5de2aad90a620eaecdc1eecb8
|
||||||
|
//go:linkname indexByte strings.IndexByte
|
||||||
|
func indexByte(s string, c byte) int {
|
||||||
|
return indexByteString(s, c)
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Package volatile provides definitions for volatile loads and stores. These
|
||||||
|
// are implemented as compiler builtins.
|
||||||
|
//
|
||||||
|
// The load operations load a volatile value. The store operations store to a
|
||||||
|
// volatile value. The compiler will emit exactly one load or store operation
|
||||||
|
// when possible and will not reorder volatile operations. However, the compiler
|
||||||
|
// may move other operations across load/store operations, so make sure that all
|
||||||
|
// relevant loads/stores are done in a volatile way if this is a problem.
|
||||||
|
//
|
||||||
|
// These loads and stores are commonly used to read/write values from memory
|
||||||
|
// mapped peripheral devices. They do not provide atomicity, use the sync/atomic
|
||||||
|
// package for that.
|
||||||
|
//
|
||||||
|
// For more details: https://llvm.org/docs/LangRef.html#volatile-memory-accesses
|
||||||
|
// and https://blog.regehr.org/archives/28.
|
||||||
|
package volatile
|
||||||
|
|
||||||
|
// LoadUint8 loads the volatile value *addr.
|
||||||
|
func LoadUint8(addr *uint8) (val uint8)
|
||||||
|
|
||||||
|
// LoadUint16 loads the volatile value *addr.
|
||||||
|
func LoadUint16(addr *uint16) (val uint16)
|
||||||
|
|
||||||
|
// LoadUint32 loads the volatile value *addr.
|
||||||
|
func LoadUint32(addr *uint32) (val uint32)
|
||||||
|
|
||||||
|
// StoreUint8 stores val to the volatile value *addr.
|
||||||
|
func StoreUint8(addr *uint8, val uint8)
|
||||||
|
|
||||||
|
// StoreUint16 stores val to the volatile value *addr.
|
||||||
|
func StoreUint16(addr *uint16, val uint16)
|
||||||
|
|
||||||
|
// StoreUint32 stores val to the volatile value *addr.
|
||||||
|
func StoreUint32(addr *uint32, val uint32)
|
||||||
@@ -5,11 +5,13 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,6 +24,7 @@ type TargetSpec struct {
|
|||||||
Inherits []string `json:"inherits"`
|
Inherits []string `json:"inherits"`
|
||||||
Triple string `json:"llvm-target"`
|
Triple string `json:"llvm-target"`
|
||||||
CPU string `json:"cpu"`
|
CPU string `json:"cpu"`
|
||||||
|
Features []string `json:"features"`
|
||||||
GOOS string `json:"goos"`
|
GOOS string `json:"goos"`
|
||||||
GOARCH string `json:"goarch"`
|
GOARCH string `json:"goarch"`
|
||||||
BuildTags []string `json:"build-tags"`
|
BuildTags []string `json:"build-tags"`
|
||||||
@@ -50,6 +53,7 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
|
|||||||
if spec2.CPU != "" {
|
if spec2.CPU != "" {
|
||||||
spec.CPU = spec2.CPU
|
spec.CPU = spec2.CPU
|
||||||
}
|
}
|
||||||
|
spec.Features = append(spec.Features, spec2.Features...)
|
||||||
if spec2.GOOS != "" {
|
if spec2.GOOS != "" {
|
||||||
spec.GOOS = spec2.GOOS
|
spec.GOOS = spec2.GOOS
|
||||||
}
|
}
|
||||||
@@ -368,3 +372,26 @@ func isGoroot(goroot string) bool {
|
|||||||
_, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go"))
|
_, err := os.Stat(filepath.Join(goroot, "src", "runtime", "internal", "sys", "zversion.go"))
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getGorootVersion returns the major and minor version for a given GOROOT path.
|
||||||
|
// If the goroot cannot be determined, (0, 0) is returned.
|
||||||
|
func getGorootVersion(goroot string) (major, minor int) {
|
||||||
|
data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION"))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := string(data)
|
||||||
|
if s[:2] != "go" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
parts := strings.Split(s[2:], ".")
|
||||||
|
if len(parts) < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore the errors, strconv.Atoi will return 0 on most errors and we
|
||||||
|
// don't really handle errors here anyway.
|
||||||
|
major, _ = strconv.Atoi(parts[0])
|
||||||
|
minor, _ = strconv.Atoi(parts[1])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"inherits": ["cortex-m"],
|
||||||
|
"llvm-target": "armv6m-none-eabi",
|
||||||
|
"build-tags": ["atsamd21e18", "atsamd21", "sam"],
|
||||||
|
"cflags": [
|
||||||
|
"--target=armv6m-none-eabi",
|
||||||
|
"-Qunused-arguments"
|
||||||
|
],
|
||||||
|
"ldflags": [
|
||||||
|
"-T", "targets/atsamd21.ld"
|
||||||
|
],
|
||||||
|
"extra-files": [
|
||||||
|
"src/device/sam/atsamd21e18a.s"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -19,5 +19,8 @@
|
|||||||
"ldflags": [
|
"ldflags": [
|
||||||
"--gc-sections"
|
"--gc-sections"
|
||||||
],
|
],
|
||||||
|
"extra-files": [
|
||||||
|
"src/device/arm/cortexm.s"
|
||||||
|
],
|
||||||
"gdb": "arm-none-eabi-gdb"
|
"gdb": "arm-none-eabi-gdb"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"inherits": ["atsamd21g18a"],
|
||||||
|
"build-tags": ["sam", "atsamd21g18a", "feather_m0"],
|
||||||
|
"flash": "bossac -d -i -e -w -v -R --offset=0x2000 {hex}"
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"inherits": ["atsamd21e18a"],
|
||||||
|
"build-tags": ["sam", "atsamd21e18a", "trinket_m0"],
|
||||||
|
"flash": "bossac -d -i -e -w -v -R --offset=0x2000 {hex}"
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
],
|
],
|
||||||
"ldflags": [
|
"ldflags": [
|
||||||
"--allow-undefined",
|
"--allow-undefined",
|
||||||
|
"--no-threads",
|
||||||
"--export-all"
|
"--export-all"
|
||||||
],
|
],
|
||||||
"emulator": ["node", "targets/wasm_exec.js"]
|
"emulator": ["node", "targets/wasm_exec.js"]
|
||||||
|
|||||||
+21
-23
@@ -240,9 +240,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
// func valueIndex(v ref, i int) ref
|
// func valueIndex(v ref, i int) ref
|
||||||
//"syscall/js.valueIndex": (sp) => {
|
"syscall/js.valueIndex": (ret_addr, v_addr, i) => {
|
||||||
// storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
|
storeValue(ret_addr, Reflect.get(loadValue(v_addr), i));
|
||||||
//},
|
},
|
||||||
|
|
||||||
// valueSetIndex(v ref, i int, x ref)
|
// valueSetIndex(v ref, i int, x ref)
|
||||||
//"syscall/js.valueSetIndex": (sp) => {
|
//"syscall/js.valueSetIndex": (sp) => {
|
||||||
@@ -291,9 +291,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
// func valueLength(v ref) int
|
// func valueLength(v ref) int
|
||||||
//"syscall/js.valueLength": (sp) => {
|
"syscall/js.valueLength": (v_addr) => {
|
||||||
// setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
|
return loadValue(v_addr).length;
|
||||||
//},
|
},
|
||||||
|
|
||||||
// valuePrepareString(v ref) (ref, int)
|
// valuePrepareString(v ref) (ref, int)
|
||||||
"syscall/js.valuePrepareString": (ret_addr, v_addr) => {
|
"syscall/js.valuePrepareString": (ret_addr, v_addr) => {
|
||||||
@@ -352,25 +352,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static _makeCallbackHelper(id, pendingCallbacks, go) {
|
_resume() {
|
||||||
return function () {
|
if (this.exited) {
|
||||||
pendingCallbacks.push({ id: id, args: arguments });
|
throw new Error("Go program has already exited");
|
||||||
go._resolveCallbackPromise();
|
}
|
||||||
};
|
this._inst.exports.resume();
|
||||||
|
if (this.exited) {
|
||||||
|
this._resolveExitPromise();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) {
|
_makeFuncWrapper(id) {
|
||||||
return function (event) {
|
const go = this;
|
||||||
if (preventDefault) {
|
return function () {
|
||||||
event.preventDefault();
|
const event = { id: id, this: this, args: arguments };
|
||||||
}
|
go._pendingEvent = event;
|
||||||
if (stopPropagation) {
|
go._resume();
|
||||||
event.stopPropagation();
|
return event.result;
|
||||||
}
|
|
||||||
if (stopImmediatePropagation) {
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
}
|
|
||||||
fn(event);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user