mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-30 00:28:41 +00:00
Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d913a62bc | |||
| 24a0f237d8 | |||
| eb9c2c276e | |||
| 519adf3aef | |||
| 4d79d473c4 | |||
| 91299b6466 | |||
| 8687f3f8f4 | |||
| f14127be76 | |||
| a9b2d8c294 | |||
| 960ab3fca4 | |||
| 94fec09b31 | |||
| 415c60551e | |||
| a9174d9184 | |||
| 46ccb6793e | |||
| 15c7d93ea9 | |||
| 6e26728391 | |||
| 0d34f933eb | |||
| 440dc8ed4e | |||
| aeb2e6f70a | |||
| e830acadf3 | |||
| 3f9609560e | |||
| e2aa3789c3 | |||
| c61657d22d | |||
| 4c0ebb5b41 | |||
| e17a2e6776 | |||
| bdfa4d28cf | |||
| 3745fb1c40 | |||
| a5ed993f8d | |||
| 3729fcfa9e | |||
| 8f8232aada | |||
| d5e11fa19b | |||
| 4ee7bf00e1 | |||
| c698e99880 | |||
| 4d5dafd360 | |||
| 1a32a68674 | |||
| 9a69c6bcca | |||
| 6841f9e245 | |||
| ef4ede43d1 | |||
| 31d3ac725f | |||
| 7ace991a2b | |||
| b258f3424b | |||
| b9cdfd9e9a | |||
| 360923abbf | |||
| ed9b2dbc03 | |||
| 3e521f710a | |||
| ecff6f8e0c | |||
| 7363bdc298 | |||
| 1cb9b948bc | |||
| 25cff20117 | |||
| ec467da83c | |||
| 5f77447e1a | |||
| 27fafb7ab5 | |||
| d1cc3c109d | |||
| 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 |
+33
-27
@@ -18,15 +18,12 @@ commands:
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||
sudo apt-get update
|
||||
sudo apt-get install \
|
||||
python3 \
|
||||
llvm<<parameters.llvm>>-dev \
|
||||
clang<<parameters.llvm>> \
|
||||
libclang<<parameters.llvm>>-dev \
|
||||
lld<<parameters.llvm>> \
|
||||
gcc-arm-linux-gnueabihf \
|
||||
libc6-dev-armel-cross \
|
||||
gcc-aarch64-linux-gnu \
|
||||
libc6-dev-arm64-cross \
|
||||
qemu-system-arm \
|
||||
qemu-user \
|
||||
gcc-avr \
|
||||
@@ -52,6 +49,18 @@ commands:
|
||||
key: llvm-source-9-v0
|
||||
paths:
|
||||
- llvm-project
|
||||
build-wasi-libc:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-v0
|
||||
- run:
|
||||
name: "Build wasi-libc"
|
||||
command: make wasi-libc CLANG=$PWD/llvm-build/bin/clang LLVM_AR=$PWD/llvm-build/bin/llvm-ar LLVM_NM=$PWD/llvm-build/bin/llvm-nm
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-v0
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
test-linux:
|
||||
steps:
|
||||
- checkout
|
||||
@@ -65,9 +74,17 @@ commands:
|
||||
- go-cache-v2-{{ checksum "go.mod" }}
|
||||
- llvm-source-linux
|
||||
- run: go install .
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-systemclang-v0
|
||||
- run: make wasi-libc
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-systemclang-v0
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- run: go test -v ./cgo ./compileopts ./interp ./transform .
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest RISCV=0
|
||||
- run: make smoketest
|
||||
- save_cache:
|
||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
@@ -83,7 +100,6 @@ commands:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
sudo apt-get install \
|
||||
python3 \
|
||||
gcc-arm-linux-gnueabihf \
|
||||
binutils-arm-none-eabi \
|
||||
libc6-dev-armel-cross \
|
||||
@@ -122,13 +138,8 @@ commands:
|
||||
key: llvm-build-9-linux-v0-assert
|
||||
paths:
|
||||
llvm-build
|
||||
- run:
|
||||
name: "Create LLVM symlinks"
|
||||
command: |
|
||||
ln -s $PWD/llvm-build/bin/clang-9 /go/bin/clang-9
|
||||
ln -s $PWD/llvm-build/bin/ld.lld /go/bin/ld.lld-9
|
||||
ln -s $PWD/llvm-build/bin/wasm-ld /go/bin/wasm-ld-9
|
||||
- run: make ASSERT=1
|
||||
- build-wasi-libc
|
||||
- run:
|
||||
name: "Test TinyGo"
|
||||
command: make ASSERT=1 test
|
||||
@@ -139,7 +150,7 @@ commands:
|
||||
- ~/.cache/tinygo
|
||||
- /go/pkg/mod
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest TINYGO=build/tinygo RISCV=0
|
||||
- run: make smoketest TINYGO=build/tinygo
|
||||
build-linux:
|
||||
steps:
|
||||
- checkout
|
||||
@@ -148,7 +159,6 @@ commands:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
sudo apt-get install \
|
||||
python3 \
|
||||
gcc-arm-linux-gnueabihf \
|
||||
binutils-arm-none-eabi \
|
||||
libc6-dev-armel-cross \
|
||||
@@ -187,12 +197,7 @@ commands:
|
||||
key: llvm-build-9-linux-v0
|
||||
paths:
|
||||
llvm-build
|
||||
- run:
|
||||
name: "Create LLVM symlinks"
|
||||
command: |
|
||||
ln -s $PWD/llvm-build/bin/clang-9 /go/bin/clang-9
|
||||
ln -s $PWD/llvm-build/bin/ld.lld /go/bin/ld.lld-9
|
||||
ln -s $PWD/llvm-build/bin/wasm-ld /go/bin/wasm-ld-9
|
||||
- build-wasi-libc
|
||||
- run:
|
||||
name: "Test TinyGo"
|
||||
command: make test
|
||||
@@ -216,11 +221,6 @@ commands:
|
||||
tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz
|
||||
ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo
|
||||
tinygo version
|
||||
- run:
|
||||
name: "Download SiFive GNU toolchain"
|
||||
command: |
|
||||
curl -O https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-linux-ubuntu14.tar.gz
|
||||
sudo tar -C /usr/local --strip-components=1 -xf riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-linux-ubuntu14.tar.gz
|
||||
- run: make smoketest
|
||||
build-macos:
|
||||
steps:
|
||||
@@ -264,10 +264,16 @@ commands:
|
||||
key: llvm-build-9-macos-v0
|
||||
paths:
|
||||
llvm-build
|
||||
- restore_cache:
|
||||
keys:
|
||||
- wasi-libc-sysroot-macos-v0
|
||||
- run:
|
||||
name: "Create LLVM symlinks"
|
||||
command: |
|
||||
ln -s $PWD/llvm-build/bin/clang-9 /usr/local/bin/clang-9
|
||||
name: "Build wasi-libc"
|
||||
command: make wasi-libc CLANG=$PWD/llvm-build/bin/clang LLVM_AR=$PWD/llvm-build/bin/llvm-ar LLVM_NM=$PWD/llvm-build/bin/llvm-nm
|
||||
- save_cache:
|
||||
key: wasi-libc-sysroot-macos-v0
|
||||
paths:
|
||||
- lib/wasi-libc/sysroot
|
||||
- run:
|
||||
name: "Test TinyGo"
|
||||
command: make test
|
||||
|
||||
@@ -14,3 +14,6 @@
|
||||
path = lib/compiler-rt
|
||||
url = https://github.com/llvm-mirror/compiler-rt.git
|
||||
branch = release_80
|
||||
[submodule "lib/wasi-libc"]
|
||||
path = lib/wasi-libc
|
||||
url = https://github.com/CraneStation/wasi-libc
|
||||
|
||||
@@ -1,3 +1,93 @@
|
||||
0.12.0
|
||||
---
|
||||
* **command line**
|
||||
- add initial FreeBSD support
|
||||
- remove getting a serial port in gdb subcommand
|
||||
- add support for debugging through JLinkGDBServer
|
||||
- fix CGo when cross compiling
|
||||
- remove default port check for Digispark as micronucleus communicates directly using HID
|
||||
- differentiate between various serial/USB error messages
|
||||
* **builder**
|
||||
- improve detection of Clang headers
|
||||
* **compiler**
|
||||
- fix assertion on empty interface
|
||||
- don't crash when encountering `types.Invalid`
|
||||
- revise defer to use heap allocations when running a variable number of times
|
||||
- improve error messages for failed imports
|
||||
- improve "function redeclared" error
|
||||
- add globaldce pass to start of optimization pipeline
|
||||
- add support for debugging globals
|
||||
- implement RISC-V CSR operations as intrinsics
|
||||
- add support for CGO_ENABLED environment variable
|
||||
- do not emit debug info for extern globals (bugfix)
|
||||
- add support for interrupts
|
||||
- implement maps for arbitrary keys
|
||||
- interp: error location for "unknown GEP" error
|
||||
- wasm-abi: create temporary allocas in the entry block
|
||||
* **cgo**
|
||||
- add support for symbols in `#define`
|
||||
- fix a bug in number tokenization
|
||||
* **standard library**
|
||||
- `machine`: avoid bytes package in USB logic
|
||||
- `runtime`: fix external address declarations
|
||||
- `runtime`: provide implementation for `internal/bytealg.IndexByte`
|
||||
* **targets**
|
||||
- `atsamd51`: fix volatile usage
|
||||
- `atsamd51`: fix ADC, updating to 12-bits precision
|
||||
- `atsamd51`: refactor SPI pin configuration to only look at pin numbers
|
||||
- `atsamd51`: switch UART to use new pin configuration
|
||||
- `atsamd51`: fix obvious bug in I2C code
|
||||
- `atsamd51`: use only the necessary UART interrupts
|
||||
- `atsamd51`: refactor I2C pin handling to auto-detect pin mode
|
||||
- `avr`: use a garbage collector
|
||||
- `fe310`: use CLINT peripheral for timekeeping
|
||||
- `fe310`: add support for PLIC interrupts
|
||||
- `fe310`: implement UART receive interrupts
|
||||
- `riscv`: support sleeping in QEMU
|
||||
- `riscv`: add bare-bones interrupt support
|
||||
- `riscv`: print exception PC and code
|
||||
- `wasm`: implement memcpy and memset
|
||||
- `wasm`: include wasi-libc
|
||||
- `wasm`: use wasi ABI for basic startup/stdout
|
||||
* **boards**
|
||||
- `arduino`: make avrdude command line compatible with Windows
|
||||
- `arduino-nano`: add this board
|
||||
- `arduino-nano33`: fix UART1 and UART2
|
||||
- `circuitplay-bluefruit`: add this board
|
||||
- `digispark`: add clock speed and pin mappings
|
||||
- `gameboy-advance`: include compiler-rt in build
|
||||
- `gameboy-advance`: implement interrupt handler
|
||||
- `hifive1b`: add support for gdb subcommand
|
||||
- `pyportal`: add this board
|
||||
- `pyportal`: remove manual SPI pin mapping as now handled by default
|
||||
|
||||
|
||||
0.11.0
|
||||
---
|
||||
* **command line**
|
||||
- add support for QEMU in `gdb` subcommand
|
||||
- use builtin Clang when building statically, dropping the clang-9 dependency
|
||||
- search for default serial port on both macOS and Linux
|
||||
- windows: support `tinygo flash` directly by using win32 wmi
|
||||
* **compiler**
|
||||
- add location information to the IR checker
|
||||
- make reflection sidetables constant globals
|
||||
- improve error locations in goroutine lowering
|
||||
- interp: improve support for maps with string keys
|
||||
- interp: add runtime fallback for mapassign operations
|
||||
* **standard library**
|
||||
- `machine`: add support for `SPI.Tx()` on play.tinygo.org
|
||||
- `machine`: rename `CPU_FREQUENCY` to `CPUFrequency()`
|
||||
* **targets**
|
||||
- `adafruit-pybadge`: add Adafruit Pybadge
|
||||
- `arduino-nano33`: allow simulation on play.tinygo.org
|
||||
- `arduino-nano33`: fix default SPI pin numbers to be D13/D11/D12
|
||||
- `circuitplay-express`: allow simulation on play.tinygo.org
|
||||
- `hifive1-qemu`: add target for testing RISC-V bare metal in QEMU
|
||||
- `riscv`: fix heap corruption due to changes in LLVM 9
|
||||
- `riscv`: add support for compiler-rt
|
||||
- `qemu`: rename to `cortex-m-qemu`
|
||||
|
||||
0.10.0
|
||||
---
|
||||
* **command line**
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to make participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all project spaces, and it also applies when
|
||||
an individual is representing the project or its community in public spaces.
|
||||
Examples of representing a project or community include using an official
|
||||
project e-mail address, posting via an official social media account, or acting
|
||||
as an appointed representative at an online or offline event. Representation of
|
||||
a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at [conduct@tinygo.org](mailto:conduct@tinygo.org). All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
@@ -15,3 +15,4 @@ Ayke van Laethem <aykevanlaethem@gmail.com>
|
||||
Daniel Esteban <conejo@conejo.me>
|
||||
Loon, LLC.
|
||||
Ron Evans <ron@hybridgroup.com>
|
||||
Jaden Weiss <jaden@jadendw.dev>
|
||||
|
||||
+5
-12
@@ -41,9 +41,8 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils python3 make binutils-avr gcc-avr avr-libc && \
|
||||
apt-get install -y apt-utils make binutils-avr gcc-avr avr-libc && \
|
||||
make gen-device-avr && \
|
||||
apt-get remove -y python3 && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean
|
||||
|
||||
@@ -59,11 +58,8 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils python3 make clang-9 && \
|
||||
make gen-device-nrf && make gen-device-stm32 && \
|
||||
apt-get remove -y python3 && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean
|
||||
apt-get install -y apt-utils make clang-9 && \
|
||||
make gen-device-nrf && make gen-device-stm32
|
||||
|
||||
# tinygo-all stage installs the needed dependencies to compile TinyGo programs for all platforms.
|
||||
FROM tinygo-wasm AS tinygo-all
|
||||
@@ -74,10 +70,7 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
||||
|
||||
RUN cd /tinygo/ && \
|
||||
apt-get update && \
|
||||
apt-get install -y apt-utils python3 make clang-9 binutils-avr gcc-avr avr-libc && \
|
||||
make gen-device && \
|
||||
apt-get remove -y python3 && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get clean
|
||||
apt-get install -y apt-utils make clang-9 binutils-avr gcc-avr avr-libc && \
|
||||
make gen-device
|
||||
|
||||
CMD ["tinygo"]
|
||||
|
||||
@@ -3,6 +3,9 @@ Copyright (c) 2018-2019 TinyGo Authors. All rights reserved.
|
||||
TinyGo includes portions of the Go standard library.
|
||||
Copyright (c) 2009-2019 The Go Authors. All rights reserved.
|
||||
|
||||
TinyGo includes portions of LLVM, which is under the Apache License v2.0 with
|
||||
LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
@@ -7,6 +7,11 @@ LLVM_BUILDDIR ?= llvm-build
|
||||
CLANG_SRC ?= llvm-project/clang
|
||||
LLD_SRC ?= llvm-project/lld
|
||||
|
||||
# Default tool selection.
|
||||
CLANG ?= clang-9
|
||||
LLVM_AR ?= llvm-ar-9
|
||||
LLVM_NM ?= llvm-nm-9
|
||||
|
||||
# Go binary and GOROOT to select
|
||||
GO ?= go
|
||||
export GOROOT = $(shell $(GO) env GOROOT)
|
||||
@@ -14,9 +19,6 @@ export GOROOT = $(shell $(GO) env GOROOT)
|
||||
# md5sum binary
|
||||
MD5SUM = md5sum
|
||||
|
||||
# Python binary
|
||||
PYTHON ?= python
|
||||
|
||||
# tinygo binary for tests
|
||||
TINYGO ?= tinygo
|
||||
|
||||
@@ -34,7 +36,7 @@ endif
|
||||
|
||||
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-avr
|
||||
|
||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines debuginfodwarf executionengine instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
|
||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf executionengine instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
EXE = .exe
|
||||
@@ -65,6 +67,11 @@ $(LIBCLANG_PATH): $(LIBCLANG_FILES)
|
||||
else ifeq ($(shell uname -s),Darwin)
|
||||
MD5SUM = md5
|
||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||
else ifeq ($(shell uname -s),FreeBSD)
|
||||
MD5SUM = md5
|
||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||
START_GROUP = -Wl,--start-group
|
||||
END_GROUP = -Wl,--end-group
|
||||
else
|
||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||
START_GROUP = -Wl,--start-group
|
||||
@@ -78,7 +85,7 @@ LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -
|
||||
|
||||
# For static linking.
|
||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||
CGO_CXXFLAGS=-std=c++11
|
||||
CGO_LDFLAGS+=$(LIBCLANG_PATH) -std=c++11 -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||
endif
|
||||
@@ -97,24 +104,28 @@ fmt-check:
|
||||
gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32
|
||||
|
||||
gen-device-avr:
|
||||
$(PYTHON) ./tools/gen-device-avr.py lib/avr/packs/atmega src/device/avr/
|
||||
$(PYTHON) ./tools/gen-device-avr.py lib/avr/packs/tiny src/device/avr/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/avr
|
||||
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
|
||||
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
|
||||
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
|
||||
@GO111MODULE=off $(GO) fmt ./src/device/avr
|
||||
|
||||
gen-device-nrf:
|
||||
$(PYTHON) ./tools/gen-device-svd.py lib/nrfx/mdk/ src/device/nrf/ --source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk
|
||||
build/gen-device-svd: ./tools/gen-device-svd/*.go
|
||||
$(GO) build -o $@ ./tools/gen-device-svd/
|
||||
|
||||
gen-device-nrf: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk lib/nrfx/mdk/ src/device/nrf/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/nrf
|
||||
|
||||
gen-device-sam:
|
||||
$(PYTHON) ./tools/gen-device-svd.py lib/cmsis-svd/data/Atmel/ src/device/sam/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel
|
||||
gen-device-sam: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel lib/cmsis-svd/data/Atmel/ src/device/sam/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/sam
|
||||
|
||||
gen-device-sifive:
|
||||
$(PYTHON) ./tools/gen-device-svd.py lib/cmsis-svd/data/SiFive-Community/ src/device/sifive/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/SiFive-Community
|
||||
gen-device-sifive: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/SiFive-Community -interrupts=software lib/cmsis-svd/data/SiFive-Community/ src/device/sifive/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/sifive
|
||||
|
||||
gen-device-stm32:
|
||||
$(PYTHON) ./tools/gen-device-svd.py lib/cmsis-svd/data/STMicro/ src/device/stm32/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro
|
||||
gen-device-stm32: build/gen-device-svd
|
||||
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro lib/cmsis-svd/data/STMicro/ src/device/stm32/
|
||||
GO111MODULE=off $(GO) fmt ./src/device/stm32
|
||||
|
||||
|
||||
@@ -133,12 +144,19 @@ $(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||
cd $(LLVM_BUILDDIR); ninja
|
||||
|
||||
|
||||
# Build wasi-libc sysroot
|
||||
.PHONY: wasi-libc
|
||||
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
||||
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||
cd lib/wasi-libc && make -j4 WASM_CC=$(CLANG) WASM_AR=$(LLVM_AR) WASM_NM=$(LLVM_NM)
|
||||
|
||||
|
||||
# Build the Go compiler.
|
||||
tinygo:
|
||||
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -o build/tinygo$(EXE) -tags byollvm .
|
||||
|
||||
test:
|
||||
test: wasi-libc
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -tags byollvm ./cgo ./compileopts ./interp ./transform .
|
||||
|
||||
tinygo-test:
|
||||
@@ -187,6 +205,8 @@ smoketest:
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -o test.wasm -tags=pca10056 examples/blinky2
|
||||
@$(MD5SUM) test.wasm
|
||||
$(TINYGO) build -o test.wasm -tags=circuitplay_express examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
# test all targets/boards
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -218,6 +238,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.gba -target=gameboy-advance examples/gba-display
|
||||
@@ -226,8 +248,12 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pybadge examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=metro-m4-airlift examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pyportal examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f103rb examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pinetime-devkit0 examples/blinky1
|
||||
@@ -239,22 +265,25 @@ smoketest:
|
||||
ifneq ($(AVR), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-nano examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
ifneq ($(RISCV), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/export
|
||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/main
|
||||
|
||||
release: tinygo gen-device
|
||||
release: tinygo gen-device wasi-libc
|
||||
@mkdir -p build/release/tinygo/bin
|
||||
@mkdir -p build/release/tinygo/lib/clang/include
|
||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
||||
@mkdir -p build/release/tinygo/lib/nrfx
|
||||
@mkdir -p build/release/tinygo/lib/wasi-libc
|
||||
@mkdir -p build/release/tinygo/pkg/armv6m-none-eabi
|
||||
@mkdir -p build/release/tinygo/pkg/armv7m-none-eabi
|
||||
@mkdir -p build/release/tinygo/pkg/armv7em-none-eabi
|
||||
@@ -267,6 +296,7 @@ release: tinygo gen-device
|
||||
@cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt
|
||||
@cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt
|
||||
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
|
||||
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
|
||||
@cp -rp src build/release/tinygo/src
|
||||
@cp -rp targets build/release/tinygo/targets
|
||||
./build/tinygo build-builtins -target=armv6m-none-eabi -o build/release/tinygo/pkg/armv6m-none-eabi/compiler-rt.a
|
||||
|
||||
@@ -43,15 +43,19 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
The following 22 microcontroller boards are currently supported:
|
||||
The following 27 microcontroller boards are currently supported:
|
||||
|
||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
||||
* [Adafruit Feather M4](https://www.adafruit.com/product/3857)
|
||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
|
||||
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
|
||||
* [Adafruit PyBadge](https://www.adafruit.com/product/4200)
|
||||
* [Adafruit PyPortal](https://www.adafruit.com/product/4116)
|
||||
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
||||
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
|
||||
* [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot)
|
||||
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
||||
* [BBC micro:bit](https://microbit.org/)
|
||||
@@ -62,6 +66,7 @@ The following 22 microcontroller boards are currently supported:
|
||||
* [Nordic Semiconductor PCA10040](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK)
|
||||
* [Nordic Semiconductor PCA10056](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
|
||||
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
||||
* [PineTime DevKit](https://www.pine64.org/pinetime/)
|
||||
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
|
||||
* [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
||||
@@ -125,3 +130,5 @@ The original reasoning was: if [Python](https://micropython.org/) can run on mic
|
||||
## License
|
||||
|
||||
This project is licensed under the BSD 3-clause license, just like the [Go project](https://golang.org/LICENSE) itself.
|
||||
|
||||
Some code has been copied from the LLVM project and is therefore licensed under [a variant of the Apache 2.0 license](http://releases.llvm.org/9.0.0/LICENSE.TXT). This has been clearly indicated in the header of these files.
|
||||
|
||||
+15
-5
@@ -6,7 +6,7 @@ trigger:
|
||||
|
||||
jobs:
|
||||
- job: Build
|
||||
timeoutInMinutes: 180
|
||||
timeoutInMinutes: 240 # 4h
|
||||
pool:
|
||||
vmImage: 'VS2017-Win2016'
|
||||
steps:
|
||||
@@ -45,6 +45,16 @@ jobs:
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: choco install qemu
|
||||
- task: CacheBeta@0
|
||||
displayName: Cache wasi-libc sysroot
|
||||
inputs:
|
||||
key: wasi-libc-sysroot-v0
|
||||
path: lib/wasi-libc/sysroot
|
||||
- task: Bash@3
|
||||
displayName: Build wasi-libc
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: PATH=/usr/bin:$PATH make wasi-libc CLANG=$PWD/llvm-build/bin/clang LLVM_AR=$PWD/llvm-build/bin/llvm-ar LLVM_NM=$PWD/llvm-build/bin/llvm-nm
|
||||
- task: Bash@3
|
||||
displayName: Test TinyGo
|
||||
inputs:
|
||||
@@ -61,9 +71,9 @@ jobs:
|
||||
export PATH="/c/Go1.13/bin:$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
||||
unset GOROOT
|
||||
make release -j4
|
||||
- publish: $(System.DefaultWorkingDirectory)/build/release.tar.gz
|
||||
displayName: Publish tarball as artifact
|
||||
artifact: tinygo.windows-amd64.tar.gz
|
||||
- publish: $(System.DefaultWorkingDirectory)/build/release/tinygo
|
||||
displayName: Publish zip as artifact
|
||||
artifact: tinygo
|
||||
- task: Bash@3
|
||||
displayName: Smoke tests
|
||||
inputs:
|
||||
@@ -71,4 +81,4 @@ jobs:
|
||||
script: |
|
||||
export PATH="/c/Go1.13/bin:$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
||||
unset GOROOT
|
||||
make smoketest TINYGO=build/tinygo AVR=0 RISCV=0
|
||||
make smoketest TINYGO=build/tinygo AVR=0
|
||||
|
||||
+15
-24
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/tinygo-org/tinygo/compiler"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"github.com/tinygo-org/tinygo/interp"
|
||||
"github.com/tinygo-org/tinygo/transform"
|
||||
)
|
||||
|
||||
// Build performs a single package to executable Go build. It takes in a package
|
||||
@@ -33,10 +34,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||
// Compile Go code to IR.
|
||||
errs := c.Compile(pkgName)
|
||||
if len(errs) != 0 {
|
||||
if len(errs) == 1 {
|
||||
return errs[0]
|
||||
}
|
||||
return &MultiError{errs}
|
||||
return newMultiError(errs)
|
||||
}
|
||||
if config.Options.PrintIR {
|
||||
fmt.Println("; Generated LLVM IR:")
|
||||
@@ -64,7 +62,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||
// stack-allocated values.
|
||||
// Use -wasm-abi=generic to disable this behaviour.
|
||||
if config.Options.WasmAbi == "js" && strings.HasPrefix(config.Triple(), "wasm") {
|
||||
err := c.ExternalInt64AsPtr()
|
||||
err := transform.ExternalInt64AsPtr(c.Module())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -72,22 +70,23 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||
|
||||
// Optimization levels here are roughly the same as Clang, but probably not
|
||||
// exactly.
|
||||
errs = nil
|
||||
switch config.Options.Opt {
|
||||
case "none:", "0":
|
||||
err = c.Optimize(0, 0, 0) // -O0
|
||||
errs = c.Optimize(0, 0, 0) // -O0
|
||||
case "1":
|
||||
err = c.Optimize(1, 0, 0) // -O1
|
||||
errs = c.Optimize(1, 0, 0) // -O1
|
||||
case "2":
|
||||
err = c.Optimize(2, 0, 225) // -O2
|
||||
errs = c.Optimize(2, 0, 225) // -O2
|
||||
case "s":
|
||||
err = c.Optimize(2, 1, 225) // -Os
|
||||
errs = c.Optimize(2, 1, 225) // -Os
|
||||
case "z":
|
||||
err = c.Optimize(2, 2, 5) // -Oz, default
|
||||
errs = c.Optimize(2, 2, 5) // -Oz, default
|
||||
default:
|
||||
err = errors.New("unknown optimization level: -opt=" + config.Options.Opt)
|
||||
errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
if len(errs) > 0 {
|
||||
return newMultiError(errs)
|
||||
}
|
||||
if err := c.Verify(); err != nil {
|
||||
return errors.New("verification failure after LLVM optimization passes")
|
||||
@@ -154,11 +153,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||
for i, path := range config.ExtraFiles() {
|
||||
abspath := filepath.Join(root, path)
|
||||
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
|
||||
cmdNames := []string{config.Target.Compiler}
|
||||
if names, ok := commands[config.Target.Compiler]; ok {
|
||||
cmdNames = names
|
||||
}
|
||||
err := execCommand(cmdNames, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
|
||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", path, err}
|
||||
}
|
||||
@@ -170,11 +165,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||
for _, file := range pkg.CFiles {
|
||||
path := filepath.Join(pkg.Package.Dir, file)
|
||||
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+file+".o")
|
||||
cmdNames := []string{config.Target.Compiler}
|
||||
if names, ok := commands[config.Target.Compiler]; ok {
|
||||
cmdNames = names
|
||||
}
|
||||
err := execCommand(cmdNames, append(config.CFlags(), "-c", "-o", outpath, path)...)
|
||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, path)...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", path, err}
|
||||
}
|
||||
@@ -217,7 +208,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
||||
} else if outext == ".uf2" {
|
||||
// Get UF2 from the .elf file.
|
||||
tmppath = filepath.Join(dir, "main"+outext)
|
||||
err := convertELFFileToUF2File(executable, tmppath)
|
||||
err := convertELFFileToUF2File(executable, tmppath, config.Target.UF2FamilyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+6
-2
@@ -158,7 +158,7 @@ var aeabiBuiltins = []string{
|
||||
|
||||
func builtinFiles(target string) []string {
|
||||
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
||||
if strings.HasPrefix(target, "arm") {
|
||||
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||
builtins = append(builtins, aeabiBuiltins...)
|
||||
}
|
||||
return builtins
|
||||
@@ -236,7 +236,11 @@ func CompileBuiltins(target string, callback func(path string) error) error {
|
||||
// Note: -fdebug-prefix-map is necessary to make the output archive
|
||||
// reproducible. Otherwise the temporary directory is stored in the
|
||||
// archive itself, which varies each run.
|
||||
err := execCommand(commands["clang"], "-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir, "-o", objpath, srcpath)
|
||||
args := []string{"-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target=" + target, "-fdebug-prefix-map=" + dir + "=" + remapDir}
|
||||
if strings.HasPrefix(target, "riscv32-") {
|
||||
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
||||
}
|
||||
err := runCCompiler("clang", append(args, "-o", objpath, srcpath)...)
|
||||
if err != nil {
|
||||
return &commandError{"failed to build", srcpath, err}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,504 @@
|
||||
// +build byollvm
|
||||
|
||||
//===-- cc1as.cpp - Clang Assembler --------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This is the entry point to the clang -cc1as functionality, which implements
|
||||
// the direct interface to the LLVM MC based assembler.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Driver/DriverDiagnostic.h"
|
||||
#include "clang/Driver/Options.h"
|
||||
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
#include "clang/Frontend/Utils.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/MC/MCAsmBackend.h"
|
||||
#include "llvm/MC/MCAsmInfo.h"
|
||||
#include "llvm/MC/MCCodeEmitter.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCInstrInfo.h"
|
||||
#include "llvm/MC/MCObjectFileInfo.h"
|
||||
#include "llvm/MC/MCObjectWriter.h"
|
||||
#include "llvm/MC/MCParser/MCAsmParser.h"
|
||||
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
|
||||
#include "llvm/MC/MCRegisterInfo.h"
|
||||
#include "llvm/MC/MCSectionMachO.h"
|
||||
#include "llvm/MC/MCStreamer.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
#include "llvm/MC/MCTargetOptions.h"
|
||||
#include "llvm/Option/Arg.h"
|
||||
#include "llvm/Option/ArgList.h"
|
||||
#include "llvm/Option/OptTable.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/FormattedStream.h"
|
||||
#include "llvm/Support/Host.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Signals.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/Support/Timer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
using namespace clang;
|
||||
using namespace clang::driver;
|
||||
using namespace clang::driver::options;
|
||||
using namespace llvm;
|
||||
using namespace llvm::opt;
|
||||
|
||||
#include "cc1as.h"
|
||||
|
||||
bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||
ArrayRef<const char *> Argv,
|
||||
DiagnosticsEngine &Diags) {
|
||||
bool Success = true;
|
||||
|
||||
// Parse the arguments.
|
||||
std::unique_ptr<OptTable> OptTbl(createDriverOptTable());
|
||||
|
||||
const unsigned IncludedFlagsBitmask = options::CC1AsOption;
|
||||
unsigned MissingArgIndex, MissingArgCount;
|
||||
InputArgList Args = OptTbl->ParseArgs(Argv, MissingArgIndex, MissingArgCount,
|
||||
IncludedFlagsBitmask);
|
||||
|
||||
// Check for missing argument error.
|
||||
if (MissingArgCount) {
|
||||
Diags.Report(diag::err_drv_missing_argument)
|
||||
<< Args.getArgString(MissingArgIndex) << MissingArgCount;
|
||||
Success = false;
|
||||
}
|
||||
|
||||
// Issue errors on unknown arguments.
|
||||
for (const Arg *A : Args.filtered(OPT_UNKNOWN)) {
|
||||
auto ArgString = A->getAsString(Args);
|
||||
std::string Nearest;
|
||||
if (OptTbl->findNearest(ArgString, Nearest, IncludedFlagsBitmask) > 1)
|
||||
Diags.Report(diag::err_drv_unknown_argument) << ArgString;
|
||||
else
|
||||
Diags.Report(diag::err_drv_unknown_argument_with_suggestion)
|
||||
<< ArgString << Nearest;
|
||||
Success = false;
|
||||
}
|
||||
|
||||
// Construct the invocation.
|
||||
|
||||
// Target Options
|
||||
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
|
||||
Opts.CPU = Args.getLastArgValue(OPT_target_cpu);
|
||||
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
||||
|
||||
// Use the default target triple if unspecified.
|
||||
if (Opts.Triple.empty())
|
||||
Opts.Triple = llvm::sys::getDefaultTargetTriple();
|
||||
|
||||
// Language Options
|
||||
Opts.IncludePaths = Args.getAllArgValues(OPT_I);
|
||||
Opts.NoInitialTextSection = Args.hasArg(OPT_n);
|
||||
Opts.SaveTemporaryLabels = Args.hasArg(OPT_msave_temp_labels);
|
||||
// Any DebugInfoKind implies GenDwarfForAssembly.
|
||||
Opts.GenDwarfForAssembly = Args.hasArg(OPT_debug_info_kind_EQ);
|
||||
|
||||
if (const Arg *A = Args.getLastArg(OPT_compress_debug_sections,
|
||||
OPT_compress_debug_sections_EQ)) {
|
||||
if (A->getOption().getID() == OPT_compress_debug_sections) {
|
||||
// TODO: be more clever about the compression type auto-detection
|
||||
Opts.CompressDebugSections = llvm::DebugCompressionType::GNU;
|
||||
} else {
|
||||
Opts.CompressDebugSections =
|
||||
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
|
||||
.Case("none", llvm::DebugCompressionType::None)
|
||||
.Case("zlib", llvm::DebugCompressionType::Z)
|
||||
.Case("zlib-gnu", llvm::DebugCompressionType::GNU)
|
||||
.Default(llvm::DebugCompressionType::None);
|
||||
}
|
||||
}
|
||||
|
||||
Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations);
|
||||
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
|
||||
Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags);
|
||||
Opts.DwarfDebugProducer = Args.getLastArgValue(OPT_dwarf_debug_producer);
|
||||
Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir);
|
||||
Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name);
|
||||
|
||||
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ))
|
||||
Opts.DebugPrefixMap.insert(StringRef(Arg).split('='));
|
||||
|
||||
// Frontend Options
|
||||
if (Args.hasArg(OPT_INPUT)) {
|
||||
bool First = true;
|
||||
for (const Arg *A : Args.filtered(OPT_INPUT)) {
|
||||
if (First) {
|
||||
Opts.InputFile = A->getValue();
|
||||
First = false;
|
||||
} else {
|
||||
Diags.Report(diag::err_drv_unknown_argument) << A->getAsString(Args);
|
||||
Success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm);
|
||||
Opts.OutputPath = Args.getLastArgValue(OPT_o);
|
||||
Opts.SplitDwarfOutput = Args.getLastArgValue(OPT_split_dwarf_output);
|
||||
if (Arg *A = Args.getLastArg(OPT_filetype)) {
|
||||
StringRef Name = A->getValue();
|
||||
unsigned OutputType = StringSwitch<unsigned>(Name)
|
||||
.Case("asm", FT_Asm)
|
||||
.Case("null", FT_Null)
|
||||
.Case("obj", FT_Obj)
|
||||
.Default(~0U);
|
||||
if (OutputType == ~0U) {
|
||||
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name;
|
||||
Success = false;
|
||||
} else
|
||||
Opts.OutputType = FileType(OutputType);
|
||||
}
|
||||
Opts.ShowHelp = Args.hasArg(OPT_help);
|
||||
Opts.ShowVersion = Args.hasArg(OPT_version);
|
||||
|
||||
// Transliterate Options
|
||||
Opts.OutputAsmVariant =
|
||||
getLastArgIntValue(Args, OPT_output_asm_variant, 0, Diags);
|
||||
Opts.ShowEncoding = Args.hasArg(OPT_show_encoding);
|
||||
Opts.ShowInst = Args.hasArg(OPT_show_inst);
|
||||
|
||||
// Assemble Options
|
||||
Opts.RelaxAll = Args.hasArg(OPT_mrelax_all);
|
||||
Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack);
|
||||
Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings);
|
||||
Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic");
|
||||
Opts.TargetABI = Args.getLastArgValue(OPT_target_abi);
|
||||
Opts.IncrementalLinkerCompatible =
|
||||
Args.hasArg(OPT_mincremental_linker_compatible);
|
||||
Opts.SymbolDefs = Args.getAllArgValues(OPT_defsym);
|
||||
|
||||
// EmbedBitcode Option. If -fembed-bitcode is enabled, set the flag.
|
||||
// EmbedBitcode behaves the same for all embed options for assembly files.
|
||||
if (auto *A = Args.getLastArg(OPT_fembed_bitcode_EQ)) {
|
||||
Opts.EmbedBitcode = llvm::StringSwitch<unsigned>(A->getValue())
|
||||
.Case("all", 1)
|
||||
.Case("bitcode", 1)
|
||||
.Case("marker", 1)
|
||||
.Default(0);
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
static std::unique_ptr<raw_fd_ostream>
|
||||
getOutputStream(StringRef Path, DiagnosticsEngine &Diags, bool Binary) {
|
||||
// Make sure that the Out file gets unlinked from the disk if we get a
|
||||
// SIGINT.
|
||||
if (Path != "-")
|
||||
sys::RemoveFileOnSignal(Path);
|
||||
|
||||
std::error_code EC;
|
||||
auto Out = llvm::make_unique<raw_fd_ostream>(
|
||||
Path, EC, (Binary ? sys::fs::F_None : sys::fs::F_Text));
|
||||
if (EC) {
|
||||
Diags.Report(diag::err_fe_unable_to_open_output) << Path << EC.message();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return Out;
|
||||
}
|
||||
|
||||
bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||
// Get the target specific parser.
|
||||
std::string Error;
|
||||
const Target *TheTarget = TargetRegistry::lookupTarget(Opts.Triple, Error);
|
||||
if (!TheTarget)
|
||||
return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
|
||||
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
|
||||
MemoryBuffer::getFileOrSTDIN(Opts.InputFile);
|
||||
|
||||
if (std::error_code EC = Buffer.getError()) {
|
||||
Error = EC.message();
|
||||
return Diags.Report(diag::err_fe_error_reading) << Opts.InputFile;
|
||||
}
|
||||
|
||||
SourceMgr SrcMgr;
|
||||
|
||||
// Tell SrcMgr about this buffer, which is what the parser will pick up.
|
||||
unsigned BufferIndex = SrcMgr.AddNewSourceBuffer(std::move(*Buffer), SMLoc());
|
||||
|
||||
// Record the location of the include directories so that the lexer can find
|
||||
// it later.
|
||||
SrcMgr.setIncludeDirs(Opts.IncludePaths);
|
||||
|
||||
std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(Opts.Triple));
|
||||
assert(MRI && "Unable to create target register info!");
|
||||
|
||||
std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, Opts.Triple));
|
||||
assert(MAI && "Unable to create target asm info!");
|
||||
|
||||
// Ensure MCAsmInfo initialization occurs before any use, otherwise sections
|
||||
// may be created with a combination of default and explicit settings.
|
||||
MAI->setCompressDebugSections(Opts.CompressDebugSections);
|
||||
|
||||
MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations);
|
||||
|
||||
bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj;
|
||||
if (Opts.OutputPath.empty())
|
||||
Opts.OutputPath = "-";
|
||||
std::unique_ptr<raw_fd_ostream> FDOS =
|
||||
getOutputStream(Opts.OutputPath, Diags, IsBinary);
|
||||
if (!FDOS)
|
||||
return true;
|
||||
std::unique_ptr<raw_fd_ostream> DwoOS;
|
||||
if (!Opts.SplitDwarfOutput.empty())
|
||||
DwoOS = getOutputStream(Opts.SplitDwarfOutput, Diags, IsBinary);
|
||||
|
||||
// FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
|
||||
// MCObjectFileInfo needs a MCContext reference in order to initialize itself.
|
||||
std::unique_ptr<MCObjectFileInfo> MOFI(new MCObjectFileInfo());
|
||||
|
||||
MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr);
|
||||
|
||||
bool PIC = false;
|
||||
if (Opts.RelocationModel == "static") {
|
||||
PIC = false;
|
||||
} else if (Opts.RelocationModel == "pic") {
|
||||
PIC = true;
|
||||
} else {
|
||||
assert(Opts.RelocationModel == "dynamic-no-pic" &&
|
||||
"Invalid PIC model!");
|
||||
PIC = false;
|
||||
}
|
||||
|
||||
MOFI->InitMCObjectFileInfo(Triple(Opts.Triple), PIC, Ctx);
|
||||
if (Opts.SaveTemporaryLabels)
|
||||
Ctx.setAllowTemporaryLabels(false);
|
||||
if (Opts.GenDwarfForAssembly)
|
||||
Ctx.setGenDwarfForAssembly(true);
|
||||
if (!Opts.DwarfDebugFlags.empty())
|
||||
Ctx.setDwarfDebugFlags(StringRef(Opts.DwarfDebugFlags));
|
||||
if (!Opts.DwarfDebugProducer.empty())
|
||||
Ctx.setDwarfDebugProducer(StringRef(Opts.DwarfDebugProducer));
|
||||
if (!Opts.DebugCompilationDir.empty())
|
||||
Ctx.setCompilationDir(Opts.DebugCompilationDir);
|
||||
else {
|
||||
// If no compilation dir is set, try to use the current directory.
|
||||
SmallString<128> CWD;
|
||||
if (!sys::fs::current_path(CWD))
|
||||
Ctx.setCompilationDir(CWD);
|
||||
}
|
||||
if (!Opts.DebugPrefixMap.empty())
|
||||
for (const auto &KV : Opts.DebugPrefixMap)
|
||||
Ctx.addDebugPrefixMapEntry(KV.first, KV.second);
|
||||
if (!Opts.MainFileName.empty())
|
||||
Ctx.setMainFileName(StringRef(Opts.MainFileName));
|
||||
Ctx.setDwarfVersion(Opts.DwarfVersion);
|
||||
if (Opts.GenDwarfForAssembly)
|
||||
Ctx.setGenDwarfRootFile(Opts.InputFile,
|
||||
SrcMgr.getMemoryBuffer(BufferIndex)->getBuffer());
|
||||
|
||||
// Build up the feature string from the target feature list.
|
||||
std::string FS;
|
||||
if (!Opts.Features.empty()) {
|
||||
FS = Opts.Features[0];
|
||||
for (unsigned i = 1, e = Opts.Features.size(); i != e; ++i)
|
||||
FS += "," + Opts.Features[i];
|
||||
}
|
||||
|
||||
std::unique_ptr<MCStreamer> Str;
|
||||
|
||||
std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
|
||||
std::unique_ptr<MCSubtargetInfo> STI(
|
||||
TheTarget->createMCSubtargetInfo(Opts.Triple, Opts.CPU, FS));
|
||||
|
||||
raw_pwrite_stream *Out = FDOS.get();
|
||||
std::unique_ptr<buffer_ostream> BOS;
|
||||
|
||||
MCTargetOptions MCOptions;
|
||||
MCOptions.ABIName = Opts.TargetABI;
|
||||
|
||||
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
|
||||
if (Opts.OutputType == AssemblerInvocation::FT_Asm) {
|
||||
MCInstPrinter *IP = TheTarget->createMCInstPrinter(
|
||||
llvm::Triple(Opts.Triple), Opts.OutputAsmVariant, *MAI, *MCII, *MRI);
|
||||
|
||||
std::unique_ptr<MCCodeEmitter> CE;
|
||||
if (Opts.ShowEncoding)
|
||||
CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
|
||||
std::unique_ptr<MCAsmBackend> MAB(
|
||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||
|
||||
auto FOut = llvm::make_unique<formatted_raw_ostream>(*Out);
|
||||
Str.reset(TheTarget->createAsmStreamer(
|
||||
Ctx, std::move(FOut), /*asmverbose*/ true,
|
||||
/*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB),
|
||||
Opts.ShowInst));
|
||||
} else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
|
||||
Str.reset(createNullStreamer(Ctx));
|
||||
} else {
|
||||
assert(Opts.OutputType == AssemblerInvocation::FT_Obj &&
|
||||
"Invalid file type!");
|
||||
if (!FDOS->supportsSeeking()) {
|
||||
BOS = make_unique<buffer_ostream>(*FDOS);
|
||||
Out = BOS.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<MCCodeEmitter> CE(
|
||||
TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
|
||||
std::unique_ptr<MCAsmBackend> MAB(
|
||||
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||
std::unique_ptr<MCObjectWriter> OW =
|
||||
DwoOS ? MAB->createDwoObjectWriter(*Out, *DwoOS)
|
||||
: MAB->createObjectWriter(*Out);
|
||||
|
||||
Triple T(Opts.Triple);
|
||||
Str.reset(TheTarget->createMCObjectStreamer(
|
||||
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
|
||||
Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
|
||||
/*DWARFMustBeAtTheEnd*/ true));
|
||||
Str.get()->InitSections(Opts.NoExecStack);
|
||||
}
|
||||
|
||||
// When -fembed-bitcode is passed to clang_as, a 1-byte marker
|
||||
// is emitted in __LLVM,__asm section if the object file is MachO format.
|
||||
if (Opts.EmbedBitcode && Ctx.getObjectFileInfo()->getObjectFileType() ==
|
||||
MCObjectFileInfo::IsMachO) {
|
||||
MCSection *AsmLabel = Ctx.getMachOSection(
|
||||
"__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly());
|
||||
Str.get()->SwitchSection(AsmLabel);
|
||||
Str.get()->EmitZeros(1);
|
||||
}
|
||||
|
||||
// Assembly to object compilation should leverage assembly info.
|
||||
Str->setUseAssemblerInfoForParsing(true);
|
||||
|
||||
bool Failed = false;
|
||||
|
||||
std::unique_ptr<MCAsmParser> Parser(
|
||||
createMCAsmParser(SrcMgr, Ctx, *Str.get(), *MAI));
|
||||
|
||||
// FIXME: init MCTargetOptions from sanitizer flags here.
|
||||
std::unique_ptr<MCTargetAsmParser> TAP(
|
||||
TheTarget->createMCAsmParser(*STI, *Parser, *MCII, MCOptions));
|
||||
if (!TAP)
|
||||
Failed = Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
|
||||
|
||||
// Set values for symbols, if any.
|
||||
for (auto &S : Opts.SymbolDefs) {
|
||||
auto Pair = StringRef(S).split('=');
|
||||
auto Sym = Pair.first;
|
||||
auto Val = Pair.second;
|
||||
int64_t Value;
|
||||
// We have already error checked this in the driver.
|
||||
Val.getAsInteger(0, Value);
|
||||
Ctx.setSymbolValue(Parser->getStreamer(), Sym, Value);
|
||||
}
|
||||
|
||||
if (!Failed) {
|
||||
Parser->setTargetParser(*TAP.get());
|
||||
Failed = Parser->Run(Opts.NoInitialTextSection);
|
||||
}
|
||||
|
||||
// Close Streamer first.
|
||||
// It might have a reference to the output stream.
|
||||
Str.reset();
|
||||
// Close the output stream early.
|
||||
BOS.reset();
|
||||
FDOS.reset();
|
||||
|
||||
// Delete output file if there were errors.
|
||||
if (Failed) {
|
||||
if (Opts.OutputPath != "-")
|
||||
sys::fs::remove(Opts.OutputPath);
|
||||
if (!Opts.SplitDwarfOutput.empty() && Opts.SplitDwarfOutput != "-")
|
||||
sys::fs::remove(Opts.SplitDwarfOutput);
|
||||
}
|
||||
|
||||
return Failed;
|
||||
}
|
||||
|
||||
static void LLVMErrorHandler(void *UserData, const std::string &Message,
|
||||
bool GenCrashDiag) {
|
||||
DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
|
||||
|
||||
Diags.Report(diag::err_fe_error_backend) << Message;
|
||||
|
||||
// We cannot recover from llvm errors.
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
|
||||
// Initialize targets and assembly printers/parsers.
|
||||
InitializeAllTargetInfos();
|
||||
InitializeAllTargetMCs();
|
||||
InitializeAllAsmParsers();
|
||||
|
||||
// Construct our diagnostic client.
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
|
||||
TextDiagnosticPrinter *DiagClient
|
||||
= new TextDiagnosticPrinter(errs(), &*DiagOpts);
|
||||
DiagClient->setPrefix("clang -cc1as");
|
||||
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
||||
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);
|
||||
|
||||
// Set an error handler, so that any LLVM backend diagnostics go through our
|
||||
// error handler.
|
||||
ScopedFatalErrorHandler FatalErrorHandler
|
||||
(LLVMErrorHandler, static_cast<void*>(&Diags));
|
||||
|
||||
// Parse the arguments.
|
||||
AssemblerInvocation Asm;
|
||||
if (!AssemblerInvocation::CreateFromArgs(Asm, Argv, Diags))
|
||||
return 1;
|
||||
|
||||
if (Asm.ShowHelp) {
|
||||
std::unique_ptr<OptTable> Opts(driver::createDriverOptTable());
|
||||
Opts->PrintHelp(llvm::outs(), "clang -cc1as [options] file...",
|
||||
"Clang Integrated Assembler",
|
||||
/*Include=*/driver::options::CC1AsOption, /*Exclude=*/0,
|
||||
/*ShowAllAliases=*/false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Honor -version.
|
||||
//
|
||||
// FIXME: Use a better -version message?
|
||||
if (Asm.ShowVersion) {
|
||||
llvm::cl::PrintVersionMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Honor -mllvm.
|
||||
//
|
||||
// FIXME: Remove this, one day.
|
||||
if (!Asm.LLVMArgs.empty()) {
|
||||
unsigned NumArgs = Asm.LLVMArgs.size();
|
||||
auto Args = llvm::make_unique<const char*[]>(NumArgs + 2);
|
||||
Args[0] = "clang (LLVM option parsing)";
|
||||
for (unsigned i = 0; i != NumArgs; ++i)
|
||||
Args[i + 1] = Asm.LLVMArgs[i].c_str();
|
||||
Args[NumArgs + 1] = nullptr;
|
||||
llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get());
|
||||
}
|
||||
|
||||
// Execute the invocation, unless there were parsing errors.
|
||||
bool Failed = Diags.hasErrorOccurred() || ExecuteAssembler(Asm, Diags);
|
||||
|
||||
// If any timers were active but haven't been destroyed yet, print their
|
||||
// results now.
|
||||
TimerGroup::printAll(errs());
|
||||
|
||||
return !!Failed;
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
//===-- cc1as.h - Clang Assembler ----------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This is the entry point to the clang -cc1as functionality, which implements
|
||||
// the direct interface to the LLVM MC based assembler.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
/// Helper class for representing a single invocation of the assembler.
|
||||
struct AssemblerInvocation {
|
||||
/// @name Target Options
|
||||
/// @{
|
||||
|
||||
/// The name of the target triple to assemble for.
|
||||
std::string Triple;
|
||||
|
||||
/// If given, the name of the target CPU to determine which instructions
|
||||
/// are legal.
|
||||
std::string CPU;
|
||||
|
||||
/// The list of target specific features to enable or disable -- this should
|
||||
/// be a list of strings starting with '+' or '-'.
|
||||
std::vector<std::string> Features;
|
||||
|
||||
/// The list of symbol definitions.
|
||||
std::vector<std::string> SymbolDefs;
|
||||
|
||||
/// @}
|
||||
/// @name Language Options
|
||||
/// @{
|
||||
|
||||
std::vector<std::string> IncludePaths;
|
||||
unsigned NoInitialTextSection : 1;
|
||||
unsigned SaveTemporaryLabels : 1;
|
||||
unsigned GenDwarfForAssembly : 1;
|
||||
unsigned RelaxELFRelocations : 1;
|
||||
unsigned DwarfVersion;
|
||||
std::string DwarfDebugFlags;
|
||||
std::string DwarfDebugProducer;
|
||||
std::string DebugCompilationDir;
|
||||
std::map<const std::string, const std::string> DebugPrefixMap;
|
||||
llvm::DebugCompressionType CompressDebugSections =
|
||||
llvm::DebugCompressionType::None;
|
||||
std::string MainFileName;
|
||||
std::string SplitDwarfOutput;
|
||||
|
||||
/// @}
|
||||
/// @name Frontend Options
|
||||
/// @{
|
||||
|
||||
std::string InputFile;
|
||||
std::vector<std::string> LLVMArgs;
|
||||
std::string OutputPath;
|
||||
enum FileType {
|
||||
FT_Asm, ///< Assembly (.s) output, transliterate mode.
|
||||
FT_Null, ///< No output, for timing purposes.
|
||||
FT_Obj ///< Object file output.
|
||||
};
|
||||
FileType OutputType;
|
||||
unsigned ShowHelp : 1;
|
||||
unsigned ShowVersion : 1;
|
||||
|
||||
/// @}
|
||||
/// @name Transliterate Options
|
||||
/// @{
|
||||
|
||||
unsigned OutputAsmVariant;
|
||||
unsigned ShowEncoding : 1;
|
||||
unsigned ShowInst : 1;
|
||||
|
||||
/// @}
|
||||
/// @name Assembler Options
|
||||
/// @{
|
||||
|
||||
unsigned RelaxAll : 1;
|
||||
unsigned NoExecStack : 1;
|
||||
unsigned FatalWarnings : 1;
|
||||
unsigned IncrementalLinkerCompatible : 1;
|
||||
unsigned EmbedBitcode : 1;
|
||||
|
||||
/// The name of the relocation model to use.
|
||||
std::string RelocationModel;
|
||||
|
||||
/// The ABI targeted by the backend. Specified using -target-abi. Empty
|
||||
/// otherwise.
|
||||
std::string TargetABI;
|
||||
|
||||
/// @}
|
||||
|
||||
public:
|
||||
AssemblerInvocation() {
|
||||
Triple = "";
|
||||
NoInitialTextSection = 0;
|
||||
InputFile = "-";
|
||||
OutputPath = "-";
|
||||
OutputType = FT_Asm;
|
||||
OutputAsmVariant = 0;
|
||||
ShowInst = 0;
|
||||
ShowEncoding = 0;
|
||||
RelaxAll = 0;
|
||||
NoExecStack = 0;
|
||||
FatalWarnings = 0;
|
||||
IncrementalLinkerCompatible = 0;
|
||||
DwarfVersion = 0;
|
||||
EmbedBitcode = 0;
|
||||
}
|
||||
|
||||
static bool CreateFromArgs(AssemblerInvocation &Res,
|
||||
ArrayRef<const char *> Argv,
|
||||
DiagnosticsEngine &Diags);
|
||||
};
|
||||
|
||||
bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags);
|
||||
@@ -0,0 +1,97 @@
|
||||
// +build byollvm
|
||||
|
||||
#include <clang/Basic/DiagnosticOptions.h>
|
||||
#include <clang/CodeGen/CodeGenAction.h>
|
||||
#include <clang/Driver/Compilation.h>
|
||||
#include <clang/Driver/Driver.h>
|
||||
#include <clang/Frontend/CompilerInstance.h>
|
||||
#include <clang/Frontend/CompilerInvocation.h>
|
||||
#include <clang/Frontend/FrontendDiagnostic.h>
|
||||
#include <clang/Frontend/TextDiagnosticPrinter.h>
|
||||
#include <clang/FrontendTool/Utils.h>
|
||||
#include <llvm/ADT/IntrusiveRefCntPtr.h>
|
||||
#include <llvm/Option/Option.h>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace clang;
|
||||
|
||||
#include "cc1as.h"
|
||||
|
||||
// This file provides C wrappers for the builtin tools cc1 and cc1as
|
||||
// provided by Clang, and calls them as the driver would call them.
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool tinygo_clang_driver(int argc, char **argv) {
|
||||
std::vector<const char*> args(argv, argv + argc);
|
||||
|
||||
// The compiler invocation needs a DiagnosticsEngine so it can report problems
|
||||
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts = new clang::DiagnosticOptions();
|
||||
clang::TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts);
|
||||
clang::DiagnosticsEngine Diags(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs()), &*DiagOpts, &DiagnosticPrinter, false);
|
||||
|
||||
// Create the clang driver
|
||||
clang::driver::Driver TheDriver(args[0], llvm::sys::getDefaultTargetTriple(), Diags);
|
||||
|
||||
// Create the set of actions to perform
|
||||
std::unique_ptr<clang::driver::Compilation> C(TheDriver.BuildCompilation(args));
|
||||
if (!C) {
|
||||
return false;
|
||||
}
|
||||
const clang::driver::JobList &Jobs = C->getJobs();
|
||||
|
||||
// There may be more than one job, for example for .S files
|
||||
// (preprocessor + assembler).
|
||||
for (auto Cmd : Jobs) {
|
||||
// Select the tool: cc1 or cc1as.
|
||||
const llvm::opt::ArgStringList &CCArgs = Cmd.getArguments();
|
||||
|
||||
if (strcmp(*CCArgs.data(), "-cc1") == 0) {
|
||||
// This is the C frontend.
|
||||
// Initialize a compiler invocation object from the clang (-cc1) arguments.
|
||||
std::unique_ptr<clang::CompilerInstance> Clang(new clang::CompilerInstance());
|
||||
bool success = clang::CompilerInvocation::CreateFromArgs(
|
||||
Clang->getInvocation(),
|
||||
const_cast<const char **>(CCArgs.data()),
|
||||
const_cast<const char **>(CCArgs.data()) + CCArgs.size(),
|
||||
Diags);
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the actual diagnostics engine.
|
||||
Clang->createDiagnostics();
|
||||
if (!Clang->hasDiagnostics()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Execute the frontend actions.
|
||||
success = ExecuteCompilerInvocation(Clang.get());
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (strcmp(*CCArgs.data(), "-cc1as") == 0) {
|
||||
// This is the assembler frontend. Parse the arguments.
|
||||
AssemblerInvocation Asm;
|
||||
if (!AssemblerInvocation::CreateFromArgs(Asm, llvm::ArrayRef<const char*>(CCArgs).slice(1), Diags))
|
||||
return false;
|
||||
|
||||
// Execute the invocation, unless there were parsing errors.
|
||||
bool failed = Diags.hasErrorOccurred() || ExecuteAssembler(Asm, Diags);
|
||||
if (failed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Unknown tool, print the tool and exit.
|
||||
fprintf(stderr, "unknown tool: %s\n", *CCArgs.data());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Commands executed successfully.
|
||||
return true;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -34,6 +34,12 @@ func init() {
|
||||
commands["ld.lld"] = append(commands["ld.lld"], "lld", "C:\\Program Files\\LLVM\\bin\\lld.exe")
|
||||
commands["wasm-ld"] = append(commands["wasm-ld"], "C:\\Program Files\\LLVM\\bin\\wasm-ld.exe")
|
||||
}
|
||||
// Add the path to the llvm90 installed from ports
|
||||
if runtime.GOOS == "freebsd" {
|
||||
commands["clang"] = append(commands["clang"], "/usr/local/llvm90/bin/clang-9")
|
||||
commands["ld.lld"] = append(commands["ld.lld"], "/usr/local/llvm90/bin/ld.lld")
|
||||
commands["wasm-ld"] = append(commands["wasm-ld"], "/usr/local/llvm90/bin/wasm-ld")
|
||||
}
|
||||
}
|
||||
|
||||
func execCommand(cmdNames []string, args ...string) error {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// +build byollvm
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo CXXFLAGS: -fno-rtti
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
bool tinygo_clang_driver(int argc, char **argv);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// runCCompiler invokes a C compiler with the given arguments.
|
||||
//
|
||||
// This version invokes the built-in Clang when trying to run the Clang compiler.
|
||||
func runCCompiler(command string, flags ...string) error {
|
||||
switch command {
|
||||
case "clang":
|
||||
// Compile this with the internal Clang compiler.
|
||||
headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
if headerPath == "" {
|
||||
return errors.New("could not locate Clang headers")
|
||||
}
|
||||
flags = append(flags, "-I"+headerPath)
|
||||
flags = append([]string{"tinygo:" + command}, flags...)
|
||||
var cflag *C.char
|
||||
buf := C.calloc(C.size_t(len(flags)), C.size_t(unsafe.Sizeof(cflag)))
|
||||
cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(flags):len(flags)]
|
||||
for i, flag := range flags {
|
||||
cflag := C.CString(flag)
|
||||
cflags[i] = cflag
|
||||
defer C.free(unsafe.Pointer(cflag))
|
||||
}
|
||||
ok := C.tinygo_clang_driver(C.int(len(flags)), (**C.char)(buf))
|
||||
if !ok {
|
||||
return errors.New("failed to compile using built-in clang")
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
// Running some other compiler. Maybe it has been defined in the
|
||||
// commands map (unlikely).
|
||||
if cmdNames, ok := commands[command]; ok {
|
||||
return execCommand(cmdNames, flags...)
|
||||
}
|
||||
// Alternatively, run the compiler directly.
|
||||
cmd := exec.Command(command, flags...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// +build !byollvm
|
||||
|
||||
package builder
|
||||
|
||||
// This file provides a way to a C compiler as an external command. See also:
|
||||
// clang-external.go
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// runCCompiler invokes a C compiler with the given arguments.
|
||||
//
|
||||
// This version always runs the compiler as an external command.
|
||||
func runCCompiler(command string, flags ...string) error {
|
||||
if cmdNames, ok := commands[command]; ok {
|
||||
return execCommand(cmdNames, flags...)
|
||||
}
|
||||
cmd := exec.Command(command, flags...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
+1
-1
@@ -71,7 +71,7 @@ func GorootVersionString(goroot string) (string, error) {
|
||||
// various ways.
|
||||
func getClangHeaderPath(TINYGOROOT string) string {
|
||||
// Check whether we're running from the source directory.
|
||||
path := filepath.Join(TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")
|
||||
path := filepath.Join(TINYGOROOT, "llvm-project", "clang", "lib", "Headers")
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
return path
|
||||
}
|
||||
|
||||
@@ -12,6 +12,20 @@ func (e *MultiError) Error() string {
|
||||
return e.Errs[0].Error()
|
||||
}
|
||||
|
||||
// newMultiError returns a *MultiError if there is more than one error, or
|
||||
// returns that error directly when there is only one. Passing an empty slice
|
||||
// will lead to a panic.
|
||||
func newMultiError(errs []error) error {
|
||||
switch len(errs) {
|
||||
case 0:
|
||||
panic("attempted to create empty MultiError")
|
||||
case 1:
|
||||
return errs[0]
|
||||
default:
|
||||
return &MultiError{errs}
|
||||
}
|
||||
}
|
||||
|
||||
// commandError is an error type to wrap os/exec.Command errors. This provides
|
||||
// some more information regarding what went wrong while running a command.
|
||||
type commandError struct {
|
||||
|
||||
+30
-9
@@ -11,26 +11,33 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// convertELFFileToUF2File converts an ELF file to a UF2 file.
|
||||
func convertELFFileToUF2File(infile, outfile string) error {
|
||||
func convertELFFileToUF2File(infile, outfile string, uf2FamilyID string) error {
|
||||
// Read the .text segment.
|
||||
targetAddress, data, err := extractROM(infile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
output, _ := convertBinToUF2(data, uint32(targetAddress))
|
||||
output, _, err := convertBinToUF2(data, uint32(targetAddress), uf2FamilyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(outfile, output, 0644)
|
||||
}
|
||||
|
||||
// convertBinToUF2 converts the binary bytes in input to UF2 formatted data.
|
||||
func convertBinToUF2(input []byte, targetAddr uint32) ([]byte, int) {
|
||||
func convertBinToUF2(input []byte, targetAddr uint32, uf2FamilyID string) ([]byte, int, error) {
|
||||
blocks := split(input, 256)
|
||||
output := make([]byte, 0)
|
||||
|
||||
bl := newUF2Block(targetAddr)
|
||||
bl, err := newUF2Block(targetAddr, uf2FamilyID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
bl.SetNumBlocks(len(blocks))
|
||||
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
@@ -41,7 +48,7 @@ func convertBinToUF2(input []byte, targetAddr uint32) ([]byte, int) {
|
||||
bl.IncrementAddress(bl.payloadSize)
|
||||
}
|
||||
|
||||
return output, len(blocks)
|
||||
return output, len(blocks), nil
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -65,18 +72,32 @@ type uf2Block struct {
|
||||
}
|
||||
|
||||
// newUF2Block returns a new uf2Block struct that has been correctly populated
|
||||
func newUF2Block(targetAddr uint32) *uf2Block {
|
||||
func newUF2Block(targetAddr uint32, uf2FamilyID string) (*uf2Block, error) {
|
||||
var flags uint32
|
||||
var familyID uint32
|
||||
if uf2FamilyID != "" {
|
||||
flags |= flagFamilyIDPresent
|
||||
v, err := strconv.ParseUint(uf2FamilyID, 0, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
familyID = uint32(v)
|
||||
}
|
||||
return &uf2Block{magicStart0: uf2MagicStart0,
|
||||
magicStart1: uf2MagicStart1,
|
||||
magicEnd: uf2MagicEnd,
|
||||
targetAddr: targetAddr,
|
||||
flags: 0x0,
|
||||
familyID: 0x0,
|
||||
flags: flags,
|
||||
familyID: familyID,
|
||||
payloadSize: 256,
|
||||
data: make([]byte, 476),
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
flagFamilyIDPresent = 0x00002000
|
||||
)
|
||||
|
||||
// Bytes converts the uf2Block to a slice of bytes that can be written to file.
|
||||
func (b *uf2Block) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 512))
|
||||
|
||||
+1
-3
@@ -22,11 +22,9 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
|
||||
func TestCGo(t *testing.T) {
|
||||
var cflags = []string{"--target=armv6m-none-eabi"}
|
||||
|
||||
for _, name := range []string{"basic", "errors", "types", "flags"} {
|
||||
for _, name := range []string{"basic", "errors", "types", "flags", "const"} {
|
||||
name := name // avoid a race condition
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Read the AST in memory.
|
||||
path := filepath.Join("testdata", name+".go")
|
||||
fset := token.NewFileSet()
|
||||
|
||||
+25
-1
@@ -52,6 +52,13 @@ func parseConstExpr(t *tokenizer) (ast.Expr, *scanner.Error) {
|
||||
}
|
||||
t.Next()
|
||||
return expr, nil
|
||||
case token.IDENT:
|
||||
expr := &ast.Ident{
|
||||
NamePos: t.pos,
|
||||
Name: "C." + t.value,
|
||||
}
|
||||
t.Next()
|
||||
return expr, nil
|
||||
case token.EOF:
|
||||
return nil, &scanner.Error{
|
||||
Pos: t.fset.Position(t.pos),
|
||||
@@ -133,8 +140,10 @@ func (t *tokenizer) Next() {
|
||||
if c == '.' {
|
||||
hasDot = true
|
||||
}
|
||||
if (c >= '0' && c <= '9') || c == '.' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') {
|
||||
if c >= '0' && c <= '9' || c == '.' || c == '_' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' {
|
||||
tokenLen = i + 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
t.value = t.buf[:tokenLen]
|
||||
@@ -150,6 +159,21 @@ func (t *tokenizer) Next() {
|
||||
t.value = strings.TrimRight(t.value, "uUlL")
|
||||
}
|
||||
return
|
||||
case c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_':
|
||||
// Identifier. Find all remaining tokens that are part of this
|
||||
// identifier.
|
||||
tokenLen := len(t.buf)
|
||||
for i, c := range t.buf {
|
||||
if c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_' {
|
||||
tokenLen = i + 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
t.value = t.buf[:tokenLen]
|
||||
t.buf = t.buf[tokenLen:]
|
||||
t.token = token.IDENT
|
||||
return
|
||||
case c == '"':
|
||||
// String constant. Find the first '"' character that is not
|
||||
// preceded by a backslash.
|
||||
|
||||
+3
-2
@@ -21,8 +21,8 @@ func TestParseConst(t *testing.T) {
|
||||
{`5)`, `error: 1:2: unexpected token )`},
|
||||
{" \t)", `error: 1:4: unexpected token )`},
|
||||
{`5.8f`, `5.8`},
|
||||
{`foo`, `error: 1:1: unexpected token ILLEGAL`}, // identifiers unimplemented
|
||||
{``, `error: 1:1: empty constant`}, // empty constants not allowed in Go
|
||||
{`foo`, `C.foo`},
|
||||
{``, `error: 1:1: empty constant`}, // empty constants not allowed in Go
|
||||
{`"foo"`, `"foo"`},
|
||||
{`"a\\n"`, `"a\\n"`},
|
||||
{`"a\n"`, `"a\n"`},
|
||||
@@ -30,6 +30,7 @@ func TestParseConst(t *testing.T) {
|
||||
{`'a'`, `'a'`},
|
||||
{`0b10`, `0b10`},
|
||||
{`0x1234_5678`, `0x1234_5678`},
|
||||
{`5 5`, `error: 1:3: unexpected token INT`}, // test for a bugfix
|
||||
} {
|
||||
fset := token.NewFileSet()
|
||||
startPos := fset.AddFile("", -1, 1000).Pos(0)
|
||||
|
||||
@@ -5,7 +5,9 @@ package cgo
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/lib/llvm-9/include
|
||||
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@9/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm90/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-9/lib -lclang
|
||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@9/lib -lclang -lffi
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm90/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
#define foo 3
|
||||
#define bar foo
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
Foo = C.foo
|
||||
Bar = C.bar
|
||||
)
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
const C.bar = C.foo
|
||||
const C.foo = 3
|
||||
|
||||
type C.int16_t = int16
|
||||
type C.int32_t = int32
|
||||
type C.int64_t = int64
|
||||
type C.int8_t = int8
|
||||
type C.uint16_t = uint16
|
||||
type C.uint32_t = uint32
|
||||
type C.uint64_t = uint64
|
||||
type C.uint8_t = uint8
|
||||
type C.uintptr_t = uintptr
|
||||
type C.char uint8
|
||||
type C.int int32
|
||||
type C.long int32
|
||||
type C.longlong int64
|
||||
type C.schar int8
|
||||
type C.short int16
|
||||
type C.uchar uint8
|
||||
type C.uint uint32
|
||||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
@@ -65,6 +65,12 @@ func (c *Config) BuildTags() []string {
|
||||
return tags
|
||||
}
|
||||
|
||||
// CgoEnabled returns true if (and only if) CGo is enabled. It is true by
|
||||
// default and false if CGO_ENABLED is set to "0".
|
||||
func (c *Config) CgoEnabled() bool {
|
||||
return goenv.Get("CGO_ENABLED") == "1"
|
||||
}
|
||||
|
||||
// GC returns the garbage collection strategy in use on this platform. Valid
|
||||
// values are "none", "leaking", and "conservative".
|
||||
func (c *Config) GC() string {
|
||||
|
||||
+21
-1
@@ -43,9 +43,11 @@ type TargetSpec struct {
|
||||
FlashMethod string `json:"flash-method"`
|
||||
FlashVolume string `json:"msd-volume-name"`
|
||||
FlashFilename string `json:"msd-firmware-name"`
|
||||
UF2FamilyID string `json:"uf2-family-id"`
|
||||
OpenOCDInterface string `json:"openocd-interface"`
|
||||
OpenOCDTarget string `json:"openocd-target"`
|
||||
OpenOCDTransport string `json:"openocd-transport"`
|
||||
JLinkDevice string `json:"jlink-device"`
|
||||
}
|
||||
|
||||
// copyProperties copies all properties that are set in spec2 into itself.
|
||||
@@ -109,6 +111,9 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
|
||||
if spec2.FlashFilename != "" {
|
||||
spec.FlashFilename = spec2.FlashFilename
|
||||
}
|
||||
if spec2.UF2FamilyID != "" {
|
||||
spec.UF2FamilyID = spec2.UF2FamilyID
|
||||
}
|
||||
if spec2.OpenOCDInterface != "" {
|
||||
spec.OpenOCDInterface = spec2.OpenOCDInterface
|
||||
}
|
||||
@@ -118,6 +123,9 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
|
||||
if spec2.OpenOCDTransport != "" {
|
||||
spec.OpenOCDTransport = spec2.OpenOCDTransport
|
||||
}
|
||||
if spec2.JLinkDevice != "" {
|
||||
spec.JLinkDevice = spec2.JLinkDevice
|
||||
}
|
||||
}
|
||||
|
||||
// load reads a target specification from the JSON in the given io.Reader. It
|
||||
@@ -221,6 +229,14 @@ func LoadTarget(target string) (*TargetSpec, error) {
|
||||
if len(tripleSplit) < 3 {
|
||||
return nil, errors.New("expected a full LLVM target or a custom target in -target flag")
|
||||
}
|
||||
if tripleSplit[0] == "arm" {
|
||||
// LLVM and Clang have a different idea of what "arm" means, so
|
||||
// upgrade to a slightly more modern ARM. In fact, when you pass
|
||||
// --target=arm--linux-gnueabihf to Clang, it will convert that
|
||||
// internally to armv7-unknown-linux-gnueabihf. Changing the
|
||||
// architecture to armv7 will keep things consistent.
|
||||
tripleSplit[0] = "armv7"
|
||||
}
|
||||
goos := tripleSplit[2]
|
||||
if strings.HasPrefix(goos, "darwin") {
|
||||
goos = "darwin"
|
||||
@@ -229,11 +245,12 @@ func LoadTarget(target string) (*TargetSpec, error) {
|
||||
"i386": "386",
|
||||
"x86_64": "amd64",
|
||||
"aarch64": "arm64",
|
||||
"armv7": "arm",
|
||||
}[tripleSplit[0]]
|
||||
if goarch == "" {
|
||||
goarch = tripleSplit[0]
|
||||
}
|
||||
return defaultTarget(goos, goarch, target)
|
||||
return defaultTarget(goos, goarch, strings.Join(tripleSplit, "-"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,6 +264,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
BuildTags: []string{goos, goarch},
|
||||
Compiler: "clang",
|
||||
Linker: "cc",
|
||||
CFlags: []string{"--target=" + triple},
|
||||
GDB: "gdb",
|
||||
PortReset: "false",
|
||||
FlashMethod: "native",
|
||||
@@ -259,11 +277,13 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
if goarch != runtime.GOARCH {
|
||||
// Some educated guesses as to how to invoke helper programs.
|
||||
if goarch == "arm" && goos == "linux" {
|
||||
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/arm-linux-gnueabihf")
|
||||
spec.Linker = "arm-linux-gnueabihf-gcc"
|
||||
spec.GDB = "arm-linux-gnueabihf-gdb"
|
||||
spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabihf"}
|
||||
}
|
||||
if goarch == "arm64" && goos == "linux" {
|
||||
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/aarch64-linux-gnu")
|
||||
spec.Linker = "aarch64-linux-gnu-gcc"
|
||||
spec.GDB = "aarch64-linux-gnu-gdb"
|
||||
spec.Emulator = []string{"qemu-aarch64", "-L", "/usr/aarch64-linux-gnu"}
|
||||
|
||||
+19
-20
@@ -98,76 +98,75 @@ func (c *Compiler) checkValue(v llvm.Value, types map[llvm.Type]struct{}, specia
|
||||
func (c *Compiler) checkInstruction(inst llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
|
||||
// check value properties
|
||||
if err := c.checkValue(inst, types, specials); err != nil {
|
||||
return fmt.Errorf("failed to validate value of instruction %q: %s", inst.Name(), err.Error())
|
||||
return errorAt(inst, err.Error())
|
||||
}
|
||||
|
||||
// check operands
|
||||
for i := 0; i < inst.OperandsCount(); i++ {
|
||||
if err := c.checkValue(inst.Operand(i), types, specials); err != nil {
|
||||
return fmt.Errorf("failed to validate argument %d of instruction %q: %s", i, inst.Name(), err.Error())
|
||||
return errorAt(inst, fmt.Sprintf("failed to validate operand %d of instruction %q: %s", i, inst.Name(), err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Compiler) checkBasicBlock(bb llvm.BasicBlock, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
|
||||
func (c *Compiler) checkBasicBlock(bb llvm.BasicBlock, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) []error {
|
||||
// check basic block value and type
|
||||
var errs []error
|
||||
if err := c.checkValue(bb.AsValue(), types, specials); err != nil {
|
||||
return fmt.Errorf("failed to validate value of basic block %s: %s", bb.AsValue().Name(), err.Error())
|
||||
errs = append(errs, errorAt(bb.Parent(), fmt.Sprintf("failed to validate value of basic block %s: %v", bb.AsValue().Name(), err)))
|
||||
}
|
||||
|
||||
// check instructions
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if err := c.checkInstruction(inst, types, specials); err != nil {
|
||||
return fmt.Errorf("failed to validate basic block %q: %s", bb.AsValue().Name(), err.Error())
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return errs
|
||||
}
|
||||
|
||||
func (c *Compiler) checkFunction(fn llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
|
||||
func (c *Compiler) checkFunction(fn llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) []error {
|
||||
// check function value and type
|
||||
var errs []error
|
||||
if err := c.checkValue(fn, types, specials); err != nil {
|
||||
return fmt.Errorf("failed to validate value of function %s: %s", fn.Name(), err.Error())
|
||||
errs = append(errs, fmt.Errorf("failed to validate value of function %s: %s", fn.Name(), err.Error()))
|
||||
}
|
||||
|
||||
// check basic blocks
|
||||
for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
if err := c.checkBasicBlock(bb, types, specials); err != nil {
|
||||
return fmt.Errorf("failed to validate basic block of function %s: %s", fn.Name(), err.Error())
|
||||
}
|
||||
errs = append(errs, c.checkBasicBlock(bb, types, specials)...)
|
||||
}
|
||||
|
||||
return nil
|
||||
return errs
|
||||
}
|
||||
|
||||
func (c *Compiler) checkModule() error {
|
||||
func (c *Compiler) checkModule() []error {
|
||||
// check for any context mismatches
|
||||
var errs []error
|
||||
switch {
|
||||
case c.mod.Context() == c.ctx:
|
||||
// this is correct
|
||||
case c.mod.Context() == llvm.GlobalContext():
|
||||
// somewhere we accidentally used the global context instead of a real context
|
||||
return errors.New("module uses global context")
|
||||
errs = append(errs, errors.New("module uses global context"))
|
||||
default:
|
||||
// we used some other context by accident
|
||||
return fmt.Errorf("module uses context %v instead of the main context %v", c.mod.Context(), c.ctx)
|
||||
errs = append(errs, fmt.Errorf("module uses context %v instead of the main context %v", c.mod.Context(), c.ctx))
|
||||
}
|
||||
|
||||
types := map[llvm.Type]struct{}{}
|
||||
specials := map[llvm.TypeKind]llvm.Type{}
|
||||
for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
if err := c.checkFunction(fn, types, specials); err != nil {
|
||||
return err
|
||||
}
|
||||
errs = append(errs, c.checkFunction(fn, types, specials)...)
|
||||
}
|
||||
for g := c.mod.FirstGlobal(); !g.IsNil(); g = llvm.NextGlobal(g) {
|
||||
if err := c.checkValue(g, types, specials); err != nil {
|
||||
return fmt.Errorf("failed to verify global %s of module: %s", g.Name(), err.Error())
|
||||
errs = append(errs, fmt.Errorf("failed to verify global %s of module: %s", g.Name(), err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return errs
|
||||
}
|
||||
|
||||
+35
-168
@@ -193,7 +193,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
GOOS: c.GOOS(),
|
||||
GOROOT: goenv.Get("GOROOT"),
|
||||
GOPATH: goenv.Get("GOPATH"),
|
||||
CgoEnabled: true,
|
||||
CgoEnabled: c.CgoEnabled(),
|
||||
UseAllFiles: false,
|
||||
Compiler: "gc", // must be one of the recognized compilers
|
||||
BuildTags: c.BuildTags(),
|
||||
@@ -203,7 +203,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
GOOS: c.GOOS(),
|
||||
GOROOT: goenv.Get("TINYGOROOT"),
|
||||
GOPATH: overlayGopath,
|
||||
CgoEnabled: true,
|
||||
CgoEnabled: c.CgoEnabled(),
|
||||
UseAllFiles: false,
|
||||
Compiler: "gc", // must be one of the recognized compilers
|
||||
BuildTags: c.BuildTags(),
|
||||
@@ -217,7 +217,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
path = path[len(tinygoPath+"/src/"):]
|
||||
}
|
||||
switch path {
|
||||
case "machine", "os", "reflect", "runtime", "runtime/volatile", "sync", "testing", "internal/reflectlite":
|
||||
case "machine", "os", "reflect", "runtime", "runtime/interrupt", "runtime/volatile", "sync", "testing", "internal/reflectlite":
|
||||
return path
|
||||
default:
|
||||
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
||||
@@ -251,13 +251,17 @@ func (c *Compiler) Compile(mainPath string) []error {
|
||||
return []error{err}
|
||||
}
|
||||
} else {
|
||||
_, err = lprogram.Import(mainPath, wd)
|
||||
_, err = lprogram.Import(mainPath, wd, token.Position{
|
||||
Filename: "build command-line-arguments",
|
||||
})
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = lprogram.Import("runtime", "")
|
||||
_, err = lprogram.Import("runtime", "", token.Position{
|
||||
Filename: "build default import",
|
||||
})
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
@@ -767,14 +771,6 @@ func (c *Compiler) attachDebugInfo(f *ir.Function) llvm.Metadata {
|
||||
}
|
||||
|
||||
func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata {
|
||||
if _, ok := c.difiles[filename]; !ok {
|
||||
dir, file := filepath.Split(filename)
|
||||
if dir != "" {
|
||||
dir = dir[:len(dir)-1]
|
||||
}
|
||||
c.difiles[filename] = c.dibuilder.CreateFile(file, dir)
|
||||
}
|
||||
|
||||
// Debug info for this function.
|
||||
diparams := make([]llvm.Metadata, 0, len(f.Params))
|
||||
for _, param := range f.Params {
|
||||
@@ -788,7 +784,7 @@ func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix,
|
||||
difunc := c.dibuilder.CreateFunction(c.difiles[filename], llvm.DIFunction{
|
||||
Name: f.RelString(nil) + suffix,
|
||||
LinkageName: f.LinkName() + suffix,
|
||||
File: c.difiles[filename],
|
||||
File: c.getDIFile(filename),
|
||||
Line: line,
|
||||
Type: diFuncType,
|
||||
LocalToUnit: true,
|
||||
@@ -801,21 +797,37 @@ func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix,
|
||||
return difunc
|
||||
}
|
||||
|
||||
// getDIFile returns a DIFile metadata node for the given filename. It tries to
|
||||
// use one that was already created, otherwise it falls back to creating a new
|
||||
// one.
|
||||
func (c *Compiler) getDIFile(filename string) llvm.Metadata {
|
||||
if _, ok := c.difiles[filename]; !ok {
|
||||
dir, file := filepath.Split(filename)
|
||||
if dir != "" {
|
||||
dir = dir[:len(dir)-1]
|
||||
}
|
||||
c.difiles[filename] = c.dibuilder.CreateFile(file, dir)
|
||||
}
|
||||
return c.difiles[filename]
|
||||
}
|
||||
|
||||
func (c *Compiler) parseFunc(frame *Frame) {
|
||||
if c.DumpSSA() {
|
||||
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
||||
}
|
||||
if !frame.fn.LLVMFn.IsDeclaration() {
|
||||
c.addError(frame.fn.Pos(), "function is already defined:"+frame.fn.LLVMFn.Name())
|
||||
errValue := frame.fn.LLVMFn.Name() + " redeclared in this program"
|
||||
fnPos := getPosition(frame.fn.LLVMFn)
|
||||
if fnPos.IsValid() {
|
||||
errValue += "\n\tprevious declaration at " + fnPos.String()
|
||||
}
|
||||
c.addError(frame.fn.Pos(), errValue)
|
||||
return
|
||||
}
|
||||
if !frame.fn.IsExported() {
|
||||
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
||||
frame.fn.LLVMFn.SetUnnamedAddr(true)
|
||||
}
|
||||
if frame.fn.IsInterrupt() && strings.HasPrefix(c.Triple(), "avr") {
|
||||
frame.fn.LLVMFn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
|
||||
}
|
||||
|
||||
// Some functions have a pragma controlling the inlining level.
|
||||
switch frame.fn.Inline() {
|
||||
@@ -1291,12 +1303,16 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
||||
return c.emitAsmFull(frame, instr)
|
||||
case strings.HasPrefix(name, "device/arm.SVCall"):
|
||||
return c.emitSVCall(frame, instr.Args)
|
||||
case strings.HasPrefix(name, "(device/riscv.CSR)."):
|
||||
return c.emitCSROperation(frame, instr)
|
||||
case strings.HasPrefix(name, "syscall.Syscall"):
|
||||
return c.emitSyscall(frame, instr)
|
||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||
return c.emitVolatileLoad(frame, instr)
|
||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||
return c.emitVolatileStore(frame, instr)
|
||||
case name == "runtime/interrupt.New":
|
||||
return c.emitInterruptGlobal(frame, instr)
|
||||
}
|
||||
|
||||
targetFunc := c.ir.GetFunction(fn)
|
||||
@@ -1569,24 +1585,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||
val := c.getValue(frame, expr.X)
|
||||
return c.parseMakeInterface(val, expr.X.Type(), expr.Pos()), nil
|
||||
case *ssa.MakeMap:
|
||||
mapType := expr.Type().Underlying().(*types.Map)
|
||||
llvmKeyType := c.getLLVMType(mapType.Key().Underlying())
|
||||
llvmValueType := c.getLLVMType(mapType.Elem().Underlying())
|
||||
keySize := c.targetData.TypeAllocSize(llvmKeyType)
|
||||
valueSize := c.targetData.TypeAllocSize(llvmValueType)
|
||||
llvmKeySize := llvm.ConstInt(c.ctx.Int8Type(), keySize, false)
|
||||
llvmValueSize := llvm.ConstInt(c.ctx.Int8Type(), valueSize, false)
|
||||
sizeHint := llvm.ConstInt(c.uintptrType, 8, false)
|
||||
if expr.Reserve != nil {
|
||||
sizeHint = c.getValue(frame, expr.Reserve)
|
||||
var err error
|
||||
sizeHint, err = c.parseConvert(expr.Reserve.Type(), types.Typ[types.Uintptr], sizeHint, expr.Pos())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
}
|
||||
hashmap := c.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
|
||||
return hashmap, nil
|
||||
return c.emitMakeMap(frame, expr)
|
||||
case *ssa.MakeSlice:
|
||||
sliceLen := c.getValue(frame, expr.Len)
|
||||
sliceCap := c.getValue(frame, expr.Cap)
|
||||
@@ -2551,138 +2550,6 @@ func (c *Compiler) NonConstGlobals() {
|
||||
}
|
||||
}
|
||||
|
||||
// When -wasm-abi flag set to "js" (default),
|
||||
// replace i64 in an external function with a stack-allocated i64*, to work
|
||||
// around the lack of 64-bit integers in JavaScript (commonly used together with
|
||||
// WebAssembly). Once that's resolved, this pass may be avoided.
|
||||
// See also the -wasm-abi= flag
|
||||
// https://github.com/WebAssembly/design/issues/1172
|
||||
func (c *Compiler) ExternalInt64AsPtr() error {
|
||||
int64Type := c.ctx.Int64Type()
|
||||
int64PtrType := llvm.PointerType(int64Type, 0)
|
||||
for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
if fn.Linkage() != llvm.ExternalLinkage {
|
||||
// Only change externally visible functions (exports and imports).
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(fn.Name(), "llvm.") || strings.HasPrefix(fn.Name(), "runtime.") {
|
||||
// Do not try to modify the signature of internal LLVM functions and
|
||||
// assume that runtime functions are only temporarily exported for
|
||||
// coroutine lowering.
|
||||
continue
|
||||
}
|
||||
|
||||
hasInt64 := false
|
||||
paramTypes := []llvm.Type{}
|
||||
|
||||
// Check return type for 64-bit integer.
|
||||
fnType := fn.Type().ElementType()
|
||||
returnType := fnType.ReturnType()
|
||||
if returnType == int64Type {
|
||||
hasInt64 = true
|
||||
paramTypes = append(paramTypes, int64PtrType)
|
||||
returnType = c.ctx.VoidType()
|
||||
}
|
||||
|
||||
// Check param types for 64-bit integers.
|
||||
for param := fn.FirstParam(); !param.IsNil(); param = llvm.NextParam(param) {
|
||||
if param.Type() == int64Type {
|
||||
hasInt64 = true
|
||||
paramTypes = append(paramTypes, int64PtrType)
|
||||
} else {
|
||||
paramTypes = append(paramTypes, param.Type())
|
||||
}
|
||||
}
|
||||
|
||||
if !hasInt64 {
|
||||
// No i64 in the paramter list.
|
||||
continue
|
||||
}
|
||||
|
||||
// Add $i64wrapper to the real function name as it is only used
|
||||
// internally.
|
||||
// Add a new function with the correct signature that is exported.
|
||||
name := fn.Name()
|
||||
fn.SetName(name + "$i64wrap")
|
||||
externalFnType := llvm.FunctionType(returnType, paramTypes, fnType.IsFunctionVarArg())
|
||||
externalFn := llvm.AddFunction(c.mod, name, externalFnType)
|
||||
|
||||
if fn.IsDeclaration() {
|
||||
// Just a declaration: the definition doesn't exist on the Go side
|
||||
// so it cannot be called from external code.
|
||||
// Update all users to call the external function.
|
||||
// The old $i64wrapper function could be removed, but it may as well
|
||||
// be left in place.
|
||||
for use := fn.FirstUse(); !use.IsNil(); use = use.NextUse() {
|
||||
call := use.User()
|
||||
c.builder.SetInsertPointBefore(call)
|
||||
callParams := []llvm.Value{}
|
||||
var retvalAlloca llvm.Value
|
||||
if fnType.ReturnType() == int64Type {
|
||||
retvalAlloca = c.builder.CreateAlloca(int64Type, "i64asptr")
|
||||
callParams = append(callParams, retvalAlloca)
|
||||
}
|
||||
for i := 0; i < call.OperandsCount()-1; i++ {
|
||||
operand := call.Operand(i)
|
||||
if operand.Type() == int64Type {
|
||||
// Pass a stack-allocated pointer instead of the value
|
||||
// itself.
|
||||
alloca := c.builder.CreateAlloca(int64Type, "i64asptr")
|
||||
c.builder.CreateStore(operand, alloca)
|
||||
callParams = append(callParams, alloca)
|
||||
} else {
|
||||
// Unchanged parameter.
|
||||
callParams = append(callParams, operand)
|
||||
}
|
||||
}
|
||||
if fnType.ReturnType() == int64Type {
|
||||
// Pass a stack-allocated pointer as the first parameter
|
||||
// where the return value should be stored, instead of using
|
||||
// the regular return value.
|
||||
c.builder.CreateCall(externalFn, callParams, call.Name())
|
||||
returnValue := c.builder.CreateLoad(retvalAlloca, "retval")
|
||||
call.ReplaceAllUsesWith(returnValue)
|
||||
call.EraseFromParentAsInstruction()
|
||||
} else {
|
||||
newCall := c.builder.CreateCall(externalFn, callParams, call.Name())
|
||||
call.ReplaceAllUsesWith(newCall)
|
||||
call.EraseFromParentAsInstruction()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The function has a definition in Go. This means that it may still
|
||||
// be called both Go and from external code.
|
||||
// Keep existing calls with the existing convention in place (for
|
||||
// better performance), but export a new wrapper function with the
|
||||
// correct calling convention.
|
||||
fn.SetLinkage(llvm.InternalLinkage)
|
||||
fn.SetUnnamedAddr(true)
|
||||
entryBlock := c.ctx.AddBasicBlock(externalFn, "entry")
|
||||
c.builder.SetInsertPointAtEnd(entryBlock)
|
||||
var callParams []llvm.Value
|
||||
if fnType.ReturnType() == int64Type {
|
||||
return errors.New("not yet implemented: exported function returns i64 with -wasm-abi=js; " +
|
||||
"see https://tinygo.org/compiler-internals/calling-convention/")
|
||||
}
|
||||
for i, origParam := range fn.Params() {
|
||||
paramValue := externalFn.Param(i)
|
||||
if origParam.Type() == int64Type {
|
||||
paramValue = c.builder.CreateLoad(paramValue, "i64")
|
||||
}
|
||||
callParams = append(callParams, paramValue)
|
||||
}
|
||||
retval := c.builder.CreateCall(fn, callParams, "")
|
||||
if retval.Type().TypeKind() == llvm.VoidTypeKind {
|
||||
c.builder.CreateRetVoid()
|
||||
} else {
|
||||
c.builder.CreateRet(retval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Emit object file (.o).
|
||||
func (c *Compiler) EmitObject(path string) error {
|
||||
llvmBuf, err := c.machine.EmitToMemoryBuffer(c.mod, llvm.ObjectFile)
|
||||
|
||||
+48
-3
@@ -14,6 +14,7 @@ package compiler
|
||||
// frames.
|
||||
|
||||
import (
|
||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||
"github.com/tinygo-org/tinygo/ir"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
@@ -34,6 +35,40 @@ func (c *Compiler) deferInitFunc(frame *Frame) {
|
||||
c.builder.CreateStore(llvm.ConstPointerNull(deferType), frame.deferPtr)
|
||||
}
|
||||
|
||||
// isInLoop checks if there is a path from a basic block to itself.
|
||||
func isInLoop(start *ssa.BasicBlock) bool {
|
||||
// Use a breadth-first search to scan backwards through the block graph.
|
||||
queue := []*ssa.BasicBlock{start}
|
||||
checked := map[*ssa.BasicBlock]struct{}{}
|
||||
|
||||
for len(queue) > 0 {
|
||||
// pop a block off of the queue
|
||||
block := queue[len(queue)-1]
|
||||
queue = queue[:len(queue)-1]
|
||||
|
||||
// Search through predecessors.
|
||||
// Searching backwards means that this is pretty fast when the block is close to the start of the function.
|
||||
// Defers are often placed near the start of the function.
|
||||
for _, pred := range block.Preds {
|
||||
if pred == start {
|
||||
// cycle found
|
||||
return true
|
||||
}
|
||||
|
||||
if _, ok := checked[pred]; ok {
|
||||
// block already checked
|
||||
continue
|
||||
}
|
||||
|
||||
// add to queue and checked map
|
||||
queue = append(queue, pred)
|
||||
checked[pred] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// emitDefer emits a single defer instruction, to be run when this function
|
||||
// returns.
|
||||
func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) {
|
||||
@@ -127,12 +162,22 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) {
|
||||
deferFrame = c.builder.CreateInsertValue(deferFrame, value, i, "")
|
||||
}
|
||||
|
||||
// Put this struct in an alloca.
|
||||
alloca := c.builder.CreateAlloca(deferFrameType, "defer.alloca")
|
||||
c.builder.CreateStore(deferFrame, alloca)
|
||||
// Put this struct in an allocation.
|
||||
var alloca llvm.Value
|
||||
if !isInLoop(instr.Block()) {
|
||||
// This can safely use a stack allocation.
|
||||
alloca = llvmutil.CreateEntryBlockAlloca(c.builder, deferFrameType, "defer.alloca")
|
||||
} else {
|
||||
// This may be hit a variable number of times, so use a heap allocation.
|
||||
size := c.targetData.TypeAllocSize(deferFrameType)
|
||||
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||
allocCall := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call")
|
||||
alloca = c.builder.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
|
||||
}
|
||||
if c.NeedsStackObjects() {
|
||||
c.trackPointer(alloca)
|
||||
}
|
||||
c.builder.CreateStore(deferFrame, alloca)
|
||||
|
||||
// Push it on top of the linked list by replacing deferPtr.
|
||||
allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"path/filepath"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
func (c *Compiler) makeError(pos token.Pos, msg string) types.Error {
|
||||
@@ -16,3 +20,42 @@ func (c *Compiler) makeError(pos token.Pos, msg string) types.Error {
|
||||
func (c *Compiler) addError(pos token.Pos, msg string) {
|
||||
c.diagnostics = append(c.diagnostics, c.makeError(pos, msg))
|
||||
}
|
||||
|
||||
// errorAt returns an error value at the location of the instruction.
|
||||
// The location information may not be complete as it depends on debug
|
||||
// information in the IR.
|
||||
func errorAt(inst llvm.Value, msg string) scanner.Error {
|
||||
return scanner.Error{
|
||||
Pos: getPosition(inst),
|
||||
Msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// getPosition returns the position information for the given value, as far as
|
||||
// it is available.
|
||||
func getPosition(val llvm.Value) token.Position {
|
||||
if !val.IsAInstruction().IsNil() {
|
||||
loc := val.InstructionDebugLoc()
|
||||
if loc.IsNil() {
|
||||
return token.Position{}
|
||||
}
|
||||
file := loc.LocationScope().ScopeFile()
|
||||
return token.Position{
|
||||
Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
|
||||
Line: int(loc.LocationLine()),
|
||||
Column: int(loc.LocationColumn()),
|
||||
}
|
||||
} else if !val.IsAFunction().IsNil() {
|
||||
loc := val.Subprogram()
|
||||
if loc.IsNil() {
|
||||
return token.Position{}
|
||||
}
|
||||
file := loc.ScopeFile()
|
||||
return token.Position{
|
||||
Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
|
||||
Line: int(loc.SubprogramLine()),
|
||||
}
|
||||
} else {
|
||||
return token.Position{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,6 @@ package compiler
|
||||
// scheduler, which runs in the background scheduling all coroutines.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -283,7 +282,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
if use.IsConstant() && use.Opcode() == llvm.PtrToInt {
|
||||
for _, call := range getUses(use) {
|
||||
if call.IsACallInst().IsNil() || call.CalledValue().Name() != "runtime.makeGoroutine" {
|
||||
return false, errors.New("async function " + f.Name() + " incorrectly used in ptrtoint, expected runtime.makeGoroutine")
|
||||
return false, errorAt(call, "async function incorrectly used in ptrtoint, expected runtime.makeGoroutine")
|
||||
}
|
||||
}
|
||||
// This is a go statement. Do not mark the parent as async, as
|
||||
@@ -303,12 +302,19 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
// Not a call instruction. Maybe a store to a global? In any
|
||||
// case, this requires support for async calls across function
|
||||
// pointers which is not yet supported.
|
||||
return false, errors.New("async function " + f.Name() + " used as function pointer")
|
||||
at := use
|
||||
if use.IsAInstruction().IsNil() {
|
||||
// The use might not be an instruction (for example, in the
|
||||
// case of a const bitcast). Fall back to reporting the
|
||||
// location of the function instead.
|
||||
at = f
|
||||
}
|
||||
return false, errorAt(at, "async function "+f.Name()+" used as function pointer")
|
||||
}
|
||||
parent := use.InstructionParent().Parent()
|
||||
for i := 0; i < use.OperandsCount()-1; i++ {
|
||||
if use.Operand(i) == f {
|
||||
return false, errors.New("async function " + f.Name() + " used as function pointer in " + parent.Name())
|
||||
return false, errorAt(use, "async function "+f.Name()+" used as function pointer")
|
||||
}
|
||||
}
|
||||
worklist = append(worklist, parent)
|
||||
@@ -805,7 +811,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||
}, "")
|
||||
wakeup := c.splitBasicBlock(inst, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup")
|
||||
wakeup := llvmutil.SplitBasicBlock(c.builder, inst, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup")
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
|
||||
@@ -906,12 +912,12 @@ func (c *Compiler) lowerMakeGoroutineCalls(sched bool) error {
|
||||
origFunc := ptrtointIn.Operand(0)
|
||||
uses := getUses(goroutine)
|
||||
if len(uses) != 1 || uses[0].IsAIntToPtrInst().IsNil() {
|
||||
return errors.New("expected exactly 1 inttoptr use of runtime.makeGoroutine")
|
||||
return errorAt(makeGoroutine, "expected exactly 1 inttoptr use of runtime.makeGoroutine")
|
||||
}
|
||||
inttoptrOut := uses[0]
|
||||
uses = getUses(inttoptrOut)
|
||||
if len(uses) != 1 || uses[0].IsACallInst().IsNil() {
|
||||
return errors.New("expected exactly 1 call use of runtime.makeGoroutine bitcast")
|
||||
return errorAt(inttoptrOut, "expected exactly 1 call use of runtime.makeGoroutine bitcast")
|
||||
}
|
||||
realCall := uses[0]
|
||||
|
||||
|
||||
@@ -162,3 +162,48 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error
|
||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0)
|
||||
return c.builder.CreateCall(target, llvmArgs, ""), nil
|
||||
}
|
||||
|
||||
// This is a compiler builtin which emits CSR instructions. It can be one of:
|
||||
//
|
||||
// func (csr CSR) Get() uintptr
|
||||
// func (csr CSR) Set(uintptr)
|
||||
// func (csr CSR) SetBits(uintptr) uintptr
|
||||
// func (csr CSR) ClearBits(uintptr) uintptr
|
||||
//
|
||||
// The csr parameter (method receiver) must be a constant. Other parameter can
|
||||
// be any value.
|
||||
func (c *Compiler) emitCSROperation(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
|
||||
csrConst, ok := call.Args[0].(*ssa.Const)
|
||||
if !ok {
|
||||
return llvm.Value{}, c.makeError(call.Pos(), "CSR must be constant")
|
||||
}
|
||||
csr := csrConst.Uint64()
|
||||
switch name := call.StaticCallee().Name(); name {
|
||||
case "Get":
|
||||
// Note that this instruction may have side effects, and thus must be
|
||||
// marked as such.
|
||||
fnType := llvm.FunctionType(c.uintptrType, nil, false)
|
||||
asm := fmt.Sprintf("csrr $0, %d", csr)
|
||||
target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0)
|
||||
return c.builder.CreateCall(target, nil, ""), nil
|
||||
case "Set":
|
||||
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.uintptrType}, false)
|
||||
asm := fmt.Sprintf("csrw %d, $0", csr)
|
||||
target := llvm.InlineAsm(fnType, asm, "r", true, false, 0)
|
||||
return c.builder.CreateCall(target, []llvm.Value{c.getValue(frame, call.Args[1])}, ""), nil
|
||||
case "SetBits":
|
||||
// Note: it may be possible to optimize this to csrrsi in many cases.
|
||||
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{c.uintptrType}, false)
|
||||
asm := fmt.Sprintf("csrrs $0, %d, $1", csr)
|
||||
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0)
|
||||
return c.builder.CreateCall(target, []llvm.Value{c.getValue(frame, call.Args[1])}, ""), nil
|
||||
case "ClearBits":
|
||||
// Note: it may be possible to optimize this to csrrci in many cases.
|
||||
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{c.uintptrType}, false)
|
||||
asm := fmt.Sprintf("csrrc $0, %d, $1", csr)
|
||||
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0)
|
||||
return c.builder.CreateCall(target, []llvm.Value{c.getValue(frame, call.Args[1])}, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, c.makeError(call.Pos(), "unknown CSR operation: "+name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,14 +279,14 @@ func (c *Compiler) getInterfaceMethodSet(typ *types.Named) llvm.Value {
|
||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||
}
|
||||
|
||||
// Every method is a *i16 reference indicating the signature of this method.
|
||||
// Every method is a *i8 reference indicating the signature of this method.
|
||||
methods := make([]llvm.Value, typ.Underlying().(*types.Interface).NumMethods())
|
||||
for i := range methods {
|
||||
method := typ.Underlying().(*types.Interface).Method(i)
|
||||
methods[i] = c.getMethodSignature(method)
|
||||
}
|
||||
|
||||
value := llvm.ConstArray(methods[0].Type(), methods)
|
||||
value := llvm.ConstArray(c.i8ptrType, methods)
|
||||
global = llvm.AddGlobal(c.mod, value.Type(), typ.String()+"$interface")
|
||||
global.SetInitializer(value)
|
||||
global.SetGlobalConstant(true)
|
||||
@@ -295,7 +295,7 @@ func (c *Compiler) getInterfaceMethodSet(typ *types.Named) llvm.Value {
|
||||
}
|
||||
|
||||
// getMethodSignature returns a global variable which is a reference to an
|
||||
// external *i16 indicating the indicating the signature of this method. It is
|
||||
// external *i8 indicating the indicating the signature of this method. It is
|
||||
// used during the interface lowering pass.
|
||||
func (c *Compiler) getMethodSignature(method *types.Func) llvm.Value {
|
||||
signature := ir.MethodSignature(method)
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
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)
|
||||
global.SetUnnamedAddr(true)
|
||||
initializer := llvm.ConstNull(globalLLVMType)
|
||||
initializer = llvm.ConstInsertValue(initializer, funcValue, []uint32{0})
|
||||
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(c.intType, uint64(id.Int64()), true), []uint32{1, 0})
|
||||
global.SetInitializer(initializer)
|
||||
|
||||
// Add debug info to the interrupt global.
|
||||
if c.Debug() {
|
||||
pos := c.ir.Program.Fset.Position(instr.Pos())
|
||||
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
|
||||
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
|
||||
LinkageName: globalName,
|
||||
File: c.getDIFile(pos.Filename),
|
||||
Line: pos.Line,
|
||||
Type: c.getDIType(globalType),
|
||||
Expr: c.dibuilder.CreateExpression(nil),
|
||||
LocalToUnit: false,
|
||||
})
|
||||
global.AddMetadata(0, diglobal)
|
||||
}
|
||||
|
||||
// Create the runtime/interrupt.Interrupt type. It is a struct with a single
|
||||
// member of type int.
|
||||
num := llvm.ConstPtrToInt(global, c.intType)
|
||||
interrupt := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num})
|
||||
|
||||
// Add dummy "use" call for AVR, because interrupts may be used even though
|
||||
// they are never referenced again. This is unlike Cortex-M or the RISC-V
|
||||
// PLIC where each interrupt must be enabled using the interrupt number, and
|
||||
// thus keeps the Interrupt object alive.
|
||||
// This call is removed during interrupt lowering.
|
||||
if strings.HasPrefix(c.Triple(), "avr") {
|
||||
useFn := c.mod.NamedFunction("runtime/interrupt.use")
|
||||
if useFn.IsNil() {
|
||||
useFnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
|
||||
useFn = llvm.AddFunction(c.mod, "runtime/interrupt.use", useFnType)
|
||||
}
|
||||
c.builder.CreateCall(useFn, []llvm.Value{interrupt}, "")
|
||||
}
|
||||
|
||||
return interrupt, nil
|
||||
}
|
||||
@@ -52,78 +52,6 @@ func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []l
|
||||
return llvmutil.EmitPointerUnpack(c.builder, c.mod, ptr, valueTypes)
|
||||
}
|
||||
|
||||
// splitBasicBlock splits a LLVM basic block into two parts. All instructions
|
||||
// after afterInst are moved into a new basic block (created right after the
|
||||
// current one) with the given name.
|
||||
func (c *Compiler) splitBasicBlock(afterInst llvm.Value, insertAfter llvm.BasicBlock, name string) llvm.BasicBlock {
|
||||
oldBlock := afterInst.InstructionParent()
|
||||
newBlock := c.ctx.InsertBasicBlock(insertAfter, name)
|
||||
var nextInstructions []llvm.Value // values to move
|
||||
|
||||
// Collect to-be-moved instructions.
|
||||
inst := afterInst
|
||||
for {
|
||||
inst = llvm.NextInstruction(inst)
|
||||
if inst.IsNil() {
|
||||
break
|
||||
}
|
||||
nextInstructions = append(nextInstructions, inst)
|
||||
}
|
||||
|
||||
// Move instructions.
|
||||
c.builder.SetInsertPointAtEnd(newBlock)
|
||||
for _, inst := range nextInstructions {
|
||||
inst.RemoveFromParentAsInstruction()
|
||||
c.builder.Insert(inst)
|
||||
}
|
||||
|
||||
// Find PHI nodes to update.
|
||||
var phiNodes []llvm.Value // PHI nodes to update
|
||||
for bb := insertAfter.Parent().FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if inst.IsAPHINode().IsNil() {
|
||||
continue
|
||||
}
|
||||
needsUpdate := false
|
||||
incomingCount := inst.IncomingCount()
|
||||
for i := 0; i < incomingCount; i++ {
|
||||
if inst.IncomingBlock(i) == oldBlock {
|
||||
needsUpdate = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !needsUpdate {
|
||||
// PHI node has no incoming edge from the old block.
|
||||
continue
|
||||
}
|
||||
phiNodes = append(phiNodes, inst)
|
||||
}
|
||||
}
|
||||
|
||||
// Update PHI nodes.
|
||||
for _, phi := range phiNodes {
|
||||
c.builder.SetInsertPointBefore(phi)
|
||||
newPhi := c.builder.CreatePHI(phi.Type(), "")
|
||||
incomingCount := phi.IncomingCount()
|
||||
incomingVals := make([]llvm.Value, incomingCount)
|
||||
incomingBlocks := make([]llvm.BasicBlock, incomingCount)
|
||||
for i := 0; i < incomingCount; i++ {
|
||||
value := phi.IncomingValue(i)
|
||||
block := phi.IncomingBlock(i)
|
||||
if block == oldBlock {
|
||||
block = newBlock
|
||||
}
|
||||
incomingVals[i] = value
|
||||
incomingBlocks[i] = block
|
||||
}
|
||||
newPhi.AddIncoming(incomingVals, incomingBlocks)
|
||||
phi.ReplaceAllUsesWith(newPhi)
|
||||
phi.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
return newBlock
|
||||
}
|
||||
|
||||
// makeGlobalArray creates a new LLVM global with the given name and integers as
|
||||
// contents, and returns the global.
|
||||
// Note that it is left with the default linkage etc., you should set
|
||||
|
||||
@@ -94,3 +94,75 @@ func getLifetimeEndFunc(mod llvm.Module) llvm.Value {
|
||||
}
|
||||
return fn
|
||||
}
|
||||
|
||||
// SplitBasicBlock splits a LLVM basic block into two parts. All instructions
|
||||
// after afterInst are moved into a new basic block (created right after the
|
||||
// current one) with the given name.
|
||||
func SplitBasicBlock(builder llvm.Builder, afterInst llvm.Value, insertAfter llvm.BasicBlock, name string) llvm.BasicBlock {
|
||||
oldBlock := afterInst.InstructionParent()
|
||||
newBlock := afterInst.Type().Context().InsertBasicBlock(insertAfter, name)
|
||||
var nextInstructions []llvm.Value // values to move
|
||||
|
||||
// Collect to-be-moved instructions.
|
||||
inst := afterInst
|
||||
for {
|
||||
inst = llvm.NextInstruction(inst)
|
||||
if inst.IsNil() {
|
||||
break
|
||||
}
|
||||
nextInstructions = append(nextInstructions, inst)
|
||||
}
|
||||
|
||||
// Move instructions.
|
||||
builder.SetInsertPointAtEnd(newBlock)
|
||||
for _, inst := range nextInstructions {
|
||||
inst.RemoveFromParentAsInstruction()
|
||||
builder.Insert(inst)
|
||||
}
|
||||
|
||||
// Find PHI nodes to update.
|
||||
var phiNodes []llvm.Value // PHI nodes to update
|
||||
for bb := insertAfter.Parent().FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||
if inst.IsAPHINode().IsNil() {
|
||||
continue
|
||||
}
|
||||
needsUpdate := false
|
||||
incomingCount := inst.IncomingCount()
|
||||
for i := 0; i < incomingCount; i++ {
|
||||
if inst.IncomingBlock(i) == oldBlock {
|
||||
needsUpdate = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !needsUpdate {
|
||||
// PHI node has no incoming edge from the old block.
|
||||
continue
|
||||
}
|
||||
phiNodes = append(phiNodes, inst)
|
||||
}
|
||||
}
|
||||
|
||||
// Update PHI nodes.
|
||||
for _, phi := range phiNodes {
|
||||
builder.SetInsertPointBefore(phi)
|
||||
newPhi := builder.CreatePHI(phi.Type(), "")
|
||||
incomingCount := phi.IncomingCount()
|
||||
incomingVals := make([]llvm.Value, incomingCount)
|
||||
incomingBlocks := make([]llvm.BasicBlock, incomingCount)
|
||||
for i := 0; i < incomingCount; i++ {
|
||||
value := phi.IncomingValue(i)
|
||||
block := phi.IncomingBlock(i)
|
||||
if block == oldBlock {
|
||||
block = newBlock
|
||||
}
|
||||
incomingVals[i] = value
|
||||
incomingBlocks[i] = block
|
||||
}
|
||||
newPhi.AddIncoming(incomingVals, incomingBlocks)
|
||||
phi.ReplaceAllUsesWith(newPhi)
|
||||
phi.EraseFromParentAsInstruction()
|
||||
}
|
||||
|
||||
return newBlock
|
||||
}
|
||||
|
||||
+63
-26
@@ -6,9 +6,45 @@ import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// emitMakeMap creates a new map object (runtime.hashmap) by allocating and
|
||||
// initializing an appropriately sized object.
|
||||
func (c *Compiler) emitMakeMap(frame *Frame, expr *ssa.MakeMap) (llvm.Value, error) {
|
||||
mapType := expr.Type().Underlying().(*types.Map)
|
||||
keyType := mapType.Key().Underlying()
|
||||
llvmValueType := c.getLLVMType(mapType.Elem().Underlying())
|
||||
var llvmKeyType llvm.Type
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// String keys.
|
||||
llvmKeyType = c.getLLVMType(keyType)
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// Trivially comparable keys.
|
||||
llvmKeyType = c.getLLVMType(keyType)
|
||||
} else {
|
||||
// All other keys. Implemented as map[interface{}]valueType for ease of
|
||||
// implementation.
|
||||
llvmKeyType = c.getLLVMRuntimeType("_interface")
|
||||
}
|
||||
keySize := c.targetData.TypeAllocSize(llvmKeyType)
|
||||
valueSize := c.targetData.TypeAllocSize(llvmValueType)
|
||||
llvmKeySize := llvm.ConstInt(c.ctx.Int8Type(), keySize, false)
|
||||
llvmValueSize := llvm.ConstInt(c.ctx.Int8Type(), valueSize, false)
|
||||
sizeHint := llvm.ConstInt(c.uintptrType, 8, false)
|
||||
if expr.Reserve != nil {
|
||||
sizeHint = c.getValue(frame, expr.Reserve)
|
||||
var err error
|
||||
sizeHint, err = c.parseConvert(expr.Reserve.Type(), types.Typ[types.Uintptr], sizeHint, expr.Pos())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
}
|
||||
hashmap := c.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
|
||||
return hashmap, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
||||
llvmValueType := c.getLLVMType(valueType)
|
||||
|
||||
@@ -19,6 +55,7 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
||||
|
||||
// Do the lookup. How it is done depends on the key type.
|
||||
var commaOkValue llvm.Value
|
||||
keyType = keyType.Underlying()
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// key is a string
|
||||
params := []llvm.Value{m, key, mapValuePtr}
|
||||
@@ -34,8 +71,14 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
||||
commaOkValue = c.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||
c.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
||||
} else {
|
||||
// Not trivially comparable using memcmp.
|
||||
return llvm.Value{}, c.makeError(pos, "only strings, bools, ints, pointers or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||
// Not trivially comparable using memcmp. Make it an interface instead.
|
||||
itfKey := key
|
||||
if _, ok := keyType.(*types.Interface); !ok {
|
||||
// Not already an interface, so convert it to an interface now.
|
||||
itfKey = c.parseMakeInterface(key, keyType, pos)
|
||||
}
|
||||
params := []llvm.Value{m, itfKey, mapValuePtr}
|
||||
commaOkValue = c.createRuntimeCall("hashmapInterfaceGet", params, "")
|
||||
}
|
||||
|
||||
// Load the resulting value from the hashmap. The value is set to the zero
|
||||
@@ -69,7 +112,14 @@ func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, p
|
||||
c.createRuntimeCall("hashmapBinarySet", params, "")
|
||||
c.emitLifetimeEnd(keyPtr, keySize)
|
||||
} else {
|
||||
c.addError(pos, "only strings, bools, ints, pointers or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||
// Key is not trivially comparable, so compare it as an interface instead.
|
||||
itfKey := key
|
||||
if _, ok := keyType.(*types.Interface); !ok {
|
||||
// Not already an interface, so convert it to an interface first.
|
||||
itfKey = c.parseMakeInterface(key, keyType, pos)
|
||||
}
|
||||
params := []llvm.Value{m, itfKey, valuePtr}
|
||||
c.createRuntimeCall("hashmapInterfaceSet", params, "")
|
||||
}
|
||||
c.emitLifetimeEnd(valuePtr, valueSize)
|
||||
}
|
||||
@@ -89,32 +139,19 @@ func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos toke
|
||||
c.emitLifetimeEnd(keyPtr, keySize)
|
||||
return nil
|
||||
} else {
|
||||
return c.makeError(pos, "only strings, bools, ints, pointers or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||
// Key is not trivially comparable, so compare it as an interface
|
||||
// instead.
|
||||
itfKey := key
|
||||
if _, ok := keyType.(*types.Interface); !ok {
|
||||
// Not already an interface, so convert it to an interface first.
|
||||
itfKey = c.parseMakeInterface(key, keyType, pos)
|
||||
}
|
||||
params := []llvm.Value{m, itfKey}
|
||||
c.createRuntimeCall("hashmapInterfaceDelete", params, "")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Get FNV-1a hash of this string.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
|
||||
func hashmapHash(data []byte) uint32 {
|
||||
var result uint32 = 2166136261 // FNV offset basis
|
||||
for _, c := range data {
|
||||
result ^= uint32(c)
|
||||
result *= 16777619 // FNV prime
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Get the topmost 8 bits of the hash, without using a special value (like 0).
|
||||
func hashmapTopHash(hash uint32) uint8 {
|
||||
tophash := uint8(hash >> 24)
|
||||
if tophash < 1 {
|
||||
// 0 means empty slot, so make it bigger.
|
||||
tophash += 1
|
||||
}
|
||||
return tophash
|
||||
}
|
||||
|
||||
// Returns true if this key type does not contain strings, interfaces etc., so
|
||||
// can be compared with runtime.memequal.
|
||||
func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||
|
||||
+27
-12
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
// Run the LLVM optimizer over the module.
|
||||
// The inliner can be disabled (if necessary) by passing 0 to the inlinerThreshold.
|
||||
func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) error {
|
||||
func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) []error {
|
||||
builder := llvm.NewPassManagerBuilder()
|
||||
defer builder.Dispose()
|
||||
builder.SetOptLevel(optLevel)
|
||||
@@ -25,9 +25,9 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
|
||||
// run a check of all of our code
|
||||
if c.VerifyIR() {
|
||||
err := c.checkModule()
|
||||
if err != nil {
|
||||
return err
|
||||
errs := c.checkModule()
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
// Run some preparatory passes for the Go optimizer.
|
||||
goPasses := llvm.NewPassManager()
|
||||
defer goPasses.Dispose()
|
||||
goPasses.AddGlobalDCEPass()
|
||||
goPasses.AddGlobalOptimizerPass()
|
||||
goPasses.AddConstantPropagationPass()
|
||||
goPasses.AddAggressiveDCEPass()
|
||||
@@ -56,7 +57,15 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
transform.OptimizeStringToBytes(c.mod)
|
||||
transform.OptimizeAllocs(c.mod)
|
||||
transform.LowerInterfaces(c.mod)
|
||||
c.LowerFuncValues()
|
||||
|
||||
errs := transform.LowerInterrupts(c.mod)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
if c.funcImplementation() == funcValueSwitch {
|
||||
transform.LowerFuncValues(c.mod)
|
||||
}
|
||||
|
||||
// After interfaces are lowered, there are many more opportunities for
|
||||
// interprocedural optimizations. To get them to work, function
|
||||
@@ -85,24 +94,30 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
|
||||
err := c.LowerGoroutines()
|
||||
if err != nil {
|
||||
return err
|
||||
return []error{err}
|
||||
}
|
||||
} else {
|
||||
// Must be run at any optimization level.
|
||||
transform.LowerInterfaces(c.mod)
|
||||
c.LowerFuncValues()
|
||||
if c.funcImplementation() == funcValueSwitch {
|
||||
transform.LowerFuncValues(c.mod)
|
||||
}
|
||||
err := c.LowerGoroutines()
|
||||
if err != nil {
|
||||
return err
|
||||
return []error{err}
|
||||
}
|
||||
errs := transform.LowerInterrupts(c.mod)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
}
|
||||
if c.VerifyIR() {
|
||||
if err := c.checkModule(); err != nil {
|
||||
return err
|
||||
if errs := c.checkModule(); errs != nil {
|
||||
return errs
|
||||
}
|
||||
}
|
||||
if err := c.Verify(); err != nil {
|
||||
return errors.New("optimizations caused a verification failure")
|
||||
return []error{errors.New("optimizations caused a verification failure")}
|
||||
}
|
||||
|
||||
if sizeLevel >= 2 {
|
||||
@@ -141,7 +156,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
||||
hasGCPass = transform.MakeGCStackSlots(c.mod) || hasGCPass
|
||||
if hasGCPass {
|
||||
if err := c.Verify(); err != nil {
|
||||
return errors.New("GC pass caused a verification failure")
|
||||
return []error{errors.New("GC pass caused a verification failure")}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,9 @@ func (s *StdSizes) Sizeof(T types.Type) int64 {
|
||||
if k == types.UnsafePointer {
|
||||
return s.PtrSize
|
||||
}
|
||||
if k == types.Invalid {
|
||||
return 0 // only relevant when there is a type error somewhere
|
||||
}
|
||||
panic("unknown basic type: " + t.String())
|
||||
case *types.Array:
|
||||
n := t.Len()
|
||||
|
||||
+33
-3
@@ -60,14 +60,44 @@ func (c *Compiler) getGlobal(g *ssa.Global) llvm.Value {
|
||||
info := c.getGlobalInfo(g)
|
||||
llvmGlobal := c.mod.NamedGlobal(info.linkName)
|
||||
if llvmGlobal.IsNil() {
|
||||
llvmType := c.getLLVMType(g.Type().(*types.Pointer).Elem())
|
||||
typ := g.Type().(*types.Pointer).Elem()
|
||||
llvmType := c.getLLVMType(typ)
|
||||
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
||||
if !info.extern {
|
||||
llvmGlobal.SetInitializer(llvm.ConstNull(llvmType))
|
||||
llvmGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
}
|
||||
if info.align > c.targetData.ABITypeAlignment(llvmType) {
|
||||
llvmGlobal.SetAlignment(info.align)
|
||||
|
||||
// Set alignment from the //go:align comment.
|
||||
var alignInBits uint32
|
||||
if info.align < 0 || info.align&(info.align-1) != 0 {
|
||||
// Check for power-of-two (or 0).
|
||||
// See: https://stackoverflow.com/a/108360
|
||||
c.addError(g.Pos(), "global variable alignment must be a positive power of two")
|
||||
} else {
|
||||
// Set the alignment only when it is a power of two.
|
||||
alignInBits = uint32(info.align) ^ uint32(info.align-1)
|
||||
if info.align > c.targetData.ABITypeAlignment(llvmType) {
|
||||
llvmGlobal.SetAlignment(info.align)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Debug() && !info.extern {
|
||||
// Add debug info.
|
||||
// TODO: this should be done for every global in the program, not just
|
||||
// the ones that are referenced from some code.
|
||||
pos := c.ir.Program.Fset.Position(g.Pos())
|
||||
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
|
||||
Name: g.RelString(nil),
|
||||
LinkageName: info.linkName,
|
||||
File: c.getDIFile(pos.Filename),
|
||||
Line: pos.Line,
|
||||
Type: c.getDIType(typ),
|
||||
LocalToUnit: false,
|
||||
Expr: c.dibuilder.CreateExpression(nil),
|
||||
AlignInBits: alignInBits,
|
||||
})
|
||||
llvmGlobal.AddMetadata(0, diglobal)
|
||||
}
|
||||
}
|
||||
return llvmGlobal
|
||||
|
||||
+1
-1
@@ -152,7 +152,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH())
|
||||
}
|
||||
switch c.GOOS() {
|
||||
case "linux":
|
||||
case "linux", "freebsd":
|
||||
// Return values: r0, r1 uintptr, err Errno
|
||||
// Pseudocode:
|
||||
// var err uintptr
|
||||
|
||||
@@ -10,5 +10,5 @@ require (
|
||||
go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect
|
||||
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef
|
||||
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257
|
||||
tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566
|
||||
)
|
||||
|
||||
@@ -30,3 +30,7 @@ tinygo.org/x/go-llvm v0.0.0-20191113125529-bad6d01809e8 h1:9Bfvso+tTVQg16UzOA614
|
||||
tinygo.org/x/go-llvm v0.0.0-20191113125529-bad6d01809e8/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257 h1:o8VDylrMN7gWemBMu8rEyuogKPhcLTdx5KrUAp9macc=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191215173731-ad71f3d24aae h1:s8J5EyxCkHxXB08UI3gk9W9IS/ekizRvSX+PfZxnAB0=
|
||||
tinygo.org/x/go-llvm v0.0.0-20191215173731-ad71f3d24aae/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566 h1:a4y30bTf7U0zDA75v2PTL+XQ2OzJetj19gK8XwQpUNY=
|
||||
tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
|
||||
@@ -18,6 +18,7 @@ var Keys = []string{
|
||||
"GOROOT",
|
||||
"GOPATH",
|
||||
"GOCACHE",
|
||||
"CGO_ENABLED",
|
||||
"TINYGOROOT",
|
||||
}
|
||||
|
||||
@@ -57,6 +58,13 @@ func Get(name string) string {
|
||||
panic("could not find cache dir: " + err.Error())
|
||||
}
|
||||
return filepath.Join(dir, "tinygo")
|
||||
case "CGO_ENABLED":
|
||||
val := os.Getenv("CGO_ENABLED")
|
||||
if val == "1" || val == "0" {
|
||||
return val
|
||||
}
|
||||
// Default to enabling CGo.
|
||||
return "1"
|
||||
case "TINYGOROOT":
|
||||
return sourceDir()
|
||||
default:
|
||||
|
||||
+67
-16
@@ -125,7 +125,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
}
|
||||
indices[i] = uint32(operand.Value().ZExtValue())
|
||||
}
|
||||
result := value.GetElementPtr(indices)
|
||||
result, err := value.GetElementPtr(indices)
|
||||
if err != nil {
|
||||
return nil, nil, fr.errorAt(inst, err.Error())
|
||||
}
|
||||
if result.Type() != inst.Type() {
|
||||
return nil, nil, fr.errorAt(inst, "interp: gep: type does not match")
|
||||
}
|
||||
@@ -264,7 +267,11 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
if elementCount == 1 {
|
||||
fr.locals[resultInst] = result
|
||||
} else {
|
||||
fr.locals[resultInst] = result.GetElementPtr([]uint32{0, 0})
|
||||
result, err := result.GetElementPtr([]uint32{0, 0})
|
||||
if err != nil {
|
||||
return nil, nil, errorAt(inst, err.Error())
|
||||
}
|
||||
fr.locals[resultInst] = result
|
||||
}
|
||||
case callee.Name() == "runtime.hashmapMake":
|
||||
// create a map
|
||||
@@ -278,24 +285,49 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
}
|
||||
case callee.Name() == "runtime.hashmapStringSet":
|
||||
// set a string key in the map
|
||||
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
||||
if !ok {
|
||||
return nil, nil, fr.errorAt(inst, "could not update map with string key")
|
||||
}
|
||||
// "key" is a Go string value, which in the TinyGo calling convention is split up
|
||||
// into separate pointer and length parameters.
|
||||
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
||||
keyLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
||||
valPtr := fr.getLocal(inst.Operand(3)).(*LocalValue)
|
||||
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
||||
if !ok || !keyBuf.IsConstant() || !keyLen.IsConstant() || !valPtr.IsConstant() {
|
||||
// The mapassign operation could not be done at compile
|
||||
// time. Do it at runtime instead.
|
||||
m := fr.getLocal(inst.Operand(0)).Value()
|
||||
fr.markDirty(m)
|
||||
llvmParams := []llvm.Value{
|
||||
m, // *runtime.hashmap
|
||||
fr.getLocal(inst.Operand(1)).Value(), // key.ptr
|
||||
fr.getLocal(inst.Operand(2)).Value(), // key.len
|
||||
fr.getLocal(inst.Operand(3)).Value(), // value (unsafe.Pointer)
|
||||
fr.getLocal(inst.Operand(4)).Value(), // context
|
||||
fr.getLocal(inst.Operand(5)).Value(), // parentHandle
|
||||
}
|
||||
fr.builder.CreateCall(callee, llvmParams, "")
|
||||
continue
|
||||
}
|
||||
// "key" is a Go string value, which in the TinyGo calling convention is split up
|
||||
// into separate pointer and length parameters.
|
||||
m.PutString(keyBuf, keyLen, valPtr)
|
||||
case callee.Name() == "runtime.hashmapBinarySet":
|
||||
// set a binary (int etc.) key in the map
|
||||
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
||||
if !ok {
|
||||
return nil, nil, fr.errorAt(inst, "could not update map")
|
||||
}
|
||||
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
||||
valPtr := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
||||
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
||||
if !ok || !keyBuf.IsConstant() || !valPtr.IsConstant() {
|
||||
// The mapassign operation could not be done at compile
|
||||
// time. Do it at runtime instead.
|
||||
m := fr.getLocal(inst.Operand(0)).Value()
|
||||
fr.markDirty(m)
|
||||
llvmParams := []llvm.Value{
|
||||
m, // *runtime.hashmap
|
||||
fr.getLocal(inst.Operand(1)).Value(), // key
|
||||
fr.getLocal(inst.Operand(2)).Value(), // value
|
||||
fr.getLocal(inst.Operand(3)).Value(), // context
|
||||
fr.getLocal(inst.Operand(4)).Value(), // parentHandle
|
||||
}
|
||||
fr.builder.CreateCall(callee, llvmParams, "")
|
||||
continue
|
||||
}
|
||||
m.PutBinary(keyBuf, valPtr)
|
||||
case callee.Name() == "runtime.stringConcat":
|
||||
// adding two strings together
|
||||
@@ -340,8 +372,16 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
// a bitcast of the original array instead of the GEP,
|
||||
// which breaks our assumptions.
|
||||
// Re-add this GEP, in the hope that it it is then of the correct type...
|
||||
dstArray = dstArray.GetElementPtr([]uint32{0, 0}).(*LocalValue)
|
||||
srcArray = srcArray.GetElementPtr([]uint32{0, 0}).(*LocalValue)
|
||||
dstArrayValue, err := dstArray.GetElementPtr([]uint32{0, 0})
|
||||
if err != nil {
|
||||
return nil, nil, errorAt(inst, err.Error())
|
||||
}
|
||||
dstArray = dstArrayValue.(*LocalValue)
|
||||
srcArrayValue, err := srcArray.GetElementPtr([]uint32{0, 0})
|
||||
if err != nil {
|
||||
return nil, nil, errorAt(inst, err.Error())
|
||||
}
|
||||
srcArray = srcArrayValue.(*LocalValue)
|
||||
}
|
||||
if fr.Eval.TargetData.TypeAllocSize(dstArray.Type().ElementType()) != elementSize {
|
||||
return nil, nil, fr.errorAt(inst, "interp: slice dst element size does not match pointer type")
|
||||
@@ -360,12 +400,21 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
return nil, nil, fr.errorAt(inst, "interp: trying to copy a slice with negative length?")
|
||||
}
|
||||
for i := int64(0); i < length; i++ {
|
||||
var err error
|
||||
// *dst = *src
|
||||
dstArray.Store(srcArray.Load())
|
||||
// dst++
|
||||
dstArray = dstArray.GetElementPtr([]uint32{1}).(*LocalValue)
|
||||
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
|
||||
if err != nil {
|
||||
return nil, nil, errorAt(inst, err.Error())
|
||||
}
|
||||
dstArray = dstArrayValue.(*LocalValue)
|
||||
// src++
|
||||
srcArray = srcArray.GetElementPtr([]uint32{1}).(*LocalValue)
|
||||
srcArrayValue, err := srcArray.GetElementPtr([]uint32{1})
|
||||
if err != nil {
|
||||
return nil, nil, errorAt(inst, err.Error())
|
||||
}
|
||||
srcArray = srcArrayValue.(*LocalValue)
|
||||
}
|
||||
case callee.Name() == "runtime.stringToBytes":
|
||||
// convert a string to a []byte
|
||||
@@ -433,6 +482,8 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
|
||||
case callee.Name() == "llvm.dbg.value":
|
||||
// do nothing
|
||||
case strings.HasPrefix(callee.Name(), "llvm.lifetime."):
|
||||
// do nothing
|
||||
case callee.Name() == "runtime.trackPointer":
|
||||
// do nothing
|
||||
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
||||
|
||||
@@ -14,6 +14,7 @@ func TestInterp(t *testing.T) {
|
||||
"basic",
|
||||
"slice-copy",
|
||||
"consteval",
|
||||
"map",
|
||||
} {
|
||||
name := name // make tc local to this closure
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
||||
+13
-8
@@ -1,6 +1,8 @@
|
||||
package interp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -39,22 +41,25 @@ type sideEffectResult struct {
|
||||
// returns whether this function has side effects and if it does, which globals
|
||||
// it mentions anywhere in this function or any called functions.
|
||||
func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) {
|
||||
switch fn.Name() {
|
||||
case "runtime.alloc":
|
||||
name := fn.Name()
|
||||
switch {
|
||||
case name == "runtime.alloc":
|
||||
// Cannot be scanned but can be interpreted.
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case "runtime.nanotime":
|
||||
case name == "runtime.nanotime":
|
||||
// Fixed value at compile time.
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case "runtime._panic":
|
||||
case name == "runtime._panic":
|
||||
return &sideEffectResult{severity: sideEffectLimited}, nil
|
||||
case "runtime.interfaceImplements":
|
||||
case name == "runtime.interfaceImplements":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case "runtime.sliceCopy":
|
||||
case name == "runtime.sliceCopy":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case "runtime.trackPointer":
|
||||
case name == "runtime.trackPointer":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case "llvm.dbg.value":
|
||||
case name == "llvm.dbg.value":
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
case strings.HasPrefix(name, "llvm.lifetime."):
|
||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||
}
|
||||
if fn.IsDeclaration() {
|
||||
|
||||
Vendored
+76
@@ -0,0 +1,76 @@
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv6m-none-eabi"
|
||||
|
||||
%runtime._string = type { i8*, i32 }
|
||||
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
|
||||
|
||||
@main.m = global %runtime.hashmap* null
|
||||
@main.binaryMap = global %runtime.hashmap* null
|
||||
@main.stringMap = global %runtime.hashmap* null
|
||||
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
|
||||
|
||||
declare %runtime.hashmap* @runtime.hashmapMake(i8, i8, i32, i8* %context, i8* %parentHandle)
|
||||
declare void @runtime.hashmapBinarySet(%runtime.hashmap*, i8*, i8*, i8* %context, i8* %parentHandle)
|
||||
declare void @runtime.hashmapStringSet(%runtime.hashmap*, i8*, i32, i8*, i8* %context, i8* %parentHandle)
|
||||
declare void @llvm.lifetime.end.p0i8(i64, i8*)
|
||||
declare void @llvm.lifetime.start.p0i8(i64, i8*)
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @main.init(i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
; Test that hashmap optimizations generally work (even with lifetimes).
|
||||
%hashmap.key = alloca i8
|
||||
%hashmap.value = alloca %runtime._string
|
||||
%0 = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 8, i32 1, i8* undef, i8* null)
|
||||
%hashmap.value.bitcast = bitcast %runtime._string* %hashmap.value to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 8, i8* %hashmap.value.bitcast)
|
||||
store %runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string* %hashmap.value
|
||||
call void @llvm.lifetime.start.p0i8(i64 1, i8* %hashmap.key)
|
||||
store i8 1, i8* %hashmap.key
|
||||
call void @runtime.hashmapBinarySet(%runtime.hashmap* %0, i8* %hashmap.key, i8* %hashmap.value.bitcast, i8* undef, i8* null)
|
||||
call void @llvm.lifetime.end.p0i8(i64 1, i8* %hashmap.key)
|
||||
call void @llvm.lifetime.end.p0i8(i64 8, i8* %hashmap.value.bitcast)
|
||||
store %runtime.hashmap* %0, %runtime.hashmap** @main.m
|
||||
|
||||
; Other tests, that can be done in a separate function.
|
||||
call void @main.testNonConstantBinarySet()
|
||||
call void @main.testNonConstantStringSet()
|
||||
ret void
|
||||
}
|
||||
|
||||
; Test that a map loaded from a global can still be used for mapassign
|
||||
; operations (with binary keys).
|
||||
define internal void @main.testNonConstantBinarySet() {
|
||||
%hashmap.key = alloca i8
|
||||
%hashmap.value = alloca i8
|
||||
; Create hashmap from global. This breaks the normal hashmapBinarySet
|
||||
; optimization, to test the fallback.
|
||||
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 1, i32 1, i8* undef, i8* null)
|
||||
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.binaryMap
|
||||
%map = load %runtime.hashmap*, %runtime.hashmap** @main.binaryMap
|
||||
; Do the binary set to the newly loaded map.
|
||||
store i8 1, i8* %hashmap.key
|
||||
store i8 2, i8* %hashmap.value
|
||||
call void @runtime.hashmapBinarySet(%runtime.hashmap* %map, i8* %hashmap.key, i8* %hashmap.value, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Test that a map loaded from a global can still be used for mapassign
|
||||
; operations (with string keys).
|
||||
define internal void @main.testNonConstantStringSet() {
|
||||
%hashmap.value = alloca i8
|
||||
; Create hashmap from global. This breaks the normal hashmapStringSet
|
||||
; optimization, to test the fallback.
|
||||
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 8, i8 1, i32 1, i8* undef, i8* null)
|
||||
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.stringMap
|
||||
%map = load %runtime.hashmap*, %runtime.hashmap** @main.stringMap
|
||||
; Do the string set to the newly loaded map.
|
||||
store i8 2, i8* %hashmap.value
|
||||
call void @runtime.hashmapStringSet(%runtime.hashmap* %map, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7, i8* %hashmap.value, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv6m-none-eabi"
|
||||
|
||||
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
|
||||
%runtime._string = type { i8*, i32 }
|
||||
|
||||
@main.m = local_unnamed_addr global %runtime.hashmap* @"main$map"
|
||||
@main.binaryMap = local_unnamed_addr global %runtime.hashmap* @"main$map.4"
|
||||
@main.stringMap = local_unnamed_addr global %runtime.hashmap* @"main$map.6"
|
||||
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
|
||||
@"main$mapbucket" = internal unnamed_addr global { [8 x i8], i8*, [8 x i8], [8 x %runtime._string] } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, [8 x i8] c"\01\00\00\00\00\00\00\00", [8 x %runtime._string] [%runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer] }
|
||||
@"main$map" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, [8 x i8], [8 x %runtime._string] }, { [8 x i8], i8*, [8 x i8], [8 x %runtime._string] }* @"main$mapbucket", i32 0, i32 0, i32 0), i32 1, i8 1, i8 8, i8 0 }
|
||||
@"main$alloca.2" = internal global i8 1
|
||||
@"main$alloca.3" = internal global i8 2
|
||||
@"main$map.4" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* null, i32 0, i8 1, i8 1, i8 0 }
|
||||
@"main$alloca.5" = internal global i8 2
|
||||
@"main$map.6" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* null, i32 0, i8 8, i8 1, i8 0 }
|
||||
|
||||
declare void @runtime.hashmapBinarySet(%runtime.hashmap*, i8*, i8*, i8*, i8*) local_unnamed_addr
|
||||
|
||||
declare void @runtime.hashmapStringSet(%runtime.hashmap*, i8*, i32, i8*, i8*, i8*) local_unnamed_addr
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @runtime.hashmapBinarySet(%runtime.hashmap* @"main$map.4", i8* @"main$alloca.2", i8* @"main$alloca.3", i8* undef, i8* null)
|
||||
call void @runtime.hashmapStringSet(%runtime.hashmap* @"main$map.6", i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7, i8* @"main$alloca.5", i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
+5
-1
@@ -24,7 +24,11 @@ func getStringBytes(strPtr Value, strLen llvm.Value) []byte {
|
||||
}
|
||||
buf := make([]byte, strLen.ZExtValue())
|
||||
for i := range buf {
|
||||
c := strPtr.GetElementPtr([]uint32{uint32(i)}).Load()
|
||||
gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
|
||||
if err != nil {
|
||||
panic(err) // TODO
|
||||
}
|
||||
c := gep.Load()
|
||||
buf[i] = byte(c.ZExtValue())
|
||||
}
|
||||
return buf
|
||||
|
||||
+20
-17
@@ -3,6 +3,7 @@ package interp
|
||||
// This file provides a litte bit of abstraction around LLVM values.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
@@ -11,13 +12,13 @@ import (
|
||||
// A Value is a LLVM value with some extra methods attached for easier
|
||||
// interpretation.
|
||||
type Value interface {
|
||||
Value() llvm.Value // returns a LLVM value
|
||||
Type() llvm.Type // equal to Value().Type()
|
||||
IsConstant() bool // returns true if this value is a constant value
|
||||
Load() llvm.Value // dereference a pointer
|
||||
Store(llvm.Value) // store to a pointer
|
||||
GetElementPtr([]uint32) Value // returns an interior pointer
|
||||
String() string // string representation, for debugging
|
||||
Value() llvm.Value // returns a LLVM value
|
||||
Type() llvm.Type // equal to Value().Type()
|
||||
IsConstant() bool // returns true if this value is a constant value
|
||||
Load() llvm.Value // dereference a pointer
|
||||
Store(llvm.Value) // store to a pointer
|
||||
GetElementPtr([]uint32) (Value, error) // returns an interior pointer
|
||||
String() string // string representation, for debugging
|
||||
}
|
||||
|
||||
// A type that simply wraps a LLVM constant value.
|
||||
@@ -97,21 +98,21 @@ func (v *LocalValue) Store(value llvm.Value) {
|
||||
}
|
||||
|
||||
// GetElementPtr returns a GEP when the underlying value is of pointer type.
|
||||
func (v *LocalValue) GetElementPtr(indices []uint32) Value {
|
||||
func (v *LocalValue) GetElementPtr(indices []uint32) (Value, error) {
|
||||
if !v.Underlying.IsAGlobalVariable().IsNil() {
|
||||
int32Type := v.Underlying.Type().Context().Int32Type()
|
||||
gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices))
|
||||
return &LocalValue{v.Eval, gep}
|
||||
return &LocalValue{v.Eval, gep}, nil
|
||||
}
|
||||
if !v.Underlying.IsAConstantExpr().IsNil() {
|
||||
switch v.Underlying.Opcode() {
|
||||
case llvm.GetElementPtr, llvm.IntToPtr, llvm.BitCast:
|
||||
int32Type := v.Underlying.Type().Context().Int32Type()
|
||||
llvmIndices := getLLVMIndices(int32Type, indices)
|
||||
return &LocalValue{v.Eval, llvm.ConstGEP(v.Underlying, llvmIndices)}
|
||||
return &LocalValue{v.Eval, llvm.ConstGEP(v.Underlying, llvmIndices)}, nil
|
||||
}
|
||||
}
|
||||
panic("interp: unknown GEP")
|
||||
return nil, errors.New("interp: unknown GEP")
|
||||
}
|
||||
|
||||
// stripPointerCasts removes all const bitcasts from pointer values, if there
|
||||
@@ -309,8 +310,8 @@ func (v *MapValue) Store(value llvm.Value) {
|
||||
|
||||
// GetElementPtr panics: maps are of reference type so their (interior)
|
||||
// addresses cannot be calculated.
|
||||
func (v *MapValue) GetElementPtr(indices []uint32) Value {
|
||||
panic("interp: GEP on a map")
|
||||
func (v *MapValue) GetElementPtr(indices []uint32) (Value, error) {
|
||||
return nil, errors.New("interp: GEP on a map")
|
||||
}
|
||||
|
||||
// PutString does a map assign operation, assuming that the map is of type
|
||||
@@ -367,10 +368,12 @@ func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
|
||||
}
|
||||
}
|
||||
|
||||
if keyPtr.Underlying.Opcode() == llvm.BitCast {
|
||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||
} else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr {
|
||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||
if !keyPtr.Underlying.IsAConstantExpr().IsNil() {
|
||||
if keyPtr.Underlying.Opcode() == llvm.BitCast {
|
||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||
} else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr {
|
||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||
}
|
||||
}
|
||||
key := keyPtr.Load()
|
||||
if v.KeyType.IsNil() {
|
||||
|
||||
@@ -27,14 +27,13 @@ type Program struct {
|
||||
// Function or method.
|
||||
type Function struct {
|
||||
*ssa.Function
|
||||
LLVMFn llvm.Value
|
||||
module string // go:wasm-module
|
||||
linkName string // go:linkname, go:export, go:interrupt
|
||||
exported bool // go:export
|
||||
nobounds bool // go:nobounds
|
||||
flag bool // used by dead code elimination
|
||||
interrupt bool // go:interrupt
|
||||
inline InlineType // go:inline
|
||||
LLVMFn llvm.Value
|
||||
module string // go:wasm-module
|
||||
linkName string // go:linkname, go:export
|
||||
exported bool // go:export
|
||||
nobounds bool // go:nobounds
|
||||
flag bool // used by dead code elimination
|
||||
inline InlineType // go:inline
|
||||
}
|
||||
|
||||
// Interface type that is at some point used in a type assert (to check whether
|
||||
@@ -243,18 +242,6 @@ func (f *Function) parsePragmas() {
|
||||
f.inline = InlineHint
|
||||
case "//go:noinline":
|
||||
f.inline = InlineNone
|
||||
case "//go:interrupt":
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
name := parts[1]
|
||||
if strings.HasSuffix(name, "_vect") {
|
||||
// AVR vector naming
|
||||
name = "__vector_" + name[:len(name)-5]
|
||||
}
|
||||
f.linkName = name
|
||||
f.exported = true
|
||||
f.interrupt = true
|
||||
case "//go:linkname":
|
||||
if len(parts) != 3 || parts[1] != f.Name() {
|
||||
continue
|
||||
@@ -288,14 +275,6 @@ func (f *Function) IsExported() bool {
|
||||
return f.exported || f.CName() != ""
|
||||
}
|
||||
|
||||
// Return true for functions annotated with //go:interrupt. The function name is
|
||||
// already customized in LinkName() to hook up in the interrupt vector.
|
||||
//
|
||||
// On some platforms (like AVR), interrupts need a special compiler flag.
|
||||
func (f *Function) IsInterrupt() bool {
|
||||
return f.interrupt
|
||||
}
|
||||
|
||||
// Return the inline directive of this function.
|
||||
func (f *Function) Inline() InlineType {
|
||||
return f.inline
|
||||
|
||||
Submodule
+1
Submodule lib/wasi-libc added at a280fead2a
+14
-3
@@ -6,6 +6,7 @@ import (
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
@@ -46,7 +47,7 @@ type Package struct {
|
||||
|
||||
// Import loads the given package relative to srcDir (for the vendor directory).
|
||||
// It only loads the current package without recursion.
|
||||
func (p *Program) Import(path, srcDir string) (*Package, error) {
|
||||
func (p *Program) Import(path, srcDir string, pos token.Position) (*Package, error) {
|
||||
if p.Packages == nil {
|
||||
p.Packages = make(map[string]*Package)
|
||||
}
|
||||
@@ -59,7 +60,10 @@ func (p *Program) Import(path, srcDir string) (*Package, error) {
|
||||
}
|
||||
buildPkg, err := ctx.Import(path, srcDir, build.ImportComment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, scanner.Error{
|
||||
Pos: pos,
|
||||
Msg: err.Error(), // TODO: define a new error type that will wrap the inner error
|
||||
}
|
||||
}
|
||||
if existingPkg, ok := p.Packages[buildPkg.ImportPath]; ok {
|
||||
// Already imported, or at least started the import.
|
||||
@@ -467,7 +471,14 @@ func (p *Package) importRecursively(includeTests bool) error {
|
||||
if _, ok := p.Imports[to]; ok {
|
||||
continue
|
||||
}
|
||||
importedPkg, err := p.Program.Import(to, p.Package.Dir)
|
||||
// Find error location.
|
||||
var pos token.Position
|
||||
if len(p.Package.ImportPos[to]) > 0 {
|
||||
pos = p.Package.ImportPos[to][0]
|
||||
} else {
|
||||
pos = token.Position{Filename: p.Package.ImportPath}
|
||||
}
|
||||
importedPkg, err := p.Program.Import(to, p.Package.Dir, pos)
|
||||
if err != nil {
|
||||
if err, ok := err.(*ImportCycleError); ok {
|
||||
err.Packages = append([]string{p.ImportPath}, err.Packages...)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
@@ -183,6 +184,14 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
return builder.Build(pkgName, fileExt, config, func(tmppath string) error {
|
||||
// do we need port reset to put MCU into bootloader mode?
|
||||
if config.Target.PortReset == "true" {
|
||||
if port == "" {
|
||||
var err error
|
||||
port, err = getDefaultPort()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := touchSerialPortAt1200bps(port)
|
||||
if err != nil {
|
||||
return &commandError{"failed to reset port", tmppath, err}
|
||||
@@ -198,10 +207,30 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
flashCmd := config.Target.FlashCommand
|
||||
fileToken := "{" + fileExt[1:] + "}"
|
||||
flashCmd = strings.Replace(flashCmd, fileToken, tmppath, -1)
|
||||
|
||||
if port == "" && strings.Contains(flashCmd, "{port}") {
|
||||
var err error
|
||||
port, err = getDefaultPort()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
flashCmd = strings.Replace(flashCmd, "{port}", port, -1)
|
||||
|
||||
// Execute the command.
|
||||
cmd := exec.Command("/bin/sh", "-c", flashCmd)
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
command := strings.Split(flashCmd, " ")
|
||||
if len(command) < 2 {
|
||||
return errors.New("invalid flash command")
|
||||
}
|
||||
cmd = exec.Command(command[0], command[1:]...)
|
||||
default:
|
||||
cmd = exec.Command("/bin/sh", "-c", flashCmd)
|
||||
}
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = goenv.Get("TINYGOROOT")
|
||||
@@ -254,7 +283,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
||||
//
|
||||
// Note: this command is expected to execute just before exiting, as it
|
||||
// modifies global state.
|
||||
func FlashGDB(pkgName, port string, ocdOutput bool, options *compileopts.Options) error {
|
||||
func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) error {
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -268,11 +297,13 @@ func FlashGDB(pkgName, port string, ocdOutput bool, options *compileopts.Options
|
||||
gdbInterface, openocdInterface := config.Programmer()
|
||||
switch gdbInterface {
|
||||
case "msd", "command", "":
|
||||
if gdbInterface == "" {
|
||||
gdbInterface = "command"
|
||||
}
|
||||
if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
|
||||
if len(config.Target.Emulator) != 0 {
|
||||
// Assume QEMU as an emulator.
|
||||
gdbInterface = "qemu"
|
||||
} else if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
|
||||
gdbInterface = "openocd"
|
||||
} else if config.Target.JLinkDevice != "" {
|
||||
gdbInterface = "jlink"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,6 +334,51 @@ func FlashGDB(pkgName, port string, ocdOutput bool, options *compileopts.Options
|
||||
// Make sure the daemon doesn't receive Ctrl-C that is intended for
|
||||
// GDB (to break the currently executing program).
|
||||
setCommandAsDaemon(daemon)
|
||||
// Start now, and kill it on exit.
|
||||
daemon.Start()
|
||||
defer func() {
|
||||
daemon.Process.Signal(os.Interrupt)
|
||||
// Maybe we should send a .Kill() after x seconds?
|
||||
daemon.Wait()
|
||||
}()
|
||||
case "jlink":
|
||||
gdbCommands = append(gdbCommands, "target remote :2331", "load", "monitor reset halt")
|
||||
|
||||
// We need a separate debugging daemon for on-chip debugging.
|
||||
daemon := exec.Command("JLinkGDBServer", "-device", config.Target.JLinkDevice)
|
||||
if ocdOutput {
|
||||
// Make it clear which output is from the daemon.
|
||||
w := &ColorWriter{
|
||||
Out: os.Stderr,
|
||||
Prefix: "jlink: ",
|
||||
Color: TermColorYellow,
|
||||
}
|
||||
daemon.Stdout = w
|
||||
daemon.Stderr = w
|
||||
}
|
||||
// Make sure the daemon doesn't receive Ctrl-C that is intended for
|
||||
// GDB (to break the currently executing program).
|
||||
setCommandAsDaemon(daemon)
|
||||
// Start now, and kill it on exit.
|
||||
daemon.Start()
|
||||
defer func() {
|
||||
daemon.Process.Signal(os.Interrupt)
|
||||
// Maybe we should send a .Kill() after x seconds?
|
||||
daemon.Wait()
|
||||
}()
|
||||
case "qemu":
|
||||
gdbCommands = append(gdbCommands, "target remote :1234")
|
||||
|
||||
// Run in an emulator.
|
||||
args := append(config.Target.Emulator[1:], tmppath, "-s", "-S")
|
||||
daemon := exec.Command(config.Target.Emulator[0], args...)
|
||||
daemon.Stdout = os.Stdout
|
||||
daemon.Stderr = os.Stderr
|
||||
|
||||
// Make sure the daemon doesn't receive Ctrl-C that is intended for
|
||||
// GDB (to break the currently executing program).
|
||||
setCommandAsDaemon(daemon)
|
||||
|
||||
// Start now, and kill it on exit.
|
||||
daemon.Start()
|
||||
defer func() {
|
||||
@@ -401,9 +477,18 @@ func touchSerialPortAt1200bps(port string) error {
|
||||
|
||||
func flashUF2UsingMSD(volume, tmppath string) error {
|
||||
// find standard UF2 info path
|
||||
infoPath := "/media/*/" + volume + "/INFO_UF2.TXT"
|
||||
if runtime.GOOS == "darwin" {
|
||||
var infoPath string
|
||||
switch runtime.GOOS {
|
||||
case "linux", "freebsd":
|
||||
infoPath = "/media/*/" + volume + "/INFO_UF2.TXT"
|
||||
case "darwin":
|
||||
infoPath = "/Volumes/" + volume + "/INFO_UF2.TXT"
|
||||
case "windows":
|
||||
path, err := windowsFindUSBDrive(volume)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
infoPath = path + "/INFO_UF2.TXT"
|
||||
}
|
||||
|
||||
d, err := filepath.Glob(infoPath)
|
||||
@@ -419,9 +504,18 @@ func flashUF2UsingMSD(volume, tmppath string) error {
|
||||
|
||||
func flashHexUsingMSD(volume, tmppath string) error {
|
||||
// find expected volume path
|
||||
destPath := "/media/*/" + volume
|
||||
if runtime.GOOS == "darwin" {
|
||||
var destPath string
|
||||
switch runtime.GOOS {
|
||||
case "linux", "freebsd":
|
||||
destPath = "/media/*/" + volume
|
||||
case "darwin":
|
||||
destPath = "/Volumes/" + volume
|
||||
case "windows":
|
||||
path, err := windowsFindUSBDrive(volume)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
destPath = path + "/"
|
||||
}
|
||||
|
||||
d, err := filepath.Glob(destPath)
|
||||
@@ -435,6 +529,29 @@ func flashHexUsingMSD(volume, tmppath string) error {
|
||||
return moveFile(tmppath, d[0]+"/flash.hex")
|
||||
}
|
||||
|
||||
func windowsFindUSBDrive(volume string) (string, error) {
|
||||
cmd := exec.Command("wmic",
|
||||
"PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'",
|
||||
"get", "DeviceID,VolumeName,FileSystem,DriveType")
|
||||
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(out.String(), "\n") {
|
||||
words := strings.Fields(line)
|
||||
if len(words) >= 3 {
|
||||
if words[1] == "2" && words[2] == "FAT" {
|
||||
return words[0], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", errors.New("unable to locate a USB device to be flashed")
|
||||
}
|
||||
|
||||
// parseSize converts a human-readable size (with k/m/g suffix) into a plain
|
||||
// number.
|
||||
func parseSize(s string) (int64, error) {
|
||||
@@ -459,6 +576,55 @@ func parseSize(s string) (int64, error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// getDefaultPort returns the default serial port depending on the operating system.
|
||||
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("no serial ports available")
|
||||
}
|
||||
|
||||
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 serial port")
|
||||
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 serial port")
|
||||
}
|
||||
|
||||
return d[0], nil
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
||||
fmt.Fprintln(os.Stderr, "version:", version)
|
||||
@@ -526,7 +692,7 @@ func main() {
|
||||
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||
port := flag.String("port", "/dev/ttyACM0", "flash port")
|
||||
port := flag.String("port", "", "flash port")
|
||||
programmer := flag.String("programmer", "", "which hardware programmer to use")
|
||||
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
||||
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
||||
@@ -629,7 +795,7 @@ func main() {
|
||||
usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
err := FlashGDB(flag.Arg(0), *port, *ocdOutput, options)
|
||||
err := FlashGDB(flag.Arg(0), *ocdOutput, options)
|
||||
handleCompilerError(err)
|
||||
}
|
||||
case "run":
|
||||
|
||||
+49
-26
@@ -12,7 +12,9 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
@@ -50,7 +52,7 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("EmulatedCortexM3", func(t *testing.T) {
|
||||
runPlatTests("qemu", matches, t)
|
||||
runPlatTests("cortex-m-qemu", matches, t)
|
||||
})
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
@@ -67,41 +69,38 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
|
||||
func runPlatTests(target string, matches []string, t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, path := range matches {
|
||||
switch {
|
||||
case target == "wasm":
|
||||
// testdata/gc.go is known not to work on WebAssembly
|
||||
if path == filepath.Join("testdata", "gc.go") {
|
||||
continue
|
||||
}
|
||||
case target == "":
|
||||
// run all tests on host
|
||||
case target == "qemu":
|
||||
// all tests are supported
|
||||
default:
|
||||
// cross-compilation of cgo is not yet supported
|
||||
if path == filepath.Join("testdata", "cgo")+string(filepath.Separator) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
path := path // redefine to avoid race condition
|
||||
|
||||
t.Run(filepath.Base(path), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
runTest(path, target, t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Due to some problems with LLD, we cannot run links in parallel, or in parallel with compiles.
|
||||
// Therefore, we put a lock around builds and run everything else in parallel.
|
||||
var buildLock sync.Mutex
|
||||
|
||||
// runBuild is a thread-safe wrapper around Build.
|
||||
func runBuild(src, out string, opts *compileopts.Options) error {
|
||||
buildLock.Lock()
|
||||
defer buildLock.Unlock()
|
||||
|
||||
return Build(src, out, opts)
|
||||
}
|
||||
|
||||
func runTest(path, target string, t *testing.T) {
|
||||
// Get the expected output for this test.
|
||||
txtpath := path[:len(path)-3] + ".txt"
|
||||
if path[len(path)-1] == os.PathSeparator {
|
||||
txtpath = path + "out.txt"
|
||||
}
|
||||
f, err := os.Open(txtpath)
|
||||
if err != nil {
|
||||
t.Fatal("could not open expected output file:", err)
|
||||
}
|
||||
expected, err := ioutil.ReadAll(f)
|
||||
expected, err := ioutil.ReadFile(txtpath)
|
||||
if err != nil {
|
||||
t.Fatal("could not read expected output file:", err)
|
||||
}
|
||||
@@ -130,7 +129,7 @@ func runTest(path, target string, t *testing.T) {
|
||||
WasmAbi: "js",
|
||||
}
|
||||
binary := filepath.Join(tmpdir, "test")
|
||||
err = Build("./"+path, binary, config)
|
||||
err = runBuild("./"+path, binary, config)
|
||||
if err != nil {
|
||||
if errLoader, ok := err.(loader.Errors); ok {
|
||||
for _, err := range errLoader.Errs {
|
||||
@@ -144,7 +143,9 @@ func runTest(path, target string, t *testing.T) {
|
||||
}
|
||||
|
||||
// Run the test.
|
||||
runComplete := make(chan struct{})
|
||||
var cmd *exec.Cmd
|
||||
ranTooLong := false
|
||||
if target == "" {
|
||||
cmd = exec.Command(binary)
|
||||
} else {
|
||||
@@ -160,13 +161,35 @@ func runTest(path, target string, t *testing.T) {
|
||||
}
|
||||
stdout := &bytes.Buffer{}
|
||||
cmd.Stdout = stdout
|
||||
if target != "" {
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
t.Fatal("failed to start:", err)
|
||||
}
|
||||
err = cmd.Run()
|
||||
go func() {
|
||||
// Terminate the process if it runs too long.
|
||||
timer := time.NewTimer(1 * time.Second)
|
||||
select {
|
||||
case <-runComplete:
|
||||
timer.Stop()
|
||||
case <-timer.C:
|
||||
ranTooLong = true
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd.Process.Signal(os.Kill) // Windows doesn't support SIGINT.
|
||||
} else {
|
||||
cmd.Process.Signal(os.Interrupt)
|
||||
}
|
||||
}
|
||||
}()
|
||||
err = cmd.Wait()
|
||||
if _, ok := err.(*exec.ExitError); ok && target != "" {
|
||||
err = nil // workaround for QEMU
|
||||
}
|
||||
close(runComplete)
|
||||
|
||||
if ranTooLong {
|
||||
stdout.WriteString("--- test ran too long, terminating...\n")
|
||||
}
|
||||
|
||||
// putchar() prints CRLF, convert it to LF.
|
||||
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
package riscv
|
||||
|
||||
// This file lists constants for CSR operations and defines methods on CSRs that
|
||||
// are implemented as compiler intrinsics.
|
||||
|
||||
// CSR constants are used for use in CSR (Control and Status Register) compiler
|
||||
// intrinsics.
|
||||
type CSR int16
|
||||
|
||||
// Get returns the value of the given CSR.
|
||||
func (csr CSR) Get() uintptr
|
||||
|
||||
// Set stores a new value in the given CSR.
|
||||
func (csr CSR) Set(uintptr)
|
||||
|
||||
// SetBits atomically sets the given bits in this ISR and returns the old value.
|
||||
func (csr CSR) SetBits(uintptr) uintptr
|
||||
|
||||
// ClearBits atomically clears the given bits in this ISR and returns the old
|
||||
// value.
|
||||
func (csr CSR) ClearBits(uintptr) uintptr
|
||||
|
||||
// CSR values defined in the RISC-V privileged specification. Not all values may
|
||||
// be available on any given chip.
|
||||
//
|
||||
// Source: https://github.com/riscv/riscv-isa-manual/blob/riscv-priv-1.10/src/priv-csrs.tex
|
||||
const (
|
||||
// User Trap Setup
|
||||
USTATUS CSR = 0x000 // User status register.
|
||||
UIE CSR = 0x004 // User interrupt-enable register.
|
||||
UTVEC CSR = 0x005 // User trap handler base address.
|
||||
|
||||
// User Trap Handling
|
||||
USCRATCH CSR = 0x040 // Scratch register for user trap handlers.
|
||||
UEPC CSR = 0x041 // User exception program counter.
|
||||
UCAUSE CSR = 0x042 // User trap cause.
|
||||
UTVAL CSR = 0x043 // User bad address or instruction.
|
||||
UIP CSR = 0x044 // User interrupt pending.
|
||||
|
||||
// User Floating-Point CSRs
|
||||
FFLAGS CSR = 0x001 // Floating-Point Accrued Exceptions.
|
||||
FRM CSR = 0x002 // Floating-Point Dynamic Rounding Mode.
|
||||
FCSR CSR = 0x003 // Floating-Point Control and Status
|
||||
|
||||
// User Counter/Timers
|
||||
CYCLE CSR = 0xC00 // Cycle counter for RDCYCLE instruction.
|
||||
TIME CSR = 0xC01 // Timer for RDTIME instruction.
|
||||
INSTRET CSR = 0xC02 // Instructions-retired counter for RDINSTRET instruction.
|
||||
HPMCOUNTER3 CSR = 0xC03 // Performance-monitoring counter 3.
|
||||
HPMCOUNTER4 CSR = 0xC04 // Performance-monitoring counter 4.
|
||||
HPMCOUNTER5 CSR = 0xC05 // Performance-monitoring counter 5.
|
||||
HPMCOUNTER6 CSR = 0xC06 // Performance-monitoring counter 6.
|
||||
HPMCOUNTER7 CSR = 0xC07 // Performance-monitoring counter 7.
|
||||
HPMCOUNTER8 CSR = 0xC08 // Performance-monitoring counter 8.
|
||||
HPMCOUNTER9 CSR = 0xC09 // Performance-monitoring counter 9.
|
||||
HPMCOUNTER10 CSR = 0xC0A // Performance-monitoring counter 10.
|
||||
HPMCOUNTER11 CSR = 0xC0B // Performance-monitoring counter 11.
|
||||
HPMCOUNTER12 CSR = 0xC0C // Performance-monitoring counter 12.
|
||||
HPMCOUNTER13 CSR = 0xC0D // Performance-monitoring counter 13.
|
||||
HPMCOUNTER14 CSR = 0xC0E // Performance-monitoring counter 14.
|
||||
HPMCOUNTER15 CSR = 0xC0F // Performance-monitoring counter 15.
|
||||
HPMCOUNTER16 CSR = 0xC10 // Performance-monitoring counter 16.
|
||||
HPMCOUNTER17 CSR = 0xC11 // Performance-monitoring counter 17.
|
||||
HPMCOUNTER18 CSR = 0xC12 // Performance-monitoring counter 18.
|
||||
HPMCOUNTER19 CSR = 0xC13 // Performance-monitoring counter 19.
|
||||
HPMCOUNTER20 CSR = 0xC14 // Performance-monitoring counter 20.
|
||||
HPMCOUNTER21 CSR = 0xC15 // Performance-monitoring counter 21.
|
||||
HPMCOUNTER22 CSR = 0xC16 // Performance-monitoring counter 22.
|
||||
HPMCOUNTER23 CSR = 0xC17 // Performance-monitoring counter 23.
|
||||
HPMCOUNTER24 CSR = 0xC18 // Performance-monitoring counter 24.
|
||||
HPMCOUNTER25 CSR = 0xC19 // Performance-monitoring counter 25.
|
||||
HPMCOUNTER26 CSR = 0xC1A // Performance-monitoring counter 26.
|
||||
HPMCOUNTER27 CSR = 0xC1B // Performance-monitoring counter 27.
|
||||
HPMCOUNTER28 CSR = 0xC1C // Performance-monitoring counter 28.
|
||||
HPMCOUNTER29 CSR = 0xC1D // Performance-monitoring counter 29.
|
||||
HPMCOUNTER30 CSR = 0xC1E // Performance-monitoring counter 30.
|
||||
HPMCOUNTER31 CSR = 0xC1F // Performance-monitoring counter 31.
|
||||
CYCLEH CSR = 0xC80 // Upper 32 bits of CYCLE, RV32I only.
|
||||
TIMEH CSR = 0xC81 // Upper 32 bits of TIME, RV32I only.
|
||||
INSTRETH CSR = 0xC82 // Upper 32 bits of INSTRET, RV32I only.
|
||||
HPMCOUNTER3H CSR = 0xC83 // Upper 32 bits of HPMCOUNTER3, RV32I only.
|
||||
HPMCOUNTER4H CSR = 0xC84 // Upper 32 bits of HPMCOUNTER4, RV32I only.
|
||||
HPMCOUNTER5H CSR = 0xC85 // Upper 32 bits of HPMCOUNTER5, RV32I only.
|
||||
HPMCOUNTER6H CSR = 0xC86 // Upper 32 bits of HPMCOUNTER6, RV32I only.
|
||||
HPMCOUNTER7H CSR = 0xC87 // Upper 32 bits of HPMCOUNTER7, RV32I only.
|
||||
HPMCOUNTER8H CSR = 0xC88 // Upper 32 bits of HPMCOUNTER8, RV32I only.
|
||||
HPMCOUNTER9H CSR = 0xC89 // Upper 32 bits of HPMCOUNTER9, RV32I only.
|
||||
HPMCOUNTER10H CSR = 0xC8A // Upper 32 bits of HPMCOUNTER10, RV32I only.
|
||||
HPMCOUNTER11H CSR = 0xC8B // Upper 32 bits of HPMCOUNTER11, RV32I only.
|
||||
HPMCOUNTER12H CSR = 0xC8C // Upper 32 bits of HPMCOUNTER12, RV32I only.
|
||||
HPMCOUNTER13H CSR = 0xC8D // Upper 32 bits of HPMCOUNTER13, RV32I only.
|
||||
HPMCOUNTER14H CSR = 0xC8E // Upper 32 bits of HPMCOUNTER14, RV32I only.
|
||||
HPMCOUNTER15H CSR = 0xC8F // Upper 32 bits of HPMCOUNTER15, RV32I only.
|
||||
HPMCOUNTER16H CSR = 0xC90 // Upper 32 bits of HPMCOUNTER16, RV32I only.
|
||||
HPMCOUNTER17H CSR = 0xC91 // Upper 32 bits of HPMCOUNTER17, RV32I only.
|
||||
HPMCOUNTER18H CSR = 0xC92 // Upper 32 bits of HPMCOUNTER18, RV32I only.
|
||||
HPMCOUNTER19H CSR = 0xC93 // Upper 32 bits of HPMCOUNTER19, RV32I only.
|
||||
HPMCOUNTER20H CSR = 0xC94 // Upper 32 bits of HPMCOUNTER20, RV32I only.
|
||||
HPMCOUNTER21H CSR = 0xC95 // Upper 32 bits of HPMCOUNTER21, RV32I only.
|
||||
HPMCOUNTER22H CSR = 0xC96 // Upper 32 bits of HPMCOUNTER22, RV32I only.
|
||||
HPMCOUNTER23H CSR = 0xC97 // Upper 32 bits of HPMCOUNTER23, RV32I only.
|
||||
HPMCOUNTER24H CSR = 0xC98 // Upper 32 bits of HPMCOUNTER24, RV32I only.
|
||||
HPMCOUNTER25H CSR = 0xC99 // Upper 32 bits of HPMCOUNTER25, RV32I only.
|
||||
HPMCOUNTER26H CSR = 0xC9A // Upper 32 bits of HPMCOUNTER26, RV32I only.
|
||||
HPMCOUNTER27H CSR = 0xC9B // Upper 32 bits of HPMCOUNTER27, RV32I only.
|
||||
HPMCOUNTER28H CSR = 0xC9C // Upper 32 bits of HPMCOUNTER28, RV32I only.
|
||||
HPMCOUNTER29H CSR = 0xC9D // Upper 32 bits of HPMCOUNTER29, RV32I only.
|
||||
HPMCOUNTER30H CSR = 0xC9E // Upper 32 bits of HPMCOUNTER30, RV32I only.
|
||||
HPMCOUNTER31H CSR = 0xC9F // Upper 32 bits of HPMCOUNTER31, RV32I only.
|
||||
|
||||
// Supervisor Trap Setup
|
||||
SSTATUS CSR = 0x100 // Supervisor status register.
|
||||
SEDELEG CSR = 0x102 // Supervisor exception delegation register.
|
||||
SIDELEG CSR = 0x103 // Supervisor interrupt delegation register.
|
||||
SIE CSR = 0x104 // Supervisor interrupt-enable register.
|
||||
STVEC CSR = 0x105 // Supervisor trap handler base address.
|
||||
SCOUNTEREN CSR = 0x106 // Supervisor counter enable.
|
||||
|
||||
// Supervisor Trap Handling
|
||||
SSCRATCH CSR = 0x140 // Scratch register for supervisor trap handlers.
|
||||
SEPC CSR = 0x141 // Supervisor exception program counter.
|
||||
SCAUSE CSR = 0x142 // Supervisor trap cause.
|
||||
STVAL CSR = 0x143 // Supervisor bad address or instruction.
|
||||
SIP CSR = 0x144 // Supervisor interrupt pending.
|
||||
|
||||
// Supervisor Protection and Translation
|
||||
SATP CSR = 0x180 // Supervisor address translation and protection.
|
||||
|
||||
// Machine Information Registers
|
||||
MVENDORID CSR = 0xF11 // Vendor ID.
|
||||
MARCHID CSR = 0xF12 // Architecture ID.
|
||||
MIMPID CSR = 0xF13 // Implementation ID.
|
||||
MHARTID CSR = 0xF14 // Hardware thread ID.
|
||||
|
||||
// Machine Trap Setup
|
||||
MSTATUS CSR = 0x300 // Machine status register.
|
||||
MISA CSR = 0x301 // ISA and extensions
|
||||
MEDELEG CSR = 0x302 // Machine exception delegation register.
|
||||
MIDELEG CSR = 0x303 // Machine interrupt delegation register.
|
||||
MIE CSR = 0x304 // Machine interrupt-enable register.
|
||||
MTVEC CSR = 0x305 // Machine trap-handler base address.
|
||||
MCOUNTEREN CSR = 0x306 // Machine counter enable.
|
||||
|
||||
// Machine Trap Handling
|
||||
MSCRATCH CSR = 0x340 // Scratch register for machine trap handlers.
|
||||
MEPC CSR = 0x341 // Machine exception program counter.
|
||||
MCAUSE CSR = 0x342 // Machine trap cause.
|
||||
MTVAL CSR = 0x343 // Machine bad address or instruction.
|
||||
MIP CSR = 0x344 // Machine interrupt pending.
|
||||
|
||||
// Machine Protection and Translation
|
||||
PMPCFG0 CSR = 0x3A0 // Physical memory protection configuration.
|
||||
PMPCFG1 CSR = 0x3A1 // Physical memory protection configuration, RV32 only.
|
||||
PMPCFG2 CSR = 0x3A2 // Physical memory protection configuration.
|
||||
PMPCFG3 CSR = 0x3A3 // Physical memory protection configuration, RV32 only.
|
||||
PMPADDR0 CSR = 0x3B0 // Physical memory protection address register 0.
|
||||
PMPADDR1 CSR = 0x3B1 // Physical memory protection address register 1.
|
||||
PMPADDR2 CSR = 0x3B2 // Physical memory protection address register 2.
|
||||
PMPADDR3 CSR = 0x3B3 // Physical memory protection address register 3.
|
||||
PMPADDR4 CSR = 0x3B4 // Physical memory protection address register 4.
|
||||
PMPADDR5 CSR = 0x3B5 // Physical memory protection address register 5.
|
||||
PMPADDR6 CSR = 0x3B6 // Physical memory protection address register 6.
|
||||
PMPADDR7 CSR = 0x3B7 // Physical memory protection address register 7.
|
||||
PMPADDR8 CSR = 0x3B8 // Physical memory protection address register 8.
|
||||
PMPADDR9 CSR = 0x3B9 // Physical memory protection address register 9.
|
||||
PMPADDR10 CSR = 0x3BA // Physical memory protection address register 10.
|
||||
PMPADDR11 CSR = 0x3BB // Physical memory protection address register 11.
|
||||
PMPADDR12 CSR = 0x3BC // Physical memory protection address register 12.
|
||||
PMPADDR13 CSR = 0x3BD // Physical memory protection address register 13.
|
||||
PMPADDR14 CSR = 0x3BE // Physical memory protection address register 14.
|
||||
PMPADDR15 CSR = 0x3BF // Physical memory protection address register 15.
|
||||
|
||||
// Machine Counter/Timers
|
||||
mcycle CSR = 0xB00 // Machine cycle counter.
|
||||
minstret CSR = 0xB02 // Machine instructions-retired counter.
|
||||
MHPMCOUNTER3 CSR = 0xB03 // Machine performance-monitoring counter 3.
|
||||
MHPMCOUNTER4 CSR = 0xB04 // Machine performance-monitoring counter 4.
|
||||
MHPMCOUNTER5 CSR = 0xB05 // Machine performance-monitoring counter 5.
|
||||
MHPMCOUNTER6 CSR = 0xB06 // Machine performance-monitoring counter 6.
|
||||
MHPMCOUNTER7 CSR = 0xB07 // Machine performance-monitoring counter 7.
|
||||
MHPMCOUNTER8 CSR = 0xB08 // Machine performance-monitoring counter 8.
|
||||
MHPMCOUNTER9 CSR = 0xB09 // Machine performance-monitoring counter 9.
|
||||
MHPMCOUNTER10 CSR = 0xB0A // Machine performance-monitoring counter 10.
|
||||
MHPMCOUNTER11 CSR = 0xB0B // Machine performance-monitoring counter 11.
|
||||
MHPMCOUNTER12 CSR = 0xB0C // Machine performance-monitoring counter 12.
|
||||
MHPMCOUNTER13 CSR = 0xB0D // Machine performance-monitoring counter 13.
|
||||
MHPMCOUNTER14 CSR = 0xB0E // Machine performance-monitoring counter 14.
|
||||
MHPMCOUNTER15 CSR = 0xB0F // Machine performance-monitoring counter 15.
|
||||
MHPMCOUNTER16 CSR = 0xB10 // Machine performance-monitoring counter 16.
|
||||
MHPMCOUNTER17 CSR = 0xB11 // Machine performance-monitoring counter 17.
|
||||
MHPMCOUNTER18 CSR = 0xB12 // Machine performance-monitoring counter 18.
|
||||
MHPMCOUNTER19 CSR = 0xB13 // Machine performance-monitoring counter 19.
|
||||
MHPMCOUNTER20 CSR = 0xB14 // Machine performance-monitoring counter 20.
|
||||
MHPMCOUNTER21 CSR = 0xB15 // Machine performance-monitoring counter 21.
|
||||
MHPMCOUNTER22 CSR = 0xB16 // Machine performance-monitoring counter 22.
|
||||
MHPMCOUNTER23 CSR = 0xB17 // Machine performance-monitoring counter 23.
|
||||
MHPMCOUNTER24 CSR = 0xB18 // Machine performance-monitoring counter 24.
|
||||
MHPMCOUNTER25 CSR = 0xB19 // Machine performance-monitoring counter 25.
|
||||
MHPMCOUNTER26 CSR = 0xB1A // Machine performance-monitoring counter 26.
|
||||
MHPMCOUNTER27 CSR = 0xB1B // Machine performance-monitoring counter 27.
|
||||
MHPMCOUNTER28 CSR = 0xB1C // Machine performance-monitoring counter 28.
|
||||
MHPMCOUNTER29 CSR = 0xB1D // Machine performance-monitoring counter 29.
|
||||
MHPMCOUNTER30 CSR = 0xB1E // Machine performance-monitoring counter 30.
|
||||
MHPMCOUNTER31 CSR = 0xB1F // Machine performance-monitoring counter 31.
|
||||
MCYCLEH CSR = 0xB80 // Upper 32 bits of MCYCLE, RV32I only.
|
||||
MINSTRETH CSR = 0xB82 // Upper 32 bits of MINSTRET, RV32I only.
|
||||
MHPMCOUNTER3H CSR = 0xB83 // Upper 32 bits of MHPMCOUNTER3, RV32I only.
|
||||
MHPMCOUNTER4H CSR = 0xB84 // Upper 32 bits of MHPMCOUNTER4, RV32I only.
|
||||
MHPMCOUNTER5H CSR = 0xB85 // Upper 32 bits of MHPMCOUNTER5, RV32I only.
|
||||
MHPMCOUNTER6H CSR = 0xB86 // Upper 32 bits of MHPMCOUNTER6, RV32I only.
|
||||
MHPMCOUNTER7H CSR = 0xB87 // Upper 32 bits of MHPMCOUNTER7, RV32I only.
|
||||
MHPMCOUNTER8H CSR = 0xB88 // Upper 32 bits of MHPMCOUNTER8, RV32I only.
|
||||
MHPMCOUNTER9H CSR = 0xB89 // Upper 32 bits of MHPMCOUNTER9, RV32I only.
|
||||
MHPMCOUNTER10H CSR = 0xB8A // Upper 32 bits of MHPMCOUNTER10, RV32I only.
|
||||
MHPMCOUNTER11H CSR = 0xB8B // Upper 32 bits of MHPMCOUNTER11, RV32I only.
|
||||
MHPMCOUNTER12H CSR = 0xB8C // Upper 32 bits of MHPMCOUNTER12, RV32I only.
|
||||
MHPMCOUNTER13H CSR = 0xB8D // Upper 32 bits of MHPMCOUNTER13, RV32I only.
|
||||
MHPMCOUNTER14H CSR = 0xB8E // Upper 32 bits of MHPMCOUNTER14, RV32I only.
|
||||
MHPMCOUNTER15H CSR = 0xB8F // Upper 32 bits of MHPMCOUNTER15, RV32I only.
|
||||
MHPMCOUNTER16H CSR = 0xB90 // Upper 32 bits of MHPMCOUNTER16, RV32I only.
|
||||
MHPMCOUNTER17H CSR = 0xB91 // Upper 32 bits of MHPMCOUNTER17, RV32I only.
|
||||
MHPMCOUNTER18H CSR = 0xB92 // Upper 32 bits of MHPMCOUNTER18, RV32I only.
|
||||
MHPMCOUNTER19H CSR = 0xB93 // Upper 32 bits of MHPMCOUNTER19, RV32I only.
|
||||
MHPMCOUNTER20H CSR = 0xB94 // Upper 32 bits of MHPMCOUNTER20, RV32I only.
|
||||
MHPMCOUNTER21H CSR = 0xB95 // Upper 32 bits of MHPMCOUNTER21, RV32I only.
|
||||
MHPMCOUNTER22H CSR = 0xB96 // Upper 32 bits of MHPMCOUNTER22, RV32I only.
|
||||
MHPMCOUNTER23H CSR = 0xB97 // Upper 32 bits of MHPMCOUNTER23, RV32I only.
|
||||
MHPMCOUNTER24H CSR = 0xB98 // Upper 32 bits of MHPMCOUNTER24, RV32I only.
|
||||
MHPMCOUNTER25H CSR = 0xB99 // Upper 32 bits of MHPMCOUNTER25, RV32I only.
|
||||
MHPMCOUNTER26H CSR = 0xB9A // Upper 32 bits of MHPMCOUNTER26, RV32I only.
|
||||
MHPMCOUNTER27H CSR = 0xB9B // Upper 32 bits of MHPMCOUNTER27, RV32I only.
|
||||
MHPMCOUNTER28H CSR = 0xB9C // Upper 32 bits of MHPMCOUNTER28, RV32I only.
|
||||
MHPMCOUNTER29H CSR = 0xB9D // Upper 32 bits of MHPMCOUNTER29, RV32I only.
|
||||
MHPMCOUNTER30H CSR = 0xB9E // Upper 32 bits of MHPMCOUNTER30, RV32I only.
|
||||
MHPMCOUNTER31H CSR = 0xB9F // Upper 32 bits of MHPMCOUNTER31, RV32I only.
|
||||
|
||||
// Machine Counter Setup
|
||||
MHPMEVENT4 CSR = 0x324 // Machine performance-monitoring event selector 4.
|
||||
MHPMEVENT5 CSR = 0x325 // Machine performance-monitoring event selector 5.
|
||||
MHPMEVENT6 CSR = 0x326 // Machine performance-monitoring event selector 6.
|
||||
MHPMEVENT7 CSR = 0x327 // Machine performance-monitoring event selector 7.
|
||||
MHPMEVENT8 CSR = 0x328 // Machine performance-monitoring event selector 8.
|
||||
MHPMEVENT9 CSR = 0x329 // Machine performance-monitoring event selector 9.
|
||||
MHPMEVENT10 CSR = 0x32A // Machine performance-monitoring event selector 10.
|
||||
MHPMEVENT11 CSR = 0x32B // Machine performance-monitoring event selector 11.
|
||||
MHPMEVENT12 CSR = 0x32C // Machine performance-monitoring event selector 12.
|
||||
MHPMEVENT13 CSR = 0x32D // Machine performance-monitoring event selector 13.
|
||||
MHPMEVENT14 CSR = 0x32E // Machine performance-monitoring event selector 14.
|
||||
MHPMEVENT15 CSR = 0x32F // Machine performance-monitoring event selector 15.
|
||||
MHPMEVENT16 CSR = 0x330 // Machine performance-monitoring event selector 16.
|
||||
MHPMEVENT17 CSR = 0x331 // Machine performance-monitoring event selector 17.
|
||||
MHPMEVENT18 CSR = 0x332 // Machine performance-monitoring event selector 18.
|
||||
MHPMEVENT19 CSR = 0x333 // Machine performance-monitoring event selector 19.
|
||||
MHPMEVENT20 CSR = 0x334 // Machine performance-monitoring event selector 20.
|
||||
MHPMEVENT21 CSR = 0x335 // Machine performance-monitoring event selector 21.
|
||||
MHPMEVENT22 CSR = 0x336 // Machine performance-monitoring event selector 22.
|
||||
MHPMEVENT23 CSR = 0x337 // Machine performance-monitoring event selector 23.
|
||||
MHPMEVENT24 CSR = 0x338 // Machine performance-monitoring event selector 24.
|
||||
MHPMEVENT25 CSR = 0x339 // Machine performance-monitoring event selector 25.
|
||||
MHPMEVENT26 CSR = 0x33A // Machine performance-monitoring event selector 26.
|
||||
MHPMEVENT27 CSR = 0x33B // Machine performance-monitoring event selector 27.
|
||||
MHPMEVENT28 CSR = 0x33C // Machine performance-monitoring event selector 28.
|
||||
MHPMEVENT29 CSR = 0x33D // Machine performance-monitoring event selector 29.
|
||||
MHPMEVENT30 CSR = 0x33E // Machine performance-monitoring event selector 30.
|
||||
MHPMEVENT31 CSR = 0x33F // Machine performance-monitoring event selector 31.
|
||||
|
||||
// Debug/Trace Registers (shared with Debug Mode)
|
||||
TSELECT CSR = 0x7A0 // Debug/Trace trigger register select.
|
||||
TDATA1 CSR = 0x7A1 // First Debug/Trace trigger data register.
|
||||
TDATA2 CSR = 0x7A2 // Second Debug/Trace trigger data register.
|
||||
TDATA3 CSR = 0x7A3 // Third Debug/Trace trigger data register.
|
||||
|
||||
// Debug Mode Registers
|
||||
DCSR CSR = 0x7B0 // Debug control and status register.
|
||||
DPC CSR = 0x7B1 // Debug PC.
|
||||
DSCRATCH CSR = 0x7B2 // Debug scratch register.
|
||||
)
|
||||
@@ -3,11 +3,58 @@
|
||||
.type _start,@function
|
||||
|
||||
_start:
|
||||
// Workaround for missing support of the la pseudo-instruction in Clang 8:
|
||||
// https://reviews.llvm.org/D55325
|
||||
lui sp, %hi(_stack_top)
|
||||
addi sp, sp, %lo(_stack_top)
|
||||
// see https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register
|
||||
lui gp, %hi(__global_pointer$)
|
||||
addi gp, gp, %lo(__global_pointer$)
|
||||
// Load the stack pointer.
|
||||
la sp, _stack_top
|
||||
|
||||
// Load the globals pointer. The program will load pointers relative to this
|
||||
// register, so it must be set to the right value on startup.
|
||||
// See: https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register
|
||||
la gp, __global_pointer$
|
||||
|
||||
// Jump to runtime.main
|
||||
call main
|
||||
|
||||
.section .text.handleInterruptASM
|
||||
.global handleInterruptASM
|
||||
.type handleInterruptASM,@function
|
||||
handleInterruptASM:
|
||||
// Save and restore all registers, because the hardware only saves/restores
|
||||
// the pc.
|
||||
// Note: we have to do this in assembly because the "interrupt"="machine"
|
||||
// attribute is broken in LLVM: https://bugs.llvm.org/show_bug.cgi?id=42984
|
||||
addi sp, sp, -64
|
||||
sw ra, 60(sp)
|
||||
sw t0, 56(sp)
|
||||
sw t1, 52(sp)
|
||||
sw t2, 48(sp)
|
||||
sw a0, 44(sp)
|
||||
sw a1, 40(sp)
|
||||
sw a2, 36(sp)
|
||||
sw a3, 32(sp)
|
||||
sw a4, 28(sp)
|
||||
sw a5, 24(sp)
|
||||
sw a6, 20(sp)
|
||||
sw a7, 16(sp)
|
||||
sw t3, 12(sp)
|
||||
sw t4, 8(sp)
|
||||
sw t5, 4(sp)
|
||||
sw t6, 0(sp)
|
||||
call handleInterrupt
|
||||
lw t6, 0(sp)
|
||||
lw t5, 4(sp)
|
||||
lw t4, 8(sp)
|
||||
lw t3, 12(sp)
|
||||
lw a7, 16(sp)
|
||||
lw a6, 20(sp)
|
||||
lw a5, 24(sp)
|
||||
lw a4, 28(sp)
|
||||
lw a3, 32(sp)
|
||||
lw a2, 36(sp)
|
||||
lw a1, 40(sp)
|
||||
lw a0, 44(sp)
|
||||
lw t2, 48(sp)
|
||||
lw t1, 52(sp)
|
||||
lw t0, 56(sp)
|
||||
lw ra, 60(sp)
|
||||
addi sp, sp, 64
|
||||
mret
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Hand created file. DO NOT DELETE.
|
||||
// STM32F103XX bitfield definitions that are not auto-generated by gen-device-svd.py
|
||||
// STM32F103XX bitfield definitions that are not auto-generated by gen-device-svd.go
|
||||
|
||||
// +build stm32,stm32f103xx
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ func main() {
|
||||
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
// timer fires 10 times per second
|
||||
arm.SetupSystemTimer(machine.CPU_FREQUENCY / 10)
|
||||
arm.SetupSystemTimer(machine.CPUFrequency() / 10)
|
||||
|
||||
for {
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
const CPU_FREQUENCY = 16000000
|
||||
// Return the current CPU frequency in hertz.
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
// LED on the Arduino
|
||||
const LED Pin = 13
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// +build arduino_nano
|
||||
|
||||
package machine
|
||||
|
||||
// Return the current CPU frequency in hertz.
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
// LED on the Arduino
|
||||
const LED Pin = 13
|
||||
|
||||
// ADC on the Arduino
|
||||
const (
|
||||
ADC0 Pin = 0
|
||||
ADC1 Pin = 1
|
||||
ADC2 Pin = 2
|
||||
ADC3 Pin = 3
|
||||
ADC4 Pin = 4 // Used by TWI for SDA
|
||||
ADC5 Pin = 5 // Used by TWI for SCL
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN Pin = 1
|
||||
UART_RX_PIN Pin = 0
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build sam,atsamd21,arduino_nano33
|
||||
// +build arduino_nano33
|
||||
|
||||
// This contains the pin mappings for the Arduino Nano33 IoT board.
|
||||
//
|
||||
@@ -6,8 +6,6 @@
|
||||
//
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0x07738135
|
||||
|
||||
@@ -54,69 +52,23 @@ const (
|
||||
USBCDC_DP_PIN Pin = PA25
|
||||
)
|
||||
|
||||
// UART1 on the Arduino Nano 33 connects to the onboard NINA-W102 WiFi chip.
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM5_USART,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
// UART1 pins
|
||||
const (
|
||||
UART_TX_PIN Pin = PA22
|
||||
UART_RX_PIN Pin = PA23
|
||||
)
|
||||
|
||||
//go:export SERCOM5_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
}
|
||||
|
||||
// UART2 on the Arduino Nano 33 connects to the normal TX/RX pins.
|
||||
var (
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM3_USART,
|
||||
SERCOM: 3,
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM3_IRQHandler
|
||||
func handleUART2() {
|
||||
// should reset IRQ
|
||||
UART2.Receive(byte((UART2.Bus.DATA.Get() & 0xFF)))
|
||||
UART2.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN Pin = A4 // SDA: SERCOM4/PAD[1]
|
||||
SCL_PIN Pin = A5 // SCL: SERCOM4/PAD[1]
|
||||
)
|
||||
|
||||
// I2C on the Arduino Nano 33.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM4_I2CM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = A2 // SCK: SERCOM0/PAD[3]
|
||||
SPI0_MOSI_PIN Pin = A3 // MOSI: SERCOM0/PAD[2]
|
||||
SPI0_MISO_PIN Pin = A6 // MISO: SERCOM0/PAD[1]
|
||||
)
|
||||
|
||||
// SPI on the Arduino Nano 33.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM0_SPI,
|
||||
SERCOM: 0,
|
||||
}
|
||||
SPI0_SCK_PIN Pin = D13 // SCK: SERCOM1/PAD[1]
|
||||
SPI0_MOSI_PIN Pin = D11 // MOSI: SERCOM1/PAD[0]
|
||||
SPI0_MISO_PIN Pin = D12 // MISO: SERCOM1/PAD[3]
|
||||
)
|
||||
|
||||
// NINA-W102 Pins
|
||||
@@ -132,23 +84,9 @@ const (
|
||||
NINA_RX Pin = PA23
|
||||
)
|
||||
|
||||
// SPI1 is connected to the NINA-W102 chip on the Arduino Nano 33.
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM2_SPI,
|
||||
SERCOM: 2,
|
||||
}
|
||||
NINA_SPI = SPI1
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN Pin = PA10
|
||||
I2S_SD_PIN Pin = PA08
|
||||
I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino Nano 33.
|
||||
)
|
||||
|
||||
// I2S on the Arduino Nano 33.
|
||||
var (
|
||||
I2S0 = I2S{Bus: sam.I2S}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// +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() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM3, UART1.handleInterrupt)
|
||||
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM5, UART2.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the Arduino Nano 33.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM4_I2CM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the Arduino Nano 33.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPI,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI1 is connected to the NINA-W102 chip on the Arduino Nano 33.
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM2_SPI,
|
||||
SERCOM: 2,
|
||||
}
|
||||
NINA_SPI = SPI1
|
||||
)
|
||||
|
||||
// I2S on the Arduino Nano 33.
|
||||
var (
|
||||
I2S0 = I2S{Bus: sam.I2S}
|
||||
)
|
||||
@@ -0,0 +1,76 @@
|
||||
// +build sam,atsamd21 arduino_nano33 circuitplay_express
|
||||
|
||||
package machine
|
||||
|
||||
// Return the current CPU frequency in hertz.
|
||||
func CPUFrequency() uint32 {
|
||||
return 48000000
|
||||
}
|
||||
|
||||
// Hardware pins
|
||||
const (
|
||||
PA00 Pin = 0
|
||||
PA01 Pin = 1
|
||||
PA02 Pin = 2
|
||||
PA03 Pin = 3
|
||||
PA04 Pin = 4
|
||||
PA05 Pin = 5
|
||||
PA06 Pin = 6
|
||||
PA07 Pin = 7
|
||||
PA08 Pin = 8
|
||||
PA09 Pin = 9
|
||||
PA10 Pin = 10
|
||||
PA11 Pin = 11
|
||||
PA12 Pin = 12
|
||||
PA13 Pin = 13
|
||||
PA14 Pin = 14
|
||||
PA15 Pin = 15
|
||||
PA16 Pin = 16
|
||||
PA17 Pin = 17
|
||||
PA18 Pin = 18
|
||||
PA19 Pin = 19
|
||||
PA20 Pin = 20
|
||||
PA21 Pin = 21
|
||||
PA22 Pin = 22
|
||||
PA23 Pin = 23
|
||||
PA24 Pin = 24
|
||||
PA25 Pin = 25
|
||||
PA26 Pin = 26
|
||||
PA27 Pin = 27
|
||||
PA28 Pin = 28
|
||||
PA29 Pin = 29
|
||||
PA30 Pin = 30
|
||||
PA31 Pin = 31
|
||||
PB00 Pin = 32
|
||||
PB01 Pin = 33
|
||||
PB02 Pin = 34
|
||||
PB03 Pin = 35
|
||||
PB04 Pin = 36
|
||||
PB05 Pin = 37
|
||||
PB06 Pin = 38
|
||||
PB07 Pin = 39
|
||||
PB08 Pin = 40
|
||||
PB09 Pin = 41
|
||||
PB10 Pin = 42
|
||||
PB11 Pin = 43
|
||||
PB12 Pin = 44
|
||||
PB13 Pin = 45
|
||||
PB14 Pin = 46
|
||||
PB15 Pin = 47
|
||||
PB16 Pin = 48
|
||||
PB17 Pin = 49
|
||||
PB18 Pin = 50
|
||||
PB19 Pin = 51
|
||||
PB20 Pin = 52
|
||||
PB21 Pin = 53
|
||||
PB22 Pin = 54
|
||||
PB23 Pin = 55
|
||||
PB24 Pin = 56
|
||||
PB25 Pin = 57
|
||||
PB26 Pin = 58
|
||||
PB27 Pin = 59
|
||||
PB28 Pin = 60
|
||||
PB29 Pin = 61
|
||||
PB30 Pin = 62
|
||||
PB31 Pin = 63
|
||||
)
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/stm32"
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// https://wiki.stm32duino.com/index.php?title=File:Bluepillpinout.gif
|
||||
const (
|
||||
@@ -61,14 +64,12 @@ var (
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART1,
|
||||
IRQVal: stm32.IRQ_USART1,
|
||||
}
|
||||
UART1 = &UART0
|
||||
)
|
||||
|
||||
//go:export USART1_IRQHandler
|
||||
func handleUART1() {
|
||||
UART1.Receive(byte((UART1.Bus.DR.Get() & 0xFF)))
|
||||
func init() {
|
||||
UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, UART0.handleInterrupt)
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// +build circuitplay_bluefruit
|
||||
|
||||
package machine
|
||||
|
||||
const HasLowFrequencyCrystal = true
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = P0_30
|
||||
D1 = P0_14
|
||||
D2 = P0_05
|
||||
D3 = P0_04
|
||||
D4 = P1_02
|
||||
D5 = P1_15
|
||||
D6 = P0_02
|
||||
D7 = P1_06
|
||||
D8 = P0_13
|
||||
D9 = P0_29
|
||||
D10 = P0_03
|
||||
D11 = P1_04
|
||||
D12 = P0_26
|
||||
D13 = P1_14
|
||||
)
|
||||
|
||||
// Analog Pins
|
||||
const (
|
||||
A1 = P0_02
|
||||
A2 = P0_29
|
||||
A3 = P0_03
|
||||
A4 = P0_04
|
||||
A5 = P0_05
|
||||
A6 = P0_30
|
||||
A7 = P0_14
|
||||
A8 = P0_28
|
||||
A9 = P0_31
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
NEOPIXELS = D8
|
||||
|
||||
BUTTONA = D4
|
||||
BUTTONB = D5
|
||||
SLIDER = D7 // built-in slide switch
|
||||
|
||||
BUTTON = BUTTONA
|
||||
BUTTON1 = BUTTONB
|
||||
|
||||
LIGHTSENSOR = A8
|
||||
TEMPSENSOR = A9
|
||||
)
|
||||
|
||||
// UART0 pins (logical UART1)
|
||||
const (
|
||||
UART_TX_PIN = P0_14 // PORTB
|
||||
UART_RX_PIN = P0_30 // PORTB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = P0_05 // I2C0 external
|
||||
SCL_PIN = P0_04 // I2C0 external
|
||||
|
||||
SDA1_PIN = P0_00 // I2C1 internal
|
||||
SCL1_PIN = P0_01 // I2C1 internal
|
||||
)
|
||||
|
||||
// SPI pins (internal flash)
|
||||
const (
|
||||
SPI0_SCK_PIN = P0_19 // SCK
|
||||
SPI0_MOSI_PIN = P0_21 // MOSI
|
||||
SPI0_MISO_PIN = P0_23 // MISO
|
||||
)
|
||||
@@ -1,9 +1,7 @@
|
||||
// +build sam,atsamd21,circuitplay_express
|
||||
// +build circuitplay_express
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
@@ -68,20 +66,6 @@ const (
|
||||
UART_RX_PIN = PB09 // PORTB
|
||||
)
|
||||
|
||||
// UART1 on the Circuit Playground Express.
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM4_USART,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PB02 // I2C0 external
|
||||
@@ -91,20 +75,6 @@ const (
|
||||
SCL1_PIN = PA01 // I2C1 internal
|
||||
)
|
||||
|
||||
// I2C on the Circuit Playground Express.
|
||||
var (
|
||||
// external device
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
// internal device
|
||||
I2C1 = I2C{
|
||||
Bus: sam.SERCOM1_I2CM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins (internal flash)
|
||||
const (
|
||||
SPI0_SCK_PIN = PA21 // SCK: SERCOM3/PAD[3]
|
||||
@@ -112,22 +82,9 @@ const (
|
||||
SPI0_MISO_PIN = PA16 // MISO: SERCOM3/PAD[0]
|
||||
)
|
||||
|
||||
// SPI on the Circuit Playground Express.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM3_SPI,
|
||||
SERCOM: 3,
|
||||
}
|
||||
)
|
||||
|
||||
// I2S pins
|
||||
const (
|
||||
I2S_SCK_PIN = PA10
|
||||
I2S_SD_PIN = PA08
|
||||
I2S_WS_PIN = NoPin // no WS, instead uses SCK to sync
|
||||
)
|
||||
|
||||
// I2S on the Circuit Playground Express.
|
||||
var (
|
||||
I2S0 = I2S{Bus: sam.I2S}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// +build sam,atsamd21,circuitplay_express
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// UART1 on the Circuit Playground Express.
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM4_USART,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM4, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C on the Circuit Playground Express.
|
||||
var (
|
||||
// external device
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
// internal device
|
||||
I2C1 = I2C{
|
||||
Bus: sam.SERCOM1_I2CM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI on the Circuit Playground Express.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM3_SPI,
|
||||
SERCOM: 3,
|
||||
}
|
||||
)
|
||||
|
||||
// I2S on the Circuit Playground Express.
|
||||
var (
|
||||
I2S0 = I2S{Bus: sam.I2S}
|
||||
)
|
||||
@@ -2,6 +2,18 @@
|
||||
|
||||
package machine
|
||||
|
||||
// Return the current CPU frequency in hertz.
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
const (
|
||||
LED Pin = 1
|
||||
P0 Pin = 0
|
||||
P1 Pin = 1
|
||||
P2 Pin = 2
|
||||
P3 Pin = 3
|
||||
P4 Pin = 4
|
||||
P5 Pin = 5
|
||||
|
||||
LED = P1
|
||||
)
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
@@ -60,9 +63,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -67,10 +67,10 @@ const (
|
||||
|
||||
// I2C on the Feather M4.
|
||||
var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM2_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOM}
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
@@ -82,14 +82,8 @@ const (
|
||||
|
||||
// SPI on the Feather M4.
|
||||
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,
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
@@ -62,9 +63,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// I2C pins
|
||||
|
||||
@@ -69,10 +69,10 @@ const (
|
||||
|
||||
// I2C on the ItsyBitsy M4.
|
||||
var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM2_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOM}
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
@@ -84,12 +84,8 @@ const (
|
||||
|
||||
// SPI on the ItsyBitsy M4.
|
||||
var (
|
||||
SPI0 = SPI{Bus: sam.SERCOM1_SPIM,
|
||||
SCK: SPI0_SCK_PIN,
|
||||
MOSI: SPI0_MOSI_PIN,
|
||||
MISO: SPI0_MISO_PIN,
|
||||
DOpad: spiTXPad2SCK3,
|
||||
DIpad: sercomRXPad3,
|
||||
MISOPinMode: PinSERCOM,
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -77,10 +77,10 @@ const (
|
||||
|
||||
// I2C on the Metro M4.
|
||||
var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM5_I2CM,
|
||||
SDA: SDA_PIN,
|
||||
SCL: SCL_PIN,
|
||||
PinMode: PinSERCOMAlt}
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
@@ -97,15 +97,8 @@ const (
|
||||
// SPI on the Metro M4.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM2_SPIM,
|
||||
SCK: SPI0_SCK_PIN,
|
||||
MOSI: SPI0_MOSI_PIN,
|
||||
MISO: SPI0_MISO_PIN,
|
||||
DOpad: spiTXPad0SCK1,
|
||||
DIpad: sercomRXPad2,
|
||||
MISOPinMode: PinSERCOM,
|
||||
MOSIPinMode: PinSERCOM,
|
||||
SCKPinMode: PinSERCOM,
|
||||
Bus: sam.SERCOM2_SPIM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
NINA_SPI = SPI0
|
||||
)
|
||||
@@ -119,14 +112,7 @@ const (
|
||||
// SPI1 on the Metro M4 on pins 11,12,13
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SCK: SPI1_SCK_PIN,
|
||||
MOSI: SPI1_MOSI_PIN,
|
||||
MISO: SPI1_MISO_PIN,
|
||||
DOpad: spiTXPad3SCK1,
|
||||
DIpad: sercomRXPad0,
|
||||
MISOPinMode: PinSERCOM,
|
||||
MOSIPinMode: PinSERCOM,
|
||||
SCKPinMode: PinSERCOM,
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/stm32"
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const (
|
||||
PA0 = portA + 0
|
||||
@@ -100,14 +103,12 @@ var (
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART2,
|
||||
IRQVal: stm32.IRQ_USART2,
|
||||
}
|
||||
UART2 = &UART0
|
||||
)
|
||||
|
||||
//go:export USART2_IRQHandler
|
||||
func handleUART2() {
|
||||
UART2.Receive(byte((UART2.Bus.DR.Get() & 0xFF)))
|
||||
func init() {
|
||||
UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
// +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,
|
||||
SERCOM: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// 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,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// TFT SPI pins
|
||||
const (
|
||||
SPI1_SCK_PIN = PB13 // SCK: SERCOM4/PAD[1]
|
||||
SPI1_MOSI_PIN = PB15 // MOSI: SERCOM4/PAD[3]
|
||||
SPI1_MISO_PIN = NoPin
|
||||
)
|
||||
|
||||
// TFT SPI on the PyBadge.
|
||||
var (
|
||||
SPI1 = SPI{
|
||||
Bus: sam.SERCOM4_SPIM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,145 @@
|
||||
// +build sam,atsamd51,pyportal
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = PB13 // NINA_RX
|
||||
D1 = PB12 // NINA_TX
|
||||
D2 = PB22 // built-in neopixel
|
||||
D3 = PA04 // PWM available
|
||||
D4 = PA05 // PWM available
|
||||
D5 = PB16 // NINA_ACK
|
||||
D6 = PB15 // NINA_GPIO0
|
||||
D7 = PB17 // NINA_RESETN
|
||||
D8 = PB14 // NINA_CS
|
||||
D9 = PB04 // TFT_RD
|
||||
D10 = PB05 // TFT_DC
|
||||
D11 = PB06 // TFT_CS
|
||||
D12 = PB07 // TFT_TE
|
||||
D13 = PB23 // built-in LED
|
||||
D24 = PA00 // TFT_RESET
|
||||
D25 = PB31 // TFT_BACKLIGHT
|
||||
D26 = PB09 // TFT_WR
|
||||
D27 = PB02 // SDA
|
||||
D28 = PB03 // SCL
|
||||
D29 = PA12 // MOSI
|
||||
D30 = PA13 // SCK
|
||||
D31 = PA14 // MISO
|
||||
D32 = PB30 // SD_CS
|
||||
D33 = PA01 // SD_CARD_DETECT
|
||||
D34 = PA16 // LCD_DATA0
|
||||
D35 = PA17 // LCD_DATA1
|
||||
D36 = PA18 // LCD_DATA2
|
||||
D37 = PA19 // LCD_DATA3
|
||||
D38 = PA20 // LCD_DATA4
|
||||
D39 = PA21 // LCD_DATA5
|
||||
D40 = PA22 // LCD_DATA6
|
||||
D41 = PA23 // LCD_DATA7
|
||||
D42 = PB10 // QSPI
|
||||
D43 = PB11 // QSPI
|
||||
D44 = PA08 // QSPI
|
||||
D45 = PA09 // QSPI
|
||||
D46 = PA10 // QSPI
|
||||
D47 = PA11 // QSPI
|
||||
D50 = PA02 // speaker amplifier shutdown
|
||||
D51 = PA15 // NINA_RTS
|
||||
|
||||
NINA_CS = D8
|
||||
NINA_ACK = D5
|
||||
NINA_GPIO0 = D6
|
||||
NINA_RESETN = D7
|
||||
|
||||
NINA_TX = D1
|
||||
NINA_RX = D0
|
||||
NINA_RTS = D51
|
||||
|
||||
LCD_DATA0 = D34
|
||||
|
||||
TFT_RD = D9
|
||||
TFT_DC = D10
|
||||
TFT_CS = D11
|
||||
TFT_TE = D12
|
||||
TFT_RESET = D24
|
||||
TFT_BACKLIGHT = D25
|
||||
TFT_WR = D26
|
||||
|
||||
NEOPIXEL = D2
|
||||
SPK_SD = D50
|
||||
)
|
||||
|
||||
// Analog pins
|
||||
const (
|
||||
A0 = PA02 // ADC0/AIN[0]
|
||||
A1 = D3 // ADC0/AIN[4]
|
||||
A2 = PA07 // ADC0/AIN[7]
|
||||
A3 = D4 // ADC0/AIN[5]
|
||||
A4 = PB00 // ADC0/AIN[12]
|
||||
A5 = PB01 // ADC0/AIN[13]
|
||||
A6 = PA06 // ADC0/AIN[6]
|
||||
A7 = PB08 // ADC1/AIN[0]
|
||||
|
||||
AUDIO_OUT = A0
|
||||
LIGHT = A2
|
||||
TOUCH_YD = A4
|
||||
TOUCH_XL = A5
|
||||
TOUCH_YU = A6
|
||||
TOUCH_XR = A7
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
)
|
||||
|
||||
// UART0 aka USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
USBCDC_DP_PIN = PA25
|
||||
)
|
||||
|
||||
// TODO: add configuration for UART on SERCOM4 that is connected to TX/RX of ESP32
|
||||
const (
|
||||
UART_TX_PIN = NoPin
|
||||
UART_RX_PIN = NoPin
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PB02 // SDA: SERCOM2/PAD[0]
|
||||
SCL_PIN = PB03 // SCL: SERCOM2/PAD[1]
|
||||
)
|
||||
|
||||
// I2C on the PyPortal.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = PA13 // SCK: SERCOM1/PAD[1]
|
||||
SPI0_MOSI_PIN = PA12 // MOSI: SERCOM1/PAD[3]
|
||||
SPI0_MISO_PIN = PA14 // MISO: SERCOM1/PAD[2]
|
||||
|
||||
NINA_MOSI = SPI0_MOSI_PIN
|
||||
NINA_MISO = SPI0_MISO_PIN
|
||||
NINA_SCK = SPI0_SCK_PIN
|
||||
)
|
||||
|
||||
// SPI on the PyPortal.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM2_SPIM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
NINA_SPI = SPI0
|
||||
)
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// used to reset into bootloader
|
||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||
@@ -51,9 +54,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
//go:export SERCOM1_IRQHandler
|
||||
func handleUART1() {
|
||||
defaultUART1Handler()
|
||||
func init() {
|
||||
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM0, UART1.handleInterrupt)
|
||||
}
|
||||
|
||||
// SPI pins
|
||||
|
||||
@@ -4,6 +4,7 @@ package machine
|
||||
|
||||
import (
|
||||
"device/avr"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
@@ -140,7 +141,7 @@ func (i2c I2C) Configure(config I2CConfig) {
|
||||
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
||||
// NOTE: TWBR should be 10 or higher for master mode.
|
||||
// It is 72 for a 16mhz board with 100kHz TWI
|
||||
avr.TWBR.Set(uint8(((CPU_FREQUENCY / config.Frequency) - 16) / 2))
|
||||
avr.TWBR.Set(uint8(((CPUFrequency() / config.Frequency) - 16) / 2))
|
||||
|
||||
// Enable twi module.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN)
|
||||
@@ -232,10 +233,22 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
config.BaudRate = 9600
|
||||
}
|
||||
|
||||
// Register the UART interrupt.
|
||||
interrupt.New(avr.IRQ_USART_RX, func(intr interrupt.Interrupt) {
|
||||
// Read register to clear it.
|
||||
data := avr.UDR0.Get()
|
||||
|
||||
// Ensure no error.
|
||||
if !avr.UCSR0A.HasBits(avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0) {
|
||||
// Put data from UDR register into buffer.
|
||||
UART0.Receive(byte(data))
|
||||
}
|
||||
})
|
||||
|
||||
// Set baud rate based on prescale formula from
|
||||
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
||||
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
||||
ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
||||
ps := ((CPUFrequency()+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
||||
avr.UBRR0H.Set(uint8(ps >> 8))
|
||||
avr.UBRR0L.Set(uint8(ps & 0xff))
|
||||
|
||||
@@ -254,15 +267,3 @@ func (uart UART) WriteByte(c byte) error {
|
||||
avr.UDR0.Set(c) // send char
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:interrupt USART_RX_vect
|
||||
func handleUSART_RX() {
|
||||
// Read register to clear it.
|
||||
data := avr.UDR0.Get()
|
||||
|
||||
// Ensure no error.
|
||||
if !avr.UCSR0A.HasBits(avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0) {
|
||||
// Put data from UDR register into buffer.
|
||||
UART0.Receive(byte(data))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,10 @@ import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 48000000
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
const (
|
||||
@@ -35,74 +34,6 @@ const (
|
||||
PinInputPulldown PinMode = 12
|
||||
)
|
||||
|
||||
// Hardware pins
|
||||
const (
|
||||
PA00 Pin = 0
|
||||
PA01 Pin = 1
|
||||
PA02 Pin = 2
|
||||
PA03 Pin = 3
|
||||
PA04 Pin = 4
|
||||
PA05 Pin = 5
|
||||
PA06 Pin = 6
|
||||
PA07 Pin = 7
|
||||
PA08 Pin = 8
|
||||
PA09 Pin = 9
|
||||
PA10 Pin = 10
|
||||
PA11 Pin = 11
|
||||
PA12 Pin = 12
|
||||
PA13 Pin = 13
|
||||
PA14 Pin = 14
|
||||
PA15 Pin = 15
|
||||
PA16 Pin = 16
|
||||
PA17 Pin = 17
|
||||
PA18 Pin = 18
|
||||
PA19 Pin = 19
|
||||
PA20 Pin = 20
|
||||
PA21 Pin = 21
|
||||
PA22 Pin = 22
|
||||
PA23 Pin = 23
|
||||
PA24 Pin = 24
|
||||
PA25 Pin = 25
|
||||
PA26 Pin = 26
|
||||
PA27 Pin = 27
|
||||
PA28 Pin = 28
|
||||
PA29 Pin = 29
|
||||
PA30 Pin = 30
|
||||
PA31 Pin = 31
|
||||
PB00 Pin = 32
|
||||
PB01 Pin = 33
|
||||
PB02 Pin = 34
|
||||
PB03 Pin = 35
|
||||
PB04 Pin = 36
|
||||
PB05 Pin = 37
|
||||
PB06 Pin = 38
|
||||
PB07 Pin = 39
|
||||
PB08 Pin = 40
|
||||
PB09 Pin = 41
|
||||
PB10 Pin = 42
|
||||
PB11 Pin = 43
|
||||
PB12 Pin = 44
|
||||
PB13 Pin = 45
|
||||
PB14 Pin = 46
|
||||
PB15 Pin = 47
|
||||
PB16 Pin = 48
|
||||
PB17 Pin = 49
|
||||
PB18 Pin = 50
|
||||
PB19 Pin = 51
|
||||
PB20 Pin = 52
|
||||
PB21 Pin = 53
|
||||
PB22 Pin = 54
|
||||
PB23 Pin = 55
|
||||
PB24 Pin = 56
|
||||
PB25 Pin = 57
|
||||
PB26 Pin = 58
|
||||
PB27 Pin = 59
|
||||
PB28 Pin = 60
|
||||
PB29 Pin = 61
|
||||
PB30 Pin = 62
|
||||
PB31 Pin = 63
|
||||
)
|
||||
|
||||
const (
|
||||
pinPadMapSERCOM0Pad0 byte = (0x10 << 1) | 0x00
|
||||
pinPadMapSERCOM1Pad0 byte = (0x20 << 1) | 0x00
|
||||
@@ -362,9 +293,10 @@ func waitADCSync() {
|
||||
|
||||
// UART on the SAMD21.
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
Bus *sam.SERCOM_USART_Type
|
||||
SERCOM uint8
|
||||
Buffer *RingBuffer
|
||||
Bus *sam.SERCOM_USART_Type
|
||||
SERCOM uint8
|
||||
Interrupt interrupt.Interrupt
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -471,10 +403,7 @@ func (uart UART) Configure(config UARTConfig) error {
|
||||
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INTENSET_RXC)
|
||||
|
||||
// Enable RX IRQ.
|
||||
// IRQ lines are in the same order as SERCOM instance numbers on SAMD21
|
||||
// chips, so the IRQ number can be trivially determined from the SERCOM
|
||||
// number.
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM0 + uint32(uart.SERCOM))
|
||||
uart.Interrupt.Enable()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -485,7 +414,7 @@ func (uart UART) SetBaudRate(br uint32) {
|
||||
// BAUD = fref / (sampleRateValue * fbaud)
|
||||
// (multiply by 8, to calculate fractional piece)
|
||||
// uint32_t baudTimes8 = (SystemCoreClock * 8) / (16 * baudrate);
|
||||
baud := (CPU_FREQUENCY * 8) / (sampleRate16X * br)
|
||||
baud := (CPUFrequency() * 8) / (sampleRate16X * br)
|
||||
|
||||
// sercom->USART.BAUD.FRAC.FP = (baudTimes8 % 8);
|
||||
// sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8);
|
||||
@@ -502,11 +431,12 @@ func (uart UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// defaultUART1Handler handles the UART1 IRQ.
|
||||
func defaultUART1Handler() {
|
||||
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||
// this UART instance.
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
// should reset IRQ
|
||||
UART1.Receive(byte((UART1.Bus.DATA.Get() & 0xFF)))
|
||||
UART1.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
|
||||
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
// I2C on the SAMD21.
|
||||
@@ -601,7 +531,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
||||
func (i2c I2C) SetBaudRate(br uint32) {
|
||||
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
|
||||
// SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
|
||||
baud := CPU_FREQUENCY/(2*br) - 5 - (((CPU_FREQUENCY / 1000000) * riseTimeNanoseconds) / (2 * 1000))
|
||||
baud := CPUFrequency()/(2*br) - 5 - (((CPUFrequency() / 1000000) * riseTimeNanoseconds) / (2 * 1000))
|
||||
i2c.Bus.BAUD.Set(baud)
|
||||
}
|
||||
|
||||
@@ -793,7 +723,7 @@ func (i2s I2S) Configure(config I2SConfig) {
|
||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_I2S_)
|
||||
|
||||
// setting clock rate for sample.
|
||||
division_factor := CPU_FREQUENCY / (config.AudioFrequency * uint32(config.DataFormat))
|
||||
division_factor := CPUFrequency() / (config.AudioFrequency * uint32(config.DataFormat))
|
||||
|
||||
// Switch Generic Clock Generator 3 to DFLL48M.
|
||||
sam.GCLK.GENDIV.Set((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENDIV_ID_Pos) |
|
||||
@@ -1113,7 +1043,7 @@ func (spi SPI) Configure(config SPIConfig) error {
|
||||
}
|
||||
|
||||
// Set synch speed for SPI
|
||||
baudRate := (CPU_FREQUENCY / (2 * config.Frequency)) - 1
|
||||
baudRate := (CPUFrequency() / (2 * config.Frequency)) - 1
|
||||
spi.Bus.BAUD.Set(uint8(baudRate))
|
||||
|
||||
// Enable SPI port.
|
||||
@@ -1438,7 +1368,8 @@ func (usbcdc USBCDC) Configure(config UARTConfig) {
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ
|
||||
arm.EnableIRQ(sam.IRQ_USB)
|
||||
intr := interrupt.New(sam.IRQ_USB, handleUSB)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
@@ -1484,8 +1415,7 @@ func handlePadCalibration() {
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
//go:export USB_IRQHandler
|
||||
func handleUSB() {
|
||||
func handleUSB(intr interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
||||
+312
-218
@@ -11,10 +11,13 @@ import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 120000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 120000000
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
@@ -110,6 +113,162 @@ const (
|
||||
PB31 Pin = 63
|
||||
)
|
||||
|
||||
const (
|
||||
pinPadMapSERCOM0Pad0 uint16 = 0x1000
|
||||
pinPadMapSERCOM1Pad0 uint16 = 0x2000
|
||||
pinPadMapSERCOM2Pad0 uint16 = 0x3000
|
||||
pinPadMapSERCOM3Pad0 uint16 = 0x4000
|
||||
pinPadMapSERCOM4Pad0 uint16 = 0x5000
|
||||
pinPadMapSERCOM5Pad0 uint16 = 0x6000
|
||||
pinPadMapSERCOM6Pad0 uint16 = 0x7000
|
||||
pinPadMapSERCOM7Pad0 uint16 = 0x8000
|
||||
pinPadMapSERCOM0Pad2 uint16 = 0x1200
|
||||
pinPadMapSERCOM1Pad2 uint16 = 0x2200
|
||||
pinPadMapSERCOM2Pad2 uint16 = 0x3200
|
||||
pinPadMapSERCOM3Pad2 uint16 = 0x4200
|
||||
pinPadMapSERCOM4Pad2 uint16 = 0x5200
|
||||
pinPadMapSERCOM5Pad2 uint16 = 0x6200
|
||||
pinPadMapSERCOM6Pad2 uint16 = 0x7200
|
||||
pinPadMapSERCOM7Pad2 uint16 = 0x8200
|
||||
|
||||
pinPadMapSERCOM0AltPad0 uint16 = 0x0010
|
||||
pinPadMapSERCOM1AltPad0 uint16 = 0x0020
|
||||
pinPadMapSERCOM2AltPad0 uint16 = 0x0030
|
||||
pinPadMapSERCOM3AltPad0 uint16 = 0x0040
|
||||
pinPadMapSERCOM4AltPad0 uint16 = 0x0050
|
||||
pinPadMapSERCOM5AltPad0 uint16 = 0x0060
|
||||
pinPadMapSERCOM6AltPad0 uint16 = 0x0070
|
||||
pinPadMapSERCOM7AltPad0 uint16 = 0x0080
|
||||
pinPadMapSERCOM0AltPad1 uint16 = 0x0011
|
||||
pinPadMapSERCOM1AltPad1 uint16 = 0x0021
|
||||
pinPadMapSERCOM2AltPad1 uint16 = 0x0031
|
||||
pinPadMapSERCOM3AltPad1 uint16 = 0x0041
|
||||
pinPadMapSERCOM4AltPad1 uint16 = 0x0051
|
||||
pinPadMapSERCOM5AltPad1 uint16 = 0x0061
|
||||
pinPadMapSERCOM6AltPad1 uint16 = 0x0071
|
||||
pinPadMapSERCOM7AltPad1 uint16 = 0x0081
|
||||
pinPadMapSERCOM0AltPad2 uint16 = 0x0012
|
||||
pinPadMapSERCOM1AltPad2 uint16 = 0x0022
|
||||
pinPadMapSERCOM2AltPad2 uint16 = 0x0032
|
||||
pinPadMapSERCOM3AltPad2 uint16 = 0x0042
|
||||
pinPadMapSERCOM4AltPad2 uint16 = 0x0052
|
||||
pinPadMapSERCOM5AltPad2 uint16 = 0x0062
|
||||
pinPadMapSERCOM6AltPad2 uint16 = 0x0072
|
||||
pinPadMapSERCOM7AltPad2 uint16 = 0x0082
|
||||
)
|
||||
|
||||
// pinPadMapping lists which pins have which SERCOMs attached to them.
|
||||
// The encoding is rather dense, with each uint16 encoding two pins and both
|
||||
// SERCOM and SERCOM-ALT.
|
||||
//
|
||||
// Observations:
|
||||
// * There are eight SERCOMs. Those SERCOM numbers can be encoded in 4 bits.
|
||||
// * Even pad numbers are usually on even pins, and odd pad numbers are usually
|
||||
// on odd pins. The exception is SERCOM-ALT, which sometimes swaps pad 0 and 1.
|
||||
// With that, there is still an invariant that the pad number for an odd pin is
|
||||
// the pad number for the corresponding even pin with the low bit toggled.
|
||||
// * Pin pads come in pairs. If PA00 has pad 0, then PA01 has pad 1.
|
||||
// With this information, we can encode SERCOM pin/pad numbers much more
|
||||
// efficiently. Due to pads coming in pairs, we can ignore half the pins: the
|
||||
// information for an odd pin can be calculated easily from the preceding even
|
||||
// pin.
|
||||
//
|
||||
// Each word below is split in two bytes. The 8 high bytes are for SERCOM and
|
||||
// the 8 low bits are for SERCOM-ALT. Of each byte, the 4 high bits encode the
|
||||
// SERCOM + 1 while the two low bits encodes the pad number (the pad number for
|
||||
// the odd pin can be trivially calculated by toggling the low bit of the pad
|
||||
// number). It encodes SERCOM + 1 instead of just the SERCOM number, to make it
|
||||
// easy to check whether a nibble is set at all.
|
||||
//
|
||||
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/60001507E.pdf
|
||||
var pinPadMapping = [32]uint16{
|
||||
// page 32
|
||||
PA00 / 2: 0 | pinPadMapSERCOM1AltPad0,
|
||||
|
||||
// page 33
|
||||
PB08 / 2: 0 | pinPadMapSERCOM4AltPad0,
|
||||
PA04 / 2: 0 | pinPadMapSERCOM0AltPad0,
|
||||
PA06 / 2: 0 | pinPadMapSERCOM0AltPad2,
|
||||
//PC04 / 2: pinPadMapSERCOM6Pad0 | 0,
|
||||
//PC06 / 2: pinPadMapSERCOM6Pad2 | 0,
|
||||
PA08 / 2: pinPadMapSERCOM0Pad0 | pinPadMapSERCOM2AltPad1,
|
||||
PA10 / 2: pinPadMapSERCOM0Pad2 | pinPadMapSERCOM2AltPad2,
|
||||
PB10 / 2: 0 | pinPadMapSERCOM4AltPad2,
|
||||
PB12 / 2: pinPadMapSERCOM4Pad0 | 0,
|
||||
PB14 / 2: pinPadMapSERCOM4Pad2 | 0,
|
||||
//PD08 / 2: pinPadMapSERCOM7Pad0 | pinPadMapSERCOM6AltPad1,
|
||||
//PD10 / 2: pinPadMapSERCOM7Pad2 | pinPadMapSERCOM6AltPad2,
|
||||
//PC10 / 2: pinPadMapSERCOM6Pad2 | pinPadMapSERCOM7AltPad2,
|
||||
|
||||
// page 34
|
||||
//PC12 / 2: pinPadMapSERCOM7Pad0 | pinPadMapSERCOM6AltPad1,
|
||||
//PC14 / 2: pinPadMapSERCOM7Pad2 | pinPadMapSERCOM6AltPad2,
|
||||
PA12 / 2: pinPadMapSERCOM2Pad0 | pinPadMapSERCOM4AltPad1,
|
||||
PA14 / 2: pinPadMapSERCOM2Pad2 | pinPadMapSERCOM4AltPad2,
|
||||
PA16 / 2: pinPadMapSERCOM1Pad0 | pinPadMapSERCOM3AltPad1,
|
||||
PA18 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM3AltPad2,
|
||||
//PC16 / 2: pinPadMapSERCOM6Pad0 | pinPadMapSERCOM0AltPad1,
|
||||
//PC18 / 2: pinPadMapSERCOM6Pad2 | pinPadMapSERCOM0AltPad2,
|
||||
//PC22 / 2: pinPadMapSERCOM1Pad0 | pinPadMapSERCOM3AltPad1,
|
||||
//PD20 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM3AltPad2,
|
||||
PB16 / 2: pinPadMapSERCOM5Pad0 | 0,
|
||||
PB18 / 2: pinPadMapSERCOM5Pad2 | pinPadMapSERCOM7AltPad2,
|
||||
|
||||
// page 35
|
||||
PB20 / 2: pinPadMapSERCOM3Pad0 | pinPadMapSERCOM7AltPad1,
|
||||
PA20 / 2: pinPadMapSERCOM5Pad2 | pinPadMapSERCOM3AltPad2,
|
||||
PA22 / 2: pinPadMapSERCOM3Pad0 | pinPadMapSERCOM5AltPad1,
|
||||
PA24 / 2: pinPadMapSERCOM3Pad2 | pinPadMapSERCOM5AltPad2,
|
||||
PB22 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM5AltPad2,
|
||||
PB24 / 2: pinPadMapSERCOM0Pad0 | pinPadMapSERCOM2AltPad1,
|
||||
PB26 / 2: pinPadMapSERCOM2Pad0 | pinPadMapSERCOM4AltPad1,
|
||||
PB28 / 2: pinPadMapSERCOM2Pad2 | pinPadMapSERCOM4AltPad2,
|
||||
//PC24 / 2: pinPadMapSERCOM0Pad2 | pinPadMapSERCOM2AltPad2,
|
||||
//PC26 / 2: pinPadMapSERCOM1Pad1 | 0, // note: PC26 doesn't support SERCOM, but PC27 does
|
||||
//PC28 / 2: pinPadMapSERCOM1Pad1 | 0, // note: PC29 doesn't exist in the datasheet?
|
||||
PA30 / 2: 0 | pinPadMapSERCOM1AltPad2,
|
||||
|
||||
// page 36
|
||||
PB30 / 2: 0 | pinPadMapSERCOM5AltPad1,
|
||||
PB00 / 2: 0 | pinPadMapSERCOM5AltPad2,
|
||||
PB02 / 2: 0 | pinPadMapSERCOM5AltPad0,
|
||||
}
|
||||
|
||||
// findPinPadMapping looks up the pad number and the pinmode for a given pin and
|
||||
// SERCOM number. The result can either be SERCOM, SERCOM-ALT, or "not found"
|
||||
// (indicated by returning ok=false). The pad number is returned to calculate
|
||||
// the DOPO/DIPO bitfields of the various serial peripherals.
|
||||
func findPinPadMapping(sercom uint8, pin Pin) (pinMode PinMode, pad uint32, ok bool) {
|
||||
bytes := pinPadMapping[pin/2]
|
||||
upper := byte(bytes >> 8)
|
||||
lower := byte(bytes & 0xff)
|
||||
|
||||
if upper != 0 {
|
||||
// SERCOM
|
||||
if (upper>>4)-1 == sercom {
|
||||
pinMode = PinSERCOM
|
||||
pad |= uint32(upper % 4)
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
if lower != 0 {
|
||||
// SERCOM-ALT
|
||||
if (lower>>4)-1 == sercom {
|
||||
pinMode = PinSERCOMAlt
|
||||
pad |= uint32(lower % 4)
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// If the pin is uneven, toggle the lowest bit of the pad number.
|
||||
if pin&1 != 0 {
|
||||
pad ^= 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Return the register and mask to enable a given GPIO pin. This can be used to
|
||||
// implement bit-banged drivers.
|
||||
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||
@@ -319,7 +478,7 @@ func InitADC() {
|
||||
|
||||
sam.ADC0.CTRLA.SetBits(sam.ADC_CTRLA_PRESCALER_DIV32 << sam.ADC_CTRLA_PRESCALER_Pos)
|
||||
// adcs[i]->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_10BIT_Val;
|
||||
sam.ADC0.CTRLB.SetBits(sam.ADC_CTRLB_RESSEL_10BIT << sam.ADC_CTRLB_RESSEL_Pos)
|
||||
sam.ADC0.CTRLB.SetBits(sam.ADC_CTRLB_RESSEL_12BIT << sam.ADC_CTRLB_RESSEL_Pos)
|
||||
|
||||
// wait for sync
|
||||
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_CTRLB) {
|
||||
@@ -349,7 +508,7 @@ func InitADC() {
|
||||
|
||||
// same for ADC1, as for ADC0
|
||||
sam.ADC1.CTRLA.SetBits(sam.ADC_CTRLA_PRESCALER_DIV32 << sam.ADC_CTRLA_PRESCALER_Pos)
|
||||
sam.ADC1.CTRLB.SetBits(sam.ADC_CTRLB_RESSEL_10BIT << sam.ADC_CTRLB_RESSEL_Pos)
|
||||
sam.ADC1.CTRLB.SetBits(sam.ADC_CTRLB_RESSEL_12BIT << sam.ADC_CTRLB_RESSEL_Pos)
|
||||
for sam.ADC1.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_CTRLB) {
|
||||
}
|
||||
sam.ADC1.SAMPCTRL.Set(5)
|
||||
@@ -387,6 +546,9 @@ func (a ADC) Get() uint16 {
|
||||
}
|
||||
|
||||
// Selection for the positive ADC input channel
|
||||
bus.INPUTCTRL.ClearBits(sam.ADC_INPUTCTRL_MUXPOS_Msk)
|
||||
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
bus.INPUTCTRL.SetBits((uint16(ch) & sam.ADC_INPUTCTRL_MUXPOS_Msk) << sam.ADC_INPUTCTRL_MUXPOS_Pos)
|
||||
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
@@ -402,7 +564,7 @@ func (a ADC) Get() uint16 {
|
||||
}
|
||||
|
||||
// Clear the Data Ready flag
|
||||
bus.INTFLAG.SetBits(sam.ADC_INTFLAG_RESRDY)
|
||||
bus.INTFLAG.ClearBits(sam.ADC_INTFLAG_RESRDY)
|
||||
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
@@ -444,16 +606,20 @@ func (a ADC) getADCChannel() uint8 {
|
||||
return 6
|
||||
case PA07:
|
||||
return 7
|
||||
case PB00:
|
||||
return 12
|
||||
case PB01:
|
||||
return 13
|
||||
case PB02:
|
||||
return 10
|
||||
return 14
|
||||
case PB03:
|
||||
return 11
|
||||
return 15
|
||||
case PA09:
|
||||
return 17
|
||||
case PA11:
|
||||
return 19
|
||||
default:
|
||||
return 0
|
||||
panic("Invalid ADC pin")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,7 +627,8 @@ func (a ADC) getADCChannel() uint8 {
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
Bus *sam.SERCOM_USART_INT_Type
|
||||
Mode PinMode
|
||||
SERCOM uint8
|
||||
IRQVal uint32 // RXC interrupt
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -469,89 +636,71 @@ var (
|
||||
UART0 = USBCDC{Buffer: NewRingBuffer()}
|
||||
|
||||
// The first hardware serial port on the SAMD51. Uses the SERCOM3 interface.
|
||||
UART1 = UART{Bus: sam.SERCOM3_USART_INT,
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Mode: PinSERCOMAlt,
|
||||
Bus: sam.SERCOM3_USART_INT,
|
||||
SERCOM: 3,
|
||||
IRQVal: sam.IRQ_SERCOM3_2, // RXC interrupt
|
||||
}
|
||||
|
||||
// The second hardware serial port on the SAMD51. Uses the SERCOM0 interface.
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: sam.SERCOM0_USART_INT,
|
||||
Mode: PinSERCOMAlt,
|
||||
SERCOM: 0,
|
||||
IRQVal: sam.IRQ_SERCOM0_2, // RXC interrupt
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
sampleRate16X = 16
|
||||
lsbFirst = 1
|
||||
sercomRXPad0 = 0
|
||||
sercomRXPad1 = 1
|
||||
sercomRXPad2 = 2
|
||||
sercomRXPad3 = 3
|
||||
sercomTXPad0 = 0 // Only for UART
|
||||
sercomTXPad2 = 1 // Only for UART
|
||||
sercomTXPad023 = 2 // Only for UART with TX on PAD0, RTS on PAD2 and CTS on PAD3
|
||||
|
||||
spiTXPad0SCK1 = 0
|
||||
spiTXPad2SCK3 = 1
|
||||
spiTXPad3SCK1 = 2
|
||||
spiTXPad0SCK3 = 3
|
||||
sampleRate16X = 16
|
||||
lsbFirst = 1
|
||||
)
|
||||
|
||||
// Configure the UART.
|
||||
func (uart UART) Configure(config UARTConfig) {
|
||||
func (uart UART) Configure(config UARTConfig) error {
|
||||
// Default baud rate to 115200.
|
||||
if config.BaudRate == 0 {
|
||||
config.BaudRate = 115200
|
||||
}
|
||||
|
||||
// determine pins
|
||||
if config.TX == 0 {
|
||||
if config.TX == 0 && config.RX == 0 {
|
||||
// use default pins
|
||||
config.TX = UART_TX_PIN
|
||||
config.RX = UART_RX_PIN
|
||||
}
|
||||
|
||||
// determine pads
|
||||
var txpad, rxpad int
|
||||
switch config.TX {
|
||||
case PA04:
|
||||
txpad = sercomTXPad0
|
||||
case PA10:
|
||||
txpad = sercomTXPad2
|
||||
case PA18:
|
||||
txpad = sercomTXPad2
|
||||
case PA16:
|
||||
txpad = sercomTXPad0
|
||||
// Determine transmit pinout.
|
||||
txPinMode, txPad, ok := findPinPadMapping(uart.SERCOM, config.TX)
|
||||
if !ok {
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
var txPinOut uint32
|
||||
// See CTRLA.RXPO bits of the SERCOM USART peripheral (page 945-946) for how
|
||||
// pads are mapped to pinout values.
|
||||
switch txPad {
|
||||
case 0:
|
||||
txPinOut = 0
|
||||
default:
|
||||
panic("Invalid TX pin for UART")
|
||||
// TODO: flow control (RTS/CTS)
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
switch config.RX {
|
||||
case PA06:
|
||||
rxpad = sercomRXPad2
|
||||
case PA07:
|
||||
rxpad = sercomRXPad3
|
||||
case PA11:
|
||||
rxpad = sercomRXPad3
|
||||
case PA18:
|
||||
rxpad = sercomRXPad2
|
||||
case PA16:
|
||||
rxpad = sercomRXPad0
|
||||
case PA19:
|
||||
rxpad = sercomRXPad3
|
||||
case PA17:
|
||||
rxpad = sercomRXPad1
|
||||
default:
|
||||
panic("Invalid RX pin for UART")
|
||||
// Determine receive pinout.
|
||||
rxPinMode, rxPad, ok := findPinPadMapping(uart.SERCOM, config.RX)
|
||||
if !ok {
|
||||
return ErrInvalidInputPin
|
||||
}
|
||||
// As you can see in the CTRLA.RXPO bits of the SERCOM USART peripheral
|
||||
// (page 945), input pins are mapped directly.
|
||||
rxPinOut := rxPad
|
||||
|
||||
// configure pins
|
||||
config.TX.Configure(PinConfig{Mode: uart.Mode})
|
||||
config.RX.Configure(PinConfig{Mode: uart.Mode})
|
||||
config.TX.Configure(PinConfig{Mode: txPinMode})
|
||||
config.RX.Configure(PinConfig{Mode: rxPinMode})
|
||||
|
||||
// reset SERCOM0
|
||||
// reset SERCOM
|
||||
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_INT_CTRLA_SWRST)
|
||||
for uart.Bus.CTRLA.HasBits(sam.SERCOM_USART_INT_CTRLA_SWRST) ||
|
||||
uart.Bus.SYNCBUSY.HasBits(sam.SERCOM_USART_INT_SYNCBUSY_SWRST) {
|
||||
@@ -584,8 +733,8 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
// set UART pads. This is not same as pins...
|
||||
// SERCOM_USART_CTRLA_TXPO(txPad) |
|
||||
// SERCOM_USART_CTRLA_RXPO(rxPad);
|
||||
uart.Bus.CTRLA.SetBits(uint32((txpad << sam.SERCOM_USART_INT_CTRLA_TXPO_Pos) |
|
||||
(rxpad << sam.SERCOM_USART_INT_CTRLA_RXPO_Pos)))
|
||||
uart.Bus.CTRLA.SetBits((txPinOut << sam.SERCOM_USART_INT_CTRLA_TXPO_Pos) |
|
||||
(rxPinOut << sam.SERCOM_USART_INT_CTRLA_RXPO_Pos))
|
||||
|
||||
// Enable Transceiver and Receiver
|
||||
//sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ;
|
||||
@@ -601,19 +750,14 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INT_INTENSET_RXC)
|
||||
|
||||
// Enable RX IRQ.
|
||||
switch uart.Bus {
|
||||
case sam.SERCOM0_USART_INT:
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM0_0)
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM0_1)
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM0_2)
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM0_OTHER)
|
||||
default:
|
||||
// Currently assumes SERCOM3
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM3_0)
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM3_1)
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM3_2)
|
||||
arm.EnableIRQ(sam.IRQ_SERCOM3_OTHER)
|
||||
}
|
||||
// This is a small note at the bottom of the NVIC section of the datasheet:
|
||||
// > The integer number specified in the source refers to the respective bit
|
||||
// > position in the INTFLAG register of respective peripheral.
|
||||
// Therefore, if we only need to listen to the RXC interrupt source (in bit
|
||||
// position 2), we only need interrupt source 2 for this SERCOM device.
|
||||
arm.EnableIRQ(uart.IRQVal)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the UART.
|
||||
@@ -639,53 +783,15 @@ func (uart UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:export SERCOM3_0_IRQHandler
|
||||
func handleSERCOM3_0() {
|
||||
handleUART1()
|
||||
}
|
||||
|
||||
//go:export SERCOM3_1_IRQHandler
|
||||
func handleSERCOM3_1() {
|
||||
handleUART1()
|
||||
}
|
||||
|
||||
//go:export SERCOM3_2_IRQHandler
|
||||
func handleSERCOM3_2() {
|
||||
handleUART1()
|
||||
}
|
||||
|
||||
//go:export SERCOM3_OTHER_IRQHandler
|
||||
func handleSERCOM3_OTHER() {
|
||||
handleUART1()
|
||||
}
|
||||
|
||||
func handleUART1() {
|
||||
// should reset IRQ
|
||||
UART1.Receive(byte((UART1.Bus.DATA.Get() & 0xFF)))
|
||||
UART1.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
|
||||
}
|
||||
|
||||
//go:export SERCOM0_0_IRQHandler
|
||||
func handleSERCOM0_0() {
|
||||
handleUART2()
|
||||
}
|
||||
|
||||
//go:export SERCOM0_1_IRQHandler
|
||||
func handleSERCOM0_1() {
|
||||
handleUART2()
|
||||
}
|
||||
|
||||
//go:export SERCOM0_2_IRQHandler
|
||||
func handleSERCOM0_2() {
|
||||
handleUART2()
|
||||
}
|
||||
|
||||
//go:export SERCOM0_OTHER_IRQHandler
|
||||
func handleSERCOM0_OTHER() {
|
||||
handleUART2()
|
||||
}
|
||||
|
||||
func handleUART2() {
|
||||
// should reset IRQ
|
||||
UART2.Receive(byte((UART2.Bus.DATA.Get() & 0xFF)))
|
||||
UART2.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
|
||||
@@ -693,10 +799,8 @@ func handleUART2() {
|
||||
|
||||
// I2C on the SAMD51.
|
||||
type I2C struct {
|
||||
Bus *sam.SERCOM_I2CM_Type
|
||||
SCL Pin
|
||||
SDA Pin
|
||||
PinMode PinMode
|
||||
Bus *sam.SERCOM_I2CM_Type
|
||||
SERCOM uint8
|
||||
}
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
@@ -729,12 +833,33 @@ const (
|
||||
const i2cTimeout = 1000
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) {
|
||||
func (i2c I2C) Configure(config I2CConfig) error {
|
||||
// Default I2C bus speed is 100 kHz.
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = TWI_FREQ_100KHZ
|
||||
}
|
||||
|
||||
// Use default I2C pins if not set.
|
||||
if config.SDA == 0 && config.SCL == 0 {
|
||||
config.SDA = SDA_PIN
|
||||
config.SCL = SCL_PIN
|
||||
}
|
||||
|
||||
sclPinMode, sclPad, ok := findPinPadMapping(i2c.SERCOM, config.SCL)
|
||||
if !ok || sclPad != 1 {
|
||||
// SCL must be on pad 1, according to section 36.4 of the datasheet.
|
||||
// Note: this is not an exhaustive test for I2C support on the pin: not
|
||||
// all pins support I2C.
|
||||
return ErrInvalidClockPin
|
||||
}
|
||||
sdaPinMode, sdaPad, ok := findPinPadMapping(i2c.SERCOM, config.SDA)
|
||||
if !ok || sdaPad != 0 {
|
||||
// SDA must be on pad 0, according to section 36.4 of the datasheet.
|
||||
// Note: this is not an exhaustive test for I2C support on the pin: not
|
||||
// all pins support I2C.
|
||||
return ErrInvalidDataPin
|
||||
}
|
||||
|
||||
// reset SERCOM
|
||||
i2c.Bus.CTRLA.SetBits(sam.SERCOM_I2CM_CTRLA_SWRST)
|
||||
for i2c.Bus.CTRLA.HasBits(sam.SERCOM_I2CM_CTRLA_SWRST) ||
|
||||
@@ -760,8 +885,10 @@ func (i2c I2C) Configure(config I2CConfig) {
|
||||
}
|
||||
|
||||
// enable pins
|
||||
i2c.SDA.Configure(PinConfig{Mode: i2c.PinMode})
|
||||
i2c.SCL.Configure(PinConfig{Mode: i2c.PinMode})
|
||||
config.SDA.Configure(PinConfig{Mode: sdaPinMode})
|
||||
config.SCL.Configure(PinConfig{Mode: sclPinMode})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the I2C.
|
||||
@@ -860,7 +987,7 @@ func (i2c I2C) WriteByte(data byte) error {
|
||||
timeout := i2cTimeout
|
||||
for !i2c.Bus.INTFLAG.HasBits(sam.SERCOM_I2CM_INTFLAG_MB) {
|
||||
// check for bus error
|
||||
if sam.SERCOM3_I2CM.STATUS.HasBits(sam.SERCOM_I2CM_STATUS_BUSERR) {
|
||||
if i2c.Bus.STATUS.HasBits(sam.SERCOM_I2CM_STATUS_BUSERR) {
|
||||
return errors.New("I2C bus error")
|
||||
}
|
||||
timeout--
|
||||
@@ -929,15 +1056,8 @@ func (i2c I2C) readByte() byte {
|
||||
|
||||
// SPI
|
||||
type SPI struct {
|
||||
Bus *sam.SERCOM_SPIM_Type
|
||||
SCK Pin
|
||||
MOSI Pin
|
||||
MISO Pin
|
||||
DOpad int
|
||||
DIpad int
|
||||
SCKPinMode PinMode
|
||||
MOSIPinMode PinMode
|
||||
MISOPinMode PinMode
|
||||
Bus *sam.SERCOM_SPIM_Type
|
||||
SERCOM uint8
|
||||
}
|
||||
|
||||
// SPIConfig is used to store config info for SPI.
|
||||
@@ -951,34 +1071,61 @@ type SPIConfig struct {
|
||||
}
|
||||
|
||||
// Configure is intended to setup the SPI interface.
|
||||
func (spi SPI) Configure(config SPIConfig) {
|
||||
doPad := spi.DOpad
|
||||
diPad := spi.DIpad
|
||||
func (spi SPI) Configure(config SPIConfig) error {
|
||||
// Use default pins if not set.
|
||||
if config.SCK == 0 && config.MOSI == 0 && config.MISO == 0 {
|
||||
config.SCK = SPI0_SCK_PIN
|
||||
config.MOSI = SPI0_MOSI_PIN
|
||||
config.MISO = SPI0_MISO_PIN
|
||||
}
|
||||
|
||||
// set default frequency
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = 4000000
|
||||
}
|
||||
|
||||
// Determine the input pinout (for MISO).
|
||||
var dataInPinout uint32
|
||||
misoPinMode, misoPad, ok := findPinPadMapping(spi.SERCOM, config.MISO)
|
||||
if config.MISO != NoPin {
|
||||
if !ok {
|
||||
return ErrInvalidInputPin
|
||||
}
|
||||
dataInPinout = misoPad // mapped directly
|
||||
}
|
||||
|
||||
// Determine the output pinout (for MOSI/SCK).
|
||||
// See DOPO field in the CTRLA register on page 986 of the datasheet.
|
||||
var dataOutPinout uint32
|
||||
sckPinMode, sckPad, ok := findPinPadMapping(spi.SERCOM, config.SCK)
|
||||
if !ok || sckPad != 1 {
|
||||
// SCK pad must always be 1
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
mosiPinMode, mosiPad, ok := findPinPadMapping(spi.SERCOM, config.MOSI)
|
||||
if !ok {
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
switch mosiPad {
|
||||
case 0:
|
||||
dataOutPinout = 0x0
|
||||
case 3:
|
||||
dataOutPinout = 0x2
|
||||
default:
|
||||
return ErrInvalidOutputPin
|
||||
}
|
||||
|
||||
// Disable SPI port.
|
||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPIM_CTRLA_ENABLE)
|
||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
// enable pins
|
||||
if spi.SCKPinMode == 0 {
|
||||
spi.SCKPinMode = PinSERCOMAlt
|
||||
config.SCK.Configure(PinConfig{Mode: sckPinMode})
|
||||
config.MOSI.Configure(PinConfig{Mode: mosiPinMode})
|
||||
if config.MISO != NoPin {
|
||||
config.MISO.Configure(PinConfig{Mode: misoPinMode})
|
||||
}
|
||||
if spi.MOSIPinMode == 0 {
|
||||
spi.MOSIPinMode = PinSERCOMAlt
|
||||
}
|
||||
if spi.MISOPinMode == 0 {
|
||||
spi.MISOPinMode = PinSERCOMAlt
|
||||
}
|
||||
|
||||
spi.SCK.Configure(PinConfig{Mode: spi.SCKPinMode})
|
||||
spi.MOSI.Configure(PinConfig{Mode: spi.MOSIPinMode})
|
||||
spi.MISO.Configure(PinConfig{Mode: spi.MISOPinMode})
|
||||
|
||||
// reset SERCOM
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_SWRST)
|
||||
@@ -987,17 +1134,17 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
}
|
||||
|
||||
// set bit transfer order
|
||||
dataOrder := 0
|
||||
dataOrder := uint32(0)
|
||||
if config.LSBFirst {
|
||||
dataOrder = 1
|
||||
}
|
||||
|
||||
// Set SPI master
|
||||
// SERCOM_SPIM_CTRLA_MODE_SPI_MASTER = 3
|
||||
spi.Bus.CTRLA.Set(uint32((3 << sam.SERCOM_SPIM_CTRLA_MODE_Pos) |
|
||||
(doPad << sam.SERCOM_SPIM_CTRLA_DOPO_Pos) |
|
||||
(diPad << sam.SERCOM_SPIM_CTRLA_DIPO_Pos) |
|
||||
(dataOrder << sam.SERCOM_SPIM_CTRLA_DORD_Pos)))
|
||||
spi.Bus.CTRLA.Set((3 << sam.SERCOM_SPIM_CTRLA_MODE_Pos) |
|
||||
(dataOutPinout << sam.SERCOM_SPIM_CTRLA_DOPO_Pos) |
|
||||
(dataInPinout << sam.SERCOM_SPIM_CTRLA_DIPO_Pos) |
|
||||
(dataOrder << sam.SERCOM_SPIM_CTRLA_DORD_Pos))
|
||||
|
||||
spi.Bus.CTRLB.SetBits((0 << sam.SERCOM_SPIM_CTRLB_CHSIZE_Pos) | // 8bit char size
|
||||
sam.SERCOM_SPIM_CTRLB_RXEN) // receive enable
|
||||
@@ -1031,6 +1178,8 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_ENABLE)
|
||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
@@ -1049,19 +1198,6 @@ func (spi SPI) Transfer(w byte) (byte, error) {
|
||||
// PWM
|
||||
const period = 0xFFFF
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// turn on timer clocks used for PWM
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||
|
||||
//use clock generator 0
|
||||
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
// Configure configures a PWM pin for output.
|
||||
func (pwm PWM) Configure() {
|
||||
// Set pin as output
|
||||
@@ -1176,34 +1312,6 @@ func (pwm PWM) setPinCfg(val uint8) {
|
||||
pwm.Pin.setPinCfg(val)
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case PA16:
|
||||
return sam.TCC1
|
||||
case PA17:
|
||||
return sam.TCC1
|
||||
case PA14:
|
||||
return sam.TCC2
|
||||
case PA15:
|
||||
return sam.TCC2
|
||||
case PA18:
|
||||
return sam.TCC1
|
||||
case PA19:
|
||||
return sam.TCC1
|
||||
case PA20:
|
||||
return sam.TCC0
|
||||
case PA21:
|
||||
return sam.TCC0
|
||||
case PA23:
|
||||
return sam.TCC0
|
||||
case PA22:
|
||||
return sam.TCC0
|
||||
default:
|
||||
return nil // not supported on this pin
|
||||
}
|
||||
}
|
||||
|
||||
// setChannel sets the value for the correct channel for PWM on this pin
|
||||
func (pwm PWM) setChannel(val uint32) {
|
||||
switch pwm.Pin {
|
||||
@@ -1227,6 +1335,8 @@ func (pwm PWM) setChannel(val uint32) {
|
||||
pwm.getTimer().CC[3].Set(val)
|
||||
case PA22:
|
||||
pwm.getTimer().CC[2].Set(val)
|
||||
case PB31:
|
||||
pwm.getTimer().CC[1].Set(val)
|
||||
default:
|
||||
return // not supported on this pin
|
||||
}
|
||||
@@ -1255,6 +1365,8 @@ func (pwm PWM) setChannelBuffer(val uint32) {
|
||||
pwm.getTimer().CCBUF[3].Set(val)
|
||||
case PA22:
|
||||
pwm.getTimer().CCBUF[2].Set(val)
|
||||
case PB31:
|
||||
pwm.getTimer().CCBUF[1].Set(val)
|
||||
default:
|
||||
return // not supported on this pin
|
||||
}
|
||||
@@ -1283,6 +1395,8 @@ func (pwm PWM) getMux() PinMode {
|
||||
return PinPWMG
|
||||
case PA22:
|
||||
return PinPWMG
|
||||
case PB31:
|
||||
return PinPWMF
|
||||
default:
|
||||
return 0 // not supported on this pin
|
||||
}
|
||||
@@ -1400,11 +1514,11 @@ func (usbcdc USBCDC) Configure(config UARTConfig) {
|
||||
// enable USB
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ
|
||||
arm.EnableIRQ(sam.IRQ_USB_OTHER)
|
||||
arm.EnableIRQ(sam.IRQ_USB_SOF_HSOF)
|
||||
arm.EnableIRQ(sam.IRQ_USB_TRCPT0)
|
||||
arm.EnableIRQ(sam.IRQ_USB_TRCPT1)
|
||||
// enable IRQ at highest priority
|
||||
interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable()
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
@@ -1450,27 +1564,7 @@ func handlePadCalibration() {
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
//go:export USB_OTHER_IRQHandler
|
||||
func handleUSBOther() {
|
||||
handleUSBIRQ()
|
||||
}
|
||||
|
||||
//go:export USB_SOF_HSOF_IRQHandler
|
||||
func handleUSBSOFHSOF() {
|
||||
handleUSBIRQ()
|
||||
}
|
||||
|
||||
//go:export USB_TRCPT0_IRQHandler
|
||||
func handleUSBTRCPT0() {
|
||||
handleUSBIRQ()
|
||||
}
|
||||
|
||||
//go:export USB_TRCPT1_IRQHandler
|
||||
func handleUSBTRCPT1() {
|
||||
handleUSBIRQ()
|
||||
}
|
||||
|
||||
func handleUSBIRQ() {
|
||||
func handleUSBIRQ(interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
@@ -2044,14 +2138,14 @@ func setEPINTENSET(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val)
|
||||
}
|
||||
|
||||
// ResetProcessor should perform a system reset in preperation
|
||||
// ResetProcessor should perform a system reset in preparation
|
||||
// to switch to the bootloader to flash new firmware.
|
||||
func ResetProcessor() {
|
||||
arm.DisableInterrupts()
|
||||
|
||||
// Perform magic reset into bootloader, as mentioned in
|
||||
// https://github.com/arduino/ArduinoCore-samd/issues/197
|
||||
*(*uint32)(unsafe.Pointer(uintptr(0x20000000 + 0x00030000 - 4))) = RESET_MAGIC_VALUE
|
||||
*(*uint32)(unsafe.Pointer(uintptr(0x20000000 + HSRAM_SIZE - 4))) = RESET_MAGIC_VALUE
|
||||
|
||||
arm.SystemReset()
|
||||
}
|
||||
|
||||
@@ -6,3 +6,48 @@
|
||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
|
||||
//
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00030000
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// turn on timer clocks used for PWM
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||
|
||||
//use clock generator 0
|
||||
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case PA16:
|
||||
return sam.TCC1
|
||||
case PA17:
|
||||
return sam.TCC1
|
||||
case PA14:
|
||||
return sam.TCC2
|
||||
case PA15:
|
||||
return sam.TCC2
|
||||
case PA18:
|
||||
return sam.TCC1
|
||||
case PA19:
|
||||
return sam.TCC1
|
||||
case PA20:
|
||||
return sam.TCC0
|
||||
case PA21:
|
||||
return sam.TCC0
|
||||
case PA23:
|
||||
return sam.TCC0
|
||||
case PA22:
|
||||
return sam.TCC0
|
||||
default:
|
||||
return nil // not supported on this pin
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,3 +6,53 @@
|
||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
|
||||
//
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00030000
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// turn on timer clocks used for PWM
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||
|
||||
//use clock generator 0
|
||||
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL[38].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case PA16:
|
||||
return sam.TCC1
|
||||
case PA17:
|
||||
return sam.TCC1
|
||||
case PA14:
|
||||
return sam.TCC2
|
||||
case PA15:
|
||||
return sam.TCC2
|
||||
case PA18:
|
||||
return sam.TCC1
|
||||
case PA19:
|
||||
return sam.TCC1
|
||||
case PA20:
|
||||
return sam.TCC0
|
||||
case PA21:
|
||||
return sam.TCC0
|
||||
case PA23:
|
||||
return sam.TCC0
|
||||
case PA22:
|
||||
return sam.TCC0
|
||||
case PB31:
|
||||
return sam.TCC4
|
||||
default:
|
||||
return nil // not supported on this pin
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// +build sam,atsamd51,atsamd51j20
|
||||
|
||||
// Peripheral abstraction layer for the atsamd51.
|
||||
//
|
||||
// Datasheet:
|
||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
|
||||
//
|
||||
package machine
|
||||
|
||||
import "device/sam"
|
||||
|
||||
const HSRAM_SIZE = 0x00040000
|
||||
|
||||
// InitPWM initializes the PWM interface.
|
||||
func InitPWM() {
|
||||
// turn on timer clocks used for PWM
|
||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||
|
||||
//use clock generator 0
|
||||
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL[38].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
||||
|
||||
// getTimer returns the timer to be used for PWM on this pin
|
||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||
switch pwm.Pin {
|
||||
case PA16:
|
||||
return sam.TCC1
|
||||
case PA17:
|
||||
return sam.TCC1
|
||||
case PA14:
|
||||
return sam.TCC2
|
||||
case PA15:
|
||||
return sam.TCC2
|
||||
case PA18:
|
||||
return sam.TCC1
|
||||
case PA19:
|
||||
return sam.TCC1
|
||||
case PA20:
|
||||
return sam.TCC0
|
||||
case PA21:
|
||||
return sam.TCC0
|
||||
case PA23:
|
||||
return sam.TCC0
|
||||
case PA22:
|
||||
return sam.TCC0
|
||||
case PB31:
|
||||
return sam.TCC4
|
||||
default:
|
||||
return nil // not supported on this pin
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,12 @@ package machine
|
||||
|
||||
import (
|
||||
"device/sifive"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 16000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
type PinMode uint8
|
||||
|
||||
@@ -63,6 +66,23 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
// 115200 baud rate is 138.
|
||||
sifive.UART0.DIV.Set(138)
|
||||
sifive.UART0.TXCTRL.Set(sifive.UART_TXCTRL_ENABLE)
|
||||
sifive.UART0.RXCTRL.Set(sifive.UART_RXCTRL_ENABLE)
|
||||
sifive.UART0.IE.Set(sifive.UART_IE_RXWM) // enable the receive interrupt (only)
|
||||
intr := interrupt.New(sifive.IRQ_UART0, UART0.handleInterrupt)
|
||||
intr.SetPriority(5)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
rxdata := uart.Bus.RXDATA.Get()
|
||||
c := byte(rxdata)
|
||||
if uint32(c) != rxdata {
|
||||
// The rxdata has other bits set than just the low 8 bits. This probably
|
||||
// means that the 'empty' flag is set, which indicates there is no data
|
||||
// to be read and the byte is garbage. Ignore this byte.
|
||||
return
|
||||
}
|
||||
uart.Receive(c)
|
||||
}
|
||||
|
||||
func (uart UART) WriteByte(c byte) {
|
||||
@@ -108,7 +128,7 @@ func (spi SPI) Configure(config SPIConfig) error {
|
||||
}
|
||||
|
||||
// div = (SPI_CFG(dev)->f_sys / (2 * frequency)) - 1;
|
||||
div := CPU_FREQUENCY/(2*config.Frequency) - 1
|
||||
div := CPUFrequency()/(2*config.Frequency) - 1
|
||||
spi.Bus.DIV.Set(div)
|
||||
|
||||
// set mode
|
||||
|
||||
@@ -8,6 +8,25 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Interrupt numbers as used on the GameBoy Advance. Register them with
|
||||
// runtime/interrupt.New.
|
||||
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
|
||||
)
|
||||
|
||||
// Make it easier to directly write to I/O RAM.
|
||||
var ioram = (*[0x400]volatile.Register8)(unsafe.Pointer(uintptr(0x04000000)))
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/nrf"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -88,8 +88,9 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
nrf.UART0.INTENSET.Set(nrf.UART_INTENSET_RXDRDY_Msk)
|
||||
|
||||
// Enable RX IRQ.
|
||||
arm.SetPriority(nrf.IRQ_UART0, 0xc0) // low priority
|
||||
arm.EnableIRQ(nrf.IRQ_UART0)
|
||||
intr := interrupt.New(nrf.IRQ_UART0, UART0.handleInterrupt)
|
||||
intr.SetPriority(0xc0) // low priority
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the UART.
|
||||
@@ -116,7 +117,7 @@ func (uart UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart UART) handleInterrupt() {
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
if nrf.UART0.EVENTS_RXDRDY.Get() != 0 {
|
||||
uart.Receive(byte(nrf.UART0.RXD.Get()))
|
||||
nrf.UART0.EVENTS_RXDRDY.Set(0x0)
|
||||
|
||||
@@ -6,7 +6,9 @@ import (
|
||||
"device/nrf"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 16000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 16000000
|
||||
}
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||
@@ -18,11 +20,6 @@ func (uart UART) setPins(tx, rx Pin) {
|
||||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||
}
|
||||
|
||||
//go:export UART0_IRQHandler
|
||||
func handleUART0() {
|
||||
UART0.handleInterrupt()
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 64000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 64000000
|
||||
}
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||
@@ -19,11 +21,6 @@ func (uart UART) setPins(tx, rx Pin) {
|
||||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||
}
|
||||
|
||||
//go:export UARTE0_UART0_IRQHandler
|
||||
func handleUART0() {
|
||||
UART0.handleInterrupt()
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||
|
||||
@@ -7,7 +7,61 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 64000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 64000000
|
||||
}
|
||||
|
||||
// Hardware pins
|
||||
const (
|
||||
P0_00 Pin = 0
|
||||
P0_01 Pin = 1
|
||||
P0_02 Pin = 2
|
||||
P0_03 Pin = 3
|
||||
P0_04 Pin = 4
|
||||
P0_05 Pin = 5
|
||||
P0_06 Pin = 6
|
||||
P0_07 Pin = 7
|
||||
P0_08 Pin = 8
|
||||
P0_09 Pin = 9
|
||||
P0_10 Pin = 10
|
||||
P0_11 Pin = 11
|
||||
P0_12 Pin = 12
|
||||
P0_13 Pin = 13
|
||||
P0_14 Pin = 14
|
||||
P0_15 Pin = 15
|
||||
P0_16 Pin = 16
|
||||
P0_17 Pin = 17
|
||||
P0_18 Pin = 18
|
||||
P0_19 Pin = 19
|
||||
P0_20 Pin = 20
|
||||
P0_21 Pin = 21
|
||||
P0_22 Pin = 22
|
||||
P0_23 Pin = 23
|
||||
P0_24 Pin = 24
|
||||
P0_25 Pin = 25
|
||||
P0_26 Pin = 26
|
||||
P0_27 Pin = 27
|
||||
P0_28 Pin = 28
|
||||
P0_29 Pin = 29
|
||||
P0_30 Pin = 30
|
||||
P0_31 Pin = 31
|
||||
P1_00 Pin = 32
|
||||
P1_01 Pin = 33
|
||||
P1_02 Pin = 34
|
||||
P1_03 Pin = 35
|
||||
P1_04 Pin = 36
|
||||
P1_05 Pin = 37
|
||||
P1_06 Pin = 38
|
||||
P1_07 Pin = 39
|
||||
P1_08 Pin = 40
|
||||
P1_09 Pin = 41
|
||||
P1_10 Pin = 42
|
||||
P1_11 Pin = 43
|
||||
P1_12 Pin = 44
|
||||
P1_13 Pin = 45
|
||||
P1_14 Pin = 46
|
||||
P1_15 Pin = 47
|
||||
)
|
||||
|
||||
// Get peripheral and pin number for this GPIO pin.
|
||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||
@@ -23,11 +77,6 @@ func (uart UART) setPins(tx, rx Pin) {
|
||||
nrf.UART0.PSEL.RXD.Set(uint32(rx))
|
||||
}
|
||||
|
||||
//go:export UARTE0_UART0_IRQHandler
|
||||
func handleUART0() {
|
||||
UART0.handleInterrupt()
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSEL.SCL.Set(uint32(scl))
|
||||
i2c.Bus.PSEL.SDA.Set(uint32(sda))
|
||||
|
||||
@@ -5,12 +5,14 @@ package machine
|
||||
// Peripheral abstraction layer for the stm32.
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/stm32"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 72000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 72000000
|
||||
}
|
||||
|
||||
const (
|
||||
PinInput PinMode = 0 // Input mode
|
||||
@@ -109,9 +111,9 @@ func (p Pin) Get() bool {
|
||||
|
||||
// UART
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
Bus *stm32.USART_Type
|
||||
IRQVal uint32
|
||||
Buffer *RingBuffer
|
||||
Bus *stm32.USART_Type
|
||||
Interrupt interrupt.Interrupt
|
||||
}
|
||||
|
||||
// Configure the UART.
|
||||
@@ -153,8 +155,8 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
uart.Bus.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
||||
|
||||
// Enable RX IRQ
|
||||
arm.SetPriority(uart.IRQVal, 0xc0)
|
||||
arm.EnableIRQ(uart.IRQVal)
|
||||
uart.Interrupt.SetPriority(0xc0)
|
||||
uart.Interrupt.Enable()
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the UART.
|
||||
@@ -163,10 +165,10 @@ func (uart UART) SetBaudRate(br uint32) {
|
||||
var divider uint32
|
||||
if uart.Bus == stm32.USART1 {
|
||||
// first divide by PCLK2 prescaler (div 1) and then desired baudrate
|
||||
divider = CPU_FREQUENCY / br
|
||||
divider = CPUFrequency() / br
|
||||
} else {
|
||||
// first divide by PCLK1 prescaler (div 2) and then desired baudrate
|
||||
divider = CPU_FREQUENCY / 2 / br
|
||||
divider = CPUFrequency() / 2 / br
|
||||
}
|
||||
uart.Bus.BRR.Set(divider)
|
||||
}
|
||||
@@ -180,6 +182,12 @@ func (uart UART) WriteByte(c byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||
// this UART instance.
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
uart.Receive(byte((uart.Bus.DR.Get() & 0xFF)))
|
||||
}
|
||||
|
||||
// SPI on the STM32.
|
||||
type SPI struct {
|
||||
Bus *stm32.SPI_Type
|
||||
@@ -360,7 +368,7 @@ func (i2c I2C) Configure(config I2CConfig) {
|
||||
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_PE)
|
||||
|
||||
// pclk1 clock speed is main frequency divided by PCLK1 prescaler (div 2)
|
||||
pclk1 := uint32(CPU_FREQUENCY / 2)
|
||||
pclk1 := CPUFrequency() / 2
|
||||
|
||||
// set freqency range to PCLK1 clock speed in MHz
|
||||
// aka setting the value 36 means to use 36 MHz clock
|
||||
|
||||
@@ -5,11 +5,13 @@ package machine
|
||||
// Peripheral abstraction layer for the stm32.
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 168000000
|
||||
func CPUFrequency() uint32 {
|
||||
return 168000000
|
||||
}
|
||||
|
||||
const (
|
||||
// Mode Flag
|
||||
@@ -201,8 +203,11 @@ func (uart UART) Configure(config UARTConfig) {
|
||||
stm32.USART2.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
||||
|
||||
// Enable RX IRQ.
|
||||
arm.SetPriority(stm32.IRQ_USART2, 0xc0)
|
||||
arm.EnableIRQ(stm32.IRQ_USART2)
|
||||
intr := interrupt.New(stm32.IRQ_USART2, func(interrupt.Interrupt) {
|
||||
UART1.Receive(byte((stm32.USART2.DR.Get() & 0xFF)))
|
||||
})
|
||||
intr.SetPriority(0xc0)
|
||||
intr.Enable()
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
@@ -213,8 +218,3 @@ func (uart UART) WriteByte(c byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:export USART2_IRQHandler
|
||||
func handleUSART2() {
|
||||
UART1.Receive(byte((stm32.USART2.DR.Get() & 0xFF)))
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build sam stm32,!stm32f407 fe310
|
||||
// +build !baremetal sam stm32,!stm32f407 fe310
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
+12
-13
@@ -3,7 +3,6 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"runtime/volatile"
|
||||
)
|
||||
@@ -348,18 +347,18 @@ const cdcSize = iadDescriptorSize +
|
||||
|
||||
// Bytes returns CDCDescriptor data.
|
||||
func (d CDCDescriptor) Bytes() []byte {
|
||||
buf := bytes.NewBuffer(make([]byte, 0))
|
||||
buf.Write(d.iad.Bytes())
|
||||
buf.Write(d.cif.Bytes())
|
||||
buf.Write(d.header.Bytes())
|
||||
buf.Write(d.controlManagement.Bytes())
|
||||
buf.Write(d.functionalDescriptor.Bytes())
|
||||
buf.Write(d.callManagement.Bytes())
|
||||
buf.Write(d.cifin.Bytes())
|
||||
buf.Write(d.dif.Bytes())
|
||||
buf.Write(d.out.Bytes())
|
||||
buf.Write(d.in.Bytes())
|
||||
return buf.Bytes()
|
||||
var buf []byte
|
||||
buf = append(buf, d.iad.Bytes()...)
|
||||
buf = append(buf, d.cif.Bytes()...)
|
||||
buf = append(buf, d.header.Bytes()...)
|
||||
buf = append(buf, d.controlManagement.Bytes()...)
|
||||
buf = append(buf, d.functionalDescriptor.Bytes()...)
|
||||
buf = append(buf, d.callManagement.Bytes()...)
|
||||
buf = append(buf, d.cifin.Bytes()...)
|
||||
buf = append(buf, d.dif.Bytes()...)
|
||||
buf = append(buf, d.out.Bytes()...)
|
||||
buf = append(buf, d.in.Bytes()...)
|
||||
return buf
|
||||
}
|
||||
|
||||
// MSCDescriptor is not used yet.
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build darwin linux,!baremetal
|
||||
// +build darwin linux,!baremetal freebsd,!baremetal
|
||||
|
||||
package os
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ const GOARCH = "wasm"
|
||||
const TargetBits = 32
|
||||
|
||||
//go:extern __heap_base
|
||||
var heapStartSymbol unsafe.Pointer
|
||||
var heapStartSymbol [0]byte
|
||||
|
||||
//go:export llvm.wasm.memory.size.i32
|
||||
func wasm_memory_size(index int32) int32
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user