mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-23 05:49:01 +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 -
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install \
|
sudo apt-get install \
|
||||||
python3 \
|
|
||||||
llvm<<parameters.llvm>>-dev \
|
llvm<<parameters.llvm>>-dev \
|
||||||
clang<<parameters.llvm>> \
|
clang<<parameters.llvm>> \
|
||||||
libclang<<parameters.llvm>>-dev \
|
libclang<<parameters.llvm>>-dev \
|
||||||
@@ -67,7 +66,7 @@ commands:
|
|||||||
- run: go install .
|
- run: go install .
|
||||||
- run: go test -v ./cgo ./compileopts ./interp ./transform .
|
- run: go test -v ./cgo ./compileopts ./interp ./transform .
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
- run: make smoketest RISCV=0
|
- run: make smoketest
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||||
paths:
|
paths:
|
||||||
@@ -83,7 +82,6 @@ commands:
|
|||||||
name: "Install apt dependencies"
|
name: "Install apt dependencies"
|
||||||
command: |
|
command: |
|
||||||
sudo apt-get install \
|
sudo apt-get install \
|
||||||
python3 \
|
|
||||||
gcc-arm-linux-gnueabihf \
|
gcc-arm-linux-gnueabihf \
|
||||||
binutils-arm-none-eabi \
|
binutils-arm-none-eabi \
|
||||||
libc6-dev-armel-cross \
|
libc6-dev-armel-cross \
|
||||||
@@ -122,12 +120,6 @@ commands:
|
|||||||
key: llvm-build-9-linux-v0-assert
|
key: llvm-build-9-linux-v0-assert
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
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: make ASSERT=1
|
||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
@@ -139,7 +131,7 @@ commands:
|
|||||||
- ~/.cache/tinygo
|
- ~/.cache/tinygo
|
||||||
- /go/pkg/mod
|
- /go/pkg/mod
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
- run: make smoketest TINYGO=build/tinygo RISCV=0
|
- run: make smoketest TINYGO=build/tinygo
|
||||||
build-linux:
|
build-linux:
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
@@ -148,7 +140,6 @@ commands:
|
|||||||
name: "Install apt dependencies"
|
name: "Install apt dependencies"
|
||||||
command: |
|
command: |
|
||||||
sudo apt-get install \
|
sudo apt-get install \
|
||||||
python3 \
|
|
||||||
gcc-arm-linux-gnueabihf \
|
gcc-arm-linux-gnueabihf \
|
||||||
binutils-arm-none-eabi \
|
binutils-arm-none-eabi \
|
||||||
libc6-dev-armel-cross \
|
libc6-dev-armel-cross \
|
||||||
@@ -187,12 +178,6 @@ commands:
|
|||||||
key: llvm-build-9-linux-v0
|
key: llvm-build-9-linux-v0
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
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:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
command: make test
|
command: make test
|
||||||
@@ -216,11 +201,6 @@ commands:
|
|||||||
tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz
|
tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz
|
||||||
ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo
|
ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo
|
||||||
tinygo version
|
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
|
- run: make smoketest
|
||||||
build-macos:
|
build-macos:
|
||||||
steps:
|
steps:
|
||||||
@@ -264,10 +244,6 @@ commands:
|
|||||||
key: llvm-build-9-macos-v0
|
key: llvm-build-9-macos-v0
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- run:
|
|
||||||
name: "Create LLVM symlinks"
|
|
||||||
command: |
|
|
||||||
ln -s $PWD/llvm-build/bin/clang-9 /usr/local/bin/clang-9
|
|
||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
command: make test
|
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
|
0.10.0
|
||||||
---
|
---
|
||||||
* **command line**
|
* **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/ && \
|
RUN cd /tinygo/ && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y apt-utils python3 make binutils-avr gcc-avr avr-libc && \
|
apt-get install -y apt-utils make binutils-avr gcc-avr avr-libc && \
|
||||||
make gen-device-avr && \
|
make gen-device-avr && \
|
||||||
apt-get remove -y python3 && \
|
|
||||||
apt-get autoremove -y && \
|
apt-get autoremove -y && \
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
|
||||||
@@ -59,11 +58,8 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
|||||||
|
|
||||||
RUN cd /tinygo/ && \
|
RUN cd /tinygo/ && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y apt-utils python3 make clang-9 && \
|
apt-get install -y apt-utils make clang-9 && \
|
||||||
make gen-device-nrf && make gen-device-stm32 && \
|
make gen-device-nrf && make gen-device-stm32
|
||||||
apt-get remove -y python3 && \
|
|
||||||
apt-get autoremove -y && \
|
|
||||||
apt-get clean
|
|
||||||
|
|
||||||
# tinygo-all stage installs the needed dependencies to compile TinyGo programs for all platforms.
|
# tinygo-all stage installs the needed dependencies to compile TinyGo programs for all platforms.
|
||||||
FROM tinygo-wasm AS tinygo-all
|
FROM tinygo-wasm AS tinygo-all
|
||||||
@@ -74,10 +70,7 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
|||||||
|
|
||||||
RUN cd /tinygo/ && \
|
RUN cd /tinygo/ && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y apt-utils python3 make clang-9 binutils-avr gcc-avr avr-libc && \
|
apt-get install -y apt-utils make clang-9 binutils-avr gcc-avr avr-libc && \
|
||||||
make gen-device && \
|
make gen-device
|
||||||
apt-get remove -y python3 && \
|
|
||||||
apt-get autoremove -y && \
|
|
||||||
apt-get clean
|
|
||||||
|
|
||||||
CMD ["tinygo"]
|
CMD ["tinygo"]
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ Copyright (c) 2018-2019 TinyGo Authors. All rights reserved.
|
|||||||
TinyGo includes portions of the Go standard library.
|
TinyGo includes portions of the Go standard library.
|
||||||
Copyright (c) 2009-2019 The Go Authors. All rights reserved.
|
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
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
met:
|
met:
|
||||||
|
|||||||
@@ -14,9 +14,6 @@ export GOROOT = $(shell $(GO) env GOROOT)
|
|||||||
# md5sum binary
|
# md5sum binary
|
||||||
MD5SUM = md5sum
|
MD5SUM = md5sum
|
||||||
|
|
||||||
# Python binary
|
|
||||||
PYTHON ?= python
|
|
||||||
|
|
||||||
# tinygo binary for tests
|
# tinygo binary for tests
|
||||||
TINYGO ?= tinygo
|
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
|
.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)
|
ifeq ($(OS),Windows_NT)
|
||||||
EXE = .exe
|
EXE = .exe
|
||||||
@@ -65,6 +62,11 @@ $(LIBCLANG_PATH): $(LIBCLANG_FILES)
|
|||||||
else ifeq ($(shell uname -s),Darwin)
|
else ifeq ($(shell uname -s),Darwin)
|
||||||
MD5SUM = md5
|
MD5SUM = md5
|
||||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
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
|
else
|
||||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||||
START_GROUP = -Wl,--start-group
|
START_GROUP = -Wl,--start-group
|
||||||
@@ -78,7 +80,7 @@ LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -
|
|||||||
|
|
||||||
# For static linking.
|
# For static linking.
|
||||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
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_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)
|
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
|
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: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32
|
||||||
|
|
||||||
gen-device-avr:
|
gen-device-avr:
|
||||||
$(PYTHON) ./tools/gen-device-avr.py lib/avr/packs/atmega src/device/avr/
|
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
|
||||||
$(PYTHON) ./tools/gen-device-avr.py lib/avr/packs/tiny src/device/avr/
|
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/avr
|
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
|
||||||
|
@GO111MODULE=off $(GO) fmt ./src/device/avr
|
||||||
|
|
||||||
gen-device-nrf:
|
build/gen-device-svd: ./tools/gen-device-svd/*.go
|
||||||
$(PYTHON) ./tools/gen-device-svd.py lib/nrfx/mdk/ src/device/nrf/ --source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk
|
$(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
|
GO111MODULE=off $(GO) fmt ./src/device/nrf
|
||||||
|
|
||||||
gen-device-sam:
|
gen-device-sam: build/gen-device-svd
|
||||||
$(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
|
./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
|
GO111MODULE=off $(GO) fmt ./src/device/sam
|
||||||
|
|
||||||
gen-device-sifive:
|
gen-device-sifive: build/gen-device-svd
|
||||||
$(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
|
./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
|
GO111MODULE=off $(GO) fmt ./src/device/sifive
|
||||||
|
|
||||||
gen-device-stm32:
|
gen-device-stm32: build/gen-device-svd
|
||||||
$(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
|
./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
|
GO111MODULE=off $(GO) fmt ./src/device/stm32
|
||||||
|
|
||||||
|
|
||||||
@@ -187,6 +193,8 @@ smoketest:
|
|||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
$(TINYGO) build -o test.wasm -tags=pca10056 examples/blinky2
|
$(TINYGO) build -o test.wasm -tags=pca10056 examples/blinky2
|
||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
|
$(TINYGO) build -o test.wasm -tags=circuitplay_express examples/blinky1
|
||||||
|
@$(MD5SUM) test.wasm
|
||||||
# test all targets/boards
|
# test all targets/boards
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -226,6 +234,8 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(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
|
$(TINYGO) build -size short -o test.hex -target=metro-m4-airlift examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f103rb examples/blinky1
|
$(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
|
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
endif
|
endif
|
||||||
ifneq ($(RISCV), 0)
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
endif
|
|
||||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/export
|
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/export
|
||||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/main
|
$(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 M0](https://www.adafruit.com/product/3727)
|
||||||
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
|
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
|
||||||
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
|
* [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)
|
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
||||||
* [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot)
|
* [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot)
|
||||||
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
* [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 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)
|
* [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/)
|
* [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)
|
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
|
||||||
* [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
* [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)
|
* [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
|
## License
|
||||||
|
|
||||||
This project is licensed under the BSD 3-clause license, just like the [Go project](https://golang.org/LICENSE) itself.
|
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:
|
jobs:
|
||||||
- job: Build
|
- job: Build
|
||||||
timeoutInMinutes: 180
|
timeoutInMinutes: 240 # 4h
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'VS2017-Win2016'
|
vmImage: 'VS2017-Win2016'
|
||||||
steps:
|
steps:
|
||||||
@@ -71,4 +71,4 @@ jobs:
|
|||||||
script: |
|
script: |
|
||||||
export PATH="/c/Go1.13/bin:$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
export PATH="/c/Go1.13/bin:$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
||||||
unset GOROOT
|
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.
|
// Compile Go code to IR.
|
||||||
errs := c.Compile(pkgName)
|
errs := c.Compile(pkgName)
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
if len(errs) == 1 {
|
return newMultiError(errs)
|
||||||
return errs[0]
|
|
||||||
}
|
|
||||||
return &MultiError{errs}
|
|
||||||
}
|
}
|
||||||
if config.Options.PrintIR {
|
if config.Options.PrintIR {
|
||||||
fmt.Println("; Generated LLVM IR:")
|
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
|
// Optimization levels here are roughly the same as Clang, but probably not
|
||||||
// exactly.
|
// exactly.
|
||||||
|
errs = nil
|
||||||
switch config.Options.Opt {
|
switch config.Options.Opt {
|
||||||
case "none:", "0":
|
case "none:", "0":
|
||||||
err = c.Optimize(0, 0, 0) // -O0
|
errs = c.Optimize(0, 0, 0) // -O0
|
||||||
case "1":
|
case "1":
|
||||||
err = c.Optimize(1, 0, 0) // -O1
|
errs = c.Optimize(1, 0, 0) // -O1
|
||||||
case "2":
|
case "2":
|
||||||
err = c.Optimize(2, 0, 225) // -O2
|
errs = c.Optimize(2, 0, 225) // -O2
|
||||||
case "s":
|
case "s":
|
||||||
err = c.Optimize(2, 1, 225) // -Os
|
errs = c.Optimize(2, 1, 225) // -Os
|
||||||
case "z":
|
case "z":
|
||||||
err = c.Optimize(2, 2, 5) // -Oz, default
|
errs = c.Optimize(2, 2, 5) // -Oz, default
|
||||||
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 {
|
if len(errs) > 0 {
|
||||||
return err
|
return newMultiError(errs)
|
||||||
}
|
}
|
||||||
if err := c.Verify(); err != nil {
|
if err := c.Verify(); err != nil {
|
||||||
return errors.New("verification failure after LLVM optimization passes")
|
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() {
|
for i, path := range config.ExtraFiles() {
|
||||||
abspath := filepath.Join(root, path)
|
abspath := filepath.Join(root, path)
|
||||||
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
|
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
|
||||||
cmdNames := []string{config.Target.Compiler}
|
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
|
||||||
if names, ok := commands[config.Target.Compiler]; ok {
|
|
||||||
cmdNames = names
|
|
||||||
}
|
|
||||||
err := execCommand(cmdNames, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to build", path, err}
|
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 {
|
for _, file := range pkg.CFiles {
|
||||||
path := filepath.Join(pkg.Package.Dir, file)
|
path := filepath.Join(pkg.Package.Dir, file)
|
||||||
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+file+".o")
|
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+file+".o")
|
||||||
cmdNames := []string{config.Target.Compiler}
|
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, path)...)
|
||||||
if names, ok := commands[config.Target.Compiler]; ok {
|
|
||||||
cmdNames = names
|
|
||||||
}
|
|
||||||
err := execCommand(cmdNames, append(config.CFlags(), "-c", "-o", outpath, path)...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to build", path, err}
|
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
|
// Note: -fdebug-prefix-map is necessary to make the output archive
|
||||||
// reproducible. Otherwise the temporary directory is stored in the
|
// reproducible. Otherwise the temporary directory is stored in the
|
||||||
// archive itself, which varies each run.
|
// 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 {
|
if err != nil {
|
||||||
return &commandError{"failed to build", srcpath, err}
|
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["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")
|
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 {
|
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.
|
// various ways.
|
||||||
func getClangHeaderPath(TINYGOROOT string) string {
|
func getClangHeaderPath(TINYGOROOT string) string {
|
||||||
// Check whether we're running from the source directory.
|
// 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) {
|
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,20 @@ func (e *MultiError) Error() string {
|
|||||||
return e.Errs[0].Error()
|
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
|
// commandError is an error type to wrap os/exec.Command errors. This provides
|
||||||
// some more information regarding what went wrong while running a command.
|
// some more information regarding what went wrong while running a command.
|
||||||
type commandError struct {
|
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) {
|
func TestCGo(t *testing.T) {
|
||||||
var cflags = []string{"--target=armv6m-none-eabi"}
|
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
|
name := name // avoid a race condition
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// Read the AST in memory.
|
// Read the AST in memory.
|
||||||
path := filepath.Join("testdata", name+".go")
|
path := filepath.Join("testdata", name+".go")
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
|
|||||||
+25
-1
@@ -52,6 +52,13 @@ func parseConstExpr(t *tokenizer) (ast.Expr, *scanner.Error) {
|
|||||||
}
|
}
|
||||||
t.Next()
|
t.Next()
|
||||||
return expr, nil
|
return expr, nil
|
||||||
|
case token.IDENT:
|
||||||
|
expr := &ast.Ident{
|
||||||
|
NamePos: t.pos,
|
||||||
|
Name: "C." + t.value,
|
||||||
|
}
|
||||||
|
t.Next()
|
||||||
|
return expr, nil
|
||||||
case token.EOF:
|
case token.EOF:
|
||||||
return nil, &scanner.Error{
|
return nil, &scanner.Error{
|
||||||
Pos: t.fset.Position(t.pos),
|
Pos: t.fset.Position(t.pos),
|
||||||
@@ -133,8 +140,10 @@ func (t *tokenizer) Next() {
|
|||||||
if c == '.' {
|
if c == '.' {
|
||||||
hasDot = true
|
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
|
tokenLen = i + 1
|
||||||
|
} else {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.value = t.buf[:tokenLen]
|
t.value = t.buf[:tokenLen]
|
||||||
@@ -150,6 +159,21 @@ func (t *tokenizer) Next() {
|
|||||||
t.value = strings.TrimRight(t.value, "uUlL")
|
t.value = strings.TrimRight(t.value, "uUlL")
|
||||||
}
|
}
|
||||||
return
|
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 == '"':
|
case c == '"':
|
||||||
// String constant. Find the first '"' character that is not
|
// String constant. Find the first '"' character that is not
|
||||||
// preceded by a backslash.
|
// preceded by a backslash.
|
||||||
|
|||||||
+2
-1
@@ -21,7 +21,7 @@ func TestParseConst(t *testing.T) {
|
|||||||
{`5)`, `error: 1:2: unexpected token )`},
|
{`5)`, `error: 1:2: unexpected token )`},
|
||||||
{" \t)", `error: 1:4: unexpected token )`},
|
{" \t)", `error: 1:4: unexpected token )`},
|
||||||
{`5.8f`, `5.8`},
|
{`5.8f`, `5.8`},
|
||||||
{`foo`, `error: 1:1: unexpected token ILLEGAL`}, // identifiers unimplemented
|
{`foo`, `C.foo`},
|
||||||
{``, `error: 1:1: empty constant`}, // empty constants not allowed in Go
|
{``, `error: 1:1: empty constant`}, // empty constants not allowed in Go
|
||||||
{`"foo"`, `"foo"`},
|
{`"foo"`, `"foo"`},
|
||||||
{`"a\\n"`, `"a\\n"`},
|
{`"a\\n"`, `"a\\n"`},
|
||||||
@@ -30,6 +30,7 @@ func TestParseConst(t *testing.T) {
|
|||||||
{`'a'`, `'a'`},
|
{`'a'`, `'a'`},
|
||||||
{`0b10`, `0b10`},
|
{`0b10`, `0b10`},
|
||||||
{`0x1234_5678`, `0x1234_5678`},
|
{`0x1234_5678`, `0x1234_5678`},
|
||||||
|
{`5 5`, `error: 1:3: unexpected token INT`}, // test for a bugfix
|
||||||
} {
|
} {
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
startPos := fset.AddFile("", -1, 1000).Pos(0)
|
startPos := fset.AddFile("", -1, 1000).Pos(0)
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ package cgo
|
|||||||
/*
|
/*
|
||||||
#cgo linux CFLAGS: -I/usr/lib/llvm-9/include
|
#cgo linux CFLAGS: -I/usr/lib/llvm-9/include
|
||||||
#cgo darwin CFLAGS: -I/usr/local/opt/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 linux LDFLAGS: -L/usr/lib/llvm-9/lib -lclang
|
||||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@9/lib -lclang -lffi
|
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@9/lib -lclang -lffi
|
||||||
|
#cgo freebsd LDFLAGS: -L/usr/local/llvm90/lib -lclang
|
||||||
*/
|
*/
|
||||||
import "C"
|
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 {
|
func (c *Compiler) checkInstruction(inst llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
|
||||||
// check value properties
|
// check value properties
|
||||||
if err := c.checkValue(inst, types, specials); err != nil {
|
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
|
// check operands
|
||||||
for i := 0; i < inst.OperandsCount(); i++ {
|
for i := 0; i < inst.OperandsCount(); i++ {
|
||||||
if err := c.checkValue(inst.Operand(i), types, specials); err != nil {
|
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
|
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
|
// check basic block value and type
|
||||||
|
var errs []error
|
||||||
if err := c.checkValue(bb.AsValue(), types, specials); err != nil {
|
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
|
// check instructions
|
||||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||||
if err := c.checkInstruction(inst, types, specials); err != nil {
|
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
|
// check function value and type
|
||||||
|
var errs []error
|
||||||
if err := c.checkValue(fn, types, specials); err != nil {
|
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
|
// check basic blocks
|
||||||
for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||||
if err := c.checkBasicBlock(bb, types, specials); err != nil {
|
errs = append(errs, c.checkBasicBlock(bb, types, specials)...)
|
||||||
return fmt.Errorf("failed to validate basic block of function %s: %s", fn.Name(), err.Error())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) checkModule() error {
|
func (c *Compiler) checkModule() []error {
|
||||||
// check for any context mismatches
|
// check for any context mismatches
|
||||||
|
var errs []error
|
||||||
switch {
|
switch {
|
||||||
case c.mod.Context() == c.ctx:
|
case c.mod.Context() == c.ctx:
|
||||||
// this is correct
|
// this is correct
|
||||||
case c.mod.Context() == llvm.GlobalContext():
|
case c.mod.Context() == llvm.GlobalContext():
|
||||||
// somewhere we accidentally used the global context instead of a real context
|
// 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:
|
default:
|
||||||
// we used some other context by accident
|
// 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{}{}
|
types := map[llvm.Type]struct{}{}
|
||||||
specials := map[llvm.TypeKind]llvm.Type{}
|
specials := map[llvm.TypeKind]llvm.Type{}
|
||||||
for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||||
if err := c.checkFunction(fn, types, specials); err != nil {
|
errs = append(errs, c.checkFunction(fn, types, specials)...)
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for g := c.mod.FirstGlobal(); !g.IsNil(); g = llvm.NextGlobal(g) {
|
for g := c.mod.FirstGlobal(); !g.IsNil(); g = llvm.NextGlobal(g) {
|
||||||
if err := c.checkValue(g, types, specials); err != nil {
|
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/"):]
|
path = path[len(tinygoPath+"/src/"):]
|
||||||
}
|
}
|
||||||
switch path {
|
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
|
return path
|
||||||
default:
|
default:
|
||||||
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
||||||
@@ -251,13 +251,17 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
return []error{err}
|
return []error{err}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err = lprogram.Import(mainPath, wd)
|
_, err = lprogram.Import(mainPath, wd, token.Position{
|
||||||
|
Filename: "build command-line-arguments",
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []error{err}
|
return []error{err}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = lprogram.Import("runtime", "")
|
_, err = lprogram.Import("runtime", "", token.Position{
|
||||||
|
Filename: "build default import",
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []error{err}
|
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 {
|
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.
|
// Debug info for this function.
|
||||||
diparams := make([]llvm.Metadata, 0, len(f.Params))
|
diparams := make([]llvm.Metadata, 0, len(f.Params))
|
||||||
for _, param := range 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{
|
difunc := c.dibuilder.CreateFunction(c.difiles[filename], llvm.DIFunction{
|
||||||
Name: f.RelString(nil) + suffix,
|
Name: f.RelString(nil) + suffix,
|
||||||
LinkageName: f.LinkName() + suffix,
|
LinkageName: f.LinkName() + suffix,
|
||||||
File: c.difiles[filename],
|
File: c.getDIFile(filename),
|
||||||
Line: line,
|
Line: line,
|
||||||
Type: diFuncType,
|
Type: diFuncType,
|
||||||
LocalToUnit: true,
|
LocalToUnit: true,
|
||||||
@@ -801,21 +797,37 @@ func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix,
|
|||||||
return difunc
|
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) {
|
func (c *Compiler) parseFunc(frame *Frame) {
|
||||||
if c.DumpSSA() {
|
if c.DumpSSA() {
|
||||||
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
||||||
}
|
}
|
||||||
if !frame.fn.LLVMFn.IsDeclaration() {
|
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
|
return
|
||||||
}
|
}
|
||||||
if !frame.fn.IsExported() {
|
if !frame.fn.IsExported() {
|
||||||
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
||||||
frame.fn.LLVMFn.SetUnnamedAddr(true)
|
frame.fn.LLVMFn.SetUnnamedAddr(true)
|
||||||
}
|
}
|
||||||
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.
|
// Some functions have a pragma controlling the inlining level.
|
||||||
switch frame.fn.Inline() {
|
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)
|
return c.emitVolatileLoad(frame, instr)
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||||
return c.emitVolatileStore(frame, instr)
|
return c.emitVolatileStore(frame, instr)
|
||||||
|
case name == "runtime/interrupt.New" && len(fn.Blocks) == 0:
|
||||||
|
return c.emitInterruptGlobal(frame, instr)
|
||||||
}
|
}
|
||||||
|
|
||||||
targetFunc := c.ir.GetFunction(fn)
|
targetFunc := c.ir.GetFunction(fn)
|
||||||
|
|||||||
+48
-3
@@ -14,6 +14,7 @@ package compiler
|
|||||||
// frames.
|
// frames.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"github.com/tinygo-org/tinygo/ir"
|
"github.com/tinygo-org/tinygo/ir"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
@@ -34,6 +35,40 @@ func (c *Compiler) deferInitFunc(frame *Frame) {
|
|||||||
c.builder.CreateStore(llvm.ConstPointerNull(deferType), frame.deferPtr)
|
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
|
// emitDefer emits a single defer instruction, to be run when this function
|
||||||
// returns.
|
// returns.
|
||||||
func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) {
|
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, "")
|
deferFrame = c.builder.CreateInsertValue(deferFrame, value, i, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put this struct in an alloca.
|
// Put this struct in an allocation.
|
||||||
alloca := c.builder.CreateAlloca(deferFrameType, "defer.alloca")
|
var alloca llvm.Value
|
||||||
c.builder.CreateStore(deferFrame, alloca)
|
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() {
|
if c.NeedsStackObjects() {
|
||||||
c.trackPointer(alloca)
|
c.trackPointer(alloca)
|
||||||
}
|
}
|
||||||
|
c.builder.CreateStore(deferFrame, alloca)
|
||||||
|
|
||||||
// Push it on top of the linked list by replacing deferPtr.
|
// Push it on top of the linked list by replacing deferPtr.
|
||||||
allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package compiler
|
package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go/scanner"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Compiler) makeError(pos token.Pos, msg string) types.Error {
|
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) {
|
func (c *Compiler) addError(pos token.Pos, msg string) {
|
||||||
c.diagnostics = append(c.diagnostics, c.makeError(pos, msg))
|
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.
|
// scheduler, which runs in the background scheduling all coroutines.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -283,7 +282,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
if use.IsConstant() && use.Opcode() == llvm.PtrToInt {
|
if use.IsConstant() && use.Opcode() == llvm.PtrToInt {
|
||||||
for _, call := range getUses(use) {
|
for _, call := range getUses(use) {
|
||||||
if call.IsACallInst().IsNil() || call.CalledValue().Name() != "runtime.makeGoroutine" {
|
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
|
// 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
|
// Not a call instruction. Maybe a store to a global? In any
|
||||||
// case, this requires support for async calls across function
|
// case, this requires support for async calls across function
|
||||||
// pointers which is not yet supported.
|
// 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()
|
parent := use.InstructionParent().Parent()
|
||||||
for i := 0; i < use.OperandsCount()-1; i++ {
|
for i := 0; i < use.OperandsCount()-1; i++ {
|
||||||
if use.Operand(i) == f {
|
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)
|
worklist = append(worklist, parent)
|
||||||
@@ -805,7 +811,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
llvm.ConstNull(c.ctx.TokenType()),
|
llvm.ConstNull(c.ctx.TokenType()),
|
||||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
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)
|
c.builder.SetInsertPointBefore(inst)
|
||||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
|
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)
|
origFunc := ptrtointIn.Operand(0)
|
||||||
uses := getUses(goroutine)
|
uses := getUses(goroutine)
|
||||||
if len(uses) != 1 || uses[0].IsAIntToPtrInst().IsNil() {
|
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]
|
inttoptrOut := uses[0]
|
||||||
uses = getUses(inttoptrOut)
|
uses = getUses(inttoptrOut)
|
||||||
if len(uses) != 1 || uses[0].IsACallInst().IsNil() {
|
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]
|
realCall := uses[0]
|
||||||
|
|
||||||
|
|||||||
@@ -279,14 +279,14 @@ func (c *Compiler) getInterfaceMethodSet(typ *types.Named) llvm.Value {
|
|||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
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())
|
methods := make([]llvm.Value, typ.Underlying().(*types.Interface).NumMethods())
|
||||||
for i := range methods {
|
for i := range methods {
|
||||||
method := typ.Underlying().(*types.Interface).Method(i)
|
method := typ.Underlying().(*types.Interface).Method(i)
|
||||||
methods[i] = c.getMethodSignature(method)
|
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 = llvm.AddGlobal(c.mod, value.Type(), typ.String()+"$interface")
|
||||||
global.SetInitializer(value)
|
global.SetInitializer(value)
|
||||||
global.SetGlobalConstant(true)
|
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
|
// 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.
|
// used during the interface lowering pass.
|
||||||
func (c *Compiler) getMethodSignature(method *types.Func) llvm.Value {
|
func (c *Compiler) getMethodSignature(method *types.Func) llvm.Value {
|
||||||
signature := ir.MethodSignature(method)
|
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)
|
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
|
// makeGlobalArray creates a new LLVM global with the given name and integers as
|
||||||
// contents, and returns the global.
|
// contents, and returns the global.
|
||||||
// Note that it is left with the default linkage etc., you should set
|
// 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
|
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.
|
// Run the LLVM optimizer over the module.
|
||||||
// The inliner can be disabled (if necessary) by passing 0 to the inlinerThreshold.
|
// 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()
|
builder := llvm.NewPassManagerBuilder()
|
||||||
defer builder.Dispose()
|
defer builder.Dispose()
|
||||||
builder.SetOptLevel(optLevel)
|
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
|
// run a check of all of our code
|
||||||
if c.VerifyIR() {
|
if c.VerifyIR() {
|
||||||
err := c.checkModule()
|
errs := c.checkModule()
|
||||||
if err != nil {
|
if errs != nil {
|
||||||
return err
|
return errs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +45,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
// Run some preparatory passes for the Go optimizer.
|
// Run some preparatory passes for the Go optimizer.
|
||||||
goPasses := llvm.NewPassManager()
|
goPasses := llvm.NewPassManager()
|
||||||
defer goPasses.Dispose()
|
defer goPasses.Dispose()
|
||||||
|
goPasses.AddGlobalDCEPass()
|
||||||
goPasses.AddGlobalOptimizerPass()
|
goPasses.AddGlobalOptimizerPass()
|
||||||
goPasses.AddConstantPropagationPass()
|
goPasses.AddConstantPropagationPass()
|
||||||
goPasses.AddAggressiveDCEPass()
|
goPasses.AddAggressiveDCEPass()
|
||||||
@@ -56,7 +57,15 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
transform.OptimizeStringToBytes(c.mod)
|
transform.OptimizeStringToBytes(c.mod)
|
||||||
transform.OptimizeAllocs(c.mod)
|
transform.OptimizeAllocs(c.mod)
|
||||||
transform.LowerInterfaces(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
|
// After interfaces are lowered, there are many more opportunities for
|
||||||
// interprocedural optimizations. To get them to work, function
|
// 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()
|
err := c.LowerGoroutines()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return []error{err}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Must be run at any optimization level.
|
// Must be run at any optimization level.
|
||||||
transform.LowerInterfaces(c.mod)
|
transform.LowerInterfaces(c.mod)
|
||||||
c.LowerFuncValues()
|
if c.funcImplementation() == funcValueSwitch {
|
||||||
|
transform.LowerFuncValues(c.mod)
|
||||||
|
}
|
||||||
err := c.LowerGoroutines()
|
err := c.LowerGoroutines()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return []error{err}
|
||||||
|
}
|
||||||
|
errs := transform.LowerInterruptRegistrations(c.mod)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.VerifyIR() {
|
if c.VerifyIR() {
|
||||||
if err := c.checkModule(); err != nil {
|
if errs := c.checkModule(); errs != nil {
|
||||||
return err
|
return errs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := c.Verify(); err != nil {
|
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 {
|
if sizeLevel >= 2 {
|
||||||
@@ -141,7 +156,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
hasGCPass = transform.MakeGCStackSlots(c.mod) || hasGCPass
|
hasGCPass = transform.MakeGCStackSlots(c.mod) || hasGCPass
|
||||||
if hasGCPass {
|
if hasGCPass {
|
||||||
if err := c.Verify(); err != nil {
|
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 {
|
if k == types.UnsafePointer {
|
||||||
return s.PtrSize
|
return s.PtrSize
|
||||||
}
|
}
|
||||||
|
if k == types.Invalid {
|
||||||
|
return 0 // only relevant when there is a type error somewhere
|
||||||
|
}
|
||||||
panic("unknown basic type: " + t.String())
|
panic("unknown basic type: " + t.String())
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
n := t.Len()
|
n := t.Len()
|
||||||
|
|||||||
+31
-1
@@ -60,16 +60,46 @@ func (c *Compiler) getGlobal(g *ssa.Global) llvm.Value {
|
|||||||
info := c.getGlobalInfo(g)
|
info := c.getGlobalInfo(g)
|
||||||
llvmGlobal := c.mod.NamedGlobal(info.linkName)
|
llvmGlobal := c.mod.NamedGlobal(info.linkName)
|
||||||
if llvmGlobal.IsNil() {
|
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)
|
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
||||||
if !info.extern {
|
if !info.extern {
|
||||||
llvmGlobal.SetInitializer(llvm.ConstNull(llvmType))
|
llvmGlobal.SetInitializer(llvm.ConstNull(llvmType))
|
||||||
llvmGlobal.SetLinkage(llvm.InternalLinkage)
|
llvmGlobal.SetLinkage(llvm.InternalLinkage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
if info.align > c.targetData.ABITypeAlignment(llvmType) {
|
||||||
llvmGlobal.SetAlignment(info.align)
|
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
|
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())
|
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH())
|
||||||
}
|
}
|
||||||
switch c.GOOS() {
|
switch c.GOOS() {
|
||||||
case "linux":
|
case "linux", "freebsd":
|
||||||
// Return values: r0, r1 uintptr, err Errno
|
// Return values: r0, r1 uintptr, err Errno
|
||||||
// Pseudocode:
|
// Pseudocode:
|
||||||
// var err uintptr
|
// var err uintptr
|
||||||
|
|||||||
@@ -10,5 +10,5 @@ require (
|
|||||||
go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45
|
go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45
|
||||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect
|
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect
|
||||||
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef
|
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-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 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-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":
|
case callee.Name() == "runtime.hashmapStringSet":
|
||||||
// set a string key in the map
|
// 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)
|
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
||||||
keyLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
keyLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
||||||
valPtr := fr.getLocal(inst.Operand(3)).(*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)
|
m.PutString(keyBuf, keyLen, valPtr)
|
||||||
case callee.Name() == "runtime.hashmapBinarySet":
|
case callee.Name() == "runtime.hashmapBinarySet":
|
||||||
// set a binary (int etc.) key in the map
|
// 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)
|
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
||||||
valPtr := fr.getLocal(inst.Operand(2)).(*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)
|
m.PutBinary(keyBuf, valPtr)
|
||||||
case callee.Name() == "runtime.stringConcat":
|
case callee.Name() == "runtime.stringConcat":
|
||||||
// adding two strings together
|
// 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)}
|
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
|
||||||
case callee.Name() == "llvm.dbg.value":
|
case callee.Name() == "llvm.dbg.value":
|
||||||
// do nothing
|
// do nothing
|
||||||
|
case strings.HasPrefix(callee.Name(), "llvm.lifetime."):
|
||||||
|
// do nothing
|
||||||
case callee.Name() == "runtime.trackPointer":
|
case callee.Name() == "runtime.trackPointer":
|
||||||
// do nothing
|
// do nothing
|
||||||
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ func TestInterp(t *testing.T) {
|
|||||||
"basic",
|
"basic",
|
||||||
"slice-copy",
|
"slice-copy",
|
||||||
"consteval",
|
"consteval",
|
||||||
|
"map",
|
||||||
} {
|
} {
|
||||||
name := name // make tc local to this closure
|
name := name // make tc local to this closure
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
|||||||
+13
-8
@@ -1,6 +1,8 @@
|
|||||||
package interp
|
package interp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"tinygo.org/x/go-llvm"
|
"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
|
// returns whether this function has side effects and if it does, which globals
|
||||||
// it mentions anywhere in this function or any called functions.
|
// it mentions anywhere in this function or any called functions.
|
||||||
func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) {
|
func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) {
|
||||||
switch fn.Name() {
|
name := fn.Name()
|
||||||
case "runtime.alloc":
|
switch {
|
||||||
|
case name == "runtime.alloc":
|
||||||
// Cannot be scanned but can be interpreted.
|
// Cannot be scanned but can be interpreted.
|
||||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||||
case "runtime.nanotime":
|
case name == "runtime.nanotime":
|
||||||
// Fixed value at compile time.
|
// Fixed value at compile time.
|
||||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||||
case "runtime._panic":
|
case name == "runtime._panic":
|
||||||
return &sideEffectResult{severity: sideEffectLimited}, nil
|
return &sideEffectResult{severity: sideEffectLimited}, nil
|
||||||
case "runtime.interfaceImplements":
|
case name == "runtime.interfaceImplements":
|
||||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||||
case "runtime.sliceCopy":
|
case name == "runtime.sliceCopy":
|
||||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||||
case "runtime.trackPointer":
|
case name == "runtime.trackPointer":
|
||||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
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
|
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||||
}
|
}
|
||||||
if fn.IsDeclaration() {
|
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
|
||||||
|
}
|
||||||
@@ -367,11 +367,13 @@ func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !keyPtr.Underlying.IsAConstantExpr().IsNil() {
|
||||||
if keyPtr.Underlying.Opcode() == llvm.BitCast {
|
if keyPtr.Underlying.Opcode() == llvm.BitCast {
|
||||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||||
} else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr {
|
} else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr {
|
||||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
key := keyPtr.Load()
|
key := keyPtr.Load()
|
||||||
if v.KeyType.IsNil() {
|
if v.KeyType.IsNil() {
|
||||||
v.KeyType = key.Type()
|
v.KeyType = key.Type()
|
||||||
|
|||||||
@@ -29,11 +29,10 @@ type Function struct {
|
|||||||
*ssa.Function
|
*ssa.Function
|
||||||
LLVMFn llvm.Value
|
LLVMFn llvm.Value
|
||||||
module string // go:wasm-module
|
module string // go:wasm-module
|
||||||
linkName string // go:linkname, go:export, go:interrupt
|
linkName string // go:linkname, go:export
|
||||||
exported bool // go:export
|
exported bool // go:export
|
||||||
nobounds bool // go:nobounds
|
nobounds bool // go:nobounds
|
||||||
flag bool // used by dead code elimination
|
flag bool // used by dead code elimination
|
||||||
interrupt bool // go:interrupt
|
|
||||||
inline InlineType // go:inline
|
inline InlineType // go:inline
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,18 +242,6 @@ func (f *Function) parsePragmas() {
|
|||||||
f.inline = InlineHint
|
f.inline = InlineHint
|
||||||
case "//go:noinline":
|
case "//go:noinline":
|
||||||
f.inline = InlineNone
|
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":
|
case "//go:linkname":
|
||||||
if len(parts) != 3 || parts[1] != f.Name() {
|
if len(parts) != 3 || parts[1] != f.Name() {
|
||||||
continue
|
continue
|
||||||
@@ -288,14 +275,6 @@ func (f *Function) IsExported() bool {
|
|||||||
return f.exported || f.CName() != ""
|
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.
|
// Return the inline directive of this function.
|
||||||
func (f *Function) Inline() InlineType {
|
func (f *Function) Inline() InlineType {
|
||||||
return f.inline
|
return f.inline
|
||||||
|
|||||||
+14
-3
@@ -6,6 +6,7 @@ import (
|
|||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
"go/build"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
|
"go/scanner"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"os"
|
"os"
|
||||||
@@ -46,7 +47,7 @@ type Package struct {
|
|||||||
|
|
||||||
// Import loads the given package relative to srcDir (for the vendor directory).
|
// Import loads the given package relative to srcDir (for the vendor directory).
|
||||||
// It only loads the current package without recursion.
|
// 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 {
|
if p.Packages == nil {
|
||||||
p.Packages = make(map[string]*Package)
|
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)
|
buildPkg, err := ctx.Import(path, srcDir, build.ImportComment)
|
||||||
if err != nil {
|
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 {
|
if existingPkg, ok := p.Packages[buildPkg.ImportPath]; ok {
|
||||||
// Already imported, or at least started the import.
|
// 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 {
|
if _, ok := p.Imports[to]; ok {
|
||||||
continue
|
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 != nil {
|
||||||
if err, ok := err.(*ImportCycleError); ok {
|
if err, ok := err.(*ImportCycleError); ok {
|
||||||
err.Packages = append([]string{p.ImportPath}, err.Packages...)
|
err.Packages = append([]string{p.ImportPath}, err.Packages...)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"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.
|
// Flash builds and flashes the built binary to the given serial port.
|
||||||
func Flash(pkgName, port string, options *compileopts.Options) error {
|
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)
|
config, err := builder.NewConfig(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -201,7 +210,18 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
flashCmd = strings.Replace(flashCmd, "{port}", port, -1)
|
flashCmd = strings.Replace(flashCmd, "{port}", port, -1)
|
||||||
|
|
||||||
// Execute the command.
|
// 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.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Dir = goenv.Get("TINYGOROOT")
|
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
|
// Note: this command is expected to execute just before exiting, as it
|
||||||
// modifies global state.
|
// 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)
|
config, err := builder.NewConfig(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -268,12 +288,13 @@ func FlashGDB(pkgName, port string, ocdOutput bool, options *compileopts.Options
|
|||||||
gdbInterface, openocdInterface := config.Programmer()
|
gdbInterface, openocdInterface := config.Programmer()
|
||||||
switch gdbInterface {
|
switch gdbInterface {
|
||||||
case "msd", "command", "":
|
case "msd", "command", "":
|
||||||
if gdbInterface == "" {
|
|
||||||
gdbInterface = "command"
|
|
||||||
}
|
|
||||||
if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
|
if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
|
||||||
gdbInterface = "openocd"
|
gdbInterface = "openocd"
|
||||||
}
|
}
|
||||||
|
if len(config.Target.Emulator) != 0 {
|
||||||
|
// Assume QEMU as an emulator.
|
||||||
|
gdbInterface = "qemu"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the GDB server, if necessary.
|
// 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
|
// Make sure the daemon doesn't receive Ctrl-C that is intended for
|
||||||
// GDB (to break the currently executing program).
|
// GDB (to break the currently executing program).
|
||||||
setCommandAsDaemon(daemon)
|
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.
|
// Start now, and kill it on exit.
|
||||||
daemon.Start()
|
daemon.Start()
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -401,9 +442,18 @@ func touchSerialPortAt1200bps(port string) error {
|
|||||||
|
|
||||||
func flashUF2UsingMSD(volume, tmppath string) error {
|
func flashUF2UsingMSD(volume, tmppath string) error {
|
||||||
// find standard UF2 info path
|
// find standard UF2 info path
|
||||||
infoPath := "/media/*/" + volume + "/INFO_UF2.TXT"
|
var infoPath string
|
||||||
if runtime.GOOS == "darwin" {
|
switch runtime.GOOS {
|
||||||
|
case "linux", "freebsd":
|
||||||
|
infoPath = "/media/*/" + volume + "/INFO_UF2.TXT"
|
||||||
|
case "darwin":
|
||||||
infoPath = "/Volumes/" + volume + "/INFO_UF2.TXT"
|
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)
|
d, err := filepath.Glob(infoPath)
|
||||||
@@ -419,9 +469,18 @@ func flashUF2UsingMSD(volume, tmppath string) error {
|
|||||||
|
|
||||||
func flashHexUsingMSD(volume, tmppath string) error {
|
func flashHexUsingMSD(volume, tmppath string) error {
|
||||||
// find expected volume path
|
// find expected volume path
|
||||||
destPath := "/media/*/" + volume
|
var destPath string
|
||||||
if runtime.GOOS == "darwin" {
|
switch runtime.GOOS {
|
||||||
|
case "linux", "freebsd":
|
||||||
|
destPath = "/media/*/" + volume
|
||||||
|
case "darwin":
|
||||||
destPath = "/Volumes/" + volume
|
destPath = "/Volumes/" + volume
|
||||||
|
case "windows":
|
||||||
|
path, err := windowsFindUSBDrive(volume)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
destPath = path + "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
d, err := filepath.Glob(destPath)
|
d, err := filepath.Glob(destPath)
|
||||||
@@ -435,6 +494,29 @@ func flashHexUsingMSD(volume, tmppath string) error {
|
|||||||
return moveFile(tmppath, d[0]+"/flash.hex")
|
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
|
// parseSize converts a human-readable size (with k/m/g suffix) into a plain
|
||||||
// number.
|
// number.
|
||||||
func parseSize(s string) (int64, error) {
|
func parseSize(s string) (int64, error) {
|
||||||
@@ -459,6 +541,56 @@ func parseSize(s string) (int64, error) {
|
|||||||
return n, err
|
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() {
|
func usage() {
|
||||||
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
||||||
fmt.Fprintln(os.Stderr, "version:", version)
|
fmt.Fprintln(os.Stderr, "version:", version)
|
||||||
@@ -526,7 +658,7 @@ func main() {
|
|||||||
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
||||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
||||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
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")
|
programmer := flag.String("programmer", "", "which hardware programmer to use")
|
||||||
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
||||||
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
||||||
@@ -629,7 +761,7 @@ func main() {
|
|||||||
usage()
|
usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
err := FlashGDB(flag.Arg(0), *port, *ocdOutput, options)
|
err := FlashGDB(flag.Arg(0), *ocdOutput, options)
|
||||||
handleCompilerError(err)
|
handleCompilerError(err)
|
||||||
}
|
}
|
||||||
case "run":
|
case "run":
|
||||||
|
|||||||
+48
-10
@@ -12,7 +12,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/loader"
|
"github.com/tinygo-org/tinygo/loader"
|
||||||
@@ -50,7 +52,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run("EmulatedCortexM3", func(t *testing.T) {
|
t.Run("EmulatedCortexM3", func(t *testing.T) {
|
||||||
runPlatTests("qemu", matches, t)
|
runPlatTests("cortex-m-qemu", matches, t)
|
||||||
})
|
})
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
@@ -67,6 +69,8 @@ func TestCompiler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runPlatTests(target string, matches []string, t *testing.T) {
|
func runPlatTests(target string, matches []string, t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
for _, path := range matches {
|
for _, path := range matches {
|
||||||
switch {
|
switch {
|
||||||
case target == "wasm":
|
case target == "wasm":
|
||||||
@@ -76,7 +80,7 @@ func runPlatTests(target string, matches []string, t *testing.T) {
|
|||||||
}
|
}
|
||||||
case target == "":
|
case target == "":
|
||||||
// run all tests on host
|
// run all tests on host
|
||||||
case target == "qemu":
|
case target == "cortex-m-qemu":
|
||||||
// all tests are supported
|
// all tests are supported
|
||||||
default:
|
default:
|
||||||
// cross-compilation of cgo is not yet supported
|
// 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.Run(filepath.Base(path), func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
runTest(path, target, t)
|
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) {
|
func runTest(path, target string, t *testing.T) {
|
||||||
// Get the expected output for this test.
|
// Get the expected output for this test.
|
||||||
txtpath := path[:len(path)-3] + ".txt"
|
txtpath := path[:len(path)-3] + ".txt"
|
||||||
if path[len(path)-1] == os.PathSeparator {
|
if path[len(path)-1] == os.PathSeparator {
|
||||||
txtpath = path + "out.txt"
|
txtpath = path + "out.txt"
|
||||||
}
|
}
|
||||||
f, err := os.Open(txtpath)
|
expected, err := ioutil.ReadFile(txtpath)
|
||||||
if err != nil {
|
|
||||||
t.Fatal("could not open expected output file:", err)
|
|
||||||
}
|
|
||||||
expected, err := ioutil.ReadAll(f)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("could not read expected output file:", err)
|
t.Fatal("could not read expected output file:", err)
|
||||||
}
|
}
|
||||||
@@ -130,7 +144,7 @@ func runTest(path, target string, t *testing.T) {
|
|||||||
WasmAbi: "js",
|
WasmAbi: "js",
|
||||||
}
|
}
|
||||||
binary := filepath.Join(tmpdir, "test")
|
binary := filepath.Join(tmpdir, "test")
|
||||||
err = Build("./"+path, binary, config)
|
err = runBuild("./"+path, binary, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errLoader, ok := err.(loader.Errors); ok {
|
if errLoader, ok := err.(loader.Errors); ok {
|
||||||
for _, err := range errLoader.Errs {
|
for _, err := range errLoader.Errs {
|
||||||
@@ -144,7 +158,9 @@ func runTest(path, target string, t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run the test.
|
// Run the test.
|
||||||
|
runComplete := make(chan struct{})
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
|
ranTooLong := false
|
||||||
if target == "" {
|
if target == "" {
|
||||||
cmd = exec.Command(binary)
|
cmd = exec.Command(binary)
|
||||||
} else {
|
} else {
|
||||||
@@ -160,13 +176,35 @@ func runTest(path, target string, t *testing.T) {
|
|||||||
}
|
}
|
||||||
stdout := &bytes.Buffer{}
|
stdout := &bytes.Buffer{}
|
||||||
cmd.Stdout = stdout
|
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 != "" {
|
if _, ok := err.(*exec.ExitError); ok && target != "" {
|
||||||
err = nil // workaround for QEMU
|
err = nil // workaround for QEMU
|
||||||
}
|
}
|
||||||
|
close(runComplete)
|
||||||
|
|
||||||
|
if ranTooLong {
|
||||||
|
stdout.WriteString("--- test ran too long, terminating...\n")
|
||||||
|
}
|
||||||
|
|
||||||
// putchar() prints CRLF, convert it to LF.
|
// putchar() prints CRLF, convert it to LF.
|
||||||
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
|
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
|
||||||
|
|||||||
@@ -3,11 +3,10 @@
|
|||||||
.type _start,@function
|
.type _start,@function
|
||||||
|
|
||||||
_start:
|
_start:
|
||||||
// Workaround for missing support of the la pseudo-instruction in Clang 8:
|
// Load the stack pointer.
|
||||||
// https://reviews.llvm.org/D55325
|
la sp, _stack_top
|
||||||
lui sp, %hi(_stack_top)
|
// Load the globals pointer. The program will load pointers relative to this
|
||||||
addi sp, sp, %lo(_stack_top)
|
// 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
|
// See: https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register
|
||||||
lui gp, %hi(__global_pointer$)
|
la gp, __global_pointer$
|
||||||
addi gp, gp, %lo(__global_pointer$)
|
|
||||||
call main
|
call main
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Hand created file. DO NOT DELETE.
|
// 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
|
// +build stm32,stm32f103xx
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ func main() {
|
|||||||
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
|
||||||
// timer fires 10 times per second
|
// timer fires 10 times per second
|
||||||
arm.SetupSystemTimer(machine.CPU_FREQUENCY / 10)
|
arm.SetupSystemTimer(machine.CPUFrequency() / 10)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
const CPU_FREQUENCY = 16000000
|
// Return the current CPU frequency in hertz.
|
||||||
|
func CPUFrequency() uint32 {
|
||||||
|
return 16000000
|
||||||
|
}
|
||||||
|
|
||||||
// LED on the Arduino
|
// LED on the Arduino
|
||||||
const LED Pin = 13
|
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.
|
// This contains the pin mappings for the Arduino Nano33 IoT board.
|
||||||
//
|
//
|
||||||
@@ -6,8 +6,6 @@
|
|||||||
//
|
//
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import "device/sam"
|
|
||||||
|
|
||||||
// used to reset into bootloader
|
// used to reset into bootloader
|
||||||
const RESET_MAGIC_VALUE = 0x07738135
|
const RESET_MAGIC_VALUE = 0x07738135
|
||||||
|
|
||||||
@@ -54,69 +52,23 @@ const (
|
|||||||
USBCDC_DP_PIN Pin = PA25
|
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
|
// UART1 pins
|
||||||
const (
|
const (
|
||||||
UART_TX_PIN Pin = PA22
|
UART_TX_PIN Pin = PA22
|
||||||
UART_RX_PIN Pin = PA23
|
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
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN Pin = A4 // SDA: SERCOM4/PAD[1]
|
SDA_PIN Pin = A4 // SDA: SERCOM4/PAD[1]
|
||||||
SCL_PIN Pin = A5 // SCL: 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
|
// SPI pins
|
||||||
const (
|
const (
|
||||||
SPI0_SCK_PIN Pin = A2 // SCK: SERCOM0/PAD[3]
|
SPI0_SCK_PIN Pin = D13 // SCK: SERCOM1/PAD[1]
|
||||||
SPI0_MOSI_PIN Pin = A3 // MOSI: SERCOM0/PAD[2]
|
SPI0_MOSI_PIN Pin = D11 // MOSI: SERCOM1/PAD[0]
|
||||||
SPI0_MISO_PIN Pin = A6 // MISO: SERCOM0/PAD[1]
|
SPI0_MISO_PIN Pin = D12 // MISO: SERCOM1/PAD[3]
|
||||||
)
|
|
||||||
|
|
||||||
// SPI on the Arduino Nano 33.
|
|
||||||
var (
|
|
||||||
SPI0 = SPI{
|
|
||||||
Bus: sam.SERCOM0_SPI,
|
|
||||||
SERCOM: 0,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NINA-W102 Pins
|
// NINA-W102 Pins
|
||||||
@@ -132,23 +84,9 @@ const (
|
|||||||
NINA_RX Pin = PA23
|
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
|
// I2S pins
|
||||||
const (
|
const (
|
||||||
I2S_SCK_PIN Pin = PA10
|
I2S_SCK_PIN Pin = PA10
|
||||||
I2S_SD_PIN Pin = PA08
|
I2S_SD_PIN Pin = PA08
|
||||||
I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino Nano 33.
|
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
|
package machine
|
||||||
|
|
||||||
import "device/stm32"
|
import (
|
||||||
|
"device/stm32"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
// https://wiki.stm32duino.com/index.php?title=File:Bluepillpinout.gif
|
// https://wiki.stm32duino.com/index.php?title=File:Bluepillpinout.gif
|
||||||
const (
|
const (
|
||||||
@@ -61,14 +64,12 @@ var (
|
|||||||
UART0 = UART{
|
UART0 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART1,
|
Bus: stm32.USART1,
|
||||||
IRQVal: stm32.IRQ_USART1,
|
|
||||||
}
|
}
|
||||||
UART1 = &UART0
|
UART1 = &UART0
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:export USART1_IRQHandler
|
func init() {
|
||||||
func handleUART1() {
|
UART0.interrupt = interrupt.New(stm32.IRQ_USART1, UART0.handleInterrupt)
|
||||||
UART1.Receive(byte((UART1.Bus.DR.Get() & 0xFF)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
// +build sam,atsamd21,circuitplay_express
|
// +build circuitplay_express
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import "device/sam"
|
|
||||||
|
|
||||||
// used to reset into bootloader
|
// used to reset into bootloader
|
||||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||||
|
|
||||||
@@ -68,20 +66,6 @@ const (
|
|||||||
UART_RX_PIN = PB09 // PORTB
|
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
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN = PB02 // I2C0 external
|
SDA_PIN = PB02 // I2C0 external
|
||||||
@@ -91,20 +75,6 @@ const (
|
|||||||
SCL1_PIN = PA01 // I2C1 internal
|
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)
|
// SPI pins (internal flash)
|
||||||
const (
|
const (
|
||||||
SPI0_SCK_PIN = PA21 // SCK: SERCOM3/PAD[3]
|
SPI0_SCK_PIN = PA21 // SCK: SERCOM3/PAD[3]
|
||||||
@@ -112,22 +82,9 @@ const (
|
|||||||
SPI0_MISO_PIN = PA16 // MISO: SERCOM3/PAD[0]
|
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
|
// I2S pins
|
||||||
const (
|
const (
|
||||||
I2S_SCK_PIN = PA10
|
I2S_SCK_PIN = PA10
|
||||||
I2S_SD_PIN = PA08
|
I2S_SD_PIN = PA08
|
||||||
I2S_WS_PIN = NoPin // no WS, instead uses SCK to sync
|
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
|
package machine
|
||||||
|
|
||||||
import "device/sam"
|
import (
|
||||||
|
"device/sam"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
// used to reset into bootloader
|
// used to reset into bootloader
|
||||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||||
@@ -60,9 +63,8 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:export SERCOM1_IRQHandler
|
func init() {
|
||||||
func handleUART1() {
|
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
|
||||||
defaultUART1Handler()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/sam"
|
"device/sam"
|
||||||
|
"runtime/interrupt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// used to reset into bootloader
|
// used to reset into bootloader
|
||||||
@@ -62,9 +63,8 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:export SERCOM1_IRQHandler
|
func init() {
|
||||||
func handleUART1() {
|
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
|
||||||
defaultUART1Handler()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import "device/stm32"
|
import (
|
||||||
|
"device/stm32"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PA0 = portA + 0
|
PA0 = portA + 0
|
||||||
@@ -100,14 +103,12 @@ var (
|
|||||||
UART0 = UART{
|
UART0 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART2,
|
Bus: stm32.USART2,
|
||||||
IRQVal: stm32.IRQ_USART2,
|
|
||||||
}
|
}
|
||||||
UART2 = &UART0
|
UART2 = &UART0
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:export USART2_IRQHandler
|
func init() {
|
||||||
func handleUART2() {
|
UART0.interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
|
||||||
UART2.Receive(byte((UART2.Bus.DR.Get() & 0xFF)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPI pins
|
// 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
|
package machine
|
||||||
|
|
||||||
import "device/sam"
|
import (
|
||||||
|
"device/sam"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
// used to reset into bootloader
|
// used to reset into bootloader
|
||||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||||
@@ -51,9 +54,8 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:export SERCOM1_IRQHandler
|
func init() {
|
||||||
func handleUART1() {
|
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM0, UART1.handleInterrupt)
|
||||||
defaultUART1Handler()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/avr"
|
"device/avr"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -140,7 +141,7 @@ func (i2c I2C) Configure(config I2CConfig) {
|
|||||||
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
||||||
// NOTE: TWBR should be 10 or higher for master mode.
|
// NOTE: TWBR should be 10 or higher for master mode.
|
||||||
// It is 72 for a 16mhz board with 100kHz TWI
|
// It is 72 for a 16mhz board with 100kHz TWI
|
||||||
avr.TWBR.Set(uint8(((CPU_FREQUENCY / config.Frequency) - 16) / 2))
|
avr.TWBR.Set(uint8(((CPUFrequency() / config.Frequency) - 16) / 2))
|
||||||
|
|
||||||
// Enable twi module.
|
// Enable twi module.
|
||||||
avr.TWCR.Set(avr.TWCR_TWEN)
|
avr.TWCR.Set(avr.TWCR_TWEN)
|
||||||
@@ -232,10 +233,22 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
config.BaudRate = 9600
|
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
|
// Set baud rate based on prescale formula from
|
||||||
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
||||||
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
||||||
ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
ps := ((CPUFrequency()+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
||||||
avr.UBRR0H.Set(uint8(ps >> 8))
|
avr.UBRR0H.Set(uint8(ps >> 8))
|
||||||
avr.UBRR0L.Set(uint8(ps & 0xff))
|
avr.UBRR0L.Set(uint8(ps & 0xff))
|
||||||
|
|
||||||
@@ -254,15 +267,3 @@ func (uart UART) WriteByte(c byte) error {
|
|||||||
avr.UDR0.Set(c) // send char
|
avr.UDR0.Set(c) // send char
|
||||||
return nil
|
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/arm"
|
||||||
"device/sam"
|
"device/sam"
|
||||||
"errors"
|
"errors"
|
||||||
|
"runtime/interrupt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 48000000
|
|
||||||
|
|
||||||
type PinMode uint8
|
type PinMode uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -35,74 +34,6 @@ const (
|
|||||||
PinInputPulldown PinMode = 12
|
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 (
|
const (
|
||||||
pinPadMapSERCOM0Pad0 byte = (0x10 << 1) | 0x00
|
pinPadMapSERCOM0Pad0 byte = (0x10 << 1) | 0x00
|
||||||
pinPadMapSERCOM1Pad0 byte = (0x20 << 1) | 0x00
|
pinPadMapSERCOM1Pad0 byte = (0x20 << 1) | 0x00
|
||||||
@@ -365,6 +296,7 @@ type UART struct {
|
|||||||
Buffer *RingBuffer
|
Buffer *RingBuffer
|
||||||
Bus *sam.SERCOM_USART_Type
|
Bus *sam.SERCOM_USART_Type
|
||||||
SERCOM uint8
|
SERCOM uint8
|
||||||
|
interrupt interrupt.Interrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -471,10 +403,7 @@ func (uart UART) Configure(config UARTConfig) error {
|
|||||||
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INTENSET_RXC)
|
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INTENSET_RXC)
|
||||||
|
|
||||||
// Enable RX IRQ.
|
// Enable RX IRQ.
|
||||||
// IRQ lines are in the same order as SERCOM instance numbers on SAMD21
|
uart.interrupt.Enable()
|
||||||
// chips, so the IRQ number can be trivially determined from the SERCOM
|
|
||||||
// number.
|
|
||||||
arm.EnableIRQ(sam.IRQ_SERCOM0 + uint32(uart.SERCOM))
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -485,7 +414,7 @@ func (uart UART) SetBaudRate(br uint32) {
|
|||||||
// BAUD = fref / (sampleRateValue * fbaud)
|
// BAUD = fref / (sampleRateValue * fbaud)
|
||||||
// (multiply by 8, to calculate fractional piece)
|
// (multiply by 8, to calculate fractional piece)
|
||||||
// uint32_t baudTimes8 = (SystemCoreClock * 8) / (16 * baudrate);
|
// 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.FP = (baudTimes8 % 8);
|
||||||
// sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8);
|
// sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8);
|
||||||
@@ -502,11 +431,12 @@ func (uart UART) WriteByte(c byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultUART1Handler handles the UART1 IRQ.
|
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||||
func defaultUART1Handler() {
|
// this UART instance.
|
||||||
|
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||||
// should reset IRQ
|
// should reset IRQ
|
||||||
UART1.Receive(byte((UART1.Bus.DATA.Get() & 0xFF)))
|
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
|
||||||
UART1.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C on the SAMD21.
|
// I2C on the SAMD21.
|
||||||
@@ -601,7 +531,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
|||||||
func (i2c I2C) SetBaudRate(br uint32) {
|
func (i2c I2C) SetBaudRate(br uint32) {
|
||||||
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
|
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
|
||||||
// SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
|
// 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)
|
i2c.Bus.BAUD.Set(baud)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -793,7 +723,7 @@ func (i2s I2S) Configure(config I2SConfig) {
|
|||||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_I2S_)
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_I2S_)
|
||||||
|
|
||||||
// setting clock rate for sample.
|
// 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.
|
// Switch Generic Clock Generator 3 to DFLL48M.
|
||||||
sam.GCLK.GENDIV.Set((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENDIV_ID_Pos) |
|
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
|
// Set synch speed for SPI
|
||||||
baudRate := (CPU_FREQUENCY / (2 * config.Frequency)) - 1
|
baudRate := (CPUFrequency() / (2 * config.Frequency)) - 1
|
||||||
spi.Bus.BAUD.Set(uint8(baudRate))
|
spi.Bus.BAUD.Set(uint8(baudRate))
|
||||||
|
|
||||||
// Enable SPI port.
|
// Enable SPI port.
|
||||||
@@ -1438,7 +1368,8 @@ func (usbcdc USBCDC) Configure(config UARTConfig) {
|
|||||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||||
|
|
||||||
// enable IRQ
|
// enable IRQ
|
||||||
arm.EnableIRQ(sam.IRQ_USB)
|
intr := interrupt.New(sam.IRQ_USB, handleUSB)
|
||||||
|
intr.Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePadCalibration() {
|
func handlePadCalibration() {
|
||||||
@@ -1484,8 +1415,7 @@ func handlePadCalibration() {
|
|||||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export USB_IRQHandler
|
func handleUSB(intr interrupt.Interrupt) {
|
||||||
func handleUSB() {
|
|
||||||
// reset all interrupt flags
|
// reset all interrupt flags
|
||||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||||
|
|||||||
@@ -11,10 +11,13 @@ import (
|
|||||||
"device/arm"
|
"device/arm"
|
||||||
"device/sam"
|
"device/sam"
|
||||||
"errors"
|
"errors"
|
||||||
|
"runtime/interrupt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 120000000
|
func CPUFrequency() uint32 {
|
||||||
|
return 120000000
|
||||||
|
}
|
||||||
|
|
||||||
type PinMode uint8
|
type PinMode uint8
|
||||||
|
|
||||||
@@ -1400,11 +1403,11 @@ func (usbcdc USBCDC) Configure(config UARTConfig) {
|
|||||||
// enable USB
|
// enable USB
|
||||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||||
|
|
||||||
// enable IRQ
|
// enable IRQ at highest priority
|
||||||
arm.EnableIRQ(sam.IRQ_USB_OTHER)
|
interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable()
|
||||||
arm.EnableIRQ(sam.IRQ_USB_SOF_HSOF)
|
interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable()
|
||||||
arm.EnableIRQ(sam.IRQ_USB_TRCPT0)
|
interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable()
|
||||||
arm.EnableIRQ(sam.IRQ_USB_TRCPT1)
|
interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePadCalibration() {
|
func handlePadCalibration() {
|
||||||
@@ -1450,27 +1453,7 @@ func handlePadCalibration() {
|
|||||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export USB_OTHER_IRQHandler
|
func handleUSBIRQ(interrupt.Interrupt) {
|
||||||
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() {
|
|
||||||
// reset all interrupt flags
|
// reset all interrupt flags
|
||||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import (
|
|||||||
"device/sifive"
|
"device/sifive"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 16000000
|
func CPUFrequency() uint32 {
|
||||||
|
return 16000000
|
||||||
|
}
|
||||||
|
|
||||||
type PinMode uint8
|
type PinMode uint8
|
||||||
|
|
||||||
@@ -108,7 +110,7 @@ func (spi SPI) Configure(config SPIConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// div = (SPI_CFG(dev)->f_sys / (2 * frequency)) - 1;
|
// 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)
|
spi.Bus.DIV.Set(div)
|
||||||
|
|
||||||
// set mode
|
// set mode
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/arm"
|
|
||||||
"device/nrf"
|
"device/nrf"
|
||||||
"errors"
|
"errors"
|
||||||
|
"runtime/interrupt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -88,8 +88,11 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
nrf.UART0.INTENSET.Set(nrf.UART_INTENSET_RXDRDY_Msk)
|
nrf.UART0.INTENSET.Set(nrf.UART_INTENSET_RXDRDY_Msk)
|
||||||
|
|
||||||
// Enable RX IRQ.
|
// Enable RX IRQ.
|
||||||
arm.SetPriority(nrf.IRQ_UART0, 0xc0) // low priority
|
intr := interrupt.New(nrf.IRQ_UART0, func(intr interrupt.Interrupt) {
|
||||||
arm.EnableIRQ(nrf.IRQ_UART0)
|
UART0.handleInterrupt()
|
||||||
|
})
|
||||||
|
intr.SetPriority(0xc0) // low priority
|
||||||
|
intr.Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBaudRate sets the communication speed for the UART.
|
// SetBaudRate sets the communication speed for the UART.
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import (
|
|||||||
"device/nrf"
|
"device/nrf"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 16000000
|
func CPUFrequency() uint32 {
|
||||||
|
return 16000000
|
||||||
|
}
|
||||||
|
|
||||||
// Get peripheral and pin number for this GPIO pin.
|
// Get peripheral and pin number for this GPIO pin.
|
||||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
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))
|
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export UART0_IRQHandler
|
|
||||||
func handleUART0() {
|
|
||||||
UART0.handleInterrupt()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i2c I2C) setPins(scl, sda Pin) {
|
func (i2c I2C) setPins(scl, sda Pin) {
|
||||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 64000000
|
func CPUFrequency() uint32 {
|
||||||
|
return 64000000
|
||||||
|
}
|
||||||
|
|
||||||
// Get peripheral and pin number for this GPIO pin.
|
// Get peripheral and pin number for this GPIO pin.
|
||||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
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))
|
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export UARTE0_UART0_IRQHandler
|
|
||||||
func handleUART0() {
|
|
||||||
UART0.handleInterrupt()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i2c I2C) setPins(scl, sda Pin) {
|
func (i2c I2C) setPins(scl, sda Pin) {
|
||||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 64000000
|
func CPUFrequency() uint32 {
|
||||||
|
return 64000000
|
||||||
|
}
|
||||||
|
|
||||||
// Get peripheral and pin number for this GPIO pin.
|
// Get peripheral and pin number for this GPIO pin.
|
||||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
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))
|
nrf.UART0.PSEL.RXD.Set(uint32(rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export UARTE0_UART0_IRQHandler
|
|
||||||
func handleUART0() {
|
|
||||||
UART0.handleInterrupt()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i2c I2C) setPins(scl, sda Pin) {
|
func (i2c I2C) setPins(scl, sda Pin) {
|
||||||
i2c.Bus.PSEL.SCL.Set(uint32(scl))
|
i2c.Bus.PSEL.SCL.Set(uint32(scl))
|
||||||
i2c.Bus.PSEL.SDA.Set(uint32(sda))
|
i2c.Bus.PSEL.SDA.Set(uint32(sda))
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ package machine
|
|||||||
// Peripheral abstraction layer for the stm32.
|
// Peripheral abstraction layer for the stm32.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/arm"
|
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
"errors"
|
"errors"
|
||||||
|
"runtime/interrupt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 72000000
|
func CPUFrequency() uint32 {
|
||||||
|
return 72000000
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PinInput PinMode = 0 // Input mode
|
PinInput PinMode = 0 // Input mode
|
||||||
@@ -111,7 +113,7 @@ func (p Pin) Get() bool {
|
|||||||
type UART struct {
|
type UART struct {
|
||||||
Buffer *RingBuffer
|
Buffer *RingBuffer
|
||||||
Bus *stm32.USART_Type
|
Bus *stm32.USART_Type
|
||||||
IRQVal uint32
|
interrupt interrupt.Interrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the UART.
|
// 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)
|
uart.Bus.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
||||||
|
|
||||||
// Enable RX IRQ
|
// Enable RX IRQ
|
||||||
arm.SetPriority(uart.IRQVal, 0xc0)
|
uart.interrupt.SetPriority(0xc0)
|
||||||
arm.EnableIRQ(uart.IRQVal)
|
uart.interrupt.Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBaudRate sets the communication speed for the UART.
|
// SetBaudRate sets the communication speed for the UART.
|
||||||
@@ -163,10 +165,10 @@ func (uart UART) SetBaudRate(br uint32) {
|
|||||||
var divider uint32
|
var divider uint32
|
||||||
if uart.Bus == stm32.USART1 {
|
if uart.Bus == stm32.USART1 {
|
||||||
// first divide by PCLK2 prescaler (div 1) and then desired baudrate
|
// first divide by PCLK2 prescaler (div 1) and then desired baudrate
|
||||||
divider = CPU_FREQUENCY / br
|
divider = CPUFrequency() / br
|
||||||
} else {
|
} else {
|
||||||
// first divide by PCLK1 prescaler (div 2) and then desired baudrate
|
// 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)
|
uart.Bus.BRR.Set(divider)
|
||||||
}
|
}
|
||||||
@@ -180,6 +182,12 @@ func (uart UART) WriteByte(c byte) error {
|
|||||||
return nil
|
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.
|
// SPI on the STM32.
|
||||||
type SPI struct {
|
type SPI struct {
|
||||||
Bus *stm32.SPI_Type
|
Bus *stm32.SPI_Type
|
||||||
@@ -360,7 +368,7 @@ func (i2c I2C) Configure(config I2CConfig) {
|
|||||||
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_PE)
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_PE)
|
||||||
|
|
||||||
// pclk1 clock speed is main frequency divided by PCLK1 prescaler (div 2)
|
// 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
|
// set freqency range to PCLK1 clock speed in MHz
|
||||||
// aka setting the value 36 means to use 36 MHz clock
|
// aka setting the value 36 means to use 36 MHz clock
|
||||||
|
|||||||
@@ -5,11 +5,13 @@ package machine
|
|||||||
// Peripheral abstraction layer for the stm32.
|
// Peripheral abstraction layer for the stm32.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/arm"
|
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
|
"runtime/interrupt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 168000000
|
func CPUFrequency() uint32 {
|
||||||
|
return 168000000
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Mode Flag
|
// 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)
|
stm32.USART2.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
||||||
|
|
||||||
// Enable RX IRQ.
|
// Enable RX IRQ.
|
||||||
arm.SetPriority(stm32.IRQ_USART2, 0xc0)
|
intr := interrupt.New(stm32.IRQ_USART2, func(interrupt.Interrupt) {
|
||||||
arm.EnableIRQ(stm32.IRQ_USART2)
|
UART1.Receive(byte((stm32.USART2.DR.Get() & 0xFF)))
|
||||||
|
})
|
||||||
|
intr.SetPriority(0xc0)
|
||||||
|
intr.Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteByte writes a byte of data to the UART.
|
// WriteByte writes a byte of data to the UART.
|
||||||
@@ -213,8 +218,3 @@ func (uart UART) WriteByte(c byte) error {
|
|||||||
}
|
}
|
||||||
return nil
|
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
|
package machine
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build darwin linux,!baremetal
|
// +build darwin linux,!baremetal freebsd,!baremetal
|
||||||
|
|
||||||
package os
|
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 (
|
import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
_ "runtime/interrupt" // make sure the interrupt handler is defined
|
||||||
)
|
)
|
||||||
|
|
||||||
type timeUnit int64
|
type timeUnit int64
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"device/arm"
|
"device/arm"
|
||||||
"device/sam"
|
"device/sam"
|
||||||
"machine"
|
"machine"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@@ -214,8 +215,14 @@ func initRTC() {
|
|||||||
sam.RTC_MODE0.CTRL.SetBits(sam.RTC_MODE0_CTRL_ENABLE)
|
sam.RTC_MODE0.CTRL.SetBits(sam.RTC_MODE0_CTRL_ENABLE)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
|
|
||||||
arm.SetPriority(sam.IRQ_RTC, 0xc0)
|
intr := interrupt.New(sam.IRQ_RTC, func(intr interrupt.Interrupt) {
|
||||||
arm.EnableIRQ(sam.IRQ_RTC)
|
// 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() {
|
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() {
|
func initUSBClock() {
|
||||||
// Turn on clock for USB
|
// Turn on clock for USB
|
||||||
sam.PM.APBBMASK.SetBits(sam.PM_APBBMASK_USB_)
|
sam.PM.APBBMASK.SetBits(sam.PM_APBBMASK_USB_)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build qemu
|
// +build cortexm,qemu
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"machine"
|
"machine"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"device/riscv"
|
|
||||||
"device/sifive"
|
"device/sifive"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -82,34 +81,4 @@ func putchar(c byte) {
|
|||||||
machine.UART0.WriteByte(c)
|
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
|
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/arm"
|
||||||
"device/nrf"
|
"device/nrf"
|
||||||
"machine"
|
"machine"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,8 +44,13 @@ func initLFCLK() {
|
|||||||
|
|
||||||
func initRTC() {
|
func initRTC() {
|
||||||
nrf.RTC1.TASKS_START.Set(1)
|
nrf.RTC1.TASKS_START.Set(1)
|
||||||
arm.SetPriority(nrf.IRQ_RTC1, 0xc0) // low priority
|
intr := interrupt.New(nrf.IRQ_RTC1, func(intr interrupt.Interrupt) {
|
||||||
arm.EnableIRQ(nrf.IRQ_RTC1)
|
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) {
|
func putchar(c byte) {
|
||||||
@@ -96,10 +102,3 @@ func rtc_sleep(ticks uint32) {
|
|||||||
arm.Asm("wfi")
|
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/arm"
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
"machine"
|
"machine"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -101,8 +102,9 @@ func initRTC() {
|
|||||||
func initTIM() {
|
func initTIM() {
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||||
|
|
||||||
arm.SetPriority(stm32.IRQ_TIM3, 0xc3)
|
intr := interrupt.New(stm32.IRQ_TIM3, handleTIM3)
|
||||||
arm.EnableIRQ(stm32.IRQ_TIM3)
|
intr.SetPriority(0xc3)
|
||||||
|
intr.Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
const asyncScheduler = false
|
const asyncScheduler = false
|
||||||
@@ -163,7 +165,7 @@ func timerSleep(ticks uint32) {
|
|||||||
// The current scaling only supports a range of 200 usec to 6553 msec.
|
// 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.
|
// 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.
|
// Set duty aka duration.
|
||||||
// STM32 dividers use n-1, i.e. n counts from 0 to n-1.
|
// 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(interrupt.Interrupt) {
|
||||||
func handleTIM3() {
|
|
||||||
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
|
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||||
// Disable the timer.
|
// Disable the timer.
|
||||||
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN)
|
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"device/arm"
|
"device/arm"
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
"machine"
|
"machine"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -121,8 +122,9 @@ var timerWakeup volatile.Register8
|
|||||||
func initTIM3() {
|
func initTIM3() {
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||||
|
|
||||||
arm.SetPriority(stm32.IRQ_TIM3, 0xc3)
|
intr := interrupt.New(stm32.IRQ_TIM3, handleTIM3)
|
||||||
arm.EnableIRQ(stm32.IRQ_TIM3)
|
intr.SetPriority(0xc3)
|
||||||
|
intr.Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable the TIM7 clock.(tick count)
|
// Enable the TIM7 clock.(tick count)
|
||||||
@@ -139,8 +141,9 @@ func initTIM7() {
|
|||||||
// Enable the timer.
|
// Enable the timer.
|
||||||
stm32.TIM7.CR1.SetBits(stm32.TIM_CR1_CEN)
|
stm32.TIM7.CR1.SetBits(stm32.TIM_CR1_CEN)
|
||||||
|
|
||||||
arm.SetPriority(stm32.IRQ_TIM7, 0xc1)
|
intr := interrupt.New(stm32.IRQ_TIM7, handleTIM7)
|
||||||
arm.EnableIRQ(stm32.IRQ_TIM7)
|
intr.SetPriority(0xc1)
|
||||||
|
intr.Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
const asyncScheduler = false
|
const asyncScheduler = false
|
||||||
@@ -183,8 +186,7 @@ func timerSleep(ticks uint32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export TIM3_IRQHandler
|
func handleTIM3(interrupt.Interrupt) {
|
||||||
func handleTIM3() {
|
|
||||||
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
|
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||||
// Disable the timer.
|
// Disable the timer.
|
||||||
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN)
|
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN)
|
||||||
@@ -197,8 +199,7 @@ func handleTIM3() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export TIM7_IRQHandler
|
func handleTIM7(interrupt.Interrupt) {
|
||||||
func handleTIM7() {
|
|
||||||
if stm32.TIM7.SR.HasBits(stm32.TIM_SR_UIF) {
|
if stm32.TIM7.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||||
// clear the update flag
|
// clear the update flag
|
||||||
stm32.TIM7.SR.ClearBits(stm32.TIM_SR_UIF)
|
stm32.TIM7.SR.ClearBits(stm32.TIM_SR_UIF)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build darwin linux,!baremetal
|
// +build darwin linux,!baremetal freebsd,!baremetal
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
|||||||
@@ -76,3 +76,17 @@ func memset(ptr unsafe.Pointer, c byte, size uintptr) unsafe.Pointer {
|
|||||||
}
|
}
|
||||||
return ptr
|
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",
|
"linkerscript": "targets/lm3s6965.ld",
|
||||||
"extra-files": [
|
"extra-files": [
|
||||||
"targets/qemu.s"
|
"targets/cortex-m-qemu.s"
|
||||||
],
|
],
|
||||||
"emulator": ["qemu-system-arm", "-machine", "lm3s6965evb", "-semihosting", "-nographic", "-kernel"]
|
"emulator": ["qemu-system-arm", "-machine", "lm3s6965evb", "-semihosting", "-nographic", "-kernel"]
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// Generic Cortex-M interrupt vector.
|
// 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
|
.syntax unified
|
||||||
|
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
OUTPUT_ARCH(arm)
|
OUTPUT_ARCH(arm)
|
||||||
ENTRY(_start)
|
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 {
|
MEMORY {
|
||||||
ewram : ORIGIN = 0x02000000, LENGTH = 256K /* on-board work RAM (2 wait states) */
|
ewram : ORIGIN = 0x02000000, LENGTH = 256K /* on-board work RAM (2 wait states) */
|
||||||
iwram : ORIGIN = 0x03000000, LENGTH = 32K /* in-chip work RAM (faster) */
|
iwram : ORIGIN = 0x03000000, LENGTH = 32K-96 /* in-chip work RAM (faster) */
|
||||||
rom : ORIGIN = 0x08000000, LENGTH = 32M /* flash ROM */
|
rom : ORIGIN = 0x08000000, LENGTH = 32M /* flash ROM */
|
||||||
}
|
}
|
||||||
|
|
||||||
__iwram_top = ORIGIN(iwram) + LENGTH(iwram);;
|
__stack_size_irq = 1K;
|
||||||
_stack_size = 3K;
|
__stack_size_usr = 2K;
|
||||||
__sp_irq = _stack_top;
|
|
||||||
__sp_usr = _stack_top - 1K;
|
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
@@ -35,8 +36,10 @@ SECTIONS
|
|||||||
.stack (NOLOAD) :
|
.stack (NOLOAD) :
|
||||||
{
|
{
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
. += _stack_size;
|
__sp_irq = .;
|
||||||
_stack_top = .;
|
. += __stack_size_irq;
|
||||||
|
__sp_usr = .;
|
||||||
|
. += __stack_size_usr;
|
||||||
} >iwram
|
} >iwram
|
||||||
|
|
||||||
/* Start address (in flash) of .data, used by startup code. */
|
/* Start address (in flash) of .data, used by startup code. */
|
||||||
|
|||||||
@@ -17,9 +17,7 @@ _start:
|
|||||||
.byte 0x00,0x00 // Checksum (80000BEh)
|
.byte 0x00,0x00 // Checksum (80000BEh)
|
||||||
|
|
||||||
start_vector:
|
start_vector:
|
||||||
mov r0, #0x4000000 // REG_BASE
|
// Configure stacks
|
||||||
str r0, [r0, #0x208]
|
|
||||||
|
|
||||||
mov r0, #0x12 // Switch to IRQ Mode
|
mov r0, #0x12 // Switch to IRQ Mode
|
||||||
msr cpsr, r0
|
msr cpsr, r0
|
||||||
ldr sp, =__sp_irq // Set IRQ stack
|
ldr sp, =__sp_irq // Set IRQ stack
|
||||||
@@ -27,7 +25,21 @@ start_vector:
|
|||||||
msr cpsr, r0
|
msr cpsr, r0
|
||||||
ldr sp, =__sp_usr // Set user stack
|
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)
|
// Jump to user code (switching to Thumb mode)
|
||||||
ldr r3, =main
|
ldr r3, =main
|
||||||
bx r3
|
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",
|
"goarch": "arm",
|
||||||
"build-tags": ["tinygo.riscv", "baremetal", "linux", "arm"],
|
"build-tags": ["tinygo.riscv", "baremetal", "linux", "arm"],
|
||||||
"gc": "conservative",
|
"gc": "conservative",
|
||||||
"compiler": "riscv64-unknown-elf-gcc",
|
"compiler": "clang",
|
||||||
"linker": "riscv64-unknown-elf-ld",
|
"linker": "ld.lld",
|
||||||
|
"rtlib": "compiler-rt",
|
||||||
"cflags": [
|
"cflags": [
|
||||||
|
"--target=riscv32--none",
|
||||||
"-march=rv32imac",
|
"-march=rv32imac",
|
||||||
"-mabi=ilp32",
|
"-mabi=ilp32",
|
||||||
"-Os",
|
"-Os",
|
||||||
@@ -21,5 +23,6 @@
|
|||||||
],
|
],
|
||||||
"extra-files": [
|
"extra-files": [
|
||||||
"src/device/riscv/start.S"
|
"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