mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-27 23:28:42 +00:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ebde8b5875 | |||
| 9d50587d63 | |||
| 5e6f1725ac | |||
| 9cdc2fa768 | |||
| 084386262a | |||
| 674ddef219 | |||
| 0933577e60 | |||
| 69c1d802e1 | |||
| b424056721 | |||
| d735df6e16 | |||
| d37bbadb54 | |||
| ab7dc45288 | |||
| eee1b995f6 | |||
| a4fa41b49d | |||
| a5a90a57b9 | |||
| 71a380ce8c | |||
| 3b2a4b64c5 | |||
| 184827e4d8 | |||
| 14474e7099 | |||
| 3656ac2fc9 | |||
| 08f01ba3ff | |||
| 699312f477 | |||
| 9644edcd5a | |||
| 46325910c5 | |||
| 923c2e7ada | |||
| 18e446561d | |||
| 447537aebe | |||
| 74e32acf33 | |||
| 072f8c354e | |||
| 0587934a44 | |||
| c2481b10f4 | |||
| d41f01f003 | |||
| 5a70c88483 | |||
| 5510dec846 | |||
| dffb9fbfa7 | |||
| 525ded3d90 | |||
| ec2658ca79 | |||
| 2004555fe2 | |||
| 9aeb8d9e06 | |||
| 34ee3883d6 | |||
| 768c652468 | |||
| 2778377ac9 | |||
| cf32607306 | |||
| ad022ef23d | |||
| c97b7221bd | |||
| 8d32a7c3a3 | |||
| 49eb414530 | |||
| 39d21e21f1 | |||
| fa8a93b4e7 | |||
| 8f9419a35d | |||
| 7bdd4a1186 | |||
| 0105f815c6 | |||
| d441f0152f | |||
| 06647aab24 | |||
| 24259cbb5f | |||
| 2f932a9eee | |||
| 93a06d1157 | |||
| 374349cfa5 | |||
| 024a0827ea | |||
| fadddc54a3 |
+2
-26
@@ -18,7 +18,6 @@ commands:
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||
sudo apt-get update
|
||||
sudo apt-get install \
|
||||
python3 \
|
||||
llvm<<parameters.llvm>>-dev \
|
||||
clang<<parameters.llvm>> \
|
||||
libclang<<parameters.llvm>>-dev \
|
||||
@@ -67,7 +66,7 @@ commands:
|
||||
- run: go install .
|
||||
- run: go test -v ./cgo ./compileopts ./interp ./transform .
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest RISCV=0
|
||||
- run: make smoketest
|
||||
- save_cache:
|
||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
@@ -83,7 +82,6 @@ commands:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
sudo apt-get install \
|
||||
python3 \
|
||||
gcc-arm-linux-gnueabihf \
|
||||
binutils-arm-none-eabi \
|
||||
libc6-dev-armel-cross \
|
||||
@@ -122,12 +120,6 @@ commands:
|
||||
key: llvm-build-9-linux-v0-assert
|
||||
paths:
|
||||
llvm-build
|
||||
- run:
|
||||
name: "Create LLVM symlinks"
|
||||
command: |
|
||||
ln -s $PWD/llvm-build/bin/clang-9 /go/bin/clang-9
|
||||
ln -s $PWD/llvm-build/bin/ld.lld /go/bin/ld.lld-9
|
||||
ln -s $PWD/llvm-build/bin/wasm-ld /go/bin/wasm-ld-9
|
||||
- run: make ASSERT=1
|
||||
- run:
|
||||
name: "Test TinyGo"
|
||||
@@ -139,7 +131,7 @@ commands:
|
||||
- ~/.cache/tinygo
|
||||
- /go/pkg/mod
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest TINYGO=build/tinygo RISCV=0
|
||||
- run: make smoketest TINYGO=build/tinygo
|
||||
build-linux:
|
||||
steps:
|
||||
- checkout
|
||||
@@ -148,7 +140,6 @@ commands:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
sudo apt-get install \
|
||||
python3 \
|
||||
gcc-arm-linux-gnueabihf \
|
||||
binutils-arm-none-eabi \
|
||||
libc6-dev-armel-cross \
|
||||
@@ -187,12 +178,6 @@ commands:
|
||||
key: llvm-build-9-linux-v0
|
||||
paths:
|
||||
llvm-build
|
||||
- run:
|
||||
name: "Create LLVM symlinks"
|
||||
command: |
|
||||
ln -s $PWD/llvm-build/bin/clang-9 /go/bin/clang-9
|
||||
ln -s $PWD/llvm-build/bin/ld.lld /go/bin/ld.lld-9
|
||||
ln -s $PWD/llvm-build/bin/wasm-ld /go/bin/wasm-ld-9
|
||||
- run:
|
||||
name: "Test TinyGo"
|
||||
command: make test
|
||||
@@ -216,11 +201,6 @@ commands:
|
||||
tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz
|
||||
ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo
|
||||
tinygo version
|
||||
- run:
|
||||
name: "Download SiFive GNU toolchain"
|
||||
command: |
|
||||
curl -O https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-linux-ubuntu14.tar.gz
|
||||
sudo tar -C /usr/local --strip-components=1 -xf riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-linux-ubuntu14.tar.gz
|
||||
- run: make smoketest
|
||||
build-macos:
|
||||
steps:
|
||||
@@ -264,10 +244,6 @@ commands:
|
||||
key: llvm-build-9-macos-v0
|
||||
paths:
|
||||
llvm-build
|
||||
- run:
|
||||
name: "Create LLVM symlinks"
|
||||
command: |
|
||||
ln -s $PWD/llvm-build/bin/clang-9 /usr/local/bin/clang-9
|
||||
- run:
|
||||
name: "Test TinyGo"
|
||||
command: make test
|
||||
|
||||
@@ -1,3 +1,29 @@
|
||||
0.11.0
|
||||
---
|
||||
* **command line**
|
||||
- add support for QEMU in `gdb` subcommand
|
||||
- use builtin Clang when building statically, dropping the clang-9 dependency
|
||||
- search for default serial port on both macOS and Linux
|
||||
- windows: support `tinygo flash` directly by using win32 wmi
|
||||
* **compiler**
|
||||
- add location information to the IR checker
|
||||
- make reflection sidetables constant globals
|
||||
- improve error locations in goroutine lowering
|
||||
- interp: improve support for maps with string keys
|
||||
- interp: add runtime fallback for mapassign operations
|
||||
* **standard library**
|
||||
- `machine`: add support for `SPI.Tx()` on play.tinygo.org
|
||||
- `machine`: rename `CPU_FREQUENCY` to `CPUFrequency()`
|
||||
* **targets**
|
||||
- `adafruit-pybadge`: add Adafruit Pybadge
|
||||
- `arduino-nano33`: allow simulation on play.tinygo.org
|
||||
- `arduino-nano33`: fix default SPI pin numbers to be D13/D11/D12
|
||||
- `circuitplay-express`: allow simulation on play.tinygo.org
|
||||
- `hifive1-qemu`: add target for testing RISC-V bare metal in QEMU
|
||||
- `riscv`: fix heap corruption due to changes in LLVM 9
|
||||
- `riscv`: add support for compiler-rt
|
||||
- `qemu`: rename to `cortex-m-qemu`
|
||||
|
||||
0.10.0
|
||||
---
|
||||
* **command line**
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to make participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all project spaces, and it also applies when
|
||||
an individual is representing the project or its community in public spaces.
|
||||
Examples of representing a project or community include using an official
|
||||
project e-mail address, posting via an official social media account, or acting
|
||||
as an appointed representative at an online or offline event. Representation of
|
||||
a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at [conduct@tinygo.org](mailto:conduct@tinygo.org). All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
+5
-12
@@ -41,9 +41,8 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils python3 make binutils-avr gcc-avr avr-libc && \
|
||||
apt-get install -y apt-utils make binutils-avr gcc-avr avr-libc && \
|
||||
make gen-device-avr && \
|
||||
apt-get remove -y python3 && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean
|
||||
|
||||
@@ -59,11 +58,8 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils python3 make clang-9 && \
|
||||
make gen-device-nrf && make gen-device-stm32 && \
|
||||
apt-get remove -y python3 && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean
|
||||
apt-get install -y apt-utils make clang-9 && \
|
||||
make gen-device-nrf && make gen-device-stm32
|
||||
|
||||
# tinygo-all stage installs the needed dependencies to compile TinyGo programs for all platforms.
|
||||
FROM tinygo-wasm AS tinygo-all
|
||||
@@ -74,10 +70,7 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils python3 make clang-9 binutils-avr gcc-avr avr-libc && \
|
||||
make gen-device && \
|
||||
apt-get remove -y python3 && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean
|
||||
apt-get install -y apt-utils make clang-9 binutils-avr gcc-avr avr-libc && \
|
||||
make gen-device
|
||||
|
||||
CMD ["tinygo"]
|
||||
|
||||
@@ -3,6 +3,9 @@ Copyright (c) 2018-2019 TinyGo Authors. All rights reserved.
|
||||
TinyGo includes portions of the Go standard library.
|
||||
Copyright (c) 2009-2019 The Go Authors. All rights reserved.
|
||||
|
||||
TinyGo includes portions of LLVM, which is under the Apache License v2.0 with
|
||||
LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
@@ -14,9 +14,6 @@ export GOROOT = $(shell $(GO) env GOROOT)
|
||||
# md5sum binary
|
||||
MD5SUM = md5sum
|
||||
|
||||
# Python binary
|
||||
PYTHON ?= python
|
||||
|
||||
# tinygo binary for tests
|
||||
TINYGO ?= tinygo
|
||||
|
||||
@@ -34,7 +31,7 @@ endif
|
||||
|
||||
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-avr
|
||||
|
||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines debuginfodwarf executionengine instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
|
||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf executionengine instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
EXE = .exe
|
||||
@@ -65,6 +62,11 @@ $(LIBCLANG_PATH): $(LIBCLANG_FILES)
|
||||
else ifeq ($(shell uname -s),Darwin)
|
||||
MD5SUM = md5
|
||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||
else ifeq ($(shell uname -s),FreeBSD)
|
||||
MD5SUM = md5
|
||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||
START_GROUP = -Wl,--start-group
|
||||
END_GROUP = -Wl,--end-group
|
||||
else
|
||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||
START_GROUP = -Wl,--start-group
|
||||
@@ -78,7 +80,7 @@ LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -
|
||||
|
||||
# For static linking.
|
||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CXXFLAGS=-std=c++11
|
||||
CGO_LDFLAGS+=$(LIBCLANG_PATH) -std=c++11 -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||
endif
|
||||
@@ -97,24 +99,28 @@ fmt-check:
|
||||
gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32
|
||||
|
||||
gen-device-avr:
|
||||
$(PYTHON) ./tools/gen-device-avr.py lib/avr/packs/atmega src/device/avr/
|
||||
$(PYTHON) ./tools/gen-device-avr.py lib/avr/packs/tiny src/device/avr/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/avr
|
||||
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
|
||||
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
|
||||
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
|
||||
@GO111MODULE=off $(GO) fmt ./src/device/avr
|
||||
|
||||
gen-device-nrf:
|
||||
$(PYTHON) ./tools/gen-device-svd.py lib/nrfx/mdk/ src/device/nrf/ --source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk
|
||||
build/gen-device-svd: ./tools/gen-device-svd/*.go
|
||||
$(GO) build -o $@ ./tools/gen-device-svd/
|
||||
|
||||
gen-device-nrf: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk lib/nrfx/mdk/ src/device/nrf/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/nrf
|
||||
|
||||
gen-device-sam:
|
||||
$(PYTHON) ./tools/gen-device-svd.py lib/cmsis-svd/data/Atmel/ src/device/sam/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel
|
||||
gen-device-sam: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel lib/cmsis-svd/data/Atmel/ src/device/sam/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/sam
|
||||
|
||||
gen-device-sifive:
|
||||
$(PYTHON) ./tools/gen-device-svd.py lib/cmsis-svd/data/SiFive-Community/ src/device/sifive/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/SiFive-Community
|
||||
gen-device-sifive: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/SiFive-Community lib/cmsis-svd/data/SiFive-Community/ src/device/sifive/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/sifive
|
||||
|
||||
gen-device-stm32:
|
||||
$(PYTHON) ./tools/gen-device-svd.py lib/cmsis-svd/data/STMicro/ src/device/stm32/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro
|
||||
gen-device-stm32: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro lib/cmsis-svd/data/STMicro/ src/device/stm32/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/stm32
|
||||
|
||||
|
||||
@@ -187,6 +193,8 @@ smoketest:
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -o test.wasm -tags=pca10056 examples/blinky2
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -o test.wasm -tags=circuitplay_express examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
# test all targets/boards
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -226,6 +234,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pybadge examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=metro-m4-airlift examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f103rb examples/blinky1
|
||||
@@ -242,10 +252,8 @@ ifneq ($(AVR), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
ifneq ($(RISCV), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/export
|
||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/main
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ The following 22 microcontroller boards are currently supported:
|
||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
|
||||
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
|
||||
* [Adafruit PyBadge](https://www.adafruit.com/product/4200)
|
||||
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
||||
* [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot)
|
||||
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
||||
@@ -62,6 +63,7 @@ The following 22 microcontroller boards are currently supported:
|
||||
* [Nordic Semiconductor PCA10040](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK)
|
||||
* [Nordic Semiconductor PCA10056](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
|
||||
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
||||
* [PineTime DevKit](https://www.pine64.org/pinetime/)
|
||||
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
|
||||
* [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
||||
@@ -125,3 +127,5 @@ The original reasoning was: if [Python](https://micropython.org/) can run on mic
|
||||
## License
|
||||
|
||||
This project is licensed under the BSD 3-clause license, just like the [Go project](https://golang.org/LICENSE) itself.
|
||||
|
||||
Some code has been copied from the LLVM project and is therefore licensed under [a variant of the Apache 2.0 license](http://releases.llvm.org/9.0.0/LICENSE.TXT). This has been clearly indicated in the header of these files.
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ trigger:
|
||||
|
||||
jobs:
|
||||
- job: Build
|
||||
timeoutInMinutes: 180
|
||||
timeoutInMinutes: 240 # 4h
|
||||
pool:
|
||||
vmImage: 'VS2017-Win2016'
|
||||
steps:
|
||||
@@ -71,4 +71,4 @@ jobs:
|
||||
script: |
|
||||
export PATH="/c/Go1.13/bin:$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
||||
unset GOROOT
|
||||
make smoketest TINYGO=build/tinygo AVR=0 RISCV=0
|
||||
make smoketest TINYGO=build/tinygo AVR=0
|
||||
|
||||
+12
-22
@@ -33,10 +33,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||
// Compile Go code to IR.
|
||||
errs := c.Compile(pkgName)
|
||||
if len(errs) != 0 {
|
||||
if len(errs) == 1 {
|
||||
return errs[0]
|
||||
}
|
||||
return &MultiError{errs}
|
||||
return newMultiError(errs)
|
||||
}
|
||||
if config.Options.PrintIR {
|
||||
fmt.Println("; Generated LLVM IR:")
|
||||
@@ -72,22 +69,23 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||
|
||||
// Optimization levels here are roughly the same as Clang, but probably not
|
||||
// exactly.
|
||||
errs = nil
|
||||
switch config.Options.Opt {
|
||||
case "none:", "0":
|
||||
err = c.Optimize(0, 0, 0) // -O0
|
||||
errs = c.Optimize(0, 0, 0) // -O0
|
||||
case "1":
|
||||
err = c.Optimize(1, 0, 0) // -O1
|
||||
errs = c.Optimize(1, 0, 0) // -O1
|
||||
case "2":
|
||||
err = c.Optimize(2, 0, 225) // -O2
|
||||
errs = c.Optimize(2, 0, 225) // -O2
|
||||
case "s":
|
||||
err = c.Optimize(2, 1, 225) // -Os
|
||||
errs = c.Optimize(2, 1, 225) // -Os
|
||||
case "z":
|
||||
err = c.Optimize(2, 2, 5) // -Oz, default
|
||||
errs = c.Optimize(2, 2, 5) // -Oz, default
|
||||
default:
|
||||
err = errors.New("unknown optimization level: -opt=" + config.Options.Opt)
|
||||
errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
if len(errs) > 0 {
|
||||
return newMultiError(errs)
|
||||
}
|
||||
if err := c.Verify(); err != nil {
|
||||
return errors.New("verification failure after LLVM optimization passes")
|
||||
@@ -154,11 +152,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||
for i, path := range config.ExtraFiles() {
|
||||
abspath := filepath.Join(root, path)
|
||||
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
|
||||
cmdNames := []string{config.Target.Compiler}
|
||||
if names, ok := commands[config.Target.Compiler]; ok {
|
||||
cmdNames = names
|
||||
}
|
||||
err := execCommand(cmdNames, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
|
||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", path, err}
|
||||
}
|
||||
@@ -170,11 +164,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||
for _, file := range pkg.CFiles {
|
||||
path := filepath.Join(pkg.Package.Dir, file)
|
||||
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+file+".o")
|
||||
cmdNames := []string{config.Target.Compiler}
|
||||
if names, ok := commands[config.Target.Compiler]; ok {
|
||||
cmdNames = names
|
||||
}
|
||||
err := execCommand(cmdNames, append(config.CFlags(), "-c", "-o", outpath, path)...)
|
||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, path)...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", path, err}
|
||||
}
|
||||
|
||||
+5
-1
@@ -236,7 +236,11 @@ func CompileBuiltins(target string, callback func(path string) error) error {
|
||||
// Note: -fdebug-prefix-map is necessary to make the output archive
|
||||
// reproducible. Otherwise the temporary directory is stored in the
|
||||
// archive itself, which varies each run.
|
||||
err := execCommand(commands["clang"], "-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir, "-o", objpath, srcpath)
|
||||
args := []string{"-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target=" + target, "-fdebug-prefix-map=" + dir + "=" + remapDir}
|
||||
if strings.HasPrefix(target, "riscv32-") {
|
||||
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
||||
}
|
||||
err := runCCompiler("clang", append(args, "-o", objpath, srcpath)...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", srcpath, err}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,504 @@
|
||||
// +build byollvm
|
||||
|
||||
//===-- cc1as.cpp - Clang Assembler --------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This is the entry point to the clang -cc1as functionality, which implements
|
||||
// the direct interface to the LLVM MC based assembler.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Driver/DriverDiagnostic.h"
|
||||
#include "clang/Driver/Options.h"
|
||||
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
#include "clang/Frontend/Utils.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/MC/MCAsmBackend.h"
|
||||
#include "llvm/MC/MCAsmInfo.h"
|
||||
#include "llvm/MC/MCCodeEmitter.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCInstrInfo.h"
|
||||
#include "llvm/MC/MCObjectFileInfo.h"
|
||||
#include "llvm/MC/MCObjectWriter.h"
|
||||
#include "llvm/MC/MCParser/MCAsmParser.h"
|
||||
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
|
||||
#include "llvm/MC/MCRegisterInfo.h"
|
||||
#include "llvm/MC/MCSectionMachO.h"
|
||||
#include "llvm/MC/MCStreamer.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
#include "llvm/MC/MCTargetOptions.h"
|
||||
#include "llvm/Option/Arg.h"
|
||||
#include "llvm/Option/ArgList.h"
|
||||
#include "llvm/Option/OptTable.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/FormattedStream.h"
|
||||
#include "llvm/Support/Host.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/Support/Timer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
using namespace clang;
|
||||
using namespace clang::driver;
|
||||
using namespace clang::driver::options;
|
||||
using namespace llvm;
|
||||
using namespace llvm::opt;
|
||||
|
||||
#include "cc1as.h"
|
||||
|
||||
bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
ArrayRef<const char *> Argv,
|
||||
DiagnosticsEngine &Diags) {
|
||||
bool Success = true;
|
||||
|
||||
// Parse the arguments.
|
||||
std::unique_ptr<OptTable> OptTbl(createDriverOptTable());
|
||||
|
||||
const unsigned IncludedFlagsBitmask = options::CC1AsOption;
|
||||
unsigned MissingArgIndex, MissingArgCount;
|
||||
InputArgList Args = OptTbl->ParseArgs(Argv, MissingArgIndex, MissingArgCount,
|
||||
IncludedFlagsBitmask);
|
||||
|
||||
// Check for missing argument error.
|
||||
if (MissingArgCount) {
|
||||
Diags.Report(diag::err_drv_missing_argument)
|
||||
<< Args.getArgString(MissingArgIndex) << MissingArgCount;
|
||||
Success = false;
|
||||
}
|
||||
|
||||
// Issue errors on unknown arguments.
|
||||
for (const Arg *A : Args.filtered(OPT_UNKNOWN)) {
|
||||
auto ArgString = A->getAsString(Args);
|
||||
std::string Nearest;
|
||||
if (OptTbl->findNearest(ArgString, Nearest, IncludedFlagsBitmask) > 1)
|
||||
Diags.Report(diag::err_drv_unknown_argument) << ArgString;
|
||||
else
|
||||
Diags.Report(diag::err_drv_unknown_argument_with_suggestion)
|
||||
<< ArgString << Nearest;
|
||||
Success = false;
|
||||
}
|
||||
|
||||
// Construct the invocation.
|
||||
|
||||
// Target Options
|
||||
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
|
||||
Opts.CPU = Args.getLastArgValue(OPT_target_cpu);
|
||||
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
||||
|
||||
// Use the default target triple if unspecified.
|
||||
if (Opts.Triple.empty())
|
||||
Opts.Triple = llvm::sys::getDefaultTargetTriple();
|
||||
|
||||
// Language Options
|
||||
Opts.IncludePaths = Args.getAllArgValues(OPT_I);
|
||||
Opts.NoInitialTextSection = Args.hasArg(OPT_n);
|
||||
Opts.SaveTemporaryLabels = Args.hasArg(OPT_msave_temp_labels);
|
||||
// Any DebugInfoKind implies GenDwarfForAssembly.
|
||||
Opts.GenDwarfForAssembly = Args.hasArg(OPT_debug_info_kind_EQ);
|
||||
|
||||
if (const Arg *A = Args.getLastArg(OPT_compress_debug_sections,
|
||||
OPT_compress_debug_sections_EQ)) {
|
||||
if (A->getOption().getID() == OPT_compress_debug_sections) {
|
||||
// TODO: be more clever about the compression type auto-detection
|
||||
Opts.CompressDebugSections = llvm::DebugCompressionType::GNU;
|
||||
} else {
|
||||
Opts.CompressDebugSections =
|
||||
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
|
||||
.Case("none", llvm::DebugCompressionType::None)
|
||||
.Case("zlib", llvm::DebugCompressionType::Z)
|
||||
.Case("zlib-gnu", llvm::DebugCompressionType::GNU)
|
||||
.Default(llvm::DebugCompressionType::None);
|
||||
}
|
||||
}
|
||||
|
||||
Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations);
|
||||
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
|
||||
Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags);
|
||||
Opts.DwarfDebugProducer = Args.getLastArgValue(OPT_dwarf_debug_producer);
|
||||
Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir);
|
||||
Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name);
|
||||
|
||||
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ))
|
||||
Opts.DebugPrefixMap.insert(StringRef(Arg).split('='));
|
||||
|
||||
// Frontend Options
|
||||
if (Args.hasArg(OPT_INPUT)) {
|
||||
bool First = true;
|
||||
for (const Arg *A : Args.filtered(OPT_INPUT)) {
|
||||
if (First) {
|
||||
Opts.InputFile = A->getValue();
|
||||
First = false;
|
||||
} else {
|
||||
Diags.Report(diag::err_drv_unknown_argument) << A->getAsString(Args);
|
||||
Success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm);
|
||||
Opts.OutputPath = Args.getLastArgValue(OPT_o);
|
||||
Opts.SplitDwarfOutput = Args.getLastArgValue(OPT_split_dwarf_output);
|
||||
if (Arg *A = Args.getLastArg(OPT_filetype)) {
|
||||
StringRef Name = A->getValue();
|
||||
unsigned OutputType = StringSwitch<unsigned>(Name)
|
||||
.Case("asm", FT_Asm)
|
||||
.Case("null", FT_Null)
|
||||
.Case("obj", FT_Obj)
|
||||
.Default(~0U);
|
||||
if (OutputType == ~0U) {
|
||||
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name;
|
||||
Success = false;
|
||||
} else
|
||||
Opts.OutputType = FileType(OutputType);
|
||||
}
|
||||
Opts.ShowHelp = Args.hasArg(OPT_help);
|
||||
Opts.ShowVersion = Args.hasArg(OPT_version);
|
||||
|
||||
// Transliterate Options
|
||||
Opts.OutputAsmVariant =
|
||||
getLastArgIntValue(Args, OPT_output_asm_variant, 0, Diags);
|
||||
Opts.ShowEncoding = Args.hasArg(OPT_show_encoding);
|
||||
Opts.ShowInst = Args.hasArg(OPT_show_inst);
|
||||
|
||||
// Assemble Options
|
||||
Opts.RelaxAll = Args.hasArg(OPT_mrelax_all);
|
||||
Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack);
|
||||
Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings);
|
||||
Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic");
|
||||
Opts.TargetABI = Args.getLastArgValue(OPT_target_abi);
|
||||
Opts.IncrementalLinkerCompatible =
|
||||
Args.hasArg(OPT_mincremental_linker_compatible);
|
||||
Opts.SymbolDefs = Args.getAllArgValues(OPT_defsym);
|
||||
|
||||
// EmbedBitcode Option. If -fembed-bitcode is enabled, set the flag.
|
||||
// EmbedBitcode behaves the same for all embed options for assembly files.
|
||||
if (auto *A = Args.getLastArg(OPT_fembed_bitcode_EQ)) {
|
||||
Opts.EmbedBitcode = llvm::StringSwitch<unsigned>(A->getValue())
|
||||
.Case("all", 1)
|
||||
.Case("bitcode", 1)
|
||||
.Case("marker", 1)
|
||||
.Default(0);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
static std::unique_ptr<raw_fd_ostream>
|
||||
getOutputStream(StringRef Path, DiagnosticsEngine &Diags, bool Binary) {
|
||||
// Make sure that the Out file gets unlinked from the disk if we get a
|
||||
// SIGINT.
|
||||
if (Path != "-")
|
||||
sys::RemoveFileOnSignal(Path);
|
||||
|
||||
std::error_code EC;
|
||||
auto Out = llvm::make_unique<raw_fd_ostream>(
|
||||
Path, EC, (Binary ? sys::fs::F_None : sys::fs::F_Text));
|
||||
if (EC) {
|
||||
Diags.Report(diag::err_fe_unable_to_open_output) << Path << EC.message();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return Out;
|
||||
}
|
||||
|
||||
bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
// Get the target specific parser.
|
||||
std::string Error;
|
||||
const Target *TheTarget = TargetRegistry::lookupTarget(Opts.Triple, Error);
|
||||
if (!TheTarget)
|
||||
return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
|
||||
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
|
||||
MemoryBuffer::getFileOrSTDIN(Opts.InputFile);
|
||||
|
||||
if (std::error_code EC = Buffer.getError()) {
|
||||
Error = EC.message();
|
||||
return Diags.Report(diag::err_fe_error_reading) << Opts.InputFile;
|
||||
}
|
||||
|
||||
SourceMgr SrcMgr;
|
||||
|
||||
// Tell SrcMgr about this buffer, which is what the parser will pick up.
|
||||
unsigned BufferIndex = SrcMgr.AddNewSourceBuffer(std::move(*Buffer), SMLoc());
|
||||
|
||||
// Record the location of the include directories so that the lexer can find
|
||||
// it later.
|
||||
SrcMgr.setIncludeDirs(Opts.IncludePaths);
|
||||
|
||||
std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(Opts.Triple));
|
||||
assert(MRI && "Unable to create target register info!");
|
||||
|
||||
std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, Opts.Triple));
|
||||
assert(MAI && "Unable to create target asm info!");
|
||||
|
||||
// Ensure MCAsmInfo initialization occurs before any use, otherwise sections
|
||||
// may be created with a combination of default and explicit settings.
|
||||
MAI->setCompressDebugSections(Opts.CompressDebugSections);
|
||||
|
||||
MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations);
|
||||
|
||||
bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj;
|
||||
if (Opts.OutputPath.empty())
|
||||
Opts.OutputPath = "-";
|
||||
std::unique_ptr<raw_fd_ostream> FDOS =
|
||||
getOutputStream(Opts.OutputPath, Diags, IsBinary);
|
||||
if (!FDOS)
|
||||
return true;
|
||||
std::unique_ptr<raw_fd_ostream> DwoOS;
|
||||
if (!Opts.SplitDwarfOutput.empty())
|
||||
DwoOS = getOutputStream(Opts.SplitDwarfOutput, Diags, IsBinary);
|
||||
|
||||
// FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
|
||||
// MCObjectFileInfo needs a MCContext reference in order to initialize itself.
|
||||
std::unique_ptr<MCObjectFileInfo> MOFI(new MCObjectFileInfo());
|
||||
|
||||
MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr);
|
||||
|
||||
bool PIC = false;
|
||||
if (Opts.RelocationModel == "static") {
|
||||
PIC = false;
|
||||
} else if (Opts.RelocationModel == "pic") {
|
||||
PIC = true;
|
||||
} else {
|
||||
assert(Opts.RelocationModel == "dynamic-no-pic" &&
|
||||
"Invalid PIC model!");
|
||||
PIC = false;
|
||||
}
|
||||
|
||||
MOFI->InitMCObjectFileInfo(Triple(Opts.Triple), PIC, Ctx);
|
||||
if (Opts.SaveTemporaryLabels)
|
||||
Ctx.setAllowTemporaryLabels(false);
|
||||
if (Opts.GenDwarfForAssembly)
|
||||
Ctx.setGenDwarfForAssembly(true);
|
||||
if (!Opts.DwarfDebugFlags.empty())
|
||||
Ctx.setDwarfDebugFlags(StringRef(Opts.DwarfDebugFlags));
|
||||
if (!Opts.DwarfDebugProducer.empty())
|
||||
Ctx.setDwarfDebugProducer(StringRef(Opts.DwarfDebugProducer));
|
||||
if (!Opts.DebugCompilationDir.empty())
|
||||
Ctx.setCompilationDir(Opts.DebugCompilationDir);
|
||||
else {
|
||||
// If no compilation dir is set, try to use the current directory.
|
||||
SmallString<128> CWD;
|
||||
if (!sys::fs::current_path(CWD))
|
||||
Ctx.setCompilationDir(CWD);
|
||||
}
|
||||
if (!Opts.DebugPrefixMap.empty())
|
||||
for (const auto &KV : Opts.DebugPrefixMap)
|
||||
Ctx.addDebugPrefixMapEntry(KV.first, KV.second);
|
||||
if (!Opts.MainFileName.empty())
|
||||
Ctx.setMainFileName(StringRef(Opts.MainFileName));
|
||||
Ctx.setDwarfVersion(Opts.DwarfVersion);
|
||||
if (Opts.GenDwarfForAssembly)
|
||||
Ctx.setGenDwarfRootFile(Opts.InputFile,
|
||||
SrcMgr.getMemoryBuffer(BufferIndex)->getBuffer());
|
||||
|
||||
// Build up the feature string from the target feature list.
|
||||
std::string FS;
|
||||
if (!Opts.Features.empty()) {
|
||||
FS = Opts.Features[0];
|
||||
for (unsigned i = 1, e = Opts.Features.size(); i != e; ++i)
|
||||
FS += "," + Opts.Features[i];
|
||||
}
|
||||
|
||||
std::unique_ptr<MCStreamer> Str;
|
||||
|
||||
std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
|
||||
std::unique_ptr<MCSubtargetInfo> STI(
|
||||
TheTarget->createMCSubtargetInfo(Opts.Triple, Opts.CPU, FS));
|
||||
|
||||
raw_pwrite_stream *Out = FDOS.get();
|
||||
std::unique_ptr<buffer_ostream> BOS;
|
||||
|
||||
MCTargetOptions MCOptions;
|
||||
MCOptions.ABIName = Opts.TargetABI;
|
||||
|
||||
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
|
||||
if (Opts.OutputType == AssemblerInvocation::FT_Asm) {
|
||||
MCInstPrinter *IP = TheTarget->createMCInstPrinter(
|
||||
llvm::Triple(Opts.Triple), Opts.OutputAsmVariant, *MAI, *MCII, *MRI);
|
||||
|
||||
std::unique_ptr<MCCodeEmitter> CE;
|
||||
if (Opts.ShowEncoding)
|
||||
CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
|
||||
std::unique_ptr<MCAsmBackend> MAB(
|
||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||
|
||||
auto FOut = llvm::make_unique<formatted_raw_ostream>(*Out);
|
||||
Str.reset(TheTarget->createAsmStreamer(
|
||||
Ctx, std::move(FOut), /*asmverbose*/ true,
|
||||
/*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB),
|
||||
Opts.ShowInst));
|
||||
} else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
|
||||
Str.reset(createNullStreamer(Ctx));
|
||||
} else {
|
||||
assert(Opts.OutputType == AssemblerInvocation::FT_Obj &&
|
||||
"Invalid file type!");
|
||||
if (!FDOS->supportsSeeking()) {
|
||||
BOS = make_unique<buffer_ostream>(*FDOS);
|
||||
Out = BOS.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<MCCodeEmitter> CE(
|
||||
TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
|
||||
std::unique_ptr<MCAsmBackend> MAB(
|
||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||
std::unique_ptr<MCObjectWriter> OW =
|
||||
DwoOS ? MAB->createDwoObjectWriter(*Out, *DwoOS)
|
||||
: MAB->createObjectWriter(*Out);
|
||||
|
||||
Triple T(Opts.Triple);
|
||||
Str.reset(TheTarget->createMCObjectStreamer(
|
||||
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
|
||||
Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
|
||||
/*DWARFMustBeAtTheEnd*/ true));
|
||||
Str.get()->InitSections(Opts.NoExecStack);
|
||||
}
|
||||
|
||||
// When -fembed-bitcode is passed to clang_as, a 1-byte marker
|
||||
// is emitted in __LLVM,__asm section if the object file is MachO format.
|
||||
if (Opts.EmbedBitcode && Ctx.getObjectFileInfo()->getObjectFileType() ==
|
||||
MCObjectFileInfo::IsMachO) {
|
||||
MCSection *AsmLabel = Ctx.getMachOSection(
|
||||
"__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly());
|
||||
Str.get()->SwitchSection(AsmLabel);
|
||||
Str.get()->EmitZeros(1);
|
||||
}
|
||||
|
||||
// Assembly to object compilation should leverage assembly info.
|
||||
Str->setUseAssemblerInfoForParsing(true);
|
||||
|
||||
bool Failed = false;
|
||||
|
||||
std::unique_ptr<MCAsmParser> Parser(
|
||||
createMCAsmParser(SrcMgr, Ctx, *Str.get(), *MAI));
|
||||
|
||||
// FIXME: init MCTargetOptions from sanitizer flags here.
|
||||
std::unique_ptr<MCTargetAsmParser> TAP(
|
||||
TheTarget->createMCAsmParser(*STI, *Parser, *MCII, MCOptions));
|
||||
if (!TAP)
|
||||
Failed = Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
|
||||
|
||||
// Set values for symbols, if any.
|
||||
for (auto &S : Opts.SymbolDefs) {
|
||||
auto Pair = StringRef(S).split('=');
|
||||
auto Sym = Pair.first;
|
||||
auto Val = Pair.second;
|
||||
int64_t Value;
|
||||
// We have already error checked this in the driver.
|
||||
Val.getAsInteger(0, Value);
|
||||
Ctx.setSymbolValue(Parser->getStreamer(), Sym, Value);
|
||||
}
|
||||
|
||||
if (!Failed) {
|
||||
Parser->setTargetParser(*TAP.get());
|
||||
Failed = Parser->Run(Opts.NoInitialTextSection);
|
||||
}
|
||||
|
||||
// Close Streamer first.
|
||||
// It might have a reference to the output stream.
|
||||
Str.reset();
|
||||
// Close the output stream early.
|
||||
BOS.reset();
|
||||
FDOS.reset();
|
||||
|
||||
// Delete output file if there were errors.
|
||||
if (Failed) {
|
||||
if (Opts.OutputPath != "-")
|
||||
sys::fs::remove(Opts.OutputPath);
|
||||
if (!Opts.SplitDwarfOutput.empty() && Opts.SplitDwarfOutput != "-")
|
||||
sys::fs::remove(Opts.SplitDwarfOutput);
|
||||
}
|
||||
|
||||
return Failed;
|
||||
}
|
||||
|
||||
static void LLVMErrorHandler(void *UserData, const std::string &Message,
|
||||
bool GenCrashDiag) {
|
||||
DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
|
||||
|
||||
Diags.Report(diag::err_fe_error_backend) << Message;
|
||||
|
||||
// We cannot recover from llvm errors.
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
|
||||
// Initialize targets and assembly printers/parsers.
|
||||
InitializeAllTargetInfos();
|
||||
InitializeAllTargetMCs();
|
||||
InitializeAllAsmParsers();
|
||||
|
||||
// Construct our diagnostic client.
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
|
||||
TextDiagnosticPrinter *DiagClient
|
||||
= new TextDiagnosticPrinter(errs(), &*DiagOpts);
|
||||
DiagClient->setPrefix("clang -cc1as");
|
||||
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
||||
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);
|
||||
|
||||
// Set an error handler, so that any LLVM backend diagnostics go through our
|
||||
// error handler.
|
||||
ScopedFatalErrorHandler FatalErrorHandler
|
||||
(LLVMErrorHandler, static_cast<void*>(&Diags));
|
||||
|
||||
// Parse the arguments.
|
||||
AssemblerInvocation Asm;
|
||||
if (!AssemblerInvocation::CreateFromArgs(Asm, Argv, Diags))
|
||||
return 1;
|
||||
|
||||
if (Asm.ShowHelp) {
|
||||
std::unique_ptr<OptTable> Opts(driver::createDriverOptTable());
|
||||
Opts->PrintHelp(llvm::outs(), "clang -cc1as [options] file...",
|
||||
"Clang Integrated Assembler",
|
||||
/*Include=*/driver::options::CC1AsOption, /*Exclude=*/0,
|
||||
/*ShowAllAliases=*/false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Honor -version.
|
||||
//
|
||||
// FIXME: Use a better -version message?
|
||||
if (Asm.ShowVersion) {
|
||||
llvm::cl::PrintVersionMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Honor -mllvm.
|
||||
//
|
||||
// FIXME: Remove this, one day.
|
||||
if (!Asm.LLVMArgs.empty()) {
|
||||
unsigned NumArgs = Asm.LLVMArgs.size();
|
||||
auto Args = llvm::make_unique<const char*[]>(NumArgs + 2);
|
||||
Args[0] = "clang (LLVM option parsing)";
|
||||
for (unsigned i = 0; i != NumArgs; ++i)
|
||||
Args[i + 1] = Asm.LLVMArgs[i].c_str();
|
||||
Args[NumArgs + 1] = nullptr;
|
||||
llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get());
|
||||
}
|
||||
|
||||
// Execute the invocation, unless there were parsing errors.
|
||||
bool Failed = Diags.hasErrorOccurred() || ExecuteAssembler(Asm, Diags);
|
||||
|
||||
// If any timers were active but haven't been destroyed yet, print their
|
||||
// results now.
|
||||
TimerGroup::printAll(errs());
|
||||
|
||||
return !!Failed;
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
//===-- cc1as.h - Clang Assembler ----------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This is the entry point to the clang -cc1as functionality, which implements
|
||||
// the direct interface to the LLVM MC based assembler.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
/// Helper class for representing a single invocation of the assembler.
|
||||
struct AssemblerInvocation {
|
||||
/// @name Target Options
|
||||
/// @{
|
||||
|
||||
/// The name of the target triple to assemble for.
|
||||
std::string Triple;
|
||||
|
||||
/// If given, the name of the target CPU to determine which instructions
|
||||
/// are legal.
|
||||
std::string CPU;
|
||||
|
||||
/// The list of target specific features to enable or disable -- this should
|
||||
/// be a list of strings starting with '+' or '-'.
|
||||
std::vector<std::string> Features;
|
||||
|
||||
/// The list of symbol definitions.
|
||||
std::vector<std::string> SymbolDefs;
|
||||
|
||||
/// @}
|
||||
/// @name Language Options
|
||||
/// @{
|
||||
|
||||
std::vector<std::string> IncludePaths;
|
||||
unsigned NoInitialTextSection : 1;
|
||||
unsigned SaveTemporaryLabels : 1;
|
||||
unsigned GenDwarfForAssembly : 1;
|
||||
unsigned RelaxELFRelocations : 1;
|
||||
unsigned DwarfVersion;
|
||||
std::string DwarfDebugFlags;
|
||||
std::string DwarfDebugProducer;
|
||||
std::string DebugCompilationDir;
|
||||
std::map<const std::string, const std::string> DebugPrefixMap;
|
||||
llvm::DebugCompressionType CompressDebugSections =
|
||||
llvm::DebugCompressionType::None;
|
||||
std::string MainFileName;
|
||||
std::string SplitDwarfOutput;
|
||||
|
||||
/// @}
|
||||
/// @name Frontend Options
|
||||
/// @{
|
||||
|
||||
std::string InputFile;
|
||||
std::vector<std::string> LLVMArgs;
|
||||
std::string OutputPath;
|
||||
enum FileType {
|
||||
FT_Asm, ///< Assembly (.s) output, transliterate mode.
|
||||
FT_Null, ///< No output, for timing purposes.
|
||||
FT_Obj ///< Object file output.
|
||||
};
|
||||
FileType OutputType;
|
||||
unsigned ShowHelp : 1;
|
||||
unsigned ShowVersion : 1;
|
||||
|
||||
/// @}
|
||||
/// @name Transliterate Options
|
||||
/// @{
|
||||
|
||||
unsigned OutputAsmVariant;
|
||||
unsigned ShowEncoding : 1;
|
||||
unsigned ShowInst : 1;
|
||||
|
||||
/// @}
|
||||
/// @name Assembler Options
|
||||
/// @{
|
||||
|
||||
unsigned RelaxAll : 1;
|
||||
unsigned NoExecStack : 1;
|
||||
unsigned FatalWarnings : 1;
|
||||
unsigned IncrementalLinkerCompatible : 1;
|
||||
unsigned EmbedBitcode : 1;
|
||||
|
||||
/// The name of the relocation model to use.
|
||||
std::string RelocationModel;
|
||||
|
||||
/// The ABI targeted by the backend. Specified using -target-abi. Empty
|
||||
/// otherwise.
|
||||
std::string TargetABI;
|
||||
|
||||
/// @}
|
||||
|
||||
public:
|
||||
AssemblerInvocation() {
|
||||
Triple = "";
|
||||
NoInitialTextSection = 0;
|
||||
InputFile = "-";
|
||||
OutputPath = "-";
|
||||
OutputType = FT_Asm;
|
||||
OutputAsmVariant = 0;
|
||||
ShowInst = 0;
|
||||
ShowEncoding = 0;
|
||||
RelaxAll = 0;
|
||||
NoExecStack = 0;
|
||||
FatalWarnings = 0;
|
||||
IncrementalLinkerCompatible = 0;
|
||||
DwarfVersion = 0;
|
||||
EmbedBitcode = 0;
|
||||
}
|
||||
|
||||
static bool CreateFromArgs(AssemblerInvocation &Res,
|
||||
ArrayRef<const char *> Argv,
|
||||
DiagnosticsEngine &Diags);
|
||||
};
|
||||
|
||||
bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags);
|
||||
@@ -0,0 +1,97 @@
|
||||
// +build byollvm
|
||||
|
||||
#include <clang/Basic/DiagnosticOptions.h>
|
||||
#include <clang/CodeGen/CodeGenAction.h>
|
||||
#include <clang/Driver/Compilation.h>
|
||||
#include <clang/Driver/Driver.h>
|
||||
#include <clang/Frontend/CompilerInstance.h>
|
||||
#include <clang/Frontend/CompilerInvocation.h>
|
||||
#include <clang/Frontend/FrontendDiagnostic.h>
|
||||
#include <clang/Frontend/TextDiagnosticPrinter.h>
|
||||
#include <clang/FrontendTool/Utils.h>
|
||||
#include <llvm/ADT/IntrusiveRefCntPtr.h>
|
||||
#include <llvm/Option/Option.h>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace clang;
|
||||
|
||||
#include "cc1as.h"
|
||||
|
||||
// This file provides C wrappers for the builtin tools cc1 and cc1as
|
||||
// provided by Clang, and calls them as the driver would call them.
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool tinygo_clang_driver(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
|
||||
// The compiler invocation needs a DiagnosticsEngine so it can report problems
|
||||
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts = new clang::DiagnosticOptions();
|
||||
clang::TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts);
|
||||
clang::DiagnosticsEngine Diags(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs()), &*DiagOpts, &DiagnosticPrinter, false);
|
||||
|
||||
// Create the clang driver
|
||||
clang::driver::Driver TheDriver(args[0], llvm::sys::getDefaultTargetTriple(), Diags);
|
||||
|
||||
// Create the set of actions to perform
|
||||
std::unique_ptr<clang::driver::Compilation> C(TheDriver.BuildCompilation(args));
|
||||
if (!C) {
|
||||
return false;
|
||||
}
|
||||
const clang::driver::JobList &Jobs = C->getJobs();
|
||||
|
||||
// There may be more than one job, for example for .S files
|
||||
// (preprocessor + assembler).
|
||||
for (auto Cmd : Jobs) {
|
||||
// Select the tool: cc1 or cc1as.
|
||||
const llvm::opt::ArgStringList &CCArgs = Cmd.getArguments();
|
||||
|
||||
if (strcmp(*CCArgs.data(), "-cc1") == 0) {
|
||||
// This is the C frontend.
|
||||
// Initialize a compiler invocation object from the clang (-cc1) arguments.
|
||||
std::unique_ptr<clang::CompilerInstance> Clang(new clang::CompilerInstance());
|
||||
bool success = clang::CompilerInvocation::CreateFromArgs(
|
||||
Clang->getInvocation(),
|
||||
const_cast<const char **>(CCArgs.data()),
|
||||
const_cast<const char **>(CCArgs.data()) + CCArgs.size(),
|
||||
Diags);
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the actual diagnostics engine.
|
||||
Clang->createDiagnostics();
|
||||
if (!Clang->hasDiagnostics()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Execute the frontend actions.
|
||||
success = ExecuteCompilerInvocation(Clang.get());
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (strcmp(*CCArgs.data(), "-cc1as") == 0) {
|
||||
// This is the assembler frontend. Parse the arguments.
|
||||
AssemblerInvocation Asm;
|
||||
if (!AssemblerInvocation::CreateFromArgs(Asm, llvm::ArrayRef<const char*>(CCArgs).slice(1), Diags))
|
||||
return false;
|
||||
|
||||
// Execute the invocation, unless there were parsing errors.
|
||||
bool failed = Diags.hasErrorOccurred() || ExecuteAssembler(Asm, Diags);
|
||||
if (failed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Unknown tool, print the tool and exit.
|
||||
fprintf(stderr, "unknown tool: %s\n", *CCArgs.data());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Commands executed successfully.
|
||||
return true;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -34,6 +34,12 @@ func init() {
|
||||
commands["ld.lld"] = append(commands["ld.lld"], "lld", "C:\\Program Files\\LLVM\\bin\\lld.exe")
|
||||
commands["wasm-ld"] = append(commands["wasm-ld"], "C:\\Program Files\\LLVM\\bin\\wasm-ld.exe")
|
||||
}
|
||||
// Add the path to the llvm90 installed from ports
|
||||
if runtime.GOOS == "freebsd" {
|
||||
commands["clang"] = append(commands["clang"], "/usr/local/llvm90/bin/clang-9")
|
||||
commands["ld.lld"] = append(commands["ld.lld"], "/usr/local/llvm90/bin/ld.lld")
|
||||
commands["wasm-ld"] = append(commands["wasm-ld"], "/usr/local/llvm90/bin/wasm-ld")
|
||||
}
|
||||
}
|
||||
|
||||
func execCommand(cmdNames []string, args ...string) error {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// +build byollvm
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo CXXFLAGS: -fno-rtti
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
bool tinygo_clang_driver(int argc, char **argv);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// runCCompiler invokes a C compiler with the given arguments.
|
||||
//
|
||||
// This version invokes the built-in Clang when trying to run the Clang compiler.
|
||||
func runCCompiler(command string, flags ...string) error {
|
||||
switch command {
|
||||
case "clang":
|
||||
// Compile this with the internal Clang compiler.
|
||||
headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
if headerPath == "" {
|
||||
return errors.New("could not locate Clang headers")
|
||||
}
|
||||
flags = append(flags, "-I"+headerPath)
|
||||
flags = append([]string{"tinygo:" + command}, flags...)
|
||||
var cflag *C.char
|
||||
buf := C.calloc(C.size_t(len(flags)), C.size_t(unsafe.Sizeof(cflag)))
|
||||
cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(flags):len(flags)]
|
||||
for i, flag := range flags {
|
||||
cflag := C.CString(flag)
|
||||
cflags[i] = cflag
|
||||
defer C.free(unsafe.Pointer(cflag))
|
||||
}
|
||||
ok := C.tinygo_clang_driver(C.int(len(flags)), (**C.char)(buf))
|
||||
if !ok {
|
||||
return errors.New("failed to compile using built-in clang")
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
// Running some other compiler. Maybe it has been defined in the
|
||||
// commands map (unlikely).
|
||||
if cmdNames, ok := commands[command]; ok {
|
||||
return execCommand(cmdNames, flags...)
|
||||
}
|
||||
// Alternatively, run the compiler directly.
|
||||
cmd := exec.Command(command, flags...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// +build !byollvm
|
||||
|
||||
package builder
|
||||
|
||||
// This file provides a way to a C compiler as an external command. See also:
|
||||
// clang-external.go
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// runCCompiler invokes a C compiler with the given arguments.
|
||||
//
|
||||
// This version always runs the compiler as an external command.
|
||||
func runCCompiler(command string, flags ...string) error {
|
||||
if cmdNames, ok := commands[command]; ok {
|
||||
return execCommand(cmdNames, flags...)
|
||||
}
|
||||
cmd := exec.Command(command, flags...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
+1
-1
@@ -71,7 +71,7 @@ func GorootVersionString(goroot string) (string, error) {
|
||||
// various ways.
|
||||
func getClangHeaderPath(TINYGOROOT string) string {
|
||||
// Check whether we're running from the source directory.
|
||||
path := filepath.Join(TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")
|
||||
path := filepath.Join(TINYGOROOT, "llvm-project", "clang", "lib", "Headers")
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
return path
|
||||
}
|
||||
|
||||
@@ -12,6 +12,20 @@ func (e *MultiError) Error() string {
|
||||
return e.Errs[0].Error()
|
||||
}
|
||||
|
||||
// newMultiError returns a *MultiError if there is more than one error, or
|
||||
// returns that error directly when there is only one. Passing an empty slice
|
||||
// will lead to a panic.
|
||||
func newMultiError(errs []error) error {
|
||||
switch len(errs) {
|
||||
case 0:
|
||||
panic("attempted to create empty MultiError")
|
||||
case 1:
|
||||
return errs[0]
|
||||
default:
|
||||
return &MultiError{errs}
|
||||
}
|
||||
}
|
||||
|
||||
// commandError is an error type to wrap os/exec.Command errors. This provides
|
||||
// some more information regarding what went wrong while running a command.
|
||||
type commandError struct {
|
||||
|
||||
+1
-3
@@ -22,11 +22,9 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
|
||||
func TestCGo(t *testing.T) {
|
||||
var cflags = []string{"--target=armv6m-none-eabi"}
|
||||
|
||||
for _, name := range []string{"basic", "errors", "types", "flags"} {
|
||||
for _, name := range []string{"basic", "errors", "types", "flags", "const"} {
|
||||
name := name // avoid a race condition
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Read the AST in memory.
|
||||
path := filepath.Join("testdata", name+".go")
|
||||
fset := token.NewFileSet()
|
||||
|
||||
+25
-1
@@ -52,6 +52,13 @@ func parseConstExpr(t *tokenizer) (ast.Expr, *scanner.Error) {
|
||||
}
|
||||
t.Next()
|
||||
return expr, nil
|
||||
case token.IDENT:
|
||||
expr := &ast.Ident{
|
||||
NamePos: t.pos,
|
||||
Name: "C." + t.value,
|
||||
}
|
||||
t.Next()
|
||||
return expr, nil
|
||||
case token.EOF:
|
||||
return nil, &scanner.Error{
|
||||
Pos: t.fset.Position(t.pos),
|
||||
@@ -133,8 +140,10 @@ func (t *tokenizer) Next() {
|
||||
if c == '.' {
|
||||
hasDot = true
|
||||
}
|
||||
if (c >= '0' && c <= '9') || c == '.' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') {
|
||||
if c >= '0' && c <= '9' || c == '.' || c == '_' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' {
|
||||
tokenLen = i + 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
t.value = t.buf[:tokenLen]
|
||||
@@ -150,6 +159,21 @@ func (t *tokenizer) Next() {
|
||||
t.value = strings.TrimRight(t.value, "uUlL")
|
||||
}
|
||||
return
|
||||
case c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_':
|
||||
// Identifier. Find all remaining tokens that are part of this
|
||||
// identifier.
|
||||
tokenLen := len(t.buf)
|
||||
for i, c := range t.buf {
|
||||
if c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_' {
|
||||
tokenLen = i + 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
t.value = t.buf[:tokenLen]
|
||||
t.buf = t.buf[tokenLen:]
|
||||
t.token = token.IDENT
|
||||
return
|
||||
case c == '"':
|
||||
// String constant. Find the first '"' character that is not
|
||||
// preceded by a backslash.
|
||||
|
||||
+3
-2
@@ -21,8 +21,8 @@ func TestParseConst(t *testing.T) {
|
||||
{`5)`, `error: 1:2: unexpected token )`},
|
||||
{" \t)", `error: 1:4: unexpected token )`},
|
||||
{`5.8f`, `5.8`},
|
||||
{`foo`, `error: 1:1: unexpected token ILLEGAL`}, // identifiers unimplemented
|
||||
{``, `error: 1:1: empty constant`}, // empty constants not allowed in Go
|
||||
{`foo`, `C.foo`},
|
||||
{``, `error: 1:1: empty constant`}, // empty constants not allowed in Go
|
||||
{`"foo"`, `"foo"`},
|
||||
{`"a\\n"`, `"a\\n"`},
|
||||
{`"a\n"`, `"a\n"`},
|
||||
@@ -30,6 +30,7 @@ func TestParseConst(t *testing.T) {
|
||||
{`'a'`, `'a'`},
|
||||
{`0b10`, `0b10`},
|
||||
{`0x1234_5678`, `0x1234_5678`},
|
||||
{`5 5`, `error: 1:3: unexpected token INT`}, // test for a bugfix
|
||||
} {
|
||||
fset := token.NewFileSet()
|
||||
startPos := fset.AddFile("", -1, 1000).Pos(0)
|
||||
|
||||
@@ -5,7 +5,9 @@ package cgo
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-9/include
|
||||
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@9/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm90/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-9/lib -lclang
|
||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@9/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm90/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
#define foo 3
|
||||
#define bar foo
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
Foo = C.foo
|
||||
Bar = C.bar
|
||||
)
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
const C.bar = C.foo
|
||||
const C.foo = 3
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
+19
-20
@@ -98,76 +98,75 @@ func (c *Compiler) checkValue(v llvm.Value, types map[llvm.Type]struct{}, specia
|
||||
func (c *Compiler) checkInstruction(inst llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
|
||||
// check value properties
|
||||
if err := c.checkValue(inst, types, specials); err != nil {
|
||||
return fmt.Errorf("failed to validate value of instruction %q: %s", inst.Name(), err.Error())
|
||||
return errorAt(inst, err.Error())
|
||||
}
|
||||
|
||||
// check operands
|
||||
for i := 0; i < inst.OperandsCount(); i++ {
|
||||
if err := c.checkValue(inst.Operand(i), types, specials); err != nil {
|
||||
return fmt.Errorf("failed to validate argument %d of instruction %q: %s", i, inst.Name(), err.Error())
|
||||
return errorAt(inst, fmt.Sprintf("failed to validate operand %d of instruction %q: %s", i, inst.Name(), err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Compiler) checkBasicBlock(bb llvm.BasicBlock, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
|
||||
func (c *Compiler) checkBasicBlock(bb llvm.BasicBlock, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) []error {
|
||||
// check basic block value and type
|
||||
var errs []error
|
||||
if err := c.checkValue(bb.AsValue(), types, specials); err != nil {
|
||||
return fmt.Errorf("failed to validate value of basic block %s: %s", bb.AsValue().Name(), err.Error())
|
||||
errs = append(errs, errorAt(bb.Parent(), fmt.Sprintf("failed to validate value of basic block %s: %v", bb.AsValue().Name(), err)))
|
||||
}
|
||||
|
||||
// check instructions
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if err := c.checkInstruction(inst, types, specials); err != nil {
|
||||
return fmt.Errorf("failed to validate basic block %q: %s", bb.AsValue().Name(), err.Error())
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return errs
|
||||
}
|
||||
|
||||
func (c *Compiler) checkFunction(fn llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
|
||||
func (c *Compiler) checkFunction(fn llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) []error {
|
||||
// check function value and type
|
||||
var errs []error
|
||||
if err := c.checkValue(fn, types, specials); err != nil {
|
||||
return fmt.Errorf("failed to validate value of function %s: %s", fn.Name(), err.Error())
|
||||
errs = append(errs, fmt.Errorf("failed to validate value of function %s: %s", fn.Name(), err.Error()))
|
||||
}
|
||||
|
||||
// check basic blocks
|
||||
for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
if err := c.checkBasicBlock(bb, types, specials); err != nil {
|
||||
return fmt.Errorf("failed to validate basic block of function %s: %s", fn.Name(), err.Error())
|
||||
}
|
||||
errs = append(errs, c.checkBasicBlock(bb, types, specials)...)
|
||||
}
|
||||
|
||||
return nil
|
||||
return errs
|
||||
}
|
||||
|
||||
func (c *Compiler) checkModule() error {
|
||||
func (c *Compiler) checkModule() []error {
|
||||
// check for any context mismatches
|
||||
var errs []error
|
||||
switch {
|
||||
case c.mod.Context() == c.ctx:
|
||||
// this is correct
|
||||
case c.mod.Context() == llvm.GlobalContext():
|
||||
// somewhere we accidentally used the global context instead of a real context
|
||||
return errors.New("module uses global context")
|
||||
errs = append(errs, errors.New("module uses global context"))
|
||||
default:
|
||||
// we used some other context by accident
|
||||
return fmt.Errorf("module uses context %v instead of the main context %v", c.mod.Context(), c.ctx)
|
||||
errs = append(errs, fmt.Errorf("module uses context %v instead of the main context %v", c.mod.Context(), c.ctx))
|
||||
}
|
||||
|
||||
types := map[llvm.Type]struct{}{}
|
||||
specials := map[llvm.TypeKind]llvm.Type{}
|
||||
for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
if err := c.checkFunction(fn, types, specials); err != nil {
|
||||
return err
|
||||
}
|
||||
errs = append(errs, c.checkFunction(fn, types, specials)...)
|
||||
}
|
||||
for g := c.mod.FirstGlobal(); !g.IsNil(); g = llvm.NextGlobal(g) {
|
||||
if err := c.checkValue(g, types, specials); err != nil {
|
||||
return fmt.Errorf("failed to verify global %s of module: %s", g.Name(), err.Error())
|
||||
errs = append(errs, fmt.Errorf("failed to verify global %s of module: %s", g.Name(), err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return errs
|
||||
}
|
||||
|
||||
+30
-16
@@ -217,7 +217,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
path = path[len(tinygoPath+"/src/"):]
|
||||
}
|
||||
switch path {
|
||||
case "machine", "os", "reflect", "runtime", "runtime/volatile", "sync", "testing", "internal/reflectlite":
|
||||
case "machine", "os", "reflect", "runtime", "runtime/interrupt", "runtime/volatile", "sync", "testing", "internal/reflectlite":
|
||||
return path
|
||||
default:
|
||||
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
||||
@@ -251,13 +251,17 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
return []error{err}
|
||||
}
|
||||
} else {
|
||||
_, err = lprogram.Import(mainPath, wd)
|
||||
_, err = lprogram.Import(mainPath, wd, token.Position{
|
||||
Filename: "build command-line-arguments",
|
||||
})
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = lprogram.Import("runtime", "")
|
||||
_, err = lprogram.Import("runtime", "", token.Position{
|
||||
Filename: "build default import",
|
||||
})
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
@@ -767,14 +771,6 @@ func (c *Compiler) attachDebugInfo(f *ir.Function) llvm.Metadata {
|
||||
}
|
||||
|
||||
func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata {
|
||||
if _, ok := c.difiles[filename]; !ok {
|
||||
dir, file := filepath.Split(filename)
|
||||
if dir != "" {
|
||||
dir = dir[:len(dir)-1]
|
||||
}
|
||||
c.difiles[filename] = c.dibuilder.CreateFile(file, dir)
|
||||
}
|
||||
|
||||
// Debug info for this function.
|
||||
diparams := make([]llvm.Metadata, 0, len(f.Params))
|
||||
for _, param := range f.Params {
|
||||
@@ -788,7 +784,7 @@ func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix,
|
||||
difunc := c.dibuilder.CreateFunction(c.difiles[filename], llvm.DIFunction{
|
||||
Name: f.RelString(nil) + suffix,
|
||||
LinkageName: f.LinkName() + suffix,
|
||||
File: c.difiles[filename],
|
||||
File: c.getDIFile(filename),
|
||||
Line: line,
|
||||
Type: diFuncType,
|
||||
LocalToUnit: true,
|
||||
@@ -801,21 +797,37 @@ func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix,
|
||||
return difunc
|
||||
}
|
||||
|
||||
// getDIFile returns a DIFile metadata node for the given filename. It tries to
|
||||
// use one that was already created, otherwise it falls back to creating a new
|
||||
// one.
|
||||
func (c *Compiler) getDIFile(filename string) llvm.Metadata {
|
||||
if _, ok := c.difiles[filename]; !ok {
|
||||
dir, file := filepath.Split(filename)
|
||||
if dir != "" {
|
||||
dir = dir[:len(dir)-1]
|
||||
}
|
||||
c.difiles[filename] = c.dibuilder.CreateFile(file, dir)
|
||||
}
|
||||
return c.difiles[filename]
|
||||
}
|
||||
|
||||
func (c *Compiler) parseFunc(frame *Frame) {
|
||||
if c.DumpSSA() {
|
||||
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
||||
}
|
||||
if !frame.fn.LLVMFn.IsDeclaration() {
|
||||
c.addError(frame.fn.Pos(), "function is already defined:"+frame.fn.LLVMFn.Name())
|
||||
errValue := frame.fn.LLVMFn.Name() + " redeclared in this program"
|
||||
fnPos := getPosition(frame.fn.LLVMFn)
|
||||
if fnPos.IsValid() {
|
||||
errValue += "\n\tprevious declaration at " + fnPos.String()
|
||||
}
|
||||
c.addError(frame.fn.Pos(), errValue)
|
||||
return
|
||||
}
|
||||
if !frame.fn.IsExported() {
|
||||
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
||||
frame.fn.LLVMFn.SetUnnamedAddr(true)
|
||||
}
|
||||
if frame.fn.IsInterrupt() && strings.HasPrefix(c.Triple(), "avr") {
|
||||
frame.fn.LLVMFn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
|
||||
}
|
||||
|
||||
// Some functions have a pragma controlling the inlining level.
|
||||
switch frame.fn.Inline() {
|
||||
@@ -1297,6 +1309,8 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
||||
return c.emitVolatileLoad(frame, instr)
|
||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||
return c.emitVolatileStore(frame, instr)
|
||||
case name == "runtime/interrupt.New" && len(fn.Blocks) == 0:
|
||||
return c.emitInterruptGlobal(frame, instr)
|
||||
}
|
||||
|
||||
targetFunc := c.ir.GetFunction(fn)
|
||||
|
||||
+48
-3
@@ -14,6 +14,7 @@ package compiler
|
||||
// frames.
|
||||
|
||||
import (
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"github.com/tinygo-org/tinygo/ir"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
@@ -34,6 +35,40 @@ func (c *Compiler) deferInitFunc(frame *Frame) {
|
||||
c.builder.CreateStore(llvm.ConstPointerNull(deferType), frame.deferPtr)
|
||||
}
|
||||
|
||||
// isInLoop checks if there is a path from a basic block to itself.
|
||||
func isInLoop(start *ssa.BasicBlock) bool {
|
||||
// Use a breadth-first search to scan backwards through the block graph.
|
||||
queue := []*ssa.BasicBlock{start}
|
||||
checked := map[*ssa.BasicBlock]struct{}{}
|
||||
|
||||
for len(queue) > 0 {
|
||||
// pop a block off of the queue
|
||||
block := queue[len(queue)-1]
|
||||
queue = queue[:len(queue)-1]
|
||||
|
||||
// Search through predecessors.
|
||||
// Searching backwards means that this is pretty fast when the block is close to the start of the function.
|
||||
// Defers are often placed near the start of the function.
|
||||
for _, pred := range block.Preds {
|
||||
if pred == start {
|
||||
// cycle found
|
||||
return true
|
||||
}
|
||||
|
||||
if _, ok := checked[pred]; ok {
|
||||
// block already checked
|
||||
continue
|
||||
}
|
||||
|
||||
// add to queue and checked map
|
||||
queue = append(queue, pred)
|
||||
checked[pred] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// emitDefer emits a single defer instruction, to be run when this function
|
||||
// returns.
|
||||
func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) {
|
||||
@@ -127,12 +162,22 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) {
|
||||
deferFrame = c.builder.CreateInsertValue(deferFrame, value, i, "")
|
||||
}
|
||||
|
||||
// Put this struct in an alloca.
|
||||
alloca := c.builder.CreateAlloca(deferFrameType, "defer.alloca")
|
||||
c.builder.CreateStore(deferFrame, alloca)
|
||||
// Put this struct in an allocation.
|
||||
var alloca llvm.Value
|
||||
if !isInLoop(instr.Block()) {
|
||||
// This can safely use a stack allocation.
|
||||
alloca = llvmutil.CreateEntryBlockAlloca(c.builder, deferFrameType, "defer.alloca")
|
||||
} else {
|
||||
// This may be hit a variable number of times, so use a heap allocation.
|
||||
size := c.targetData.TypeAllocSize(deferFrameType)
|
||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||
allocCall := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call")
|
||||
alloca = c.builder.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
|
||||
}
|
||||
if c.NeedsStackObjects() {
|
||||
c.trackPointer(alloca)
|
||||
}
|
||||
c.builder.CreateStore(deferFrame, alloca)
|
||||
|
||||
// Push it on top of the linked list by replacing deferPtr.
|
||||
allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"path/filepath"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
func (c *Compiler) makeError(pos token.Pos, msg string) types.Error {
|
||||
@@ -16,3 +20,42 @@ func (c *Compiler) makeError(pos token.Pos, msg string) types.Error {
|
||||
func (c *Compiler) addError(pos token.Pos, msg string) {
|
||||
c.diagnostics = append(c.diagnostics, c.makeError(pos, msg))
|
||||
}
|
||||
|
||||
// errorAt returns an error value at the location of the instruction.
|
||||
// The location information may not be complete as it depends on debug
|
||||
// information in the IR.
|
||||
func errorAt(inst llvm.Value, msg string) scanner.Error {
|
||||
return scanner.Error{
|
||||
Pos: getPosition(inst),
|
||||
Msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// getPosition returns the position information for the given value, as far as
|
||||
// it is available.
|
||||
func getPosition(val llvm.Value) token.Position {
|
||||
if !val.IsAInstruction().IsNil() {
|
||||
loc := val.InstructionDebugLoc()
|
||||
if loc.IsNil() {
|
||||
return token.Position{}
|
||||
}
|
||||
file := loc.LocationScope().ScopeFile()
|
||||
return token.Position{
|
||||
Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
|
||||
Line: int(loc.LocationLine()),
|
||||
Column: int(loc.LocationColumn()),
|
||||
}
|
||||
} else if !val.IsAFunction().IsNil() {
|
||||
loc := val.Subprogram()
|
||||
if loc.IsNil() {
|
||||
return token.Position{}
|
||||
}
|
||||
file := loc.ScopeFile()
|
||||
return token.Position{
|
||||
Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
|
||||
Line: int(loc.SubprogramLine()),
|
||||
}
|
||||
} else {
|
||||
return token.Position{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,6 @@ package compiler
|
||||
// scheduler, which runs in the background scheduling all coroutines.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -283,7 +282,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
if use.IsConstant() && use.Opcode() == llvm.PtrToInt {
|
||||
for _, call := range getUses(use) {
|
||||
if call.IsACallInst().IsNil() || call.CalledValue().Name() != "runtime.makeGoroutine" {
|
||||
return false, errors.New("async function " + f.Name() + " incorrectly used in ptrtoint, expected runtime.makeGoroutine")
|
||||
return false, errorAt(call, "async function incorrectly used in ptrtoint, expected runtime.makeGoroutine")
|
||||
}
|
||||
}
|
||||
// This is a go statement. Do not mark the parent as async, as
|
||||
@@ -303,12 +302,19 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
// Not a call instruction. Maybe a store to a global? In any
|
||||
// case, this requires support for async calls across function
|
||||
// pointers which is not yet supported.
|
||||
return false, errors.New("async function " + f.Name() + " used as function pointer")
|
||||
at := use
|
||||
if use.IsAInstruction().IsNil() {
|
||||
// The use might not be an instruction (for example, in the
|
||||
// case of a const bitcast). Fall back to reporting the
|
||||
// location of the function instead.
|
||||
at = f
|
||||
}
|
||||
return false, errorAt(at, "async function "+f.Name()+" used as function pointer")
|
||||
}
|
||||
parent := use.InstructionParent().Parent()
|
||||
for i := 0; i < use.OperandsCount()-1; i++ {
|
||||
if use.Operand(i) == f {
|
||||
return false, errors.New("async function " + f.Name() + " used as function pointer in " + parent.Name())
|
||||
return false, errorAt(use, "async function "+f.Name()+" used as function pointer")
|
||||
}
|
||||
}
|
||||
worklist = append(worklist, parent)
|
||||
@@ -805,7 +811,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||
}, "")
|
||||
wakeup := c.splitBasicBlock(inst, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup")
|
||||
wakeup := llvmutil.SplitBasicBlock(c.builder, inst, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup")
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
|
||||
@@ -906,12 +912,12 @@ func (c *Compiler) lowerMakeGoroutineCalls(sched bool) error {
|
||||
origFunc := ptrtointIn.Operand(0)
|
||||
uses := getUses(goroutine)
|
||||
if len(uses) != 1 || uses[0].IsAIntToPtrInst().IsNil() {
|
||||
return errors.New("expected exactly 1 inttoptr use of runtime.makeGoroutine")
|
||||
return errorAt(makeGoroutine, "expected exactly 1 inttoptr use of runtime.makeGoroutine")
|
||||
}
|
||||
inttoptrOut := uses[0]
|
||||
uses = getUses(inttoptrOut)
|
||||
if len(uses) != 1 || uses[0].IsACallInst().IsNil() {
|
||||
return errors.New("expected exactly 1 call use of runtime.makeGoroutine bitcast")
|
||||
return errorAt(inttoptrOut, "expected exactly 1 call use of runtime.makeGoroutine bitcast")
|
||||
}
|
||||
realCall := uses[0]
|
||||
|
||||
|
||||
@@ -279,14 +279,14 @@ func (c *Compiler) getInterfaceMethodSet(typ *types.Named) llvm.Value {
|
||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||
}
|
||||
|
||||
// Every method is a *i16 reference indicating the signature of this method.
|
||||
// Every method is a *i8 reference indicating the signature of this method.
|
||||
methods := make([]llvm.Value, typ.Underlying().(*types.Interface).NumMethods())
|
||||
for i := range methods {
|
||||
method := typ.Underlying().(*types.Interface).Method(i)
|
||||
methods[i] = c.getMethodSignature(method)
|
||||
}
|
||||
|
||||
value := llvm.ConstArray(methods[0].Type(), methods)
|
||||
value := llvm.ConstArray(c.i8ptrType, methods)
|
||||
global = llvm.AddGlobal(c.mod, value.Type(), typ.String()+"$interface")
|
||||
global.SetInitializer(value)
|
||||
global.SetGlobalConstant(true)
|
||||
@@ -295,7 +295,7 @@ func (c *Compiler) getInterfaceMethodSet(typ *types.Named) llvm.Value {
|
||||
}
|
||||
|
||||
// getMethodSignature returns a global variable which is a reference to an
|
||||
// external *i16 indicating the indicating the signature of this method. It is
|
||||
// external *i8 indicating the indicating the signature of this method. It is
|
||||
// used during the interface lowering pass.
|
||||
func (c *Compiler) getMethodSignature(method *types.Func) llvm.Value {
|
||||
signature := ir.MethodSignature(method)
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// emitInterruptGlobal creates a new runtime/interrupt.Interrupt struct that
|
||||
// will be lowered to a real interrupt during interrupt lowering.
|
||||
//
|
||||
// This two-stage approach allows unused interrupts to be optimized away if
|
||||
// necessary.
|
||||
func (c *Compiler) emitInterruptGlobal(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
||||
// Get the interrupt number, which must be a compile-time constant.
|
||||
id, ok := instr.Args[0].(*ssa.Const)
|
||||
if !ok {
|
||||
return llvm.Value{}, c.makeError(instr.Pos(), "interrupt ID is not a constant")
|
||||
}
|
||||
|
||||
// Get the func value, which also must be a compile time constant.
|
||||
// Note that bound functions are allowed if the function has a pointer
|
||||
// receiver and is a global. This is rather strict but still allows for
|
||||
// idiomatic Go code.
|
||||
funcValue := c.getValue(frame, instr.Args[1])
|
||||
if funcValue.IsAConstant().IsNil() {
|
||||
// Try to determine the cause of the non-constantness for a nice error
|
||||
// message.
|
||||
switch instr.Args[1].(type) {
|
||||
case *ssa.MakeClosure:
|
||||
// This may also be a bound method.
|
||||
return llvm.Value{}, c.makeError(instr.Pos(), "closures are not supported in interrupt.New")
|
||||
}
|
||||
// Fall back to a generic error.
|
||||
return llvm.Value{}, c.makeError(instr.Pos(), "interrupt function must be constant")
|
||||
}
|
||||
|
||||
// Create a new global of type runtime/interrupt.handle. Globals of this
|
||||
// type are lowered in the interrupt lowering pass.
|
||||
globalType := c.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type()
|
||||
globalLLVMType := c.getLLVMType(globalType)
|
||||
globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
||||
if global := c.mod.NamedGlobal(globalName); !global.IsNil() {
|
||||
return llvm.Value{}, c.makeError(instr.Pos(), "interrupt redeclared in this program")
|
||||
}
|
||||
global := llvm.AddGlobal(c.mod, globalLLVMType, globalName)
|
||||
global.SetLinkage(llvm.PrivateLinkage)
|
||||
global.SetGlobalConstant(true)
|
||||
initializer := llvm.ConstNull(globalLLVMType)
|
||||
initializer = llvm.ConstInsertValue(initializer, funcValue, []uint32{0})
|
||||
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(c.intType, uint64(id.Int64()), true), []uint32{1, 0})
|
||||
global.SetInitializer(initializer)
|
||||
|
||||
// Add debug info to the interrupt global.
|
||||
if c.Debug() {
|
||||
pos := c.ir.Program.Fset.Position(instr.Pos())
|
||||
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
|
||||
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
|
||||
LinkageName: globalName,
|
||||
File: c.getDIFile(pos.Filename),
|
||||
Line: pos.Line,
|
||||
Type: c.getDIType(globalType),
|
||||
Expr: c.dibuilder.CreateExpression(nil),
|
||||
LocalToUnit: false,
|
||||
})
|
||||
global.AddMetadata(0, diglobal)
|
||||
}
|
||||
|
||||
// Create the runtime/interrupt.Interrupt type. It is a struct with a single
|
||||
// member of type int.
|
||||
num := llvm.ConstPtrToInt(global, c.intType)
|
||||
interrupt := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num})
|
||||
|
||||
// Add dummy "use" call for AVR, because interrupts may be used even though
|
||||
// they are never referenced again. This is unlike Cortex-M or the RISC-V
|
||||
// PLIC where each interrupt must be enabled using the interrupt number, and
|
||||
// thus keeps the Interrupt object alive.
|
||||
// This call is removed during interrupt lowering.
|
||||
if strings.HasPrefix(c.Triple(), "avr") {
|
||||
useFn := c.mod.NamedFunction("runtime/interrupt.use")
|
||||
if useFn.IsNil() {
|
||||
useFnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
|
||||
useFn = llvm.AddFunction(c.mod, "runtime/interrupt.use", useFnType)
|
||||
}
|
||||
c.builder.CreateCall(useFn, []llvm.Value{interrupt}, "")
|
||||
}
|
||||
|
||||
return interrupt, nil
|
||||
}
|
||||
@@ -52,78 +52,6 @@ func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []l
|
||||
return llvmutil.EmitPointerUnpack(c.builder, c.mod, ptr, valueTypes)
|
||||
}
|
||||
|
||||
// splitBasicBlock splits a LLVM basic block into two parts. All instructions
|
||||
// after afterInst are moved into a new basic block (created right after the
|
||||
// current one) with the given name.
|
||||
func (c *Compiler) splitBasicBlock(afterInst llvm.Value, insertAfter llvm.BasicBlock, name string) llvm.BasicBlock {
|
||||
oldBlock := afterInst.InstructionParent()
|
||||
newBlock := c.ctx.InsertBasicBlock(insertAfter, name)
|
||||
var nextInstructions []llvm.Value // values to move
|
||||
|
||||
// Collect to-be-moved instructions.
|
||||
inst := afterInst
|
||||
for {
|
||||
inst = llvm.NextInstruction(inst)
|
||||
if inst.IsNil() {
|
||||
break
|
||||
}
|
||||
nextInstructions = append(nextInstructions, inst)
|
||||
}
|
||||
|
||||
// Move instructions.
|
||||
c.builder.SetInsertPointAtEnd(newBlock)
|
||||
for _, inst := range nextInstructions {
|
||||
inst.RemoveFromParentAsInstruction()
|
||||
c.builder.Insert(inst)
|
||||
}
|
||||
|
||||
// Find PHI nodes to update.
|
||||
var phiNodes []llvm.Value // PHI nodes to update
|
||||
for bb := insertAfter.Parent().FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if inst.IsAPHINode().IsNil() {
|
||||
continue
|
||||
}
|
||||
needsUpdate := false
|
||||
incomingCount := inst.IncomingCount()
|
||||
for i := 0; i < incomingCount; i++ {
|
||||
if inst.IncomingBlock(i) == oldBlock {
|
||||
needsUpdate = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !needsUpdate {
|
||||
// PHI node has no incoming edge from the old block.
|
||||
continue
|
||||
}
|
||||
phiNodes = append(phiNodes, inst)
|
||||
}
|
||||
}
|
||||
|
||||
// Update PHI nodes.
|
||||
for _, phi := range phiNodes {
|
||||
c.builder.SetInsertPointBefore(phi)
|
||||
newPhi := c.builder.CreatePHI(phi.Type(), "")
|
||||
incomingCount := phi.IncomingCount()
|
||||
incomingVals := make([]llvm.Value, incomingCount)
|
||||
incomingBlocks := make([]llvm.BasicBlock, incomingCount)
|
||||
for i := 0; i < incomingCount; i++ {
|
||||
value := phi.IncomingValue(i)
|
||||
block := phi.IncomingBlock(i)
|
||||
if block == oldBlock {
|
||||
block = newBlock
|
||||
}
|
||||
incomingVals[i] = value
|
||||
incomingBlocks[i] = block
|
||||
}
|
||||
newPhi.AddIncoming(incomingVals, incomingBlocks)
|
||||
phi.ReplaceAllUsesWith(newPhi)
|
||||
phi.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
return newBlock
|
||||
}
|
||||
|
||||
// makeGlobalArray creates a new LLVM global with the given name and integers as
|
||||
// contents, and returns the global.
|
||||
// Note that it is left with the default linkage etc., you should set
|
||||
|
||||
@@ -94,3 +94,75 @@ func getLifetimeEndFunc(mod llvm.Module) llvm.Value {
|
||||
}
|
||||
return fn
|
||||
}
|
||||
|
||||
// SplitBasicBlock splits a LLVM basic block into two parts. All instructions
|
||||
// after afterInst are moved into a new basic block (created right after the
|
||||
// current one) with the given name.
|
||||
func SplitBasicBlock(builder llvm.Builder, afterInst llvm.Value, insertAfter llvm.BasicBlock, name string) llvm.BasicBlock {
|
||||
oldBlock := afterInst.InstructionParent()
|
||||
newBlock := afterInst.Type().Context().InsertBasicBlock(insertAfter, name)
|
||||
var nextInstructions []llvm.Value // values to move
|
||||
|
||||
// Collect to-be-moved instructions.
|
||||
inst := afterInst
|
||||
for {
|
||||
inst = llvm.NextInstruction(inst)
|
||||
if inst.IsNil() {
|
||||
break
|
||||
}
|
||||
nextInstructions = append(nextInstructions, inst)
|
||||
}
|
||||
|
||||
// Move instructions.
|
||||
builder.SetInsertPointAtEnd(newBlock)
|
||||
for _, inst := range nextInstructions {
|
||||
inst.RemoveFromParentAsInstruction()
|
||||
builder.Insert(inst)
|
||||
}
|
||||
|
||||
// Find PHI nodes to update.
|
||||
var phiNodes []llvm.Value // PHI nodes to update
|
||||
for bb := insertAfter.Parent().FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if inst.IsAPHINode().IsNil() {
|
||||
continue
|
||||
}
|
||||
needsUpdate := false
|
||||
incomingCount := inst.IncomingCount()
|
||||
for i := 0; i < incomingCount; i++ {
|
||||
if inst.IncomingBlock(i) == oldBlock {
|
||||
needsUpdate = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !needsUpdate {
|
||||
// PHI node has no incoming edge from the old block.
|
||||
continue
|
||||
}
|
||||
phiNodes = append(phiNodes, inst)
|
||||
}
|
||||
}
|
||||
|
||||
// Update PHI nodes.
|
||||
for _, phi := range phiNodes {
|
||||
builder.SetInsertPointBefore(phi)
|
||||
newPhi := builder.CreatePHI(phi.Type(), "")
|
||||
incomingCount := phi.IncomingCount()
|
||||
incomingVals := make([]llvm.Value, incomingCount)
|
||||
incomingBlocks := make([]llvm.BasicBlock, incomingCount)
|
||||
for i := 0; i < incomingCount; i++ {
|
||||
value := phi.IncomingValue(i)
|
||||
block := phi.IncomingBlock(i)
|
||||
if block == oldBlock {
|
||||
block = newBlock
|
||||
}
|
||||
incomingVals[i] = value
|
||||
incomingBlocks[i] = block
|
||||
}
|
||||
newPhi.AddIncoming(incomingVals, incomingBlocks)
|
||||
phi.ReplaceAllUsesWith(newPhi)
|
||||
phi.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
return newBlock
|
||||
}
|
||||
|
||||
+27
-12
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
// Run the LLVM optimizer over the module.
|
||||
// The inliner can be disabled (if necessary) by passing 0 to the inlinerThreshold.
|
||||
func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) error {
|
||||
func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) []error {
|
||||
builder := llvm.NewPassManagerBuilder()
|
||||
defer builder.Dispose()
|
||||
builder.SetOptLevel(optLevel)
|
||||
@@ -25,9 +25,9 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
|
||||
// run a check of all of our code
|
||||
if c.VerifyIR() {
|
||||
err := c.checkModule()
|
||||
if err != nil {
|
||||
return err
|
||||
errs := c.checkModule()
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
// Run some preparatory passes for the Go optimizer.
|
||||
goPasses := llvm.NewPassManager()
|
||||
defer goPasses.Dispose()
|
||||
goPasses.AddGlobalDCEPass()
|
||||
goPasses.AddGlobalOptimizerPass()
|
||||
goPasses.AddConstantPropagationPass()
|
||||
goPasses.AddAggressiveDCEPass()
|
||||
@@ -56,7 +57,15 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
transform.OptimizeStringToBytes(c.mod)
|
||||
transform.OptimizeAllocs(c.mod)
|
||||
transform.LowerInterfaces(c.mod)
|
||||
c.LowerFuncValues()
|
||||
|
||||
errs := transform.LowerInterruptRegistrations(c.mod)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
if c.funcImplementation() == funcValueSwitch {
|
||||
transform.LowerFuncValues(c.mod)
|
||||
}
|
||||
|
||||
// After interfaces are lowered, there are many more opportunities for
|
||||
// interprocedural optimizations. To get them to work, function
|
||||
@@ -85,24 +94,30 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
|
||||
err := c.LowerGoroutines()
|
||||
if err != nil {
|
||||
return err
|
||||
return []error{err}
|
||||
}
|
||||
} else {
|
||||
// Must be run at any optimization level.
|
||||
transform.LowerInterfaces(c.mod)
|
||||
c.LowerFuncValues()
|
||||
if c.funcImplementation() == funcValueSwitch {
|
||||
transform.LowerFuncValues(c.mod)
|
||||
}
|
||||
err := c.LowerGoroutines()
|
||||
if err != nil {
|
||||
return err
|
||||
return []error{err}
|
||||
}
|
||||
errs := transform.LowerInterruptRegistrations(c.mod)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
}
|
||||
if c.VerifyIR() {
|
||||
if err := c.checkModule(); err != nil {
|
||||
return err
|
||||
if errs := c.checkModule(); errs != nil {
|
||||
return errs
|
||||
}
|
||||
}
|
||||
if err := c.Verify(); err != nil {
|
||||
return errors.New("optimizations caused a verification failure")
|
||||
return []error{errors.New("optimizations caused a verification failure")}
|
||||
}
|
||||
|
||||
if sizeLevel >= 2 {
|
||||
@@ -141,7 +156,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
hasGCPass = transform.MakeGCStackSlots(c.mod) || hasGCPass
|
||||
if hasGCPass {
|
||||
if err := c.Verify(); err != nil {
|
||||
return errors.New("GC pass caused a verification failure")
|
||||
return []error{errors.New("GC pass caused a verification failure")}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,9 @@ func (s *StdSizes) Sizeof(T types.Type) int64 {
|
||||
if k == types.UnsafePointer {
|
||||
return s.PtrSize
|
||||
}
|
||||
if k == types.Invalid {
|
||||
return 0 // only relevant when there is a type error somewhere
|
||||
}
|
||||
panic("unknown basic type: " + t.String())
|
||||
case *types.Array:
|
||||
n := t.Len()
|
||||
|
||||
+33
-3
@@ -60,14 +60,44 @@ func (c *Compiler) getGlobal(g *ssa.Global) llvm.Value {
|
||||
info := c.getGlobalInfo(g)
|
||||
llvmGlobal := c.mod.NamedGlobal(info.linkName)
|
||||
if llvmGlobal.IsNil() {
|
||||
llvmType := c.getLLVMType(g.Type().(*types.Pointer).Elem())
|
||||
typ := g.Type().(*types.Pointer).Elem()
|
||||
llvmType := c.getLLVMType(typ)
|
||||
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
||||
if !info.extern {
|
||||
llvmGlobal.SetInitializer(llvm.ConstNull(llvmType))
|
||||
llvmGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
}
|
||||
if info.align > c.targetData.ABITypeAlignment(llvmType) {
|
||||
llvmGlobal.SetAlignment(info.align)
|
||||
|
||||
// Set alignment from the //go:align comment.
|
||||
var alignInBits uint32
|
||||
if info.align < 0 || info.align&(info.align-1) != 0 {
|
||||
// Check for power-of-two (or 0).
|
||||
// See: https://stackoverflow.com/a/108360
|
||||
c.addError(g.Pos(), "global variable alignment must be a positive power of two")
|
||||
} else {
|
||||
// Set the alignment only when it is a power of two.
|
||||
alignInBits = uint32(info.align) ^ uint32(info.align-1)
|
||||
if info.align > c.targetData.ABITypeAlignment(llvmType) {
|
||||
llvmGlobal.SetAlignment(info.align)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Debug() {
|
||||
// Add debug info.
|
||||
// TODO: this should be done for every global in the program, not just
|
||||
// the ones that are referenced from some code.
|
||||
pos := c.ir.Program.Fset.Position(g.Pos())
|
||||
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
|
||||
Name: g.RelString(nil),
|
||||
LinkageName: info.linkName,
|
||||
File: c.getDIFile(pos.Filename),
|
||||
Line: pos.Line,
|
||||
Type: c.getDIType(typ),
|
||||
LocalToUnit: false,
|
||||
Expr: c.dibuilder.CreateExpression(nil),
|
||||
AlignInBits: alignInBits,
|
||||
})
|
||||
llvmGlobal.AddMetadata(0, diglobal)
|
||||
}
|
||||
}
|
||||
return llvmGlobal
|
||||
|
||||
+1
-1
@@ -152,7 +152,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH())
|
||||
}
|
||||
switch c.GOOS() {
|
||||
case "linux":
|
||||
case "linux", "freebsd":
|
||||
// Return values: r0, r1 uintptr, err Errno
|
||||
// Pseudocode:
|
||||
// var err uintptr
|
||||
|
||||
@@ -10,5 +10,5 @@ require (
|
||||
go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect
|
||||
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef
|
||||
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257
|
||||
tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566
|
||||
)
|
||||
|
||||
@@ -30,3 +30,7 @@ tinygo.org/x/go-llvm v0.0.0-20191113125529-bad6d01809e8 h1:9Bfvso+tTVQg16UzOA614
|
||||
tinygo.org/x/go-llvm v0.0.0-20191113125529-bad6d01809e8/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257 h1:o8VDylrMN7gWemBMu8rEyuogKPhcLTdx5KrUAp9macc=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191215173731-ad71f3d24aae h1:s8J5EyxCkHxXB08UI3gk9W9IS/ekizRvSX+PfZxnAB0=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191215173731-ad71f3d24aae/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566 h1:a4y30bTf7U0zDA75v2PTL+XQ2OzJetj19gK8XwQpUNY=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
|
||||
+37
-10
@@ -278,24 +278,49 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
}
|
||||
case callee.Name() == "runtime.hashmapStringSet":
|
||||
// set a string key in the map
|
||||
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
||||
if !ok {
|
||||
return nil, nil, fr.errorAt(inst, "could not update map with string key")
|
||||
}
|
||||
// "key" is a Go string value, which in the TinyGo calling convention is split up
|
||||
// into separate pointer and length parameters.
|
||||
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
||||
keyLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
||||
valPtr := fr.getLocal(inst.Operand(3)).(*LocalValue)
|
||||
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
||||
if !ok || !keyBuf.IsConstant() || !keyLen.IsConstant() || !valPtr.IsConstant() {
|
||||
// The mapassign operation could not be done at compile
|
||||
// time. Do it at runtime instead.
|
||||
m := fr.getLocal(inst.Operand(0)).Value()
|
||||
fr.markDirty(m)
|
||||
llvmParams := []llvm.Value{
|
||||
m, // *runtime.hashmap
|
||||
fr.getLocal(inst.Operand(1)).Value(), // key.ptr
|
||||
fr.getLocal(inst.Operand(2)).Value(), // key.len
|
||||
fr.getLocal(inst.Operand(3)).Value(), // value (unsafe.Pointer)
|
||||
fr.getLocal(inst.Operand(4)).Value(), // context
|
||||
fr.getLocal(inst.Operand(5)).Value(), // parentHandle
|
||||
}
|
||||
fr.builder.CreateCall(callee, llvmParams, "")
|
||||
continue
|
||||
}
|
||||
// "key" is a Go string value, which in the TinyGo calling convention is split up
|
||||
// into separate pointer and length parameters.
|
||||
m.PutString(keyBuf, keyLen, valPtr)
|
||||
case callee.Name() == "runtime.hashmapBinarySet":
|
||||
// set a binary (int etc.) key in the map
|
||||
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
||||
if !ok {
|
||||
return nil, nil, fr.errorAt(inst, "could not update map")
|
||||
}
|
||||
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
||||
valPtr := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
||||
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
||||
if !ok || !keyBuf.IsConstant() || !valPtr.IsConstant() {
|
||||
// The mapassign operation could not be done at compile
|
||||
// time. Do it at runtime instead.
|
||||
m := fr.getLocal(inst.Operand(0)).Value()
|
||||
fr.markDirty(m)
|
||||
llvmParams := []llvm.Value{
|
||||
m, // *runtime.hashmap
|
||||
fr.getLocal(inst.Operand(1)).Value(), // key
|
||||
fr.getLocal(inst.Operand(2)).Value(), // value
|
||||
fr.getLocal(inst.Operand(3)).Value(), // context
|
||||
fr.getLocal(inst.Operand(4)).Value(), // parentHandle
|
||||
}
|
||||
fr.builder.CreateCall(callee, llvmParams, "")
|
||||
continue
|
||||
}
|
||||
m.PutBinary(keyBuf, valPtr)
|
||||
case callee.Name() == "runtime.stringConcat":
|
||||
// adding two strings together
|
||||
@@ -433,6 +458,8 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
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(), "llvm.lifetime."):
|
||||
// do nothing
|
||||
case callee.Name() == "runtime.trackPointer":
|
||||
// do nothing
|
||||
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
||||
|
||||
@@ -14,6 +14,7 @@ func TestInterp(t *testing.T) {
|
||||
"basic",
|
||||
"slice-copy",
|
||||
"consteval",
|
||||
"map",
|
||||
} {
|
||||
name := name // make tc local to this closure
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
||||
+13
-8
@@ -1,6 +1,8 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -39,22 +41,25 @@ type sideEffectResult struct {
|
||||
// returns whether this function has side effects and if it does, which globals
|
||||
// it mentions anywhere in this function or any called functions.
|
||||
func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) {
|
||||
switch fn.Name() {
|
||||
case "runtime.alloc":
|
||||
name := fn.Name()
|
||||
switch {
|
||||
case name == "runtime.alloc":
|
||||
// Cannot be scanned but can be interpreted.
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case "runtime.nanotime":
|
||||
case name == "runtime.nanotime":
|
||||
// Fixed value at compile time.
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case "runtime._panic":
|
||||
case name == "runtime._panic":
|
||||
return &sideEffectResult{severity: sideEffectLimited}, nil
|
||||
case "runtime.interfaceImplements":
|
||||
case name == "runtime.interfaceImplements":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case "runtime.sliceCopy":
|
||||
case name == "runtime.sliceCopy":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case "runtime.trackPointer":
|
||||
case name == "runtime.trackPointer":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case "llvm.dbg.value":
|
||||
case name == "llvm.dbg.value":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case strings.HasPrefix(name, "llvm.lifetime."):
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
}
|
||||
if fn.IsDeclaration() {
|
||||
|
||||
Vendored
+76
@@ -0,0 +1,76 @@
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv6m-none-eabi"
|
||||
|
||||
%runtime._string = type { i8*, i32 }
|
||||
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
|
||||
|
||||
@main.m = global %runtime.hashmap* null
|
||||
@main.binaryMap = global %runtime.hashmap* null
|
||||
@main.stringMap = global %runtime.hashmap* null
|
||||
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
|
||||
|
||||
declare %runtime.hashmap* @runtime.hashmapMake(i8, i8, i32, i8* %context, i8* %parentHandle)
|
||||
declare void @runtime.hashmapBinarySet(%runtime.hashmap*, i8*, i8*, i8* %context, i8* %parentHandle)
|
||||
declare void @runtime.hashmapStringSet(%runtime.hashmap*, i8*, i32, i8*, i8* %context, i8* %parentHandle)
|
||||
declare void @llvm.lifetime.end.p0i8(i64, i8*)
|
||||
declare void @llvm.lifetime.start.p0i8(i64, i8*)
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @main.init(i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
; Test that hashmap optimizations generally work (even with lifetimes).
|
||||
%hashmap.key = alloca i8
|
||||
%hashmap.value = alloca %runtime._string
|
||||
%0 = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 8, i32 1, i8* undef, i8* null)
|
||||
%hashmap.value.bitcast = bitcast %runtime._string* %hashmap.value to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 8, i8* %hashmap.value.bitcast)
|
||||
store %runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string* %hashmap.value
|
||||
call void @llvm.lifetime.start.p0i8(i64 1, i8* %hashmap.key)
|
||||
store i8 1, i8* %hashmap.key
|
||||
call void @runtime.hashmapBinarySet(%runtime.hashmap* %0, i8* %hashmap.key, i8* %hashmap.value.bitcast, i8* undef, i8* null)
|
||||
call void @llvm.lifetime.end.p0i8(i64 1, i8* %hashmap.key)
|
||||
call void @llvm.lifetime.end.p0i8(i64 8, i8* %hashmap.value.bitcast)
|
||||
store %runtime.hashmap* %0, %runtime.hashmap** @main.m
|
||||
|
||||
; Other tests, that can be done in a separate function.
|
||||
call void @main.testNonConstantBinarySet()
|
||||
call void @main.testNonConstantStringSet()
|
||||
ret void
|
||||
}
|
||||
|
||||
; Test that a map loaded from a global can still be used for mapassign
|
||||
; operations (with binary keys).
|
||||
define internal void @main.testNonConstantBinarySet() {
|
||||
%hashmap.key = alloca i8
|
||||
%hashmap.value = alloca i8
|
||||
; Create hashmap from global. This breaks the normal hashmapBinarySet
|
||||
; optimization, to test the fallback.
|
||||
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 1, i32 1, i8* undef, i8* null)
|
||||
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.binaryMap
|
||||
%map = load %runtime.hashmap*, %runtime.hashmap** @main.binaryMap
|
||||
; Do the binary set to the newly loaded map.
|
||||
store i8 1, i8* %hashmap.key
|
||||
store i8 2, i8* %hashmap.value
|
||||
call void @runtime.hashmapBinarySet(%runtime.hashmap* %map, i8* %hashmap.key, i8* %hashmap.value, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Test that a map loaded from a global can still be used for mapassign
|
||||
; operations (with string keys).
|
||||
define internal void @main.testNonConstantStringSet() {
|
||||
%hashmap.value = alloca i8
|
||||
; Create hashmap from global. This breaks the normal hashmapStringSet
|
||||
; optimization, to test the fallback.
|
||||
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 8, i8 1, i32 1, i8* undef, i8* null)
|
||||
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.stringMap
|
||||
%map = load %runtime.hashmap*, %runtime.hashmap** @main.stringMap
|
||||
; Do the string set to the newly loaded map.
|
||||
store i8 2, i8* %hashmap.value
|
||||
call void @runtime.hashmapStringSet(%runtime.hashmap* %map, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7, i8* %hashmap.value, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv6m-none-eabi"
|
||||
|
||||
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
|
||||
%runtime._string = type { i8*, i32 }
|
||||
|
||||
@main.m = local_unnamed_addr global %runtime.hashmap* @"main$map"
|
||||
@main.binaryMap = local_unnamed_addr global %runtime.hashmap* @"main$map.4"
|
||||
@main.stringMap = local_unnamed_addr global %runtime.hashmap* @"main$map.6"
|
||||
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
|
||||
@"main$mapbucket" = internal unnamed_addr global { [8 x i8], i8*, [8 x i8], [8 x %runtime._string] } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, [8 x i8] c"\01\00\00\00\00\00\00\00", [8 x %runtime._string] [%runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer] }
|
||||
@"main$map" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, [8 x i8], [8 x %runtime._string] }, { [8 x i8], i8*, [8 x i8], [8 x %runtime._string] }* @"main$mapbucket", i32 0, i32 0, i32 0), i32 1, i8 1, i8 8, i8 0 }
|
||||
@"main$alloca.2" = internal global i8 1
|
||||
@"main$alloca.3" = internal global i8 2
|
||||
@"main$map.4" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* null, i32 0, i8 1, i8 1, i8 0 }
|
||||
@"main$alloca.5" = internal global i8 2
|
||||
@"main$map.6" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* null, i32 0, i8 8, i8 1, i8 0 }
|
||||
|
||||
declare void @runtime.hashmapBinarySet(%runtime.hashmap*, i8*, i8*, i8*, i8*) local_unnamed_addr
|
||||
|
||||
declare void @runtime.hashmapStringSet(%runtime.hashmap*, i8*, i32, i8*, i8*, i8*) local_unnamed_addr
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @runtime.hashmapBinarySet(%runtime.hashmap* @"main$map.4", i8* @"main$alloca.2", i8* @"main$alloca.3", i8* undef, i8* null)
|
||||
call void @runtime.hashmapStringSet(%runtime.hashmap* @"main$map.6", i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7, i8* @"main$alloca.5", i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
+6
-4
@@ -367,10 +367,12 @@ func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
|
||||
}
|
||||
}
|
||||
|
||||
if keyPtr.Underlying.Opcode() == llvm.BitCast {
|
||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||
} else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr {
|
||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||
if !keyPtr.Underlying.IsAConstantExpr().IsNil() {
|
||||
if keyPtr.Underlying.Opcode() == llvm.BitCast {
|
||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||
} else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr {
|
||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||
}
|
||||
}
|
||||
key := keyPtr.Load()
|
||||
if v.KeyType.IsNil() {
|
||||
|
||||
@@ -27,14 +27,13 @@ type Program struct {
|
||||
// Function or method.
|
||||
type Function struct {
|
||||
*ssa.Function
|
||||
LLVMFn llvm.Value
|
||||
module string // go:wasm-module
|
||||
linkName string // go:linkname, go:export, go:interrupt
|
||||
exported bool // go:export
|
||||
nobounds bool // go:nobounds
|
||||
flag bool // used by dead code elimination
|
||||
interrupt bool // go:interrupt
|
||||
inline InlineType // go:inline
|
||||
LLVMFn llvm.Value
|
||||
module string // go:wasm-module
|
||||
linkName string // go:linkname, go:export
|
||||
exported bool // go:export
|
||||
nobounds bool // go:nobounds
|
||||
flag bool // used by dead code elimination
|
||||
inline InlineType // go:inline
|
||||
}
|
||||
|
||||
// Interface type that is at some point used in a type assert (to check whether
|
||||
@@ -243,18 +242,6 @@ func (f *Function) parsePragmas() {
|
||||
f.inline = InlineHint
|
||||
case "//go:noinline":
|
||||
f.inline = InlineNone
|
||||
case "//go:interrupt":
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
name := parts[1]
|
||||
if strings.HasSuffix(name, "_vect") {
|
||||
// AVR vector naming
|
||||
name = "__vector_" + name[:len(name)-5]
|
||||
}
|
||||
f.linkName = name
|
||||
f.exported = true
|
||||
f.interrupt = true
|
||||
case "//go:linkname":
|
||||
if len(parts) != 3 || parts[1] != f.Name() {
|
||||
continue
|
||||
@@ -288,14 +275,6 @@ func (f *Function) IsExported() bool {
|
||||
return f.exported || f.CName() != ""
|
||||
}
|
||||
|
||||
// Return true for functions annotated with //go:interrupt. The function name is
|
||||
// already customized in LinkName() to hook up in the interrupt vector.
|
||||
//
|
||||
// On some platforms (like AVR), interrupts need a special compiler flag.
|
||||
func (f *Function) IsInterrupt() bool {
|
||||
return f.interrupt
|
||||
}
|
||||
|
||||
// Return the inline directive of this function.
|
||||
func (f *Function) Inline() InlineType {
|
||||
return f.inline
|
||||
|
||||
+14
-3
@@ -6,6 +6,7 @@ import (
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
@@ -46,7 +47,7 @@ type Package struct {
|
||||
|
||||
// Import loads the given package relative to srcDir (for the vendor directory).
|
||||
// It only loads the current package without recursion.
|
||||
func (p *Program) Import(path, srcDir string) (*Package, error) {
|
||||
func (p *Program) Import(path, srcDir string, pos token.Position) (*Package, error) {
|
||||
if p.Packages == nil {
|
||||
p.Packages = make(map[string]*Package)
|
||||
}
|
||||
@@ -59,7 +60,10 @@ func (p *Program) Import(path, srcDir string) (*Package, error) {
|
||||
}
|
||||
buildPkg, err := ctx.Import(path, srcDir, build.ImportComment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, scanner.Error{
|
||||
Pos: pos,
|
||||
Msg: err.Error(), // TODO: define a new error type that will wrap the inner error
|
||||
}
|
||||
}
|
||||
if existingPkg, ok := p.Packages[buildPkg.ImportPath]; ok {
|
||||
// Already imported, or at least started the import.
|
||||
@@ -467,7 +471,14 @@ func (p *Package) importRecursively(includeTests bool) error {
|
||||
if _, ok := p.Imports[to]; ok {
|
||||
continue
|
||||
}
|
||||
importedPkg, err := p.Program.Import(to, p.Package.Dir)
|
||||
// Find error location.
|
||||
var pos token.Position
|
||||
if len(p.Package.ImportPos[to]) > 0 {
|
||||
pos = p.Package.ImportPos[to][0]
|
||||
} else {
|
||||
pos = token.Position{Filename: p.Package.ImportPath}
|
||||
}
|
||||
importedPkg, err := p.Program.Import(to, p.Package.Dir, pos)
|
||||
if err != nil {
|
||||
if err, ok := err.(*ImportCycleError); ok {
|
||||
err.Packages = append([]string{p.ImportPath}, err.Packages...)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
@@ -144,6 +145,14 @@ func Test(pkgName string, options *compileopts.Options) error {
|
||||
|
||||
// Flash builds and flashes the built binary to the given serial port.
|
||||
func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
if port == "" {
|
||||
var err error
|
||||
port, err = getDefaultPort()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -201,7 +210,18 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
flashCmd = strings.Replace(flashCmd, "{port}", port, -1)
|
||||
|
||||
// Execute the command.
|
||||
cmd := exec.Command("/bin/sh", "-c", flashCmd)
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
command := strings.Split(flashCmd, " ")
|
||||
if len(command) < 2 {
|
||||
return errors.New("invalid flash command")
|
||||
}
|
||||
cmd = exec.Command(command[0], command[1:]...)
|
||||
default:
|
||||
cmd = exec.Command("/bin/sh", "-c", flashCmd)
|
||||
}
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = goenv.Get("TINYGOROOT")
|
||||
@@ -254,7 +274,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
//
|
||||
// Note: this command is expected to execute just before exiting, as it
|
||||
// modifies global state.
|
||||
func FlashGDB(pkgName, port string, ocdOutput bool, options *compileopts.Options) error {
|
||||
func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) error {
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -268,12 +288,13 @@ func FlashGDB(pkgName, port string, ocdOutput bool, options *compileopts.Options
|
||||
gdbInterface, openocdInterface := config.Programmer()
|
||||
switch gdbInterface {
|
||||
case "msd", "command", "":
|
||||
if gdbInterface == "" {
|
||||
gdbInterface = "command"
|
||||
}
|
||||
if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
|
||||
gdbInterface = "openocd"
|
||||
}
|
||||
if len(config.Target.Emulator) != 0 {
|
||||
// Assume QEMU as an emulator.
|
||||
gdbInterface = "qemu"
|
||||
}
|
||||
}
|
||||
|
||||
// Run the GDB server, if necessary.
|
||||
@@ -303,6 +324,26 @@ func FlashGDB(pkgName, port string, ocdOutput bool, options *compileopts.Options
|
||||
// Make sure the daemon doesn't receive Ctrl-C that is intended for
|
||||
// GDB (to break the currently executing program).
|
||||
setCommandAsDaemon(daemon)
|
||||
// Start now, and kill it on exit.
|
||||
daemon.Start()
|
||||
defer func() {
|
||||
daemon.Process.Signal(os.Interrupt)
|
||||
// Maybe we should send a .Kill() after x seconds?
|
||||
daemon.Wait()
|
||||
}()
|
||||
case "qemu":
|
||||
gdbCommands = append(gdbCommands, "target remote :1234")
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], tmppath, "-s", "-S")
|
||||
daemon := exec.Command(config.Target.Emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
|
||||
// Make sure the daemon doesn't receive Ctrl-C that is intended for
|
||||
// GDB (to break the currently executing program).
|
||||
setCommandAsDaemon(daemon)
|
||||
|
||||
// Start now, and kill it on exit.
|
||||
daemon.Start()
|
||||
defer func() {
|
||||
@@ -401,9 +442,18 @@ func touchSerialPortAt1200bps(port string) error {
|
||||
|
||||
func flashUF2UsingMSD(volume, tmppath string) error {
|
||||
// find standard UF2 info path
|
||||
infoPath := "/media/*/" + volume + "/INFO_UF2.TXT"
|
||||
if runtime.GOOS == "darwin" {
|
||||
var infoPath string
|
||||
switch runtime.GOOS {
|
||||
case "linux", "freebsd":
|
||||
infoPath = "/media/*/" + volume + "/INFO_UF2.TXT"
|
||||
case "darwin":
|
||||
infoPath = "/Volumes/" + volume + "/INFO_UF2.TXT"
|
||||
case "windows":
|
||||
path, err := windowsFindUSBDrive(volume)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
infoPath = path + "/INFO_UF2.TXT"
|
||||
}
|
||||
|
||||
d, err := filepath.Glob(infoPath)
|
||||
@@ -419,9 +469,18 @@ func flashUF2UsingMSD(volume, tmppath string) error {
|
||||
|
||||
func flashHexUsingMSD(volume, tmppath string) error {
|
||||
// find expected volume path
|
||||
destPath := "/media/*/" + volume
|
||||
if runtime.GOOS == "darwin" {
|
||||
var destPath string
|
||||
switch runtime.GOOS {
|
||||
case "linux", "freebsd":
|
||||
destPath = "/media/*/" + volume
|
||||
case "darwin":
|
||||
destPath = "/Volumes/" + volume
|
||||
case "windows":
|
||||
path, err := windowsFindUSBDrive(volume)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
destPath = path + "/"
|
||||
}
|
||||
|
||||
d, err := filepath.Glob(destPath)
|
||||
@@ -435,6 +494,29 @@ func flashHexUsingMSD(volume, tmppath string) error {
|
||||
return moveFile(tmppath, d[0]+"/flash.hex")
|
||||
}
|
||||
|
||||
func windowsFindUSBDrive(volume string) (string, error) {
|
||||
cmd := exec.Command("wmic",
|
||||
"PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'",
|
||||
"get", "DeviceID,VolumeName,FileSystem,DriveType")
|
||||
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(out.String(), "\n") {
|
||||
words := strings.Fields(line)
|
||||
if len(words) >= 3 {
|
||||
if words[1] == "2" && words[2] == "FAT" {
|
||||
return words[0], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.New("unable to locate a USB device to be flashed")
|
||||
}
|
||||
|
||||
// parseSize converts a human-readable size (with k/m/g suffix) into a plain
|
||||
// number.
|
||||
func parseSize(s string) (int64, error) {
|
||||
@@ -459,6 +541,56 @@ func parseSize(s string) (int64, error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// getDefaultPort returns the default serial port depending on the operating system.
|
||||
// Currently only supports macOS and Linux.
|
||||
func getDefaultPort() (port string, err error) {
|
||||
var portPath string
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
portPath = "/dev/cu.usb*"
|
||||
case "linux":
|
||||
portPath = "/dev/ttyACM*"
|
||||
case "freebsd":
|
||||
portPath = "/dev/cuaU*"
|
||||
case "windows":
|
||||
cmd := exec.Command("wmic",
|
||||
"PATH", "Win32_SerialPort", "WHERE", "Caption LIKE 'USB Serial%'", "GET", "DeviceID")
|
||||
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if out.String() == "No Instance(s) Available." {
|
||||
return "", errors.New("unable to locate a USB device to be flashed")
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(out.String(), "\n") {
|
||||
words := strings.Fields(line)
|
||||
if len(words) == 1 {
|
||||
if strings.Contains(words[0], "COM") {
|
||||
return words[0], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.New("unable to locate a USB device to be flashed")
|
||||
default:
|
||||
return "", errors.New("unable to search for a default USB device to be flashed on this OS")
|
||||
}
|
||||
|
||||
d, err := filepath.Glob(portPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if d == nil {
|
||||
return "", errors.New("unable to locate a USB device to be flashed")
|
||||
}
|
||||
|
||||
return d[0], nil
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
||||
fmt.Fprintln(os.Stderr, "version:", version)
|
||||
@@ -526,7 +658,7 @@ func main() {
|
||||
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||
port := flag.String("port", "/dev/ttyACM0", "flash port")
|
||||
port := flag.String("port", "", "flash port")
|
||||
programmer := flag.String("programmer", "", "which hardware programmer to use")
|
||||
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
||||
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
||||
@@ -629,7 +761,7 @@ func main() {
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
err := FlashGDB(flag.Arg(0), *port, *ocdOutput, options)
|
||||
err := FlashGDB(flag.Arg(0), *ocdOutput, options)
|
||||
handleCompilerError(err)
|
||||
}
|
||||
case "run":
|
||||
|
||||
+49
-11
@@ -12,7 +12,9 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
@@ -50,7 +52,7 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("EmulatedCortexM3", func(t *testing.T) {
|
||||
runPlatTests("qemu", matches, t)
|
||||
runPlatTests("cortex-m-qemu", matches, t)
|
||||
})
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
@@ -67,6 +69,8 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
|
||||
func runPlatTests(target string, matches []string, t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, path := range matches {
|
||||
switch {
|
||||
case target == "wasm":
|
||||
@@ -76,7 +80,7 @@ func runPlatTests(target string, matches []string, t *testing.T) {
|
||||
}
|
||||
case target == "":
|
||||
// run all tests on host
|
||||
case target == "qemu":
|
||||
case target == "cortex-m-qemu":
|
||||
// all tests are supported
|
||||
default:
|
||||
// cross-compilation of cgo is not yet supported
|
||||
@@ -86,22 +90,32 @@ func runPlatTests(target string, matches []string, t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run(filepath.Base(path), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
runTest(path, target, t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Due to some problems with LLD, we cannot run links in parallel, or in parallel with compiles.
|
||||
// Therefore, we put a lock around builds and run everything else in parallel.
|
||||
var buildLock sync.Mutex
|
||||
|
||||
// runBuild is a thread-safe wrapper around Build.
|
||||
func runBuild(src, out string, opts *compileopts.Options) error {
|
||||
buildLock.Lock()
|
||||
defer buildLock.Unlock()
|
||||
|
||||
return Build(src, out, opts)
|
||||
}
|
||||
|
||||
func runTest(path, target string, t *testing.T) {
|
||||
// Get the expected output for this test.
|
||||
txtpath := path[:len(path)-3] + ".txt"
|
||||
if path[len(path)-1] == os.PathSeparator {
|
||||
txtpath = path + "out.txt"
|
||||
}
|
||||
f, err := os.Open(txtpath)
|
||||
if err != nil {
|
||||
t.Fatal("could not open expected output file:", err)
|
||||
}
|
||||
expected, err := ioutil.ReadAll(f)
|
||||
expected, err := ioutil.ReadFile(txtpath)
|
||||
if err != nil {
|
||||
t.Fatal("could not read expected output file:", err)
|
||||
}
|
||||
@@ -130,7 +144,7 @@ func runTest(path, target string, t *testing.T) {
|
||||
WasmAbi: "js",
|
||||
}
|
||||
binary := filepath.Join(tmpdir, "test")
|
||||
err = Build("./"+path, binary, config)
|
||||
err = runBuild("./"+path, binary, config)
|
||||
if err != nil {
|
||||
if errLoader, ok := err.(loader.Errors); ok {
|
||||
for _, err := range errLoader.Errs {
|
||||
@@ -144,7 +158,9 @@ func runTest(path, target string, t *testing.T) {
|
||||
}
|
||||
|
||||
// Run the test.
|
||||
runComplete := make(chan struct{})
|
||||
var cmd *exec.Cmd
|
||||
ranTooLong := false
|
||||
if target == "" {
|
||||
cmd = exec.Command(binary)
|
||||
} else {
|
||||
@@ -160,13 +176,35 @@ func runTest(path, target string, t *testing.T) {
|
||||
}
|
||||
stdout := &bytes.Buffer{}
|
||||
cmd.Stdout = stdout
|
||||
if target != "" {
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
t.Fatal("failed to start:", err)
|
||||
}
|
||||
err = cmd.Run()
|
||||
go func() {
|
||||
// Terminate the process if it runs too long.
|
||||
timer := time.NewTimer(1 * time.Second)
|
||||
select {
|
||||
case <-runComplete:
|
||||
timer.Stop()
|
||||
case <-timer.C:
|
||||
ranTooLong = true
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd.Process.Signal(os.Kill) // Windows doesn't support SIGINT.
|
||||
} else {
|
||||
cmd.Process.Signal(os.Interrupt)
|
||||
}
|
||||
}
|
||||
}()
|
||||
err = cmd.Wait()
|
||||
if _, ok := err.(*exec.ExitError); ok && target != "" {
|
||||
err = nil // workaround for QEMU
|
||||
}
|
||||
close(runComplete)
|
||||
|
||||
if ranTooLong {
|
||||
stdout.WriteString("--- test ran too long, terminating...\n")
|
||||
}
|
||||
|
||||
// putchar() prints CRLF, convert it to LF.
|
||||
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
.type _start,@function
|
||||
|
||||
_start:
|
||||
// Workaround for missing support of the la pseudo-instruction in Clang 8:
|
||||
// https://reviews.llvm.org/D55325
|
||||
lui sp, %hi(_stack_top)
|
||||
addi sp, sp, %lo(_stack_top)
|
||||
// see https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register
|
||||
lui gp, %hi(__global_pointer$)
|
||||
addi gp, gp, %lo(__global_pointer$)
|
||||
// Load the stack pointer.
|
||||
la sp, _stack_top
|
||||
// Load the globals pointer. The program will load pointers relative to this
|
||||
// register, so it must be set to the right value on startup.
|
||||
// See: https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register
|
||||
la gp, __global_pointer$
|
||||
call main
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// STM32F103XX bitfield definitions that are not auto-generated by gen-device-svd.py
|
||||
// STM32F103XX bitfield definitions that are not auto-generated by gen-device-svd.go
|
||||
|
||||
// +build stm32,stm32f103xx
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ func main() {
|
||||
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
// timer fires 10 times per second
|
||||
arm.SetupSystemTimer(machine.CPU_FREQUENCY / 10)
|
||||
arm.SetupSystemTimer(machine.CPUFrequency() / 10)
|
||||
|
||||
for {
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
const CPU_FREQUENCY = 16000000
|
||||
// Return the current CPU frequency in hertz.
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
// LED on the Arduino
|
||||
const LED Pin = 13
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build sam,atsamd21,arduino_nano33
|
||||
// +build arduino_nano33
|
||||
|
||||
// This contains the pin mappings for the Arduino Nano33 IoT board.
|
||||
//
|
||||
@@ -6,8 +6,6 @@
|
||||
//
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0x07738135
|
||||
|
||||
@@ -54,69 +52,23 @@ const (
|
||||
USBCDC_DP_PIN Pin = PA25
|
||||
)
|
||||
|
||||
// UART1 on the Arduino Nano 33 connects to the onboard NINA-W102 WiFi chip.
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM5_USART,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN Pin = PA22
|
||||
UART_RX_PIN Pin = PA23
|
||||
)
|
||||
|
||||
//go:export SERCOM5_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
}
|
||||
|
||||
// UART2 on the Arduino Nano 33 connects to the normal TX/RX pins.
|
||||
var (
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM3_USART,
|
||||
SERCOM: 3,
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM3_IRQHandler
|
||||
func handleUART2() {
|
||||
// should reset IRQ
|
||||
UART2.Receive(byte((UART2.Bus.DATA.Get() & 0xFF)))
|
||||
UART2.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN Pin = A4 // SDA: SERCOM4/PAD[1]
|
||||
SCL_PIN Pin = A5 // SCL: SERCOM4/PAD[1]
|
||||
)
|
||||
|
||||
// I2C on the Arduino Nano 33.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM4_I2CM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = A2 // SCK: SERCOM0/PAD[3]
|
||||
SPI0_MOSI_PIN Pin = A3 // MOSI: SERCOM0/PAD[2]
|
||||
SPI0_MISO_PIN Pin = A6 // MISO: SERCOM0/PAD[1]
|
||||
)
|
||||
|
||||
// SPI on the Arduino Nano 33.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM0_SPI,
|
||||
SERCOM: 0,
|
||||
}
|
||||
SPI0_SCK_PIN Pin = D13 // SCK: SERCOM1/PAD[1]
|
||||
SPI0_MOSI_PIN Pin = D11 // MOSI: SERCOM1/PAD[0]
|
||||
SPI0_MISO_PIN Pin = D12 // MISO: SERCOM1/PAD[3]
|
||||
)
|
||||
|
||||
// NINA-W102 Pins
|
||||
@@ -132,23 +84,9 @@ const (
|
||||
NINA_RX Pin = PA23
|
||||
)
|
||||
|
||||
// SPI1 is connected to the NINA-W102 chip on the Arduino Nano 33.
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM2_SPI,
|
||||
SERCOM: 2,
|
||||
}
|
||||
NINA_SPI = SPI1
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN Pin = PA10
|
||||
I2S_SD_PIN Pin = PA08
|
||||
I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino Nano 33.
|
||||
)
|
||||
|
||||
// I2S on the Arduino Nano 33.
|
||||
var (
|
||||
I2S0 = I2S{Bus: sam.I2S}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// +build sam,atsamd21,arduino_nano33
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// UART1 on the Arduino Nano 33 connects to the onboard NINA-W102 WiFi chip.
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM3_USART,
|
||||
SERCOM: 3,
|
||||
}
|
||||
)
|
||||
|
||||
// UART2 on the Arduino Nano 33 connects to the normal TX/RX pins.
|
||||
var (
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM5_USART,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Work around circular definitions.
|
||||
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM3, UART1.handleInterrupt)
|
||||
UART2.interrupt = interrupt.New(sam.IRQ_SERCOM5, UART2.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the Arduino Nano 33.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM4_I2CM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the Arduino Nano 33.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPI,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI1 is connected to the NINA-W102 chip on the Arduino Nano 33.
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM2_SPI,
|
||||
SERCOM: 2,
|
||||
}
|
||||
NINA_SPI = SPI1
|
||||
)
|
||||
|
||||
// I2S on the Arduino Nano 33.
|
||||
var (
|
||||
I2S0 = I2S{Bus: sam.I2S}
|
||||
)
|
||||
@@ -0,0 +1,76 @@
|
||||
// +build sam,atsamd21 arduino_nano33 circuitplay_express
|
||||
|
||||
package machine
|
||||
|
||||
// Return the current CPU frequency in hertz.
|
||||
func CPUFrequency() uint32 {
|
||||
return 48000000
|
||||
}
|
||||
|
||||
// Hardware pins
|
||||
const (
|
||||
PA00 Pin = 0
|
||||
PA01 Pin = 1
|
||||
PA02 Pin = 2
|
||||
PA03 Pin = 3
|
||||
PA04 Pin = 4
|
||||
PA05 Pin = 5
|
||||
PA06 Pin = 6
|
||||
PA07 Pin = 7
|
||||
PA08 Pin = 8
|
||||
PA09 Pin = 9
|
||||
PA10 Pin = 10
|
||||
PA11 Pin = 11
|
||||
PA12 Pin = 12
|
||||
PA13 Pin = 13
|
||||
PA14 Pin = 14
|
||||
PA15 Pin = 15
|
||||
PA16 Pin = 16
|
||||
PA17 Pin = 17
|
||||
PA18 Pin = 18
|
||||
PA19 Pin = 19
|
||||
PA20 Pin = 20
|
||||
PA21 Pin = 21
|
||||
PA22 Pin = 22
|
||||
PA23 Pin = 23
|
||||
PA24 Pin = 24
|
||||
PA25 Pin = 25
|
||||
PA26 Pin = 26
|
||||
PA27 Pin = 27
|
||||
PA28 Pin = 28
|
||||
PA29 Pin = 29
|
||||
PA30 Pin = 30
|
||||
PA31 Pin = 31
|
||||
PB00 Pin = 32
|
||||
PB01 Pin = 33
|
||||
PB02 Pin = 34
|
||||
PB03 Pin = 35
|
||||
PB04 Pin = 36
|
||||
PB05 Pin = 37
|
||||
PB06 Pin = 38
|
||||
PB07 Pin = 39
|
||||
PB08 Pin = 40
|
||||
PB09 Pin = 41
|
||||
PB10 Pin = 42
|
||||
PB11 Pin = 43
|
||||
PB12 Pin = 44
|
||||
PB13 Pin = 45
|
||||
PB14 Pin = 46
|
||||
PB15 Pin = 47
|
||||
PB16 Pin = 48
|
||||
PB17 Pin = 49
|
||||
PB18 Pin = 50
|
||||
PB19 Pin = 51
|
||||
PB20 Pin = 52
|
||||
PB21 Pin = 53
|
||||
PB22 Pin = 54
|
||||
PB23 Pin = 55
|
||||
PB24 Pin = 56
|
||||
PB25 Pin = 57
|
||||
PB26 Pin = 58
|
||||
PB27 Pin = 59
|
||||
PB28 Pin = 60
|
||||
PB29 Pin = 61
|
||||
PB30 Pin = 62
|
||||
PB31 Pin = 63
|
||||
)
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/stm32"
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// https://wiki.stm32duino.com/index.php?title=File:Bluepillpinout.gif
|
||||
const (
|
||||
@@ -61,14 +64,12 @@ var (
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART1,
|
||||
IRQVal: stm32.IRQ_USART1,
|
||||
}
|
||||
UART1 = &UART0
|
||||
)
|
||||
|
||||
//go:export USART1_IRQHandler
|
||||
func handleUART1() {
|
||||
UART1.Receive(byte((UART1.Bus.DR.Get() & 0xFF)))
|
||||
func init() {
|
||||
UART0.interrupt = interrupt.New(stm32.IRQ_USART1, UART0.handleInterrupt)
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
// +build sam,atsamd21,circuitplay_express
|
||||
// +build circuitplay_express
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
@@ -68,20 +66,6 @@ const (
|
||||
UART_RX_PIN = PB09 // PORTB
|
||||
)
|
||||
|
||||
// UART1 on the Circuit Playground Express.
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM4_USART,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PB02 // I2C0 external
|
||||
@@ -91,20 +75,6 @@ const (
|
||||
SCL1_PIN = PA01 // I2C1 internal
|
||||
)
|
||||
|
||||
// I2C on the Circuit Playground Express.
|
||||
var (
|
||||
// external device
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
// internal device
|
||||
I2C1 = I2C{
|
||||
Bus: sam.SERCOM1_I2CM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins (internal flash)
|
||||
const (
|
||||
SPI0_SCK_PIN = PA21 // SCK: SERCOM3/PAD[3]
|
||||
@@ -112,22 +82,9 @@ const (
|
||||
SPI0_MISO_PIN = PA16 // MISO: SERCOM3/PAD[0]
|
||||
)
|
||||
|
||||
// SPI on the Circuit Playground Express.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM3_SPI,
|
||||
SERCOM: 3,
|
||||
}
|
||||
)
|
||||
|
||||
// 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}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// +build sam,atsamd21,circuitplay_express
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// UART1 on the Circuit Playground Express.
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM4_USART,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM4, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the Circuit Playground Express.
|
||||
var (
|
||||
// external device
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
// internal device
|
||||
I2C1 = I2C{
|
||||
Bus: sam.SERCOM1_I2CM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the Circuit Playground Express.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM3_SPI,
|
||||
SERCOM: 3,
|
||||
}
|
||||
)
|
||||
|
||||
// I2S on the Circuit Playground Express.
|
||||
var (
|
||||
I2S0 = I2S{Bus: sam.I2S}
|
||||
)
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
@@ -60,9 +63,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
func init() {
|
||||
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -4,6 +4,7 @@ package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
@@ -62,9 +63,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
func init() {
|
||||
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/stm32"
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const (
|
||||
PA0 = portA + 0
|
||||
@@ -100,14 +103,12 @@ var (
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART2,
|
||||
IRQVal: stm32.IRQ_USART2,
|
||||
}
|
||||
UART2 = &UART0
|
||||
)
|
||||
|
||||
//go:export USART2_IRQHandler
|
||||
func handleUART2() {
|
||||
UART2.Receive(byte((UART2.Bus.DR.Get() & 0xFF)))
|
||||
func init() {
|
||||
UART0.interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
// +build sam,atsamd51,pybadge
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = PB17 // UART0 RX/PWM available
|
||||
D1 = PB16 // UART0 TX/PWM available
|
||||
D2 = PB03
|
||||
D3 = PB02
|
||||
D4 = PA14 // PWM available
|
||||
D5 = PA16 // PWM available
|
||||
D6 = PA18 // PWM available
|
||||
D7 = PB14
|
||||
D8 = PA15 // built-in neopixel
|
||||
D9 = PA19 // PWM available
|
||||
D10 = PA20 // can be used for PWM or UART1 TX
|
||||
D11 = PA21 // can be used for PWM or UART1 RX
|
||||
D12 = PA22 // PWM available
|
||||
D13 = PA23 // PWM available
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = PA02 // ADC/AIN[0]
|
||||
A1 = PA05 // ADC/AIN[2]
|
||||
A2 = PB08 // ADC/AIN[3]
|
||||
A3 = PB09 // ADC/AIN[4]
|
||||
A4 = PA04 // ADC/AIN[5]
|
||||
A5 = PA06 // ADC/AIN[6]
|
||||
A6 = PB01 // ADC/AIN[12]/VMEAS
|
||||
A7 = PB04 // ADC/AIN[6]/LIGHT
|
||||
A8 = D2 // ADC/AIN[14]
|
||||
A9 = D3 // ADC/AIN[15]
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
NEOPIXELS = D8
|
||||
|
||||
LIGHTSENSOR = A7
|
||||
|
||||
BUTTON_LATCH = PB00
|
||||
BUTTON_OUT = PB30
|
||||
BUTTON_CLK = PB31
|
||||
|
||||
TFT_DC = PB05
|
||||
TFT_CS = PB07
|
||||
TFT_RST = PA00
|
||||
TFT_LITE = PA01
|
||||
|
||||
SPEAKER_ENABLE = PA27
|
||||
|
||||
QSPI_SCK = PB10
|
||||
QSPI_CS = PB11
|
||||
QSPI_DATA_1 = PA08
|
||||
QSPI_DATA_2 = PA09
|
||||
QSPI_DATA_3 = PA10
|
||||
QSPI_DATA_4 = PA11
|
||||
)
|
||||
|
||||
const (
|
||||
BUTTON_LEFT_MASK = 1
|
||||
BUTTON_UP_MASK = 2
|
||||
BUTTON_DOWN_MASK = 4
|
||||
BUTTON_RIGHT_MASK = 8
|
||||
BUTTON_SELECT_MASK = 16
|
||||
BUTTON_START_MASK = 32
|
||||
BUTTON_A_MASK = 64
|
||||
BUTTON_B_MASK = 128
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN = D1
|
||||
UART_RX_PIN = D0
|
||||
)
|
||||
|
||||
// UART1 var is on SERCOM3, defined in atsamd51.go
|
||||
|
||||
// UART2 pins
|
||||
const (
|
||||
UART2_TX_PIN = A4
|
||||
UART2_RX_PIN = A5
|
||||
)
|
||||
|
||||
// UART2 var is on SERCOM0, defined in atsamd51.go
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PA12 // SDA: SERCOM2/PAD[0]
|
||||
SCL_PIN = PA13 // SCL: SERCOM2/PAD[1]
|
||||
)
|
||||
|
||||
// I2C on the ItsyBitsy M4.
|
||||
var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM2_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOM}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = PA17 // SCK: SERCOM1/PAD[1]
|
||||
SPI0_MOSI_PIN = PB23 // MOSI: SERCOM1/PAD[3]
|
||||
SPI0_MISO_PIN = PB22 // MISO: SERCOM1/PAD[2]
|
||||
)
|
||||
|
||||
// SPI on the PyBadge.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM1_SPIM,
|
||||
SCK: SPI0_SCK_PIN,
|
||||
MOSI: SPI0_MOSI_PIN,
|
||||
MISO: SPI0_MISO_PIN,
|
||||
DOpad: spiTXPad3SCK1,
|
||||
DIpad: sercomRXPad2,
|
||||
MISOPinMode: PinSERCOM,
|
||||
MOSIPinMode: PinSERCOM,
|
||||
SCKPinMode: PinSERCOM,
|
||||
}
|
||||
)
|
||||
|
||||
// TFT SPI pins
|
||||
const (
|
||||
SPI1_SCK_PIN = PB13 // SCK: SERCOM4/PAD[1]
|
||||
SPI1_MOSI_PIN = PB15 // MOSI: SERCOM4/PAD[3]
|
||||
)
|
||||
|
||||
// TFT SPI on the PyBadge.
|
||||
var (
|
||||
SPI1 = SPI{Bus: sam.SERCOM4_SPIM,
|
||||
SCK: SPI1_SCK_PIN,
|
||||
MOSI: SPI1_MOSI_PIN,
|
||||
DOpad: spiTXPad3SCK1,
|
||||
MOSIPinMode: PinSERCOM,
|
||||
SCKPinMode: PinSERCOM,
|
||||
}
|
||||
)
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
@@ -51,9 +54,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
func init() {
|
||||
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM0, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
|
||||
@@ -4,6 +4,7 @@ package machine
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
@@ -140,7 +141,7 @@ func (i2c I2C) Configure(config I2CConfig) {
|
||||
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
||||
// NOTE: TWBR should be 10 or higher for master mode.
|
||||
// It is 72 for a 16mhz board with 100kHz TWI
|
||||
avr.TWBR.Set(uint8(((CPU_FREQUENCY / config.Frequency) - 16) / 2))
|
||||
avr.TWBR.Set(uint8(((CPUFrequency() / config.Frequency) - 16) / 2))
|
||||
|
||||
// Enable twi module.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN)
|
||||
@@ -232,10 +233,22 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
config.BaudRate = 9600
|
||||
}
|
||||
|
||||
// Register the UART interrupt.
|
||||
interrupt.New(avr.IRQ_USART_RX, func(intr interrupt.Interrupt) {
|
||||
// Read register to clear it.
|
||||
data := avr.UDR0.Get()
|
||||
|
||||
// Ensure no error.
|
||||
if !avr.UCSR0A.HasBits(avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0) {
|
||||
// Put data from UDR register into buffer.
|
||||
UART0.Receive(byte(data))
|
||||
}
|
||||
})
|
||||
|
||||
// Set baud rate based on prescale formula from
|
||||
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
||||
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
||||
ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
||||
ps := ((CPUFrequency()+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
||||
avr.UBRR0H.Set(uint8(ps >> 8))
|
||||
avr.UBRR0L.Set(uint8(ps & 0xff))
|
||||
|
||||
@@ -254,15 +267,3 @@ func (uart UART) WriteByte(c byte) error {
|
||||
avr.UDR0.Set(c) // send char
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:interrupt USART_RX_vect
|
||||
func handleUSART_RX() {
|
||||
// Read register to clear it.
|
||||
data := avr.UDR0.Get()
|
||||
|
||||
// Ensure no error.
|
||||
if !avr.UCSR0A.HasBits(avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0) {
|
||||
// Put data from UDR register into buffer.
|
||||
UART0.Receive(byte(data))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,10 @@ import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 48000000
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
@@ -35,74 +34,6 @@ const (
|
||||
PinInputPulldown PinMode = 12
|
||||
)
|
||||
|
||||
// Hardware pins
|
||||
const (
|
||||
PA00 Pin = 0
|
||||
PA01 Pin = 1
|
||||
PA02 Pin = 2
|
||||
PA03 Pin = 3
|
||||
PA04 Pin = 4
|
||||
PA05 Pin = 5
|
||||
PA06 Pin = 6
|
||||
PA07 Pin = 7
|
||||
PA08 Pin = 8
|
||||
PA09 Pin = 9
|
||||
PA10 Pin = 10
|
||||
PA11 Pin = 11
|
||||
PA12 Pin = 12
|
||||
PA13 Pin = 13
|
||||
PA14 Pin = 14
|
||||
PA15 Pin = 15
|
||||
PA16 Pin = 16
|
||||
PA17 Pin = 17
|
||||
PA18 Pin = 18
|
||||
PA19 Pin = 19
|
||||
PA20 Pin = 20
|
||||
PA21 Pin = 21
|
||||
PA22 Pin = 22
|
||||
PA23 Pin = 23
|
||||
PA24 Pin = 24
|
||||
PA25 Pin = 25
|
||||
PA26 Pin = 26
|
||||
PA27 Pin = 27
|
||||
PA28 Pin = 28
|
||||
PA29 Pin = 29
|
||||
PA30 Pin = 30
|
||||
PA31 Pin = 31
|
||||
PB00 Pin = 32
|
||||
PB01 Pin = 33
|
||||
PB02 Pin = 34
|
||||
PB03 Pin = 35
|
||||
PB04 Pin = 36
|
||||
PB05 Pin = 37
|
||||
PB06 Pin = 38
|
||||
PB07 Pin = 39
|
||||
PB08 Pin = 40
|
||||
PB09 Pin = 41
|
||||
PB10 Pin = 42
|
||||
PB11 Pin = 43
|
||||
PB12 Pin = 44
|
||||
PB13 Pin = 45
|
||||
PB14 Pin = 46
|
||||
PB15 Pin = 47
|
||||
PB16 Pin = 48
|
||||
PB17 Pin = 49
|
||||
PB18 Pin = 50
|
||||
PB19 Pin = 51
|
||||
PB20 Pin = 52
|
||||
PB21 Pin = 53
|
||||
PB22 Pin = 54
|
||||
PB23 Pin = 55
|
||||
PB24 Pin = 56
|
||||
PB25 Pin = 57
|
||||
PB26 Pin = 58
|
||||
PB27 Pin = 59
|
||||
PB28 Pin = 60
|
||||
PB29 Pin = 61
|
||||
PB30 Pin = 62
|
||||
PB31 Pin = 63
|
||||
)
|
||||
|
||||
const (
|
||||
pinPadMapSERCOM0Pad0 byte = (0x10 << 1) | 0x00
|
||||
pinPadMapSERCOM1Pad0 byte = (0x20 << 1) | 0x00
|
||||
@@ -362,9 +293,10 @@ func waitADCSync() {
|
||||
|
||||
// UART on the SAMD21.
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
Bus *sam.SERCOM_USART_Type
|
||||
SERCOM uint8
|
||||
Buffer *RingBuffer
|
||||
Bus *sam.SERCOM_USART_Type
|
||||
SERCOM uint8
|
||||
interrupt interrupt.Interrupt
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -471,10 +403,7 @@ func (uart UART) Configure(config UARTConfig) error {
|
||||
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INTENSET_RXC)
|
||||
|
||||
// Enable RX IRQ.
|
||||
// IRQ lines are in the same order as SERCOM instance numbers on SAMD21
|
||||
// chips, so the IRQ number can be trivially determined from the SERCOM
|
||||
// number.
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM0 + uint32(uart.SERCOM))
|
||||
uart.interrupt.Enable()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -485,7 +414,7 @@ func (uart UART) SetBaudRate(br uint32) {
|
||||
// BAUD = fref / (sampleRateValue * fbaud)
|
||||
// (multiply by 8, to calculate fractional piece)
|
||||
// uint32_t baudTimes8 = (SystemCoreClock * 8) / (16 * baudrate);
|
||||
baud := (CPU_FREQUENCY * 8) / (sampleRate16X * br)
|
||||
baud := (CPUFrequency() * 8) / (sampleRate16X * br)
|
||||
|
||||
// sercom->USART.BAUD.FRAC.FP = (baudTimes8 % 8);
|
||||
// sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8);
|
||||
@@ -502,11 +431,12 @@ func (uart UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// defaultUART1Handler handles the UART1 IRQ.
|
||||
func defaultUART1Handler() {
|
||||
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||
// this UART instance.
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
// should reset IRQ
|
||||
UART1.Receive(byte((UART1.Bus.DATA.Get() & 0xFF)))
|
||||
UART1.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
|
||||
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
// I2C on the SAMD21.
|
||||
@@ -601,7 +531,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
||||
func (i2c I2C) SetBaudRate(br uint32) {
|
||||
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
|
||||
// SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
|
||||
baud := CPU_FREQUENCY/(2*br) - 5 - (((CPU_FREQUENCY / 1000000) * riseTimeNanoseconds) / (2 * 1000))
|
||||
baud := CPUFrequency()/(2*br) - 5 - (((CPUFrequency() / 1000000) * riseTimeNanoseconds) / (2 * 1000))
|
||||
i2c.Bus.BAUD.Set(baud)
|
||||
}
|
||||
|
||||
@@ -793,7 +723,7 @@ func (i2s I2S) Configure(config I2SConfig) {
|
||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_I2S_)
|
||||
|
||||
// setting clock rate for sample.
|
||||
division_factor := CPU_FREQUENCY / (config.AudioFrequency * uint32(config.DataFormat))
|
||||
division_factor := CPUFrequency() / (config.AudioFrequency * uint32(config.DataFormat))
|
||||
|
||||
// Switch Generic Clock Generator 3 to DFLL48M.
|
||||
sam.GCLK.GENDIV.Set((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENDIV_ID_Pos) |
|
||||
@@ -1113,7 +1043,7 @@ func (spi SPI) Configure(config SPIConfig) error {
|
||||
}
|
||||
|
||||
// Set synch speed for SPI
|
||||
baudRate := (CPU_FREQUENCY / (2 * config.Frequency)) - 1
|
||||
baudRate := (CPUFrequency() / (2 * config.Frequency)) - 1
|
||||
spi.Bus.BAUD.Set(uint8(baudRate))
|
||||
|
||||
// Enable SPI port.
|
||||
@@ -1438,7 +1368,8 @@ func (usbcdc USBCDC) Configure(config UARTConfig) {
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ
|
||||
arm.EnableIRQ(sam.IRQ_USB)
|
||||
intr := interrupt.New(sam.IRQ_USB, handleUSB)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
@@ -1484,8 +1415,7 @@ func handlePadCalibration() {
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
//go:export USB_IRQHandler
|
||||
func handleUSB() {
|
||||
func handleUSB(intr interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
||||
@@ -11,10 +11,13 @@ import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 120000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 120000000
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
@@ -1400,11 +1403,11 @@ func (usbcdc USBCDC) Configure(config UARTConfig) {
|
||||
// enable USB
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ
|
||||
arm.EnableIRQ(sam.IRQ_USB_OTHER)
|
||||
arm.EnableIRQ(sam.IRQ_USB_SOF_HSOF)
|
||||
arm.EnableIRQ(sam.IRQ_USB_TRCPT0)
|
||||
arm.EnableIRQ(sam.IRQ_USB_TRCPT1)
|
||||
// enable IRQ at highest priority
|
||||
interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable()
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
@@ -1450,27 +1453,7 @@ func handlePadCalibration() {
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
//go:export USB_OTHER_IRQHandler
|
||||
func handleUSBOther() {
|
||||
handleUSBIRQ()
|
||||
}
|
||||
|
||||
//go:export USB_SOF_HSOF_IRQHandler
|
||||
func handleUSBSOFHSOF() {
|
||||
handleUSBIRQ()
|
||||
}
|
||||
|
||||
//go:export USB_TRCPT0_IRQHandler
|
||||
func handleUSBTRCPT0() {
|
||||
handleUSBIRQ()
|
||||
}
|
||||
|
||||
//go:export USB_TRCPT1_IRQHandler
|
||||
func handleUSBTRCPT1() {
|
||||
handleUSBIRQ()
|
||||
}
|
||||
|
||||
func handleUSBIRQ() {
|
||||
func handleUSBIRQ(interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
||||
@@ -6,7 +6,9 @@ import (
|
||||
"device/sifive"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 16000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
@@ -108,7 +110,7 @@ func (spi SPI) Configure(config SPIConfig) error {
|
||||
}
|
||||
|
||||
// div = (SPI_CFG(dev)->f_sys / (2 * frequency)) - 1;
|
||||
div := CPU_FREQUENCY/(2*config.Frequency) - 1
|
||||
div := CPUFrequency()/(2*config.Frequency) - 1
|
||||
spi.Bus.DIV.Set(div)
|
||||
|
||||
// set mode
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/nrf"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -88,8 +88,11 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
nrf.UART0.INTENSET.Set(nrf.UART_INTENSET_RXDRDY_Msk)
|
||||
|
||||
// Enable RX IRQ.
|
||||
arm.SetPriority(nrf.IRQ_UART0, 0xc0) // low priority
|
||||
arm.EnableIRQ(nrf.IRQ_UART0)
|
||||
intr := interrupt.New(nrf.IRQ_UART0, func(intr interrupt.Interrupt) {
|
||||
UART0.handleInterrupt()
|
||||
})
|
||||
intr.SetPriority(0xc0) // low priority
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the UART.
|
||||
|
||||
@@ -6,7 +6,9 @@ import (
|
||||
"device/nrf"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 16000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||
@@ -18,11 +20,6 @@ func (uart UART) setPins(tx, rx Pin) {
|
||||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||
}
|
||||
|
||||
//go:export UART0_IRQHandler
|
||||
func handleUART0() {
|
||||
UART0.handleInterrupt()
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 64000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 64000000
|
||||
}
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||
@@ -19,11 +21,6 @@ func (uart UART) setPins(tx, rx Pin) {
|
||||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||
}
|
||||
|
||||
//go:export UARTE0_UART0_IRQHandler
|
||||
func handleUART0() {
|
||||
UART0.handleInterrupt()
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 64000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 64000000
|
||||
}
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||
@@ -23,11 +25,6 @@ func (uart UART) setPins(tx, rx Pin) {
|
||||
nrf.UART0.PSEL.RXD.Set(uint32(rx))
|
||||
}
|
||||
|
||||
//go:export UARTE0_UART0_IRQHandler
|
||||
func handleUART0() {
|
||||
UART0.handleInterrupt()
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSEL.SCL.Set(uint32(scl))
|
||||
i2c.Bus.PSEL.SDA.Set(uint32(sda))
|
||||
|
||||
@@ -5,12 +5,14 @@ package machine
|
||||
// Peripheral abstraction layer for the stm32.
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/stm32"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 72000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 72000000
|
||||
}
|
||||
|
||||
const (
|
||||
PinInput PinMode = 0 // Input mode
|
||||
@@ -109,9 +111,9 @@ func (p Pin) Get() bool {
|
||||
|
||||
// UART
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
Bus *stm32.USART_Type
|
||||
IRQVal uint32
|
||||
Buffer *RingBuffer
|
||||
Bus *stm32.USART_Type
|
||||
interrupt interrupt.Interrupt
|
||||
}
|
||||
|
||||
// Configure the UART.
|
||||
@@ -153,8 +155,8 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
uart.Bus.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
||||
|
||||
// Enable RX IRQ
|
||||
arm.SetPriority(uart.IRQVal, 0xc0)
|
||||
arm.EnableIRQ(uart.IRQVal)
|
||||
uart.interrupt.SetPriority(0xc0)
|
||||
uart.interrupt.Enable()
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the UART.
|
||||
@@ -163,10 +165,10 @@ func (uart UART) SetBaudRate(br uint32) {
|
||||
var divider uint32
|
||||
if uart.Bus == stm32.USART1 {
|
||||
// first divide by PCLK2 prescaler (div 1) and then desired baudrate
|
||||
divider = CPU_FREQUENCY / br
|
||||
divider = CPUFrequency() / br
|
||||
} else {
|
||||
// first divide by PCLK1 prescaler (div 2) and then desired baudrate
|
||||
divider = CPU_FREQUENCY / 2 / br
|
||||
divider = CPUFrequency() / 2 / br
|
||||
}
|
||||
uart.Bus.BRR.Set(divider)
|
||||
}
|
||||
@@ -180,6 +182,12 @@ func (uart UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||
// this UART instance.
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
uart.Receive(byte((uart.Bus.DR.Get() & 0xFF)))
|
||||
}
|
||||
|
||||
// SPI on the STM32.
|
||||
type SPI struct {
|
||||
Bus *stm32.SPI_Type
|
||||
@@ -360,7 +368,7 @@ func (i2c I2C) Configure(config I2CConfig) {
|
||||
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_PE)
|
||||
|
||||
// pclk1 clock speed is main frequency divided by PCLK1 prescaler (div 2)
|
||||
pclk1 := uint32(CPU_FREQUENCY / 2)
|
||||
pclk1 := CPUFrequency() / 2
|
||||
|
||||
// set freqency range to PCLK1 clock speed in MHz
|
||||
// aka setting the value 36 means to use 36 MHz clock
|
||||
|
||||
@@ -5,11 +5,13 @@ package machine
|
||||
// Peripheral abstraction layer for the stm32.
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 168000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 168000000
|
||||
}
|
||||
|
||||
const (
|
||||
// Mode Flag
|
||||
@@ -201,8 +203,11 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
stm32.USART2.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
||||
|
||||
// Enable RX IRQ.
|
||||
arm.SetPriority(stm32.IRQ_USART2, 0xc0)
|
||||
arm.EnableIRQ(stm32.IRQ_USART2)
|
||||
intr := interrupt.New(stm32.IRQ_USART2, func(interrupt.Interrupt) {
|
||||
UART1.Receive(byte((stm32.USART2.DR.Get() & 0xFF)))
|
||||
})
|
||||
intr.SetPriority(0xc0)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
@@ -213,8 +218,3 @@ func (uart UART) WriteByte(c byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:export USART2_IRQHandler
|
||||
func handleUSART2() {
|
||||
UART1.Receive(byte((stm32.USART2.DR.Get() & 0xFF)))
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build sam stm32,!stm32f407 fe310
|
||||
// +build !baremetal sam stm32,!stm32f407 fe310
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build darwin linux,!baremetal
|
||||
// +build darwin linux,!baremetal freebsd,!baremetal
|
||||
|
||||
package os
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Package interrupt provides access to hardware interrupts. It provides a way
|
||||
// to define interrupts and to enable/disable them.
|
||||
package interrupt
|
||||
|
||||
// Interrupt provides direct access to hardware interrupts. You can configure
|
||||
// this interrupt through this interface.
|
||||
//
|
||||
// Do not use the zero value of an Interrupt object. Instead, call New to obtain
|
||||
// an interrupt handle.
|
||||
type Interrupt struct {
|
||||
// Make this number unexported so it cannot be set directly. This provides
|
||||
// some encapsulation.
|
||||
num int
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// +build cortexm
|
||||
|
||||
package interrupt
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
)
|
||||
|
||||
// Enable enables this interrupt. Right after calling this function, the
|
||||
// interrupt may be invoked if it was already pending.
|
||||
func (irq Interrupt) Enable() {
|
||||
arm.EnableIRQ(uint32(irq.num))
|
||||
}
|
||||
|
||||
// SetPriority sets the interrupt priority for this interrupt. A lower number
|
||||
// means a higher priority. Additionally, most hardware doesn't implement all
|
||||
// priority bits (only the uppoer bits).
|
||||
//
|
||||
// Examples: 0xff (lowest priority), 0xc0 (low priority), 0x00 (highest possible
|
||||
// priority).
|
||||
func (irq Interrupt) SetPriority(priority uint8) {
|
||||
arm.SetPriority(uint32(irq.num), uint32(priority))
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// +build gameboyadvance
|
||||
|
||||
package interrupt
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var handlers = [14]func(Interrupt){}
|
||||
|
||||
const (
|
||||
IRQ_VBLANK = 0
|
||||
IRQ_HBLANK = 1
|
||||
IRQ_VCOUNT = 2
|
||||
IRQ_TIMER0 = 3
|
||||
IRQ_TIMER1 = 4
|
||||
IRQ_TIMER2 = 5
|
||||
IRQ_TIMER3 = 6
|
||||
IRQ_COM = 7
|
||||
IRQ_DMA0 = 8
|
||||
IRQ_DMA1 = 9
|
||||
IRQ_DMA2 = 10
|
||||
IRQ_DMA3 = 11
|
||||
IRQ_KEYPAD = 12
|
||||
IRQ_GAMEPAK = 13
|
||||
)
|
||||
|
||||
var (
|
||||
regInterruptEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000200)))
|
||||
regInterruptRequestFlags = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000202)))
|
||||
regInterruptMasterEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000208)))
|
||||
)
|
||||
|
||||
// New creates a new Interrupt object. Do not call it multiple times. If you do,
|
||||
// make sure the interrupt is disabled while you do so. The last call will set
|
||||
// the active interrupt handler.
|
||||
func New(id int, handler func(Interrupt)) Interrupt {
|
||||
handlers[id] = handler
|
||||
return Interrupt{id}
|
||||
}
|
||||
|
||||
// Enable enables this interrupt. Right after calling this function, the
|
||||
// interrupt may be invoked if it was already pending.
|
||||
func (irq Interrupt) Enable() {
|
||||
regInterruptEnable.SetBits(1 << irq.num)
|
||||
}
|
||||
|
||||
//export handleInterrupt
|
||||
func handleInterrupt() {
|
||||
flags := regInterruptRequestFlags.Get()
|
||||
for i := range handlers {
|
||||
if flags & (1 << i) != 0 {
|
||||
irq := Interrupt{i}
|
||||
regInterruptRequestFlags.Set(1 << i) // acknowledge interrupt
|
||||
handlers[i](irq)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// +build avr riscv,baremetal cortexm
|
||||
|
||||
package interrupt
|
||||
|
||||
// New is a compiler intrinsic that creates a new Interrupt object. You may call
|
||||
// it only once, and must pass constant parameters to it. That means that the
|
||||
// interrupt ID must be a Go constant and that the handler must be a simple
|
||||
// function: closures are not supported.
|
||||
func New(id int, handler func(Interrupt)) Interrupt
|
||||
|
||||
// Register is used to declare an interrupt. You should not normally call this
|
||||
// function: it is only for telling the compiler about the mapping between an
|
||||
// interrupt number and the interrupt handler name.
|
||||
func Register(id int, handlerName string) int
|
||||
|
||||
type handle struct {
|
||||
handler func(Interrupt)
|
||||
Interrupt
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// +build freebsd
|
||||
|
||||
package runtime
|
||||
|
||||
const GOOS = "freebsd"
|
||||
@@ -4,6 +4,7 @@ package runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
_ "runtime/interrupt" // make sure the interrupt handler is defined
|
||||
)
|
||||
|
||||
type timeUnit int64
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -214,8 +215,14 @@ func initRTC() {
|
||||
sam.RTC_MODE0.CTRL.SetBits(sam.RTC_MODE0_CTRL_ENABLE)
|
||||
waitForSync()
|
||||
|
||||
arm.SetPriority(sam.IRQ_RTC, 0xc0)
|
||||
arm.EnableIRQ(sam.IRQ_RTC)
|
||||
intr := interrupt.New(sam.IRQ_RTC, func(intr interrupt.Interrupt) {
|
||||
// disable IRQ for CMP0 compare
|
||||
sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
|
||||
timerWakeup.Set(1)
|
||||
})
|
||||
intr.SetPriority(0xc0)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
func waitForSync() {
|
||||
@@ -286,14 +293,6 @@ func timerSleep(ticks uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
//go:export RTC_IRQHandler
|
||||
func handleRTC() {
|
||||
// disable IRQ for CMP0 compare
|
||||
sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0)
|
||||
|
||||
timerWakeup.Set(1)
|
||||
}
|
||||
|
||||
func initUSBClock() {
|
||||
// Turn on clock for USB
|
||||
sam.PM.APBBMASK.SetBits(sam.PM_APBBMASK_USB_)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build qemu
|
||||
// +build cortexm,qemu
|
||||
|
||||
package runtime
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"machine"
|
||||
"unsafe"
|
||||
|
||||
"device/riscv"
|
||||
"device/sifive"
|
||||
)
|
||||
|
||||
@@ -82,34 +81,4 @@ func putchar(c byte) {
|
||||
machine.UART0.WriteByte(c)
|
||||
}
|
||||
|
||||
func ticks() timeUnit {
|
||||
// Combining the low bits and the high bits yields a time span of over 270
|
||||
// years without counter rollover.
|
||||
highBits := sifive.RTC.RTCHI.Get()
|
||||
for {
|
||||
lowBits := sifive.RTC.RTCLO.Get()
|
||||
newHighBits := sifive.RTC.RTCHI.Get()
|
||||
if newHighBits == highBits {
|
||||
// High bits stayed the same.
|
||||
return timeUnit(lowBits) | (timeUnit(highBits) << 32)
|
||||
}
|
||||
// Retry, because there was a rollover in the low bits (happening every
|
||||
// 1.5 days).
|
||||
highBits = newHighBits
|
||||
}
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
|
||||
func sleepTicks(d timeUnit) {
|
||||
target := ticks() + d
|
||||
for ticks() < target {
|
||||
}
|
||||
}
|
||||
|
||||
func abort() {
|
||||
// lock up forever
|
||||
for {
|
||||
riscv.Asm("wfi")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// +build fe310,!qemu
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/riscv"
|
||||
"device/sifive"
|
||||
)
|
||||
|
||||
func abort() {
|
||||
// lock up forever
|
||||
for {
|
||||
riscv.Asm("wfi")
|
||||
}
|
||||
}
|
||||
|
||||
func ticks() timeUnit {
|
||||
// Combining the low bits and the high bits yields a time span of over 270
|
||||
// years without counter rollover.
|
||||
highBits := sifive.RTC.RTCHI.Get()
|
||||
for {
|
||||
lowBits := sifive.RTC.RTCLO.Get()
|
||||
newHighBits := sifive.RTC.RTCHI.Get()
|
||||
if newHighBits == highBits {
|
||||
// High bits stayed the same.
|
||||
println("bits:", highBits, lowBits)
|
||||
return timeUnit(lowBits) | (timeUnit(highBits) << 32)
|
||||
}
|
||||
// Retry, because there was a rollover in the low bits (happening every
|
||||
// 1.5 days).
|
||||
highBits = newHighBits
|
||||
}
|
||||
}
|
||||
|
||||
func sleepTicks(d timeUnit) {
|
||||
target := ticks() + d
|
||||
for ticks() < target {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// +build fe310,qemu
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Special memory-mapped device to exit tests, created by SiFive.
|
||||
var testExit = (*volatile.Register32)(unsafe.Pointer(uintptr(0x100000)))
|
||||
|
||||
var timestamp timeUnit
|
||||
|
||||
func abort() {
|
||||
// Signal a successful exit.
|
||||
testExit.Set(0x5555)
|
||||
}
|
||||
|
||||
func ticks() timeUnit {
|
||||
return timestamp
|
||||
}
|
||||
|
||||
func sleepTicks(d timeUnit) {
|
||||
// Note: QEMU doesn't seem to support the RTC peripheral at the time of
|
||||
// writing so just simulate sleeping here.
|
||||
timestamp += d
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"device/arm"
|
||||
"device/nrf"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
@@ -43,8 +44,13 @@ func initLFCLK() {
|
||||
|
||||
func initRTC() {
|
||||
nrf.RTC1.TASKS_START.Set(1)
|
||||
arm.SetPriority(nrf.IRQ_RTC1, 0xc0) // low priority
|
||||
arm.EnableIRQ(nrf.IRQ_RTC1)
|
||||
intr := interrupt.New(nrf.IRQ_RTC1, func(intr interrupt.Interrupt) {
|
||||
nrf.RTC1.INTENCLR.Set(nrf.RTC_INTENSET_COMPARE0)
|
||||
nrf.RTC1.EVENTS_COMPARE[0].Set(0)
|
||||
rtc_wakeup.Set(1)
|
||||
})
|
||||
intr.SetPriority(0xc0) // low priority
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
@@ -96,10 +102,3 @@ func rtc_sleep(ticks uint32) {
|
||||
arm.Asm("wfi")
|
||||
}
|
||||
}
|
||||
|
||||
//go:export RTC1_IRQHandler
|
||||
func handleRTC1() {
|
||||
nrf.RTC1.INTENCLR.Set(nrf.RTC_INTENSET_COMPARE0)
|
||||
nrf.RTC1.EVENTS_COMPARE[0].Set(0)
|
||||
rtc_wakeup.Set(1)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"device/arm"
|
||||
"device/stm32"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
@@ -101,8 +102,9 @@ func initRTC() {
|
||||
func initTIM() {
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||
|
||||
arm.SetPriority(stm32.IRQ_TIM3, 0xc3)
|
||||
arm.EnableIRQ(stm32.IRQ_TIM3)
|
||||
intr := interrupt.New(stm32.IRQ_TIM3, handleTIM3)
|
||||
intr.SetPriority(0xc3)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
@@ -163,7 +165,7 @@ func timerSleep(ticks uint32) {
|
||||
// The current scaling only supports a range of 200 usec to 6553 msec.
|
||||
|
||||
// prescale counter down from 72mhz to 10khz aka 0.1 ms frequency.
|
||||
stm32.TIM3.PSC.Set(machine.CPU_FREQUENCY/10000 - 1) // 7199
|
||||
stm32.TIM3.PSC.Set(machine.CPUFrequency()/10000 - 1) // 7199
|
||||
|
||||
// Set duty aka duration.
|
||||
// STM32 dividers use n-1, i.e. n counts from 0 to n-1.
|
||||
@@ -186,8 +188,7 @@ func timerSleep(ticks uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
//go:export TIM3_IRQHandler
|
||||
func handleTIM3() {
|
||||
func handleTIM3(interrupt.Interrupt) {
|
||||
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||
// Disable the timer.
|
||||
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"device/arm"
|
||||
"device/stm32"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
@@ -121,8 +122,9 @@ var timerWakeup volatile.Register8
|
||||
func initTIM3() {
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||
|
||||
arm.SetPriority(stm32.IRQ_TIM3, 0xc3)
|
||||
arm.EnableIRQ(stm32.IRQ_TIM3)
|
||||
intr := interrupt.New(stm32.IRQ_TIM3, handleTIM3)
|
||||
intr.SetPriority(0xc3)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
// Enable the TIM7 clock.(tick count)
|
||||
@@ -139,8 +141,9 @@ func initTIM7() {
|
||||
// Enable the timer.
|
||||
stm32.TIM7.CR1.SetBits(stm32.TIM_CR1_CEN)
|
||||
|
||||
arm.SetPriority(stm32.IRQ_TIM7, 0xc1)
|
||||
arm.EnableIRQ(stm32.IRQ_TIM7)
|
||||
intr := interrupt.New(stm32.IRQ_TIM7, handleTIM7)
|
||||
intr.SetPriority(0xc1)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
const asyncScheduler = false
|
||||
@@ -183,8 +186,7 @@ func timerSleep(ticks uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
//go:export TIM3_IRQHandler
|
||||
func handleTIM3() {
|
||||
func handleTIM3(interrupt.Interrupt) {
|
||||
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||
// Disable the timer.
|
||||
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN)
|
||||
@@ -197,8 +199,7 @@ func handleTIM3() {
|
||||
}
|
||||
}
|
||||
|
||||
//go:export TIM7_IRQHandler
|
||||
func handleTIM7() {
|
||||
func handleTIM7(interrupt.Interrupt) {
|
||||
if stm32.TIM7.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||
// clear the update flag
|
||||
stm32.TIM7.SR.ClearBits(stm32.TIM_SR_UIF)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build darwin linux,!baremetal
|
||||
// +build darwin linux,!baremetal freebsd,!baremetal
|
||||
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -76,3 +76,17 @@ func memset(ptr unsafe.Pointer, c byte, size uintptr) unsafe.Pointer {
|
||||
}
|
||||
return ptr
|
||||
}
|
||||
|
||||
// Implement memmove for LLVM and compiler-rt.
|
||||
//go:export memmove
|
||||
func libc_memmove(dst, src unsafe.Pointer, size uintptr) unsafe.Pointer {
|
||||
memmove(dst, src, size)
|
||||
return dst
|
||||
}
|
||||
|
||||
// Implement memcpy for LLVM and compiler-rt.
|
||||
//go:export memcpy
|
||||
func libc_memcpy(dst, src unsafe.Pointer, size uintptr) unsafe.Pointer {
|
||||
memcpy(dst, src, size)
|
||||
return dst
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
],
|
||||
"linkerscript": "targets/lm3s6965.ld",
|
||||
"extra-files": [
|
||||
"targets/qemu.s"
|
||||
"targets/cortex-m-qemu.s"
|
||||
],
|
||||
"emulator": ["qemu-system-arm", "-machine", "lm3s6965evb", "-semihosting", "-nographic", "-kernel"]
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Generic Cortex-M interrupt vector.
|
||||
// This vector is used by the QEMU target.
|
||||
// This vector is used by the Cortex-M QEMU target.
|
||||
|
||||
.syntax unified
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
OUTPUT_ARCH(arm)
|
||||
ENTRY(_start)
|
||||
|
||||
/* Note: iwram is reduced by 96 bytes because the last part of that RAM
|
||||
* (starting at 0x03007FA0) is used for interrupt handling.
|
||||
*/
|
||||
MEMORY {
|
||||
ewram : ORIGIN = 0x02000000, LENGTH = 256K /* on-board work RAM (2 wait states) */
|
||||
iwram : ORIGIN = 0x03000000, LENGTH = 32K /* in-chip work RAM (faster) */
|
||||
rom : ORIGIN = 0x08000000, LENGTH = 32M /* flash ROM */
|
||||
ewram : ORIGIN = 0x02000000, LENGTH = 256K /* on-board work RAM (2 wait states) */
|
||||
iwram : ORIGIN = 0x03000000, LENGTH = 32K-96 /* in-chip work RAM (faster) */
|
||||
rom : ORIGIN = 0x08000000, LENGTH = 32M /* flash ROM */
|
||||
}
|
||||
|
||||
__iwram_top = ORIGIN(iwram) + LENGTH(iwram);;
|
||||
_stack_size = 3K;
|
||||
__sp_irq = _stack_top;
|
||||
__sp_usr = _stack_top - 1K;
|
||||
__stack_size_irq = 1K;
|
||||
__stack_size_usr = 2K;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
@@ -35,8 +36,10 @@ SECTIONS
|
||||
.stack (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
. += _stack_size;
|
||||
_stack_top = .;
|
||||
__sp_irq = .;
|
||||
. += __stack_size_irq;
|
||||
__sp_usr = .;
|
||||
. += __stack_size_usr;
|
||||
} >iwram
|
||||
|
||||
/* Start address (in flash) of .data, used by startup code. */
|
||||
|
||||
@@ -17,9 +17,7 @@ _start:
|
||||
.byte 0x00,0x00 // Checksum (80000BEh)
|
||||
|
||||
start_vector:
|
||||
mov r0, #0x4000000 // REG_BASE
|
||||
str r0, [r0, #0x208]
|
||||
|
||||
// Configure stacks
|
||||
mov r0, #0x12 // Switch to IRQ Mode
|
||||
msr cpsr, r0
|
||||
ldr sp, =__sp_irq // Set IRQ stack
|
||||
@@ -27,7 +25,21 @@ start_vector:
|
||||
msr cpsr, r0
|
||||
ldr sp, =__sp_usr // Set user stack
|
||||
|
||||
// Configure interrupt handler
|
||||
mov r0, #0x4000000 // REG_BASE
|
||||
ldr r1, =handleInterruptARM
|
||||
str r1, [r0, #-4] // actually storing to 0x03007FFC due to mirroring
|
||||
|
||||
// Enable interrupts
|
||||
mov r1, #1
|
||||
str r1, [r0, #0x208] // 0x04000208 Interrupt Master Enable
|
||||
|
||||
// Jump to user code (switching to Thumb mode)
|
||||
ldr r3, =main
|
||||
bx r3
|
||||
|
||||
// Small interrupt handler that immediately jumps to a function defined in the
|
||||
// program (in Thumb) for further processing.
|
||||
handleInterruptARM:
|
||||
ldr r0, =handleInterrupt
|
||||
bx r0
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"inherits": ["fe310"],
|
||||
"build-tags": ["hifive1b", "qemu"],
|
||||
"linkerscript": "targets/hifive1-qemu.ld",
|
||||
"emulator": ["qemu-system-riscv32", "-machine", "sifive_e", "-nographic", "-kernel"]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
/* memory map:
|
||||
* https://github.com/sifive/freedom-e-sdk/blob/v201908-branch/bsp/sifive-hifive1/metal.default.lds
|
||||
*/
|
||||
MEMORY
|
||||
{
|
||||
FLASH_TEXT (rw) : ORIGIN = 0x20400000, LENGTH = 0x1fc00000
|
||||
RAM (xrw) : ORIGIN = 0x80000000, LENGTH = 0x4000
|
||||
}
|
||||
|
||||
_stack_size = 2K;
|
||||
|
||||
INCLUDE "targets/riscv.ld"
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"inherits": ["atsamd51j19a"],
|
||||
"build-tags": ["sam", "atsamd51j19a", "pybadge"],
|
||||
"flash-1200-bps-reset": "true",
|
||||
"flash-method": "msd",
|
||||
"msd-volume-name": "PYBADGEBOOT",
|
||||
"msd-firmware-name": "arcade.uf2"
|
||||
}
|
||||
+6
-3
@@ -4,9 +4,11 @@
|
||||
"goarch": "arm",
|
||||
"build-tags": ["tinygo.riscv", "baremetal", "linux", "arm"],
|
||||
"gc": "conservative",
|
||||
"compiler": "riscv64-unknown-elf-gcc",
|
||||
"linker": "riscv64-unknown-elf-ld",
|
||||
"compiler": "clang",
|
||||
"linker": "ld.lld",
|
||||
"rtlib": "compiler-rt",
|
||||
"cflags": [
|
||||
"--target=riscv32--none",
|
||||
"-march=rv32imac",
|
||||
"-mabi=ilp32",
|
||||
"-Os",
|
||||
@@ -21,5 +23,6 @@
|
||||
],
|
||||
"extra-files": [
|
||||
"src/device/riscv/start.S"
|
||||
]
|
||||
],
|
||||
"gdb": "riscv64-unknown-elf-gdb"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user