Compare commits

...

59 Commits

Author SHA1 Message Date
Ayke van Laethem da85710894 main: version 0.6.0 2019-05-29 16:38:04 +02:00
j7b 0ae467d3e2 llvm cpu features 2019-05-28 15:41:34 +02:00
Justin Clift 4442b1304e Trivial typo fix 2019-05-28 09:55:35 +02:00
Ayke van Laethem 83ab61e261 ci: install Go 1.12.5 on macOS
This should fix compatibility with the Go 1.12 stdlib:
https://github.com/tinygo-org/tinygo/issues/368
2019-05-27 19:47:12 +02:00
Ron Evans 2504754325 machine/samd21: use HasBits() method to simplify bit comparisons
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-27 18:43:11 +02:00
Ron Evans be491abc46 machine/stm32: use HasBits() method to simplify bit comparisons
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-27 18:43:11 +02:00
Ron Evans 31189deb3b machine/avr: use HasBits() method to simplify bit comparisons
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-27 18:43:11 +02:00
Ron Evans 90cd3f8ea5 tools: generate volatile HasBits() method in device wrappers to simplify bit comparison code
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-27 18:43:11 +02:00
Ayke van Laethem 2f95a5d452 ci: use go modules instead of dep
This also updates the LLVM build to include this commit:
https://github.com/llvm-mirror/llvm/commit/d519e424c503d2d2a723284664a509a6cd5401a8
2019-05-27 18:42:36 +02:00
Ayke van Laethem eb1d834dd4 wasm: add support for js.FuncOf 2019-05-27 13:35:59 +02:00
Ayke van Laethem 3313decb68 compiler,runtime: make panic functions camelCase
Rename panic functions to be runtime.nilPanic, runtime.lookupPanic, and
runtime.slicePanic.
2019-05-27 13:35:59 +02:00
Ron Evans 191a076956 docs: update list of supported MCU boards
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-27 09:43:24 +02:00
Ayke van Laethem 0f2dcba7b3 syscall: implement Exit call on unix 2019-05-27 07:50:13 +02:00
Ayke van Laethem 94b8214529 machine: refactor pins to be of Pin type 2019-05-26 20:48:50 +02:00
Ayke van Laethem 421ef04efb wasm: fix Makefile to avoid debuginfo 2019-05-25 18:40:56 +02:00
Ayke van Laethem f7687c43aa wasm: fix wasm-ld hang
See the following bugs for more information:
https://bugs.llvm.org/show_bug.cgi?id=41508
https://bugs.llvm.org/show_bug.cgi?id=37064
2019-05-25 18:25:46 +02:00
Ayke van Laethem 5a7bab8808 main: add the absolute path to clang-8 on macOS
This avoids the need to correctly set $PATH if LLVM 8 has been installed
using Homebrew.
2019-05-24 19:53:43 +02:00
Ron Evans 3a73e64557 tools/gen-device: complete refactor to new generator based on volatile package
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-24 16:28:47 +02:00
Ron Evans 9f8340a970 machine/nrf: refactor to use volatile package/API
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-24 15:44:33 +02:00
k-brk 0f6873cf02 machine/stm32f103xx: fix i2c 2 byte read 2019-05-24 15:26:06 +02:00
Ayke van Laethem f2cd4d12e8 compiler,runtime: fix multiple definitions of a single function
strings.IndexByte was implemented in the runtime up to Go 1.11. It is
implemented using a direct call to internal/bytealg.IndexByte since Go
1.12.

Make sure we remain compatible with both.
2019-05-24 14:51:40 +02:00
Ayke van Laethem 7e6a54ac62 main: add build tags for the Go stdlib version 2019-05-24 14:51:40 +02:00
Ayke van Laethem 7156afca9e interp: support some more expressions in const icmp 2019-05-24 14:00:23 +02:00
Ayke van Laethem 87ac804642 ci: make sure that all examples are included in the smoketests 2019-05-24 13:51:20 +02:00
Ayke van Laethem edcb11f9f6 ci: move tests from CircleCI config to Makefile
This makes it easier to run smoke tests locally.
2019-05-24 13:51:20 +02:00
Ayke van Laethem 3568254593 machine/atsamd21: fix analog pin mode 2019-05-23 20:37:10 +02:00
Ayke van Laethem 3bf4c06c99 interp: work around limitation of constfolding in IR builder
The IR builder does not appear to fold comparisons of constant inttoptr
instructions to const null pointer. So do it manually during
interpretatin, as a somewhat ugly hack.

This fixes https://github.com/tinygo-org/tinygo/issues/363
2019-05-21 19:33:19 +02:00
Ayke van Laethem f1d9e7b75e interp: make errors during branches more reliable
Previously, there was a suble error in that .IsConstant() is not always
allowed to be called, resulting in a i1 that was not 0 or 1. Fix this by
checking for the constants directly and adding some more diagnostics to
catch more cases.
2019-05-21 19:33:19 +02:00
Ron Evans e4d53daa02 machine/stm32: refactor to use new volatile package for all register access
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-21 15:08:43 +02:00
Ayke van Laethem 98a3047b58 all: add go.mod and go.sum files for vgo support
Add required files for vgo support. The new vgo system defaults to on
since Go 1.13 so we should be prepared for that once the release hits.
It is also useful for testing Go 1.13 before the release.
2019-05-20 11:40:29 +02:00
Ayke van Laethem ba85c82fbb arm: print an error when a HardFault occurs
This is very useful for debugging. It differentiates between a stack
overflow and other errors (because it's easy to see when a stack
overflow occurs) and prints the old stack pointer and program counter if
available.
2019-05-20 11:39:17 +02:00
Ayke van Laethem 7b6ef65fe7 compiler: create temporary allocas with appropriate lifetimes
Make sure all allocas are created in the entry block and are given the
right lifetimes. This is good for code quality:

  * Moving allocas to the entry block makes sure they are always
    allocated statically (avoiding the need for a frame pointer) and do
    not grow the stack on each new alloca instruction. This is
    especially useful in loops where it could otherwise lead to a stack
    overflow even though there is no recursion.
  * Adding lifetime markers allows LLVM to reuse stack areas for
    different allocas as long as their lifetimes do not overlap.

All in all, this reduces code size in all tested cases for the BBC
micro:bit, and reduces code size for most cases for WebAssembly.
2019-05-20 09:52:42 +02:00
Ron Evans de032cddd2 machine/sam: Refactor all machine/runtime code to use new volatile package/API
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-19 19:53:11 +02:00
Ron Evans 51c6b972bf machine/feather-m0: correct board build tag and add placeholders for I2S interface
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-19 19:52:14 +02:00
Anthony Elder 4cd151faf5 Add Feather M0 Board (#356)
* Add Feather M0 Board
2019-05-19 17:10:17 +02:00
Ayke van Laethem 5342d392aa interp: improve scan for loads
During a scan, consider loads from dirty globals to be dirty and check
whether they have any local side effects.

This fixes a problem with the new volatile operations that are now in
methods on registers instead of being emitted inline as volatile
instructions.
2019-05-18 18:30:22 +02:00
Ayke van Laethem f94af9f61e compiler: avoid some obviously false nil checks
Pointers to globals are never nil.
2019-05-18 18:30:22 +02:00
Ayke van Laethem 7ada00790c cgo: print better error messages for unknown types
Types used in a program may not be implemented. Print a nice error
message explaining the situation, instead of just prepending C. to the
type spelling (and hoping the user knows what that undefined reference
means).
2019-05-17 19:37:20 +02:00
Ayke van Laethem dfa713040a cgo: add support for enum types
Enum types are implemented as named types (with possible accompanying
typedefs as type aliases). The constants inside the enums are treated as
Go constants like in the Go toolchain.
2019-05-17 19:37:20 +02:00
Ron Evans 82dc14b741 docker: do not remove make from tinygo-dev docker image, to make it easier to run drivers CI build, which uses the tinygo-dev image
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-15 11:51:26 +02:00
Ron Evans d3f2237d44 docs: update README with new boards Adafruit Trinket M0, and STM32F407 Discovery
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-14 19:38:39 +02:00
Martin Treml fc2ed2bdd0 [Board] Adafruit Trinket (#333)
* Add support for Adafruit Trinket-M0 board
2019-05-14 19:30:39 +02:00
Ayke van Laethem e0cf74e638 avr: use register wrappers that use runtime/volatile.*Uint8 calls
This avoids the //go:volatile pragma on types in Go source code, at
least for AVR targets.
2019-05-14 12:24:01 +02:00
Ayke van Laethem 6f6afb0515 compiler: add //go:inline pragma 2019-05-14 12:24:01 +02:00
Ayke van Laethem 397b90753c compiler: implement volatile operations as compiler builtins
The long term goal is to remove the //go:volatile hack.
2019-05-14 12:24:01 +02:00
Ayke van Laethem 3c2639ad55 compiler: insert nil checks when storing to a pointer
This does increase code size, but it is necessary to avoid undefined
behavior.
2019-05-14 12:24:01 +02:00
Ayke van Laethem 371c468e8e compiler: add debug info for function arguments
This commit adds debug info to function arguments, so that in many cases
you can see them when compiling with less optimizations enabled.
Unfortunately, due to the way Go SSA works, it is hard to preserve them
in many cases.
Local variables are not yet saved.

Also, change the language type to C, to make sure lldb shows function
arguments. The previous language was Modula 3, apparently due to a
off-by-one error somewhere.
2019-05-14 11:18:38 +02:00
Ayke van Laethem 0a40219680 compiler: implement comparing channel values 2019-05-14 11:18:38 +02:00
Ayke van Laethem c981f14e61 compiler: simplify some interface code
No error is produced, so no error needs to be returned. It was missed in
https://github.com/tinygo-org/tinygo/pull/294.

Also, it fixes this smelly code:

    if err != nil {
        return <something>, nil
    }

There could never be an error, so the code was already dead.
2019-05-14 09:59:00 +02:00
Ayke van Laethem 763b9d7d10 runtime: implement growing hashmaps
Add support for growing hashmaps beyond their initial size.
2019-05-14 09:59:00 +02:00
Ayke van Laethem 55fc7b904a compiler,runtime: use the size hint when creating a new map
It defaults to hint/8 number of buckets. This number may be tuned in the
future.
2019-05-14 09:59:00 +02:00
Ayke van Laethem 17c42810d0 compiler: improve hashmaps by avoiding dynamic allocas
By moving all allocas used in hashmap operations to the entry block, the
stack frame remains at a fixed size known at compile time. This avoids
stack overflows when doing map operations in loops and in general
improves code quality: the compiled size of testdata/map.go went from
3776 to 3632 in .text size.
2019-05-14 09:59:00 +02:00
Justin Clift 064d001550 Trivial typo fixes 2019-05-13 17:11:19 +02:00
seph a4cd3bb77c Test for functional argument passing (#336)
* Test for functional argument passing
2019-05-13 14:40:58 +02:00
Anthony Elder 8d3f19bc84 Fix I2C signalStop in readLastByte for Microbit (#344)
* Fix I2C signalStop in readLastByte for Microbit
2019-05-13 14:30:25 +02:00
Ron Evans d90f1947d9 machine/samd21: Initial implementation of I2S hardware interface using Circuit Playground Express
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-05-12 21:51:07 +02:00
Ayke van Laethem 11567c62d4 cgo: refactor; support multiple cgo files in a single package
This is a big commit that does a few things:

  * It moves CGo processing into a separate package. It never really
    belonged in the loader package, and certainly not now that the
    loader package may be refactored into a driver package.
  * It adds support for multiple CGo files (files that import package
    "C") in a single package. Previously, this led to multiple
    definition errors in the Go typecheck phase because certain C
    symbols were defined multiple times in all the files. Now it
    generates a new fake AST that defines these, to avoid multiple
    definition errors.
  * It improves debug info in a few edge cases that are probably not
    relevant outside of bugs in cgo itself.
2019-05-12 10:49:15 +02:00
Ayke van Laethem 4619207f99 cgo: don't crash on import "C" without comment
This doesn't make a lot of sense, but we shouldn't crash on it.
2019-05-12 10:49:15 +02:00
Ayke van Laethem 99587fe073 cgo: add support for #define constants
These are converted to Go constants where possible.
2019-05-12 10:49:15 +02:00
116 changed files with 5053 additions and 2737 deletions
+17 -54
View File
@@ -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
+49
View File
@@ -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
View File
@@ -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
+40 -1
View File
@@ -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
+7 -4
View File
@@ -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
View File
@@ -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
}
+196 -58
View File
@@ -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);
}
+6 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+40
View File
@@ -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
View File
@@ -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
View File
@@ -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
+4 -4
View File
@@ -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)
+16 -3
View File
@@ -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
View File
@@ -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,
+48
View File
@@ -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
View File
@@ -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())
+26
View File
@@ -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
View File
@@ -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
} }
+10
View File
@@ -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
)
+14
View File
@@ -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
View File
@@ -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)
-3
View File
@@ -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
View File
@@ -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)
} }
+26
View File
@@ -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
}
+29 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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 {
+13 -1
View File
@@ -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 {
+21
View File
@@ -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
+2 -2
View File
@@ -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()
+2 -2
View File
@@ -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)
+4 -4
View File
@@ -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()
+6 -6
View File
@@ -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() {
+16 -16
View File
@@ -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())
+3 -3
View File
@@ -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() {
+25
View File
@@ -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], "...")
}
}
+3 -5
View File
@@ -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()
+7 -2
View File
@@ -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:
+19
View File
@@ -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>
+27
View File
@@ -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)
}
+26
View File
@@ -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();
+9 -9
View File
@@ -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
) )
+15 -3
View File
@@ -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}
)
+1 -1
View File
@@ -3,5 +3,5 @@
package machine package machine
const ( const (
LED = 1 LED Pin = 1
) )
+82
View File
@@ -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.
)
+13 -1
View File
@@ -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}
)
+42 -42
View File
@@ -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
) )
+11 -11
View File
@@ -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
) )
+14 -14
View File
@@ -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
) )
+23 -23
View File
@@ -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
) )
+23 -23
View File
@@ -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
) )
+17 -17
View File
@@ -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
) )
+73
View File
@@ -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.
)
+54
View File
@@ -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
View File
@@ -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
} }
+59 -59
View File
@@ -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))
} }
File diff suppressed because it is too large Load Diff
+334
View File
@@ -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)
}
}
+569
View File
@@ -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))
}
}
+8 -8
View File
@@ -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
View File
@@ -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.
+16 -16
View File
@@ -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
View File
@@ -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
+12 -12
View File
@@ -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))
} }
+47 -47
View File
@@ -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
} }
+49 -49
View File
@@ -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
} }
+2 -2
View File
@@ -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
View File
@@ -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")
+57 -57
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+7 -3
View File
@@ -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")
}
+1 -1
View File
@@ -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:
+4 -4
View File
@@ -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
View File
@@ -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()
+42
View File
@@ -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()
}
+60
View File
@@ -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()
}
+50
View File
@@ -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
View File
@@ -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
} }
+35 -35
View File
@@ -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
+40 -39
View File
@@ -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++
} }
} }
+8
View File
@@ -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)
}
+8 -1
View File
@@ -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
+3
View File
@@ -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
} }
+4 -3
View File
@@ -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
+12
View File
@@ -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)
}
+34
View File
@@ -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)
+27
View File
@@ -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
}
+15
View File
@@ -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"
]
}
+3
View File
@@ -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"
} }
+5
View File
@@ -0,0 +1,5 @@
{
"inherits": ["atsamd21g18a"],
"build-tags": ["sam", "atsamd21g18a", "feather_m0"],
"flash": "bossac -d -i -e -w -v -R --offset=0x2000 {hex}"
}
+5
View File
@@ -0,0 +1,5 @@
{
"inherits": ["atsamd21e18a"],
"build-tags": ["sam", "atsamd21e18a", "trinket_m0"],
"flash": "bossac -d -i -e -w -v -R --offset=0x2000 {hex}"
}
+1
View File
@@ -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
View File
@@ -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