mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-18 03:24:00 +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 -
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install \
|
sudo apt-get install \
|
||||||
python3 \
|
|
||||||
llvm<<parameters.llvm>>-dev \
|
llvm<<parameters.llvm>>-dev \
|
||||||
clang<<parameters.llvm>> \
|
clang<<parameters.llvm>> \
|
||||||
libclang<<parameters.llvm>>-dev \
|
libclang<<parameters.llvm>>-dev \
|
||||||
lld<<parameters.llvm>> \
|
lld<<parameters.llvm>> \
|
||||||
gcc-arm-linux-gnueabihf \
|
gcc-arm-linux-gnueabihf \
|
||||||
libc6-dev-armel-cross \
|
|
||||||
gcc-aarch64-linux-gnu \
|
gcc-aarch64-linux-gnu \
|
||||||
libc6-dev-arm64-cross \
|
|
||||||
qemu-system-arm \
|
qemu-system-arm \
|
||||||
qemu-user \
|
qemu-user \
|
||||||
gcc-avr \
|
gcc-avr \
|
||||||
@@ -52,6 +49,18 @@ commands:
|
|||||||
key: llvm-source-9-v0
|
key: llvm-source-9-v0
|
||||||
paths:
|
paths:
|
||||||
- llvm-project
|
- 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:
|
test-linux:
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
@@ -65,9 +74,17 @@ commands:
|
|||||||
- go-cache-v2-{{ checksum "go.mod" }}
|
- go-cache-v2-{{ checksum "go.mod" }}
|
||||||
- llvm-source-linux
|
- llvm-source-linux
|
||||||
- run: go install .
|
- 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: go test -v ./cgo ./compileopts ./interp ./transform .
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
- run: make smoketest RISCV=0
|
- run: make smoketest
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||||
paths:
|
paths:
|
||||||
@@ -83,7 +100,6 @@ commands:
|
|||||||
name: "Install apt dependencies"
|
name: "Install apt dependencies"
|
||||||
command: |
|
command: |
|
||||||
sudo apt-get install \
|
sudo apt-get install \
|
||||||
python3 \
|
|
||||||
gcc-arm-linux-gnueabihf \
|
gcc-arm-linux-gnueabihf \
|
||||||
binutils-arm-none-eabi \
|
binutils-arm-none-eabi \
|
||||||
libc6-dev-armel-cross \
|
libc6-dev-armel-cross \
|
||||||
@@ -122,13 +138,8 @@ commands:
|
|||||||
key: llvm-build-9-linux-v0-assert
|
key: llvm-build-9-linux-v0-assert
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- run:
|
|
||||||
name: "Create LLVM symlinks"
|
|
||||||
command: |
|
|
||||||
ln -s $PWD/llvm-build/bin/clang-9 /go/bin/clang-9
|
|
||||||
ln -s $PWD/llvm-build/bin/ld.lld /go/bin/ld.lld-9
|
|
||||||
ln -s $PWD/llvm-build/bin/wasm-ld /go/bin/wasm-ld-9
|
|
||||||
- run: make ASSERT=1
|
- run: make ASSERT=1
|
||||||
|
- build-wasi-libc
|
||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
command: make ASSERT=1 test
|
command: make ASSERT=1 test
|
||||||
@@ -139,7 +150,7 @@ commands:
|
|||||||
- ~/.cache/tinygo
|
- ~/.cache/tinygo
|
||||||
- /go/pkg/mod
|
- /go/pkg/mod
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
- run: make smoketest TINYGO=build/tinygo RISCV=0
|
- run: make smoketest TINYGO=build/tinygo
|
||||||
build-linux:
|
build-linux:
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
@@ -148,7 +159,6 @@ commands:
|
|||||||
name: "Install apt dependencies"
|
name: "Install apt dependencies"
|
||||||
command: |
|
command: |
|
||||||
sudo apt-get install \
|
sudo apt-get install \
|
||||||
python3 \
|
|
||||||
gcc-arm-linux-gnueabihf \
|
gcc-arm-linux-gnueabihf \
|
||||||
binutils-arm-none-eabi \
|
binutils-arm-none-eabi \
|
||||||
libc6-dev-armel-cross \
|
libc6-dev-armel-cross \
|
||||||
@@ -187,12 +197,7 @@ commands:
|
|||||||
key: llvm-build-9-linux-v0
|
key: llvm-build-9-linux-v0
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- run:
|
- build-wasi-libc
|
||||||
name: "Create LLVM symlinks"
|
|
||||||
command: |
|
|
||||||
ln -s $PWD/llvm-build/bin/clang-9 /go/bin/clang-9
|
|
||||||
ln -s $PWD/llvm-build/bin/ld.lld /go/bin/ld.lld-9
|
|
||||||
ln -s $PWD/llvm-build/bin/wasm-ld /go/bin/wasm-ld-9
|
|
||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
command: make test
|
command: make test
|
||||||
@@ -216,11 +221,6 @@ commands:
|
|||||||
tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz
|
tar -C ~/lib -xf /tmp/tinygo.linux-amd64.tar.gz
|
||||||
ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo
|
ln -s ~/lib/tinygo/bin/tinygo /go/bin/tinygo
|
||||||
tinygo version
|
tinygo version
|
||||||
- run:
|
|
||||||
name: "Download SiFive GNU toolchain"
|
|
||||||
command: |
|
|
||||||
curl -O https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-linux-ubuntu14.tar.gz
|
|
||||||
sudo tar -C /usr/local --strip-components=1 -xf riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-linux-ubuntu14.tar.gz
|
|
||||||
- run: make smoketest
|
- run: make smoketest
|
||||||
build-macos:
|
build-macos:
|
||||||
steps:
|
steps:
|
||||||
@@ -264,10 +264,16 @@ commands:
|
|||||||
key: llvm-build-9-macos-v0
|
key: llvm-build-9-macos-v0
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- wasi-libc-sysroot-macos-v0
|
||||||
- run:
|
- run:
|
||||||
name: "Create LLVM symlinks"
|
name: "Build wasi-libc"
|
||||||
command: |
|
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
|
||||||
ln -s $PWD/llvm-build/bin/clang-9 /usr/local/bin/clang-9
|
- save_cache:
|
||||||
|
key: wasi-libc-sysroot-macos-v0
|
||||||
|
paths:
|
||||||
|
- lib/wasi-libc/sysroot
|
||||||
- run:
|
- run:
|
||||||
name: "Test TinyGo"
|
name: "Test TinyGo"
|
||||||
command: make test
|
command: make test
|
||||||
|
|||||||
@@ -14,3 +14,6 @@
|
|||||||
path = lib/compiler-rt
|
path = lib/compiler-rt
|
||||||
url = https://github.com/llvm-mirror/compiler-rt.git
|
url = https://github.com/llvm-mirror/compiler-rt.git
|
||||||
branch = release_80
|
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
|
0.10.0
|
||||||
---
|
---
|
||||||
* **command line**
|
* **command line**
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to make participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||||
|
level of experience, education, socio-economic status, nationality, personal
|
||||||
|
appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all project spaces, and it also applies when
|
||||||
|
an individual is representing the project or its community in public spaces.
|
||||||
|
Examples of representing a project or community include using an official
|
||||||
|
project e-mail address, posting via an official social media account, or acting
|
||||||
|
as an appointed representative at an online or offline event. Representation of
|
||||||
|
a project may be further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at [conduct@tinygo.org](mailto:conduct@tinygo.org). All
|
||||||
|
complaints will be reviewed and investigated and will result in a response that
|
||||||
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see
|
||||||
|
https://www.contributor-covenant.org/faq
|
||||||
@@ -15,3 +15,4 @@ Ayke van Laethem <aykevanlaethem@gmail.com>
|
|||||||
Daniel Esteban <conejo@conejo.me>
|
Daniel Esteban <conejo@conejo.me>
|
||||||
Loon, LLC.
|
Loon, LLC.
|
||||||
Ron Evans <ron@hybridgroup.com>
|
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/ && \
|
RUN cd /tinygo/ && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y apt-utils python3 make binutils-avr gcc-avr avr-libc && \
|
apt-get install -y apt-utils make binutils-avr gcc-avr avr-libc && \
|
||||||
make gen-device-avr && \
|
make gen-device-avr && \
|
||||||
apt-get remove -y python3 && \
|
|
||||||
apt-get autoremove -y && \
|
apt-get autoremove -y && \
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
|
||||||
@@ -59,11 +58,8 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
|||||||
|
|
||||||
RUN cd /tinygo/ && \
|
RUN cd /tinygo/ && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y apt-utils python3 make clang-9 && \
|
apt-get install -y apt-utils make clang-9 && \
|
||||||
make gen-device-nrf && make gen-device-stm32 && \
|
make gen-device-nrf && make gen-device-stm32
|
||||||
apt-get remove -y python3 && \
|
|
||||||
apt-get autoremove -y && \
|
|
||||||
apt-get clean
|
|
||||||
|
|
||||||
# tinygo-all stage installs the needed dependencies to compile TinyGo programs for all platforms.
|
# tinygo-all stage installs the needed dependencies to compile TinyGo programs for all platforms.
|
||||||
FROM tinygo-wasm AS tinygo-all
|
FROM tinygo-wasm AS tinygo-all
|
||||||
@@ -74,10 +70,7 @@ COPY --from=tinygo-base /tinygo/lib /tinygo/lib
|
|||||||
|
|
||||||
RUN cd /tinygo/ && \
|
RUN cd /tinygo/ && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y apt-utils python3 make clang-9 binutils-avr gcc-avr avr-libc && \
|
apt-get install -y apt-utils make clang-9 binutils-avr gcc-avr avr-libc && \
|
||||||
make gen-device && \
|
make gen-device
|
||||||
apt-get remove -y python3 && \
|
|
||||||
apt-get autoremove -y && \
|
|
||||||
apt-get clean
|
|
||||||
|
|
||||||
CMD ["tinygo"]
|
CMD ["tinygo"]
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ Copyright (c) 2018-2019 TinyGo Authors. All rights reserved.
|
|||||||
TinyGo includes portions of the Go standard library.
|
TinyGo includes portions of the Go standard library.
|
||||||
Copyright (c) 2009-2019 The Go Authors. All rights reserved.
|
Copyright (c) 2009-2019 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
TinyGo includes portions of LLVM, which is under the Apache License v2.0 with
|
||||||
|
LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
met:
|
met:
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ LLVM_BUILDDIR ?= llvm-build
|
|||||||
CLANG_SRC ?= llvm-project/clang
|
CLANG_SRC ?= llvm-project/clang
|
||||||
LLD_SRC ?= llvm-project/lld
|
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 binary and GOROOT to select
|
||||||
GO ?= go
|
GO ?= go
|
||||||
export GOROOT = $(shell $(GO) env GOROOT)
|
export GOROOT = $(shell $(GO) env GOROOT)
|
||||||
@@ -14,9 +19,6 @@ export GOROOT = $(shell $(GO) env GOROOT)
|
|||||||
# md5sum binary
|
# md5sum binary
|
||||||
MD5SUM = md5sum
|
MD5SUM = md5sum
|
||||||
|
|
||||||
# Python binary
|
|
||||||
PYTHON ?= python
|
|
||||||
|
|
||||||
# tinygo binary for tests
|
# tinygo binary for tests
|
||||||
TINYGO ?= tinygo
|
TINYGO ?= tinygo
|
||||||
|
|
||||||
@@ -34,7 +36,7 @@ endif
|
|||||||
|
|
||||||
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-avr
|
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-avr
|
||||||
|
|
||||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines debuginfodwarf executionengine instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
|
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf executionengine instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
|
||||||
|
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
EXE = .exe
|
EXE = .exe
|
||||||
@@ -65,6 +67,11 @@ $(LIBCLANG_PATH): $(LIBCLANG_FILES)
|
|||||||
else ifeq ($(shell uname -s),Darwin)
|
else ifeq ($(shell uname -s),Darwin)
|
||||||
MD5SUM = md5
|
MD5SUM = md5
|
||||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||||
|
else ifeq ($(shell uname -s),FreeBSD)
|
||||||
|
MD5SUM = md5
|
||||||
|
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||||
|
START_GROUP = -Wl,--start-group
|
||||||
|
END_GROUP = -Wl,--end-group
|
||||||
else
|
else
|
||||||
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
LIBCLANG_PATH = $(abspath $(LLVM_BUILDDIR))/lib/libclang.a
|
||||||
START_GROUP = -Wl,--start-group
|
START_GROUP = -Wl,--start-group
|
||||||
@@ -78,7 +85,7 @@ LLD_LIBS = $(START_GROUP) -llldCOFF -llldCommon -llldCore -llldDriver -llldELF -
|
|||||||
|
|
||||||
# For static linking.
|
# For static linking.
|
||||||
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
|
||||||
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
CGO_CPPFLAGS=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
|
||||||
CGO_CXXFLAGS=-std=c++11
|
CGO_CXXFLAGS=-std=c++11
|
||||||
CGO_LDFLAGS+=$(LIBCLANG_PATH) -std=c++11 -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
CGO_LDFLAGS+=$(LIBCLANG_PATH) -std=c++11 -L$(abspath $(LLVM_BUILDDIR)/lib) $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
|
||||||
endif
|
endif
|
||||||
@@ -97,24 +104,28 @@ fmt-check:
|
|||||||
gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32
|
gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32
|
||||||
|
|
||||||
gen-device-avr:
|
gen-device-avr:
|
||||||
$(PYTHON) ./tools/gen-device-avr.py lib/avr/packs/atmega src/device/avr/
|
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
|
||||||
$(PYTHON) ./tools/gen-device-avr.py lib/avr/packs/tiny src/device/avr/
|
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/avr
|
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
|
||||||
|
@GO111MODULE=off $(GO) fmt ./src/device/avr
|
||||||
|
|
||||||
gen-device-nrf:
|
build/gen-device-svd: ./tools/gen-device-svd/*.go
|
||||||
$(PYTHON) ./tools/gen-device-svd.py lib/nrfx/mdk/ src/device/nrf/ --source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk
|
$(GO) build -o $@ ./tools/gen-device-svd/
|
||||||
|
|
||||||
|
gen-device-nrf: build/gen-device-svd
|
||||||
|
./build/gen-device-svd -source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk lib/nrfx/mdk/ src/device/nrf/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/nrf
|
GO111MODULE=off $(GO) fmt ./src/device/nrf
|
||||||
|
|
||||||
gen-device-sam:
|
gen-device-sam: build/gen-device-svd
|
||||||
$(PYTHON) ./tools/gen-device-svd.py lib/cmsis-svd/data/Atmel/ src/device/sam/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel lib/cmsis-svd/data/Atmel/ src/device/sam/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/sam
|
GO111MODULE=off $(GO) fmt ./src/device/sam
|
||||||
|
|
||||||
gen-device-sifive:
|
gen-device-sifive: build/gen-device-svd
|
||||||
$(PYTHON) ./tools/gen-device-svd.py lib/cmsis-svd/data/SiFive-Community/ src/device/sifive/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/SiFive-Community
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/SiFive-Community -interrupts=software lib/cmsis-svd/data/SiFive-Community/ src/device/sifive/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/sifive
|
GO111MODULE=off $(GO) fmt ./src/device/sifive
|
||||||
|
|
||||||
gen-device-stm32:
|
gen-device-stm32: build/gen-device-svd
|
||||||
$(PYTHON) ./tools/gen-device-svd.py lib/cmsis-svd/data/STMicro/ src/device/stm32/ --source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/STMicro lib/cmsis-svd/data/STMicro/ src/device/stm32/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/stm32
|
GO111MODULE=off $(GO) fmt ./src/device/stm32
|
||||||
|
|
||||||
|
|
||||||
@@ -133,12 +144,19 @@ $(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
|||||||
cd $(LLVM_BUILDDIR); 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.
|
# Build the Go compiler.
|
||||||
tinygo:
|
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
|
@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 .
|
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 .
|
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -tags byollvm ./cgo ./compileopts ./interp ./transform .
|
||||||
|
|
||||||
tinygo-test:
|
tinygo-test:
|
||||||
@@ -187,6 +205,8 @@ smoketest:
|
|||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
$(TINYGO) build -o test.wasm -tags=pca10056 examples/blinky2
|
$(TINYGO) build -o test.wasm -tags=pca10056 examples/blinky2
|
||||||
@$(MD5SUM) test.wasm
|
@$(MD5SUM) test.wasm
|
||||||
|
$(TINYGO) build -o test.wasm -tags=circuitplay_express examples/blinky1
|
||||||
|
@$(MD5SUM) test.wasm
|
||||||
# test all targets/boards
|
# test all targets/boards
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -218,6 +238,8 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2
|
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2
|
||||||
@$(MD5SUM) test.hex
|
@$(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
|
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.gba -target=gameboy-advance examples/gba-display
|
$(TINYGO) build -size short -o test.gba -target=gameboy-advance examples/gba-display
|
||||||
@@ -226,8 +248,12 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pybadge examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=metro-m4-airlift examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=metro-m4-airlift examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pyportal examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f103rb examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=nucleo-f103rb examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pinetime-devkit0 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pinetime-devkit0 examples/blinky1
|
||||||
@@ -239,22 +265,25 @@ smoketest:
|
|||||||
ifneq ($(AVR), 0)
|
ifneq ($(AVR), 0)
|
||||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(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
|
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
endif
|
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
|
||||||
ifneq ($(RISCV), 0)
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
endif
|
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/export
|
||||||
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/main
|
$(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/bin
|
||||||
@mkdir -p build/release/tinygo/lib/clang/include
|
@mkdir -p build/release/tinygo/lib/clang/include
|
||||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||||
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
||||||
@mkdir -p build/release/tinygo/lib/nrfx
|
@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/armv6m-none-eabi
|
||||||
@mkdir -p build/release/tinygo/pkg/armv7m-none-eabi
|
@mkdir -p build/release/tinygo/pkg/armv7m-none-eabi
|
||||||
@mkdir -p build/release/tinygo/pkg/armv7em-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/LICENSE.TXT build/release/tinygo/lib/compiler-rt
|
||||||
@cp -rp lib/compiler-rt/README.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/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 src build/release/tinygo/src
|
||||||
@cp -rp targets build/release/tinygo/targets
|
@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
|
./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.
|
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 Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||||
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
|
||||||
* [Adafruit Feather M4](https://www.adafruit.com/product/3857)
|
* [Adafruit Feather M4](https://www.adafruit.com/product/3857)
|
||||||
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
* [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727)
|
||||||
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
|
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
|
||||||
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
|
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
|
||||||
|
* [Adafruit PyBadge](https://www.adafruit.com/product/4200)
|
||||||
|
* [Adafruit PyPortal](https://www.adafruit.com/product/4116)
|
||||||
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
|
* [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 Nano33 IoT](https://store.arduino.cc/nano-33-iot)
|
||||||
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
||||||
* [BBC micro:bit](https://microbit.org/)
|
* [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 PCA10040](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF52-DK)
|
||||||
* [Nordic Semiconductor PCA10056](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
|
* [Nordic Semiconductor PCA10056](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
|
||||||
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
|
||||||
|
* [PineTime DevKit](https://www.pine64.org/pinetime/)
|
||||||
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
|
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
|
||||||
* [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
* [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
|
||||||
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
|
||||||
@@ -125,3 +130,5 @@ The original reasoning was: if [Python](https://micropython.org/) can run on mic
|
|||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the BSD 3-clause license, just like the [Go project](https://golang.org/LICENSE) itself.
|
This project is licensed under the BSD 3-clause license, just like the [Go project](https://golang.org/LICENSE) itself.
|
||||||
|
|
||||||
|
Some code has been copied from the LLVM project and is therefore licensed under [a variant of the Apache 2.0 license](http://releases.llvm.org/9.0.0/LICENSE.TXT). This has been clearly indicated in the header of these files.
|
||||||
|
|||||||
+15
-5
@@ -6,7 +6,7 @@ trigger:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: Build
|
- job: Build
|
||||||
timeoutInMinutes: 180
|
timeoutInMinutes: 240 # 4h
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'VS2017-Win2016'
|
vmImage: 'VS2017-Win2016'
|
||||||
steps:
|
steps:
|
||||||
@@ -45,6 +45,16 @@ jobs:
|
|||||||
inputs:
|
inputs:
|
||||||
targetType: inline
|
targetType: inline
|
||||||
script: choco install qemu
|
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
|
- task: Bash@3
|
||||||
displayName: Test TinyGo
|
displayName: Test TinyGo
|
||||||
inputs:
|
inputs:
|
||||||
@@ -61,9 +71,9 @@ jobs:
|
|||||||
export PATH="/c/Go1.13/bin:$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
export PATH="/c/Go1.13/bin:$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
||||||
unset GOROOT
|
unset GOROOT
|
||||||
make release -j4
|
make release -j4
|
||||||
- publish: $(System.DefaultWorkingDirectory)/build/release.tar.gz
|
- publish: $(System.DefaultWorkingDirectory)/build/release/tinygo
|
||||||
displayName: Publish tarball as artifact
|
displayName: Publish zip as artifact
|
||||||
artifact: tinygo.windows-amd64.tar.gz
|
artifact: tinygo
|
||||||
- task: Bash@3
|
- task: Bash@3
|
||||||
displayName: Smoke tests
|
displayName: Smoke tests
|
||||||
inputs:
|
inputs:
|
||||||
@@ -71,4 +81,4 @@ jobs:
|
|||||||
script: |
|
script: |
|
||||||
export PATH="/c/Go1.13/bin:$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
export PATH="/c/Go1.13/bin:$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
||||||
unset GOROOT
|
unset GOROOT
|
||||||
make smoketest TINYGO=build/tinygo AVR=0 RISCV=0
|
make smoketest TINYGO=build/tinygo AVR=0
|
||||||
|
|||||||
+15
-24
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/tinygo-org/tinygo/compiler"
|
"github.com/tinygo-org/tinygo/compiler"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
"github.com/tinygo-org/tinygo/interp"
|
"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
|
// 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.
|
// Compile Go code to IR.
|
||||||
errs := c.Compile(pkgName)
|
errs := c.Compile(pkgName)
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
if len(errs) == 1 {
|
return newMultiError(errs)
|
||||||
return errs[0]
|
|
||||||
}
|
|
||||||
return &MultiError{errs}
|
|
||||||
}
|
}
|
||||||
if config.Options.PrintIR {
|
if config.Options.PrintIR {
|
||||||
fmt.Println("; Generated LLVM IR:")
|
fmt.Println("; Generated LLVM IR:")
|
||||||
@@ -64,7 +62,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
|||||||
// stack-allocated values.
|
// stack-allocated values.
|
||||||
// Use -wasm-abi=generic to disable this behaviour.
|
// Use -wasm-abi=generic to disable this behaviour.
|
||||||
if config.Options.WasmAbi == "js" && strings.HasPrefix(config.Triple(), "wasm") {
|
if config.Options.WasmAbi == "js" && strings.HasPrefix(config.Triple(), "wasm") {
|
||||||
err := c.ExternalInt64AsPtr()
|
err := transform.ExternalInt64AsPtr(c.Module())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// Optimization levels here are roughly the same as Clang, but probably not
|
||||||
// exactly.
|
// exactly.
|
||||||
|
errs = nil
|
||||||
switch config.Options.Opt {
|
switch config.Options.Opt {
|
||||||
case "none:", "0":
|
case "none:", "0":
|
||||||
err = c.Optimize(0, 0, 0) // -O0
|
errs = c.Optimize(0, 0, 0) // -O0
|
||||||
case "1":
|
case "1":
|
||||||
err = c.Optimize(1, 0, 0) // -O1
|
errs = c.Optimize(1, 0, 0) // -O1
|
||||||
case "2":
|
case "2":
|
||||||
err = c.Optimize(2, 0, 225) // -O2
|
errs = c.Optimize(2, 0, 225) // -O2
|
||||||
case "s":
|
case "s":
|
||||||
err = c.Optimize(2, 1, 225) // -Os
|
errs = c.Optimize(2, 1, 225) // -Os
|
||||||
case "z":
|
case "z":
|
||||||
err = c.Optimize(2, 2, 5) // -Oz, default
|
errs = c.Optimize(2, 2, 5) // -Oz, default
|
||||||
default:
|
default:
|
||||||
err = errors.New("unknown optimization level: -opt=" + config.Options.Opt)
|
errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if len(errs) > 0 {
|
||||||
return err
|
return newMultiError(errs)
|
||||||
}
|
}
|
||||||
if err := c.Verify(); err != nil {
|
if err := c.Verify(); err != nil {
|
||||||
return errors.New("verification failure after LLVM optimization passes")
|
return errors.New("verification failure after LLVM optimization passes")
|
||||||
@@ -154,11 +153,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
|||||||
for i, path := range config.ExtraFiles() {
|
for i, path := range config.ExtraFiles() {
|
||||||
abspath := filepath.Join(root, path)
|
abspath := filepath.Join(root, path)
|
||||||
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
|
outpath := filepath.Join(dir, "extra-"+strconv.Itoa(i)+"-"+filepath.Base(path)+".o")
|
||||||
cmdNames := []string{config.Target.Compiler}
|
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
|
||||||
if names, ok := commands[config.Target.Compiler]; ok {
|
|
||||||
cmdNames = names
|
|
||||||
}
|
|
||||||
err := execCommand(cmdNames, append(config.CFlags(), "-c", "-o", outpath, abspath)...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to build", path, err}
|
return &commandError{"failed to build", path, err}
|
||||||
}
|
}
|
||||||
@@ -170,11 +165,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
|||||||
for _, file := range pkg.CFiles {
|
for _, file := range pkg.CFiles {
|
||||||
path := filepath.Join(pkg.Package.Dir, file)
|
path := filepath.Join(pkg.Package.Dir, file)
|
||||||
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+file+".o")
|
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+file+".o")
|
||||||
cmdNames := []string{config.Target.Compiler}
|
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, path)...)
|
||||||
if names, ok := commands[config.Target.Compiler]; ok {
|
|
||||||
cmdNames = names
|
|
||||||
}
|
|
||||||
err := execCommand(cmdNames, append(config.CFlags(), "-c", "-o", outpath, path)...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to build", path, err}
|
return &commandError{"failed to build", path, err}
|
||||||
}
|
}
|
||||||
@@ -217,7 +208,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
|||||||
} else if outext == ".uf2" {
|
} else if outext == ".uf2" {
|
||||||
// Get UF2 from the .elf file.
|
// Get UF2 from the .elf file.
|
||||||
tmppath = filepath.Join(dir, "main"+outext)
|
tmppath = filepath.Join(dir, "main"+outext)
|
||||||
err := convertELFFileToUF2File(executable, tmppath)
|
err := convertELFFileToUF2File(executable, tmppath, config.Target.UF2FamilyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -158,7 +158,7 @@ var aeabiBuiltins = []string{
|
|||||||
|
|
||||||
func builtinFiles(target string) []string {
|
func builtinFiles(target string) []string {
|
||||||
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
|
||||||
if strings.HasPrefix(target, "arm") {
|
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
||||||
builtins = append(builtins, aeabiBuiltins...)
|
builtins = append(builtins, aeabiBuiltins...)
|
||||||
}
|
}
|
||||||
return builtins
|
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
|
// Note: -fdebug-prefix-map is necessary to make the output archive
|
||||||
// reproducible. Otherwise the temporary directory is stored in the
|
// reproducible. Otherwise the temporary directory is stored in the
|
||||||
// archive itself, which varies each run.
|
// archive itself, which varies each run.
|
||||||
err := execCommand(commands["clang"], "-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir, "-o", objpath, srcpath)
|
args := []string{"-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target=" + target, "-fdebug-prefix-map=" + dir + "=" + remapDir}
|
||||||
|
if strings.HasPrefix(target, "riscv32-") {
|
||||||
|
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
||||||
|
}
|
||||||
|
err := runCCompiler("clang", append(args, "-o", objpath, srcpath)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to build", srcpath, err}
|
return &commandError{"failed to build", srcpath, err}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,504 @@
|
|||||||
|
// +build byollvm
|
||||||
|
|
||||||
|
//===-- cc1as.cpp - Clang Assembler --------------------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This is the entry point to the clang -cc1as functionality, which implements
|
||||||
|
// the direct interface to the LLVM MC based assembler.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "clang/Basic/Diagnostic.h"
|
||||||
|
#include "clang/Basic/DiagnosticOptions.h"
|
||||||
|
#include "clang/Driver/DriverDiagnostic.h"
|
||||||
|
#include "clang/Driver/Options.h"
|
||||||
|
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||||
|
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||||
|
#include "clang/Frontend/Utils.h"
|
||||||
|
#include "llvm/ADT/STLExtras.h"
|
||||||
|
#include "llvm/ADT/StringSwitch.h"
|
||||||
|
#include "llvm/ADT/Triple.h"
|
||||||
|
#include "llvm/IR/DataLayout.h"
|
||||||
|
#include "llvm/MC/MCAsmBackend.h"
|
||||||
|
#include "llvm/MC/MCAsmInfo.h"
|
||||||
|
#include "llvm/MC/MCCodeEmitter.h"
|
||||||
|
#include "llvm/MC/MCContext.h"
|
||||||
|
#include "llvm/MC/MCInstrInfo.h"
|
||||||
|
#include "llvm/MC/MCObjectFileInfo.h"
|
||||||
|
#include "llvm/MC/MCObjectWriter.h"
|
||||||
|
#include "llvm/MC/MCParser/MCAsmParser.h"
|
||||||
|
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
|
||||||
|
#include "llvm/MC/MCRegisterInfo.h"
|
||||||
|
#include "llvm/MC/MCSectionMachO.h"
|
||||||
|
#include "llvm/MC/MCStreamer.h"
|
||||||
|
#include "llvm/MC/MCSubtargetInfo.h"
|
||||||
|
#include "llvm/MC/MCTargetOptions.h"
|
||||||
|
#include "llvm/Option/Arg.h"
|
||||||
|
#include "llvm/Option/ArgList.h"
|
||||||
|
#include "llvm/Option/OptTable.h"
|
||||||
|
#include "llvm/Support/CommandLine.h"
|
||||||
|
#include "llvm/Support/ErrorHandling.h"
|
||||||
|
#include "llvm/Support/FileSystem.h"
|
||||||
|
#include "llvm/Support/FormattedStream.h"
|
||||||
|
#include "llvm/Support/Host.h"
|
||||||
|
#include "llvm/Support/MemoryBuffer.h"
|
||||||
|
#include "llvm/Support/Path.h"
|
||||||
|
#include "llvm/Support/Signals.h"
|
||||||
|
#include "llvm/Support/SourceMgr.h"
|
||||||
|
#include "llvm/Support/TargetRegistry.h"
|
||||||
|
#include "llvm/Support/TargetSelect.h"
|
||||||
|
#include "llvm/Support/Timer.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <system_error>
|
||||||
|
using namespace clang;
|
||||||
|
using namespace clang::driver;
|
||||||
|
using namespace clang::driver::options;
|
||||||
|
using namespace llvm;
|
||||||
|
using namespace llvm::opt;
|
||||||
|
|
||||||
|
#include "cc1as.h"
|
||||||
|
|
||||||
|
bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
|
||||||
|
ArrayRef<const char *> Argv,
|
||||||
|
DiagnosticsEngine &Diags) {
|
||||||
|
bool Success = true;
|
||||||
|
|
||||||
|
// Parse the arguments.
|
||||||
|
std::unique_ptr<OptTable> OptTbl(createDriverOptTable());
|
||||||
|
|
||||||
|
const unsigned IncludedFlagsBitmask = options::CC1AsOption;
|
||||||
|
unsigned MissingArgIndex, MissingArgCount;
|
||||||
|
InputArgList Args = OptTbl->ParseArgs(Argv, MissingArgIndex, MissingArgCount,
|
||||||
|
IncludedFlagsBitmask);
|
||||||
|
|
||||||
|
// Check for missing argument error.
|
||||||
|
if (MissingArgCount) {
|
||||||
|
Diags.Report(diag::err_drv_missing_argument)
|
||||||
|
<< Args.getArgString(MissingArgIndex) << MissingArgCount;
|
||||||
|
Success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue errors on unknown arguments.
|
||||||
|
for (const Arg *A : Args.filtered(OPT_UNKNOWN)) {
|
||||||
|
auto ArgString = A->getAsString(Args);
|
||||||
|
std::string Nearest;
|
||||||
|
if (OptTbl->findNearest(ArgString, Nearest, IncludedFlagsBitmask) > 1)
|
||||||
|
Diags.Report(diag::err_drv_unknown_argument) << ArgString;
|
||||||
|
else
|
||||||
|
Diags.Report(diag::err_drv_unknown_argument_with_suggestion)
|
||||||
|
<< ArgString << Nearest;
|
||||||
|
Success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the invocation.
|
||||||
|
|
||||||
|
// Target Options
|
||||||
|
Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
|
||||||
|
Opts.CPU = Args.getLastArgValue(OPT_target_cpu);
|
||||||
|
Opts.Features = Args.getAllArgValues(OPT_target_feature);
|
||||||
|
|
||||||
|
// Use the default target triple if unspecified.
|
||||||
|
if (Opts.Triple.empty())
|
||||||
|
Opts.Triple = llvm::sys::getDefaultTargetTriple();
|
||||||
|
|
||||||
|
// Language Options
|
||||||
|
Opts.IncludePaths = Args.getAllArgValues(OPT_I);
|
||||||
|
Opts.NoInitialTextSection = Args.hasArg(OPT_n);
|
||||||
|
Opts.SaveTemporaryLabels = Args.hasArg(OPT_msave_temp_labels);
|
||||||
|
// Any DebugInfoKind implies GenDwarfForAssembly.
|
||||||
|
Opts.GenDwarfForAssembly = Args.hasArg(OPT_debug_info_kind_EQ);
|
||||||
|
|
||||||
|
if (const Arg *A = Args.getLastArg(OPT_compress_debug_sections,
|
||||||
|
OPT_compress_debug_sections_EQ)) {
|
||||||
|
if (A->getOption().getID() == OPT_compress_debug_sections) {
|
||||||
|
// TODO: be more clever about the compression type auto-detection
|
||||||
|
Opts.CompressDebugSections = llvm::DebugCompressionType::GNU;
|
||||||
|
} else {
|
||||||
|
Opts.CompressDebugSections =
|
||||||
|
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
|
||||||
|
.Case("none", llvm::DebugCompressionType::None)
|
||||||
|
.Case("zlib", llvm::DebugCompressionType::Z)
|
||||||
|
.Case("zlib-gnu", llvm::DebugCompressionType::GNU)
|
||||||
|
.Default(llvm::DebugCompressionType::None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations);
|
||||||
|
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
|
||||||
|
Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags);
|
||||||
|
Opts.DwarfDebugProducer = Args.getLastArgValue(OPT_dwarf_debug_producer);
|
||||||
|
Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir);
|
||||||
|
Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name);
|
||||||
|
|
||||||
|
for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ))
|
||||||
|
Opts.DebugPrefixMap.insert(StringRef(Arg).split('='));
|
||||||
|
|
||||||
|
// Frontend Options
|
||||||
|
if (Args.hasArg(OPT_INPUT)) {
|
||||||
|
bool First = true;
|
||||||
|
for (const Arg *A : Args.filtered(OPT_INPUT)) {
|
||||||
|
if (First) {
|
||||||
|
Opts.InputFile = A->getValue();
|
||||||
|
First = false;
|
||||||
|
} else {
|
||||||
|
Diags.Report(diag::err_drv_unknown_argument) << A->getAsString(Args);
|
||||||
|
Success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm);
|
||||||
|
Opts.OutputPath = Args.getLastArgValue(OPT_o);
|
||||||
|
Opts.SplitDwarfOutput = Args.getLastArgValue(OPT_split_dwarf_output);
|
||||||
|
if (Arg *A = Args.getLastArg(OPT_filetype)) {
|
||||||
|
StringRef Name = A->getValue();
|
||||||
|
unsigned OutputType = StringSwitch<unsigned>(Name)
|
||||||
|
.Case("asm", FT_Asm)
|
||||||
|
.Case("null", FT_Null)
|
||||||
|
.Case("obj", FT_Obj)
|
||||||
|
.Default(~0U);
|
||||||
|
if (OutputType == ~0U) {
|
||||||
|
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name;
|
||||||
|
Success = false;
|
||||||
|
} else
|
||||||
|
Opts.OutputType = FileType(OutputType);
|
||||||
|
}
|
||||||
|
Opts.ShowHelp = Args.hasArg(OPT_help);
|
||||||
|
Opts.ShowVersion = Args.hasArg(OPT_version);
|
||||||
|
|
||||||
|
// Transliterate Options
|
||||||
|
Opts.OutputAsmVariant =
|
||||||
|
getLastArgIntValue(Args, OPT_output_asm_variant, 0, Diags);
|
||||||
|
Opts.ShowEncoding = Args.hasArg(OPT_show_encoding);
|
||||||
|
Opts.ShowInst = Args.hasArg(OPT_show_inst);
|
||||||
|
|
||||||
|
// Assemble Options
|
||||||
|
Opts.RelaxAll = Args.hasArg(OPT_mrelax_all);
|
||||||
|
Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack);
|
||||||
|
Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings);
|
||||||
|
Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic");
|
||||||
|
Opts.TargetABI = Args.getLastArgValue(OPT_target_abi);
|
||||||
|
Opts.IncrementalLinkerCompatible =
|
||||||
|
Args.hasArg(OPT_mincremental_linker_compatible);
|
||||||
|
Opts.SymbolDefs = Args.getAllArgValues(OPT_defsym);
|
||||||
|
|
||||||
|
// EmbedBitcode Option. If -fembed-bitcode is enabled, set the flag.
|
||||||
|
// EmbedBitcode behaves the same for all embed options for assembly files.
|
||||||
|
if (auto *A = Args.getLastArg(OPT_fembed_bitcode_EQ)) {
|
||||||
|
Opts.EmbedBitcode = llvm::StringSwitch<unsigned>(A->getValue())
|
||||||
|
.Case("all", 1)
|
||||||
|
.Case("bitcode", 1)
|
||||||
|
.Case("marker", 1)
|
||||||
|
.Default(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<raw_fd_ostream>
|
||||||
|
getOutputStream(StringRef Path, DiagnosticsEngine &Diags, bool Binary) {
|
||||||
|
// Make sure that the Out file gets unlinked from the disk if we get a
|
||||||
|
// SIGINT.
|
||||||
|
if (Path != "-")
|
||||||
|
sys::RemoveFileOnSignal(Path);
|
||||||
|
|
||||||
|
std::error_code EC;
|
||||||
|
auto Out = llvm::make_unique<raw_fd_ostream>(
|
||||||
|
Path, EC, (Binary ? sys::fs::F_None : sys::fs::F_Text));
|
||||||
|
if (EC) {
|
||||||
|
Diags.Report(diag::err_fe_unable_to_open_output) << Path << EC.message();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags) {
|
||||||
|
// Get the target specific parser.
|
||||||
|
std::string Error;
|
||||||
|
const Target *TheTarget = TargetRegistry::lookupTarget(Opts.Triple, Error);
|
||||||
|
if (!TheTarget)
|
||||||
|
return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
|
||||||
|
|
||||||
|
ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
|
||||||
|
MemoryBuffer::getFileOrSTDIN(Opts.InputFile);
|
||||||
|
|
||||||
|
if (std::error_code EC = Buffer.getError()) {
|
||||||
|
Error = EC.message();
|
||||||
|
return Diags.Report(diag::err_fe_error_reading) << Opts.InputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceMgr SrcMgr;
|
||||||
|
|
||||||
|
// Tell SrcMgr about this buffer, which is what the parser will pick up.
|
||||||
|
unsigned BufferIndex = SrcMgr.AddNewSourceBuffer(std::move(*Buffer), SMLoc());
|
||||||
|
|
||||||
|
// Record the location of the include directories so that the lexer can find
|
||||||
|
// it later.
|
||||||
|
SrcMgr.setIncludeDirs(Opts.IncludePaths);
|
||||||
|
|
||||||
|
std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(Opts.Triple));
|
||||||
|
assert(MRI && "Unable to create target register info!");
|
||||||
|
|
||||||
|
std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, Opts.Triple));
|
||||||
|
assert(MAI && "Unable to create target asm info!");
|
||||||
|
|
||||||
|
// Ensure MCAsmInfo initialization occurs before any use, otherwise sections
|
||||||
|
// may be created with a combination of default and explicit settings.
|
||||||
|
MAI->setCompressDebugSections(Opts.CompressDebugSections);
|
||||||
|
|
||||||
|
MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations);
|
||||||
|
|
||||||
|
bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj;
|
||||||
|
if (Opts.OutputPath.empty())
|
||||||
|
Opts.OutputPath = "-";
|
||||||
|
std::unique_ptr<raw_fd_ostream> FDOS =
|
||||||
|
getOutputStream(Opts.OutputPath, Diags, IsBinary);
|
||||||
|
if (!FDOS)
|
||||||
|
return true;
|
||||||
|
std::unique_ptr<raw_fd_ostream> DwoOS;
|
||||||
|
if (!Opts.SplitDwarfOutput.empty())
|
||||||
|
DwoOS = getOutputStream(Opts.SplitDwarfOutput, Diags, IsBinary);
|
||||||
|
|
||||||
|
// FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
|
||||||
|
// MCObjectFileInfo needs a MCContext reference in order to initialize itself.
|
||||||
|
std::unique_ptr<MCObjectFileInfo> MOFI(new MCObjectFileInfo());
|
||||||
|
|
||||||
|
MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr);
|
||||||
|
|
||||||
|
bool PIC = false;
|
||||||
|
if (Opts.RelocationModel == "static") {
|
||||||
|
PIC = false;
|
||||||
|
} else if (Opts.RelocationModel == "pic") {
|
||||||
|
PIC = true;
|
||||||
|
} else {
|
||||||
|
assert(Opts.RelocationModel == "dynamic-no-pic" &&
|
||||||
|
"Invalid PIC model!");
|
||||||
|
PIC = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOFI->InitMCObjectFileInfo(Triple(Opts.Triple), PIC, Ctx);
|
||||||
|
if (Opts.SaveTemporaryLabels)
|
||||||
|
Ctx.setAllowTemporaryLabels(false);
|
||||||
|
if (Opts.GenDwarfForAssembly)
|
||||||
|
Ctx.setGenDwarfForAssembly(true);
|
||||||
|
if (!Opts.DwarfDebugFlags.empty())
|
||||||
|
Ctx.setDwarfDebugFlags(StringRef(Opts.DwarfDebugFlags));
|
||||||
|
if (!Opts.DwarfDebugProducer.empty())
|
||||||
|
Ctx.setDwarfDebugProducer(StringRef(Opts.DwarfDebugProducer));
|
||||||
|
if (!Opts.DebugCompilationDir.empty())
|
||||||
|
Ctx.setCompilationDir(Opts.DebugCompilationDir);
|
||||||
|
else {
|
||||||
|
// If no compilation dir is set, try to use the current directory.
|
||||||
|
SmallString<128> CWD;
|
||||||
|
if (!sys::fs::current_path(CWD))
|
||||||
|
Ctx.setCompilationDir(CWD);
|
||||||
|
}
|
||||||
|
if (!Opts.DebugPrefixMap.empty())
|
||||||
|
for (const auto &KV : Opts.DebugPrefixMap)
|
||||||
|
Ctx.addDebugPrefixMapEntry(KV.first, KV.second);
|
||||||
|
if (!Opts.MainFileName.empty())
|
||||||
|
Ctx.setMainFileName(StringRef(Opts.MainFileName));
|
||||||
|
Ctx.setDwarfVersion(Opts.DwarfVersion);
|
||||||
|
if (Opts.GenDwarfForAssembly)
|
||||||
|
Ctx.setGenDwarfRootFile(Opts.InputFile,
|
||||||
|
SrcMgr.getMemoryBuffer(BufferIndex)->getBuffer());
|
||||||
|
|
||||||
|
// Build up the feature string from the target feature list.
|
||||||
|
std::string FS;
|
||||||
|
if (!Opts.Features.empty()) {
|
||||||
|
FS = Opts.Features[0];
|
||||||
|
for (unsigned i = 1, e = Opts.Features.size(); i != e; ++i)
|
||||||
|
FS += "," + Opts.Features[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<MCStreamer> Str;
|
||||||
|
|
||||||
|
std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
|
||||||
|
std::unique_ptr<MCSubtargetInfo> STI(
|
||||||
|
TheTarget->createMCSubtargetInfo(Opts.Triple, Opts.CPU, FS));
|
||||||
|
|
||||||
|
raw_pwrite_stream *Out = FDOS.get();
|
||||||
|
std::unique_ptr<buffer_ostream> BOS;
|
||||||
|
|
||||||
|
MCTargetOptions MCOptions;
|
||||||
|
MCOptions.ABIName = Opts.TargetABI;
|
||||||
|
|
||||||
|
// FIXME: There is a bit of code duplication with addPassesToEmitFile.
|
||||||
|
if (Opts.OutputType == AssemblerInvocation::FT_Asm) {
|
||||||
|
MCInstPrinter *IP = TheTarget->createMCInstPrinter(
|
||||||
|
llvm::Triple(Opts.Triple), Opts.OutputAsmVariant, *MAI, *MCII, *MRI);
|
||||||
|
|
||||||
|
std::unique_ptr<MCCodeEmitter> CE;
|
||||||
|
if (Opts.ShowEncoding)
|
||||||
|
CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
|
||||||
|
std::unique_ptr<MCAsmBackend> MAB(
|
||||||
|
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||||
|
|
||||||
|
auto FOut = llvm::make_unique<formatted_raw_ostream>(*Out);
|
||||||
|
Str.reset(TheTarget->createAsmStreamer(
|
||||||
|
Ctx, std::move(FOut), /*asmverbose*/ true,
|
||||||
|
/*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB),
|
||||||
|
Opts.ShowInst));
|
||||||
|
} else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
|
||||||
|
Str.reset(createNullStreamer(Ctx));
|
||||||
|
} else {
|
||||||
|
assert(Opts.OutputType == AssemblerInvocation::FT_Obj &&
|
||||||
|
"Invalid file type!");
|
||||||
|
if (!FDOS->supportsSeeking()) {
|
||||||
|
BOS = make_unique<buffer_ostream>(*FDOS);
|
||||||
|
Out = BOS.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<MCCodeEmitter> CE(
|
||||||
|
TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
|
||||||
|
std::unique_ptr<MCAsmBackend> MAB(
|
||||||
|
TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
|
||||||
|
std::unique_ptr<MCObjectWriter> OW =
|
||||||
|
DwoOS ? MAB->createDwoObjectWriter(*Out, *DwoOS)
|
||||||
|
: MAB->createObjectWriter(*Out);
|
||||||
|
|
||||||
|
Triple T(Opts.Triple);
|
||||||
|
Str.reset(TheTarget->createMCObjectStreamer(
|
||||||
|
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
|
||||||
|
Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
|
||||||
|
/*DWARFMustBeAtTheEnd*/ true));
|
||||||
|
Str.get()->InitSections(Opts.NoExecStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// When -fembed-bitcode is passed to clang_as, a 1-byte marker
|
||||||
|
// is emitted in __LLVM,__asm section if the object file is MachO format.
|
||||||
|
if (Opts.EmbedBitcode && Ctx.getObjectFileInfo()->getObjectFileType() ==
|
||||||
|
MCObjectFileInfo::IsMachO) {
|
||||||
|
MCSection *AsmLabel = Ctx.getMachOSection(
|
||||||
|
"__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly());
|
||||||
|
Str.get()->SwitchSection(AsmLabel);
|
||||||
|
Str.get()->EmitZeros(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assembly to object compilation should leverage assembly info.
|
||||||
|
Str->setUseAssemblerInfoForParsing(true);
|
||||||
|
|
||||||
|
bool Failed = false;
|
||||||
|
|
||||||
|
std::unique_ptr<MCAsmParser> Parser(
|
||||||
|
createMCAsmParser(SrcMgr, Ctx, *Str.get(), *MAI));
|
||||||
|
|
||||||
|
// FIXME: init MCTargetOptions from sanitizer flags here.
|
||||||
|
std::unique_ptr<MCTargetAsmParser> TAP(
|
||||||
|
TheTarget->createMCAsmParser(*STI, *Parser, *MCII, MCOptions));
|
||||||
|
if (!TAP)
|
||||||
|
Failed = Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
|
||||||
|
|
||||||
|
// Set values for symbols, if any.
|
||||||
|
for (auto &S : Opts.SymbolDefs) {
|
||||||
|
auto Pair = StringRef(S).split('=');
|
||||||
|
auto Sym = Pair.first;
|
||||||
|
auto Val = Pair.second;
|
||||||
|
int64_t Value;
|
||||||
|
// We have already error checked this in the driver.
|
||||||
|
Val.getAsInteger(0, Value);
|
||||||
|
Ctx.setSymbolValue(Parser->getStreamer(), Sym, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Failed) {
|
||||||
|
Parser->setTargetParser(*TAP.get());
|
||||||
|
Failed = Parser->Run(Opts.NoInitialTextSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close Streamer first.
|
||||||
|
// It might have a reference to the output stream.
|
||||||
|
Str.reset();
|
||||||
|
// Close the output stream early.
|
||||||
|
BOS.reset();
|
||||||
|
FDOS.reset();
|
||||||
|
|
||||||
|
// Delete output file if there were errors.
|
||||||
|
if (Failed) {
|
||||||
|
if (Opts.OutputPath != "-")
|
||||||
|
sys::fs::remove(Opts.OutputPath);
|
||||||
|
if (!Opts.SplitDwarfOutput.empty() && Opts.SplitDwarfOutput != "-")
|
||||||
|
sys::fs::remove(Opts.SplitDwarfOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LLVMErrorHandler(void *UserData, const std::string &Message,
|
||||||
|
bool GenCrashDiag) {
|
||||||
|
DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
|
||||||
|
|
||||||
|
Diags.Report(diag::err_fe_error_backend) << Message;
|
||||||
|
|
||||||
|
// We cannot recover from llvm errors.
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
|
||||||
|
// Initialize targets and assembly printers/parsers.
|
||||||
|
InitializeAllTargetInfos();
|
||||||
|
InitializeAllTargetMCs();
|
||||||
|
InitializeAllAsmParsers();
|
||||||
|
|
||||||
|
// Construct our diagnostic client.
|
||||||
|
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
|
||||||
|
TextDiagnosticPrinter *DiagClient
|
||||||
|
= new TextDiagnosticPrinter(errs(), &*DiagOpts);
|
||||||
|
DiagClient->setPrefix("clang -cc1as");
|
||||||
|
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
|
||||||
|
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);
|
||||||
|
|
||||||
|
// Set an error handler, so that any LLVM backend diagnostics go through our
|
||||||
|
// error handler.
|
||||||
|
ScopedFatalErrorHandler FatalErrorHandler
|
||||||
|
(LLVMErrorHandler, static_cast<void*>(&Diags));
|
||||||
|
|
||||||
|
// Parse the arguments.
|
||||||
|
AssemblerInvocation Asm;
|
||||||
|
if (!AssemblerInvocation::CreateFromArgs(Asm, Argv, Diags))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (Asm.ShowHelp) {
|
||||||
|
std::unique_ptr<OptTable> Opts(driver::createDriverOptTable());
|
||||||
|
Opts->PrintHelp(llvm::outs(), "clang -cc1as [options] file...",
|
||||||
|
"Clang Integrated Assembler",
|
||||||
|
/*Include=*/driver::options::CC1AsOption, /*Exclude=*/0,
|
||||||
|
/*ShowAllAliases=*/false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Honor -version.
|
||||||
|
//
|
||||||
|
// FIXME: Use a better -version message?
|
||||||
|
if (Asm.ShowVersion) {
|
||||||
|
llvm::cl::PrintVersionMessage();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Honor -mllvm.
|
||||||
|
//
|
||||||
|
// FIXME: Remove this, one day.
|
||||||
|
if (!Asm.LLVMArgs.empty()) {
|
||||||
|
unsigned NumArgs = Asm.LLVMArgs.size();
|
||||||
|
auto Args = llvm::make_unique<const char*[]>(NumArgs + 2);
|
||||||
|
Args[0] = "clang (LLVM option parsing)";
|
||||||
|
for (unsigned i = 0; i != NumArgs; ++i)
|
||||||
|
Args[i + 1] = Asm.LLVMArgs[i].c_str();
|
||||||
|
Args[NumArgs + 1] = nullptr;
|
||||||
|
llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the invocation, unless there were parsing errors.
|
||||||
|
bool Failed = Diags.hasErrorOccurred() || ExecuteAssembler(Asm, Diags);
|
||||||
|
|
||||||
|
// If any timers were active but haven't been destroyed yet, print their
|
||||||
|
// results now.
|
||||||
|
TimerGroup::printAll(errs());
|
||||||
|
|
||||||
|
return !!Failed;
|
||||||
|
}
|
||||||
+120
@@ -0,0 +1,120 @@
|
|||||||
|
//===-- cc1as.h - Clang Assembler ----------------------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This is the entry point to the clang -cc1as functionality, which implements
|
||||||
|
// the direct interface to the LLVM MC based assembler.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
|
|
||||||
|
/// Helper class for representing a single invocation of the assembler.
|
||||||
|
struct AssemblerInvocation {
|
||||||
|
/// @name Target Options
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/// The name of the target triple to assemble for.
|
||||||
|
std::string Triple;
|
||||||
|
|
||||||
|
/// If given, the name of the target CPU to determine which instructions
|
||||||
|
/// are legal.
|
||||||
|
std::string CPU;
|
||||||
|
|
||||||
|
/// The list of target specific features to enable or disable -- this should
|
||||||
|
/// be a list of strings starting with '+' or '-'.
|
||||||
|
std::vector<std::string> Features;
|
||||||
|
|
||||||
|
/// The list of symbol definitions.
|
||||||
|
std::vector<std::string> SymbolDefs;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
/// @name Language Options
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
std::vector<std::string> IncludePaths;
|
||||||
|
unsigned NoInitialTextSection : 1;
|
||||||
|
unsigned SaveTemporaryLabels : 1;
|
||||||
|
unsigned GenDwarfForAssembly : 1;
|
||||||
|
unsigned RelaxELFRelocations : 1;
|
||||||
|
unsigned DwarfVersion;
|
||||||
|
std::string DwarfDebugFlags;
|
||||||
|
std::string DwarfDebugProducer;
|
||||||
|
std::string DebugCompilationDir;
|
||||||
|
std::map<const std::string, const std::string> DebugPrefixMap;
|
||||||
|
llvm::DebugCompressionType CompressDebugSections =
|
||||||
|
llvm::DebugCompressionType::None;
|
||||||
|
std::string MainFileName;
|
||||||
|
std::string SplitDwarfOutput;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
/// @name Frontend Options
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
std::string InputFile;
|
||||||
|
std::vector<std::string> LLVMArgs;
|
||||||
|
std::string OutputPath;
|
||||||
|
enum FileType {
|
||||||
|
FT_Asm, ///< Assembly (.s) output, transliterate mode.
|
||||||
|
FT_Null, ///< No output, for timing purposes.
|
||||||
|
FT_Obj ///< Object file output.
|
||||||
|
};
|
||||||
|
FileType OutputType;
|
||||||
|
unsigned ShowHelp : 1;
|
||||||
|
unsigned ShowVersion : 1;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
/// @name Transliterate Options
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
unsigned OutputAsmVariant;
|
||||||
|
unsigned ShowEncoding : 1;
|
||||||
|
unsigned ShowInst : 1;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
/// @name Assembler Options
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
unsigned RelaxAll : 1;
|
||||||
|
unsigned NoExecStack : 1;
|
||||||
|
unsigned FatalWarnings : 1;
|
||||||
|
unsigned IncrementalLinkerCompatible : 1;
|
||||||
|
unsigned EmbedBitcode : 1;
|
||||||
|
|
||||||
|
/// The name of the relocation model to use.
|
||||||
|
std::string RelocationModel;
|
||||||
|
|
||||||
|
/// The ABI targeted by the backend. Specified using -target-abi. Empty
|
||||||
|
/// otherwise.
|
||||||
|
std::string TargetABI;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
public:
|
||||||
|
AssemblerInvocation() {
|
||||||
|
Triple = "";
|
||||||
|
NoInitialTextSection = 0;
|
||||||
|
InputFile = "-";
|
||||||
|
OutputPath = "-";
|
||||||
|
OutputType = FT_Asm;
|
||||||
|
OutputAsmVariant = 0;
|
||||||
|
ShowInst = 0;
|
||||||
|
ShowEncoding = 0;
|
||||||
|
RelaxAll = 0;
|
||||||
|
NoExecStack = 0;
|
||||||
|
FatalWarnings = 0;
|
||||||
|
IncrementalLinkerCompatible = 0;
|
||||||
|
DwarfVersion = 0;
|
||||||
|
EmbedBitcode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateFromArgs(AssemblerInvocation &Res,
|
||||||
|
ArrayRef<const char *> Argv,
|
||||||
|
DiagnosticsEngine &Diags);
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ExecuteAssembler(AssemblerInvocation &Opts, DiagnosticsEngine &Diags);
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
// +build byollvm
|
||||||
|
|
||||||
|
#include <clang/Basic/DiagnosticOptions.h>
|
||||||
|
#include <clang/CodeGen/CodeGenAction.h>
|
||||||
|
#include <clang/Driver/Compilation.h>
|
||||||
|
#include <clang/Driver/Driver.h>
|
||||||
|
#include <clang/Frontend/CompilerInstance.h>
|
||||||
|
#include <clang/Frontend/CompilerInvocation.h>
|
||||||
|
#include <clang/Frontend/FrontendDiagnostic.h>
|
||||||
|
#include <clang/Frontend/TextDiagnosticPrinter.h>
|
||||||
|
#include <clang/FrontendTool/Utils.h>
|
||||||
|
#include <llvm/ADT/IntrusiveRefCntPtr.h>
|
||||||
|
#include <llvm/Option/Option.h>
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
using namespace clang;
|
||||||
|
|
||||||
|
#include "cc1as.h"
|
||||||
|
|
||||||
|
// This file provides C wrappers for the builtin tools cc1 and cc1as
|
||||||
|
// provided by Clang, and calls them as the driver would call them.
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
bool tinygo_clang_driver(int argc, char **argv) {
|
||||||
|
std::vector<const char*> args(argv, argv + argc);
|
||||||
|
|
||||||
|
// The compiler invocation needs a DiagnosticsEngine so it can report problems
|
||||||
|
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts = new clang::DiagnosticOptions();
|
||||||
|
clang::TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts);
|
||||||
|
clang::DiagnosticsEngine Diags(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs()), &*DiagOpts, &DiagnosticPrinter, false);
|
||||||
|
|
||||||
|
// Create the clang driver
|
||||||
|
clang::driver::Driver TheDriver(args[0], llvm::sys::getDefaultTargetTriple(), Diags);
|
||||||
|
|
||||||
|
// Create the set of actions to perform
|
||||||
|
std::unique_ptr<clang::driver::Compilation> C(TheDriver.BuildCompilation(args));
|
||||||
|
if (!C) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const clang::driver::JobList &Jobs = C->getJobs();
|
||||||
|
|
||||||
|
// There may be more than one job, for example for .S files
|
||||||
|
// (preprocessor + assembler).
|
||||||
|
for (auto Cmd : Jobs) {
|
||||||
|
// Select the tool: cc1 or cc1as.
|
||||||
|
const llvm::opt::ArgStringList &CCArgs = Cmd.getArguments();
|
||||||
|
|
||||||
|
if (strcmp(*CCArgs.data(), "-cc1") == 0) {
|
||||||
|
// This is the C frontend.
|
||||||
|
// Initialize a compiler invocation object from the clang (-cc1) arguments.
|
||||||
|
std::unique_ptr<clang::CompilerInstance> Clang(new clang::CompilerInstance());
|
||||||
|
bool success = clang::CompilerInvocation::CreateFromArgs(
|
||||||
|
Clang->getInvocation(),
|
||||||
|
const_cast<const char **>(CCArgs.data()),
|
||||||
|
const_cast<const char **>(CCArgs.data()) + CCArgs.size(),
|
||||||
|
Diags);
|
||||||
|
if (!success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the actual diagnostics engine.
|
||||||
|
Clang->createDiagnostics();
|
||||||
|
if (!Clang->hasDiagnostics()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the frontend actions.
|
||||||
|
success = ExecuteCompilerInvocation(Clang.get());
|
||||||
|
if (!success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (strcmp(*CCArgs.data(), "-cc1as") == 0) {
|
||||||
|
// This is the assembler frontend. Parse the arguments.
|
||||||
|
AssemblerInvocation Asm;
|
||||||
|
if (!AssemblerInvocation::CreateFromArgs(Asm, llvm::ArrayRef<const char*>(CCArgs).slice(1), Diags))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Execute the invocation, unless there were parsing errors.
|
||||||
|
bool failed = Diags.hasErrorOccurred() || ExecuteAssembler(Asm, Diags);
|
||||||
|
if (failed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Unknown tool, print the tool and exit.
|
||||||
|
fprintf(stderr, "unknown tool: %s\n", *CCArgs.data());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commands executed successfully.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
@@ -34,6 +34,12 @@ func init() {
|
|||||||
commands["ld.lld"] = append(commands["ld.lld"], "lld", "C:\\Program Files\\LLVM\\bin\\lld.exe")
|
commands["ld.lld"] = append(commands["ld.lld"], "lld", "C:\\Program Files\\LLVM\\bin\\lld.exe")
|
||||||
commands["wasm-ld"] = append(commands["wasm-ld"], "C:\\Program Files\\LLVM\\bin\\wasm-ld.exe")
|
commands["wasm-ld"] = append(commands["wasm-ld"], "C:\\Program Files\\LLVM\\bin\\wasm-ld.exe")
|
||||||
}
|
}
|
||||||
|
// Add the path to the llvm90 installed from ports
|
||||||
|
if runtime.GOOS == "freebsd" {
|
||||||
|
commands["clang"] = append(commands["clang"], "/usr/local/llvm90/bin/clang-9")
|
||||||
|
commands["ld.lld"] = append(commands["ld.lld"], "/usr/local/llvm90/bin/ld.lld")
|
||||||
|
commands["wasm-ld"] = append(commands["wasm-ld"], "/usr/local/llvm90/bin/wasm-ld")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func execCommand(cmdNames []string, args ...string) error {
|
func execCommand(cmdNames []string, args ...string) error {
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// +build byollvm
|
||||||
|
|
||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CXXFLAGS: -fno-rtti
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
bool tinygo_clang_driver(int argc, char **argv);
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// runCCompiler invokes a C compiler with the given arguments.
|
||||||
|
//
|
||||||
|
// This version invokes the built-in Clang when trying to run the Clang compiler.
|
||||||
|
func runCCompiler(command string, flags ...string) error {
|
||||||
|
switch command {
|
||||||
|
case "clang":
|
||||||
|
// Compile this with the internal Clang compiler.
|
||||||
|
headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||||
|
if headerPath == "" {
|
||||||
|
return errors.New("could not locate Clang headers")
|
||||||
|
}
|
||||||
|
flags = append(flags, "-I"+headerPath)
|
||||||
|
flags = append([]string{"tinygo:" + command}, flags...)
|
||||||
|
var cflag *C.char
|
||||||
|
buf := C.calloc(C.size_t(len(flags)), C.size_t(unsafe.Sizeof(cflag)))
|
||||||
|
cflags := (*[1 << 10]*C.char)(unsafe.Pointer(buf))[:len(flags):len(flags)]
|
||||||
|
for i, flag := range flags {
|
||||||
|
cflag := C.CString(flag)
|
||||||
|
cflags[i] = cflag
|
||||||
|
defer C.free(unsafe.Pointer(cflag))
|
||||||
|
}
|
||||||
|
ok := C.tinygo_clang_driver(C.int(len(flags)), (**C.char)(buf))
|
||||||
|
if !ok {
|
||||||
|
return errors.New("failed to compile using built-in clang")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
// Running some other compiler. Maybe it has been defined in the
|
||||||
|
// commands map (unlikely).
|
||||||
|
if cmdNames, ok := commands[command]; ok {
|
||||||
|
return execCommand(cmdNames, flags...)
|
||||||
|
}
|
||||||
|
// Alternatively, run the compiler directly.
|
||||||
|
cmd := exec.Command(command, flags...)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// +build !byollvm
|
||||||
|
|
||||||
|
package builder
|
||||||
|
|
||||||
|
// This file provides a way to a C compiler as an external command. See also:
|
||||||
|
// clang-external.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// runCCompiler invokes a C compiler with the given arguments.
|
||||||
|
//
|
||||||
|
// This version always runs the compiler as an external command.
|
||||||
|
func runCCompiler(command string, flags ...string) error {
|
||||||
|
if cmdNames, ok := commands[command]; ok {
|
||||||
|
return execCommand(cmdNames, flags...)
|
||||||
|
}
|
||||||
|
cmd := exec.Command(command, flags...)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
+1
-1
@@ -71,7 +71,7 @@ func GorootVersionString(goroot string) (string, error) {
|
|||||||
// various ways.
|
// various ways.
|
||||||
func getClangHeaderPath(TINYGOROOT string) string {
|
func getClangHeaderPath(TINYGOROOT string) string {
|
||||||
// Check whether we're running from the source directory.
|
// Check whether we're running from the source directory.
|
||||||
path := filepath.Join(TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")
|
path := filepath.Join(TINYGOROOT, "llvm-project", "clang", "lib", "Headers")
|
||||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,20 @@ func (e *MultiError) Error() string {
|
|||||||
return e.Errs[0].Error()
|
return e.Errs[0].Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newMultiError returns a *MultiError if there is more than one error, or
|
||||||
|
// returns that error directly when there is only one. Passing an empty slice
|
||||||
|
// will lead to a panic.
|
||||||
|
func newMultiError(errs []error) error {
|
||||||
|
switch len(errs) {
|
||||||
|
case 0:
|
||||||
|
panic("attempted to create empty MultiError")
|
||||||
|
case 1:
|
||||||
|
return errs[0]
|
||||||
|
default:
|
||||||
|
return &MultiError{errs}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// commandError is an error type to wrap os/exec.Command errors. This provides
|
// commandError is an error type to wrap os/exec.Command errors. This provides
|
||||||
// some more information regarding what went wrong while running a command.
|
// some more information regarding what went wrong while running a command.
|
||||||
type commandError struct {
|
type commandError struct {
|
||||||
|
|||||||
+30
-9
@@ -11,26 +11,33 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// convertELFFileToUF2File converts an ELF file to a UF2 file.
|
// 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.
|
// Read the .text segment.
|
||||||
targetAddress, data, err := extractROM(infile)
|
targetAddress, data, err := extractROM(infile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
return ioutil.WriteFile(outfile, output, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertBinToUF2 converts the binary bytes in input to UF2 formatted data.
|
// 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)
|
blocks := split(input, 256)
|
||||||
output := make([]byte, 0)
|
output := make([]byte, 0)
|
||||||
|
|
||||||
bl := newUF2Block(targetAddr)
|
bl, err := newUF2Block(targetAddr, uf2FamilyID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
bl.SetNumBlocks(len(blocks))
|
bl.SetNumBlocks(len(blocks))
|
||||||
|
|
||||||
for i := 0; i < len(blocks); i++ {
|
for i := 0; i < len(blocks); i++ {
|
||||||
@@ -41,7 +48,7 @@ func convertBinToUF2(input []byte, targetAddr uint32) ([]byte, int) {
|
|||||||
bl.IncrementAddress(bl.payloadSize)
|
bl.IncrementAddress(bl.payloadSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
return output, len(blocks)
|
return output, len(blocks), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -65,17 +72,31 @@ type uf2Block struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newUF2Block returns a new uf2Block struct that has been correctly populated
|
// 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,
|
return &uf2Block{magicStart0: uf2MagicStart0,
|
||||||
magicStart1: uf2MagicStart1,
|
magicStart1: uf2MagicStart1,
|
||||||
magicEnd: uf2MagicEnd,
|
magicEnd: uf2MagicEnd,
|
||||||
targetAddr: targetAddr,
|
targetAddr: targetAddr,
|
||||||
flags: 0x0,
|
flags: flags,
|
||||||
familyID: 0x0,
|
familyID: familyID,
|
||||||
payloadSize: 256,
|
payloadSize: 256,
|
||||||
data: make([]byte, 476),
|
data: make([]byte, 476),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
const (
|
||||||
|
flagFamilyIDPresent = 0x00002000
|
||||||
|
)
|
||||||
|
|
||||||
// Bytes converts the uf2Block to a slice of bytes that can be written to file.
|
// Bytes converts the uf2Block to a slice of bytes that can be written to file.
|
||||||
func (b *uf2Block) Bytes() []byte {
|
func (b *uf2Block) Bytes() []byte {
|
||||||
|
|||||||
+1
-3
@@ -22,11 +22,9 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
|
|||||||
func TestCGo(t *testing.T) {
|
func TestCGo(t *testing.T) {
|
||||||
var cflags = []string{"--target=armv6m-none-eabi"}
|
var cflags = []string{"--target=armv6m-none-eabi"}
|
||||||
|
|
||||||
for _, name := range []string{"basic", "errors", "types", "flags"} {
|
for _, name := range []string{"basic", "errors", "types", "flags", "const"} {
|
||||||
name := name // avoid a race condition
|
name := name // avoid a race condition
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// Read the AST in memory.
|
// Read the AST in memory.
|
||||||
path := filepath.Join("testdata", name+".go")
|
path := filepath.Join("testdata", name+".go")
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
|
|||||||
+25
-1
@@ -52,6 +52,13 @@ func parseConstExpr(t *tokenizer) (ast.Expr, *scanner.Error) {
|
|||||||
}
|
}
|
||||||
t.Next()
|
t.Next()
|
||||||
return expr, nil
|
return expr, nil
|
||||||
|
case token.IDENT:
|
||||||
|
expr := &ast.Ident{
|
||||||
|
NamePos: t.pos,
|
||||||
|
Name: "C." + t.value,
|
||||||
|
}
|
||||||
|
t.Next()
|
||||||
|
return expr, nil
|
||||||
case token.EOF:
|
case token.EOF:
|
||||||
return nil, &scanner.Error{
|
return nil, &scanner.Error{
|
||||||
Pos: t.fset.Position(t.pos),
|
Pos: t.fset.Position(t.pos),
|
||||||
@@ -133,8 +140,10 @@ func (t *tokenizer) Next() {
|
|||||||
if c == '.' {
|
if c == '.' {
|
||||||
hasDot = true
|
hasDot = true
|
||||||
}
|
}
|
||||||
if (c >= '0' && c <= '9') || c == '.' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') {
|
if c >= '0' && c <= '9' || c == '.' || c == '_' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' {
|
||||||
tokenLen = i + 1
|
tokenLen = i + 1
|
||||||
|
} else {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.value = t.buf[:tokenLen]
|
t.value = t.buf[:tokenLen]
|
||||||
@@ -150,6 +159,21 @@ func (t *tokenizer) Next() {
|
|||||||
t.value = strings.TrimRight(t.value, "uUlL")
|
t.value = strings.TrimRight(t.value, "uUlL")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
case c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_':
|
||||||
|
// Identifier. Find all remaining tokens that are part of this
|
||||||
|
// identifier.
|
||||||
|
tokenLen := len(t.buf)
|
||||||
|
for i, c := range t.buf {
|
||||||
|
if c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_' {
|
||||||
|
tokenLen = i + 1
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.value = t.buf[:tokenLen]
|
||||||
|
t.buf = t.buf[tokenLen:]
|
||||||
|
t.token = token.IDENT
|
||||||
|
return
|
||||||
case c == '"':
|
case c == '"':
|
||||||
// String constant. Find the first '"' character that is not
|
// String constant. Find the first '"' character that is not
|
||||||
// preceded by a backslash.
|
// preceded by a backslash.
|
||||||
|
|||||||
+2
-1
@@ -21,7 +21,7 @@ func TestParseConst(t *testing.T) {
|
|||||||
{`5)`, `error: 1:2: unexpected token )`},
|
{`5)`, `error: 1:2: unexpected token )`},
|
||||||
{" \t)", `error: 1:4: unexpected token )`},
|
{" \t)", `error: 1:4: unexpected token )`},
|
||||||
{`5.8f`, `5.8`},
|
{`5.8f`, `5.8`},
|
||||||
{`foo`, `error: 1:1: unexpected token ILLEGAL`}, // identifiers unimplemented
|
{`foo`, `C.foo`},
|
||||||
{``, `error: 1:1: empty constant`}, // empty constants not allowed in Go
|
{``, `error: 1:1: empty constant`}, // empty constants not allowed in Go
|
||||||
{`"foo"`, `"foo"`},
|
{`"foo"`, `"foo"`},
|
||||||
{`"a\\n"`, `"a\\n"`},
|
{`"a\\n"`, `"a\\n"`},
|
||||||
@@ -30,6 +30,7 @@ func TestParseConst(t *testing.T) {
|
|||||||
{`'a'`, `'a'`},
|
{`'a'`, `'a'`},
|
||||||
{`0b10`, `0b10`},
|
{`0b10`, `0b10`},
|
||||||
{`0x1234_5678`, `0x1234_5678`},
|
{`0x1234_5678`, `0x1234_5678`},
|
||||||
|
{`5 5`, `error: 1:3: unexpected token INT`}, // test for a bugfix
|
||||||
} {
|
} {
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
startPos := fset.AddFile("", -1, 1000).Pos(0)
|
startPos := fset.AddFile("", -1, 1000).Pos(0)
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ package cgo
|
|||||||
/*
|
/*
|
||||||
#cgo linux CFLAGS: -I/usr/lib/llvm-9/include
|
#cgo linux CFLAGS: -I/usr/lib/llvm-9/include
|
||||||
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@9/include
|
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@9/include
|
||||||
|
#cgo freebsd CFLAGS: -I/usr/local/llvm90/include
|
||||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-9/lib -lclang
|
#cgo linux LDFLAGS: -L/usr/lib/llvm-9/lib -lclang
|
||||||
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@9/lib -lclang -lffi
|
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@9/lib -lclang -lffi
|
||||||
|
#cgo freebsd LDFLAGS: -L/usr/local/llvm90/lib -lclang
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define foo 3
|
||||||
|
#define bar foo
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
const (
|
||||||
|
Foo = C.foo
|
||||||
|
Bar = C.bar
|
||||||
|
)
|
||||||
Vendored
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
const C.bar = C.foo
|
||||||
|
const C.foo = 3
|
||||||
|
|
||||||
|
type C.int16_t = int16
|
||||||
|
type C.int32_t = int32
|
||||||
|
type C.int64_t = int64
|
||||||
|
type C.int8_t = int8
|
||||||
|
type C.uint16_t = uint16
|
||||||
|
type C.uint32_t = uint32
|
||||||
|
type C.uint64_t = uint64
|
||||||
|
type C.uint8_t = uint8
|
||||||
|
type C.uintptr_t = uintptr
|
||||||
|
type C.char uint8
|
||||||
|
type C.int int32
|
||||||
|
type C.long int32
|
||||||
|
type C.longlong int64
|
||||||
|
type C.schar int8
|
||||||
|
type C.short int16
|
||||||
|
type C.uchar uint8
|
||||||
|
type C.uint uint32
|
||||||
|
type C.ulong uint32
|
||||||
|
type C.ulonglong uint64
|
||||||
|
type C.ushort uint16
|
||||||
@@ -65,6 +65,12 @@ func (c *Config) BuildTags() []string {
|
|||||||
return tags
|
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
|
// GC returns the garbage collection strategy in use on this platform. Valid
|
||||||
// values are "none", "leaking", and "conservative".
|
// values are "none", "leaking", and "conservative".
|
||||||
func (c *Config) GC() string {
|
func (c *Config) GC() string {
|
||||||
|
|||||||
+21
-1
@@ -43,9 +43,11 @@ type TargetSpec struct {
|
|||||||
FlashMethod string `json:"flash-method"`
|
FlashMethod string `json:"flash-method"`
|
||||||
FlashVolume string `json:"msd-volume-name"`
|
FlashVolume string `json:"msd-volume-name"`
|
||||||
FlashFilename string `json:"msd-firmware-name"`
|
FlashFilename string `json:"msd-firmware-name"`
|
||||||
|
UF2FamilyID string `json:"uf2-family-id"`
|
||||||
OpenOCDInterface string `json:"openocd-interface"`
|
OpenOCDInterface string `json:"openocd-interface"`
|
||||||
OpenOCDTarget string `json:"openocd-target"`
|
OpenOCDTarget string `json:"openocd-target"`
|
||||||
OpenOCDTransport string `json:"openocd-transport"`
|
OpenOCDTransport string `json:"openocd-transport"`
|
||||||
|
JLinkDevice string `json:"jlink-device"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyProperties copies all properties that are set in spec2 into itself.
|
// copyProperties copies all properties that are set in spec2 into itself.
|
||||||
@@ -109,6 +111,9 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
|
|||||||
if spec2.FlashFilename != "" {
|
if spec2.FlashFilename != "" {
|
||||||
spec.FlashFilename = spec2.FlashFilename
|
spec.FlashFilename = spec2.FlashFilename
|
||||||
}
|
}
|
||||||
|
if spec2.UF2FamilyID != "" {
|
||||||
|
spec.UF2FamilyID = spec2.UF2FamilyID
|
||||||
|
}
|
||||||
if spec2.OpenOCDInterface != "" {
|
if spec2.OpenOCDInterface != "" {
|
||||||
spec.OpenOCDInterface = spec2.OpenOCDInterface
|
spec.OpenOCDInterface = spec2.OpenOCDInterface
|
||||||
}
|
}
|
||||||
@@ -118,6 +123,9 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
|
|||||||
if spec2.OpenOCDTransport != "" {
|
if spec2.OpenOCDTransport != "" {
|
||||||
spec.OpenOCDTransport = 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
|
// 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 {
|
if len(tripleSplit) < 3 {
|
||||||
return nil, errors.New("expected a full LLVM target or a custom target in -target flag")
|
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]
|
goos := tripleSplit[2]
|
||||||
if strings.HasPrefix(goos, "darwin") {
|
if strings.HasPrefix(goos, "darwin") {
|
||||||
goos = "darwin"
|
goos = "darwin"
|
||||||
@@ -229,11 +245,12 @@ func LoadTarget(target string) (*TargetSpec, error) {
|
|||||||
"i386": "386",
|
"i386": "386",
|
||||||
"x86_64": "amd64",
|
"x86_64": "amd64",
|
||||||
"aarch64": "arm64",
|
"aarch64": "arm64",
|
||||||
|
"armv7": "arm",
|
||||||
}[tripleSplit[0]]
|
}[tripleSplit[0]]
|
||||||
if goarch == "" {
|
if goarch == "" {
|
||||||
goarch = tripleSplit[0]
|
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},
|
BuildTags: []string{goos, goarch},
|
||||||
Compiler: "clang",
|
Compiler: "clang",
|
||||||
Linker: "cc",
|
Linker: "cc",
|
||||||
|
CFlags: []string{"--target=" + triple},
|
||||||
GDB: "gdb",
|
GDB: "gdb",
|
||||||
PortReset: "false",
|
PortReset: "false",
|
||||||
FlashMethod: "native",
|
FlashMethod: "native",
|
||||||
@@ -259,11 +277,13 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
if goarch != runtime.GOARCH {
|
if goarch != runtime.GOARCH {
|
||||||
// Some educated guesses as to how to invoke helper programs.
|
// Some educated guesses as to how to invoke helper programs.
|
||||||
if goarch == "arm" && goos == "linux" {
|
if goarch == "arm" && goos == "linux" {
|
||||||
|
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/arm-linux-gnueabihf")
|
||||||
spec.Linker = "arm-linux-gnueabihf-gcc"
|
spec.Linker = "arm-linux-gnueabihf-gcc"
|
||||||
spec.GDB = "arm-linux-gnueabihf-gdb"
|
spec.GDB = "arm-linux-gnueabihf-gdb"
|
||||||
spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabihf"}
|
spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabihf"}
|
||||||
}
|
}
|
||||||
if goarch == "arm64" && goos == "linux" {
|
if goarch == "arm64" && goos == "linux" {
|
||||||
|
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/aarch64-linux-gnu")
|
||||||
spec.Linker = "aarch64-linux-gnu-gcc"
|
spec.Linker = "aarch64-linux-gnu-gcc"
|
||||||
spec.GDB = "aarch64-linux-gnu-gdb"
|
spec.GDB = "aarch64-linux-gnu-gdb"
|
||||||
spec.Emulator = []string{"qemu-aarch64", "-L", "/usr/aarch64-linux-gnu"}
|
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 {
|
func (c *Compiler) checkInstruction(inst llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
|
||||||
// check value properties
|
// check value properties
|
||||||
if err := c.checkValue(inst, types, specials); err != nil {
|
if err := c.checkValue(inst, types, specials); err != nil {
|
||||||
return fmt.Errorf("failed to validate value of instruction %q: %s", inst.Name(), err.Error())
|
return errorAt(inst, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// check operands
|
// check operands
|
||||||
for i := 0; i < inst.OperandsCount(); i++ {
|
for i := 0; i < inst.OperandsCount(); i++ {
|
||||||
if err := c.checkValue(inst.Operand(i), types, specials); err != nil {
|
if err := c.checkValue(inst.Operand(i), types, specials); err != nil {
|
||||||
return fmt.Errorf("failed to validate argument %d of instruction %q: %s", i, inst.Name(), err.Error())
|
return errorAt(inst, fmt.Sprintf("failed to validate operand %d of instruction %q: %s", i, inst.Name(), err.Error()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) checkBasicBlock(bb llvm.BasicBlock, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
|
func (c *Compiler) checkBasicBlock(bb llvm.BasicBlock, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) []error {
|
||||||
// check basic block value and type
|
// check basic block value and type
|
||||||
|
var errs []error
|
||||||
if err := c.checkValue(bb.AsValue(), types, specials); err != nil {
|
if err := c.checkValue(bb.AsValue(), types, specials); err != nil {
|
||||||
return fmt.Errorf("failed to validate value of basic block %s: %s", bb.AsValue().Name(), err.Error())
|
errs = append(errs, errorAt(bb.Parent(), fmt.Sprintf("failed to validate value of basic block %s: %v", bb.AsValue().Name(), err)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// check instructions
|
// check instructions
|
||||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||||
if err := c.checkInstruction(inst, types, specials); err != nil {
|
if err := c.checkInstruction(inst, types, specials); err != nil {
|
||||||
return fmt.Errorf("failed to validate basic block %q: %s", bb.AsValue().Name(), err.Error())
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) checkFunction(fn llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) error {
|
func (c *Compiler) checkFunction(fn llvm.Value, types map[llvm.Type]struct{}, specials map[llvm.TypeKind]llvm.Type) []error {
|
||||||
// check function value and type
|
// check function value and type
|
||||||
|
var errs []error
|
||||||
if err := c.checkValue(fn, types, specials); err != nil {
|
if err := c.checkValue(fn, types, specials); err != nil {
|
||||||
return fmt.Errorf("failed to validate value of function %s: %s", fn.Name(), err.Error())
|
errs = append(errs, fmt.Errorf("failed to validate value of function %s: %s", fn.Name(), err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// check basic blocks
|
// check basic blocks
|
||||||
for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||||
if err := c.checkBasicBlock(bb, types, specials); err != nil {
|
errs = append(errs, c.checkBasicBlock(bb, types, specials)...)
|
||||||
return fmt.Errorf("failed to validate basic block of function %s: %s", fn.Name(), err.Error())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) checkModule() error {
|
func (c *Compiler) checkModule() []error {
|
||||||
// check for any context mismatches
|
// check for any context mismatches
|
||||||
|
var errs []error
|
||||||
switch {
|
switch {
|
||||||
case c.mod.Context() == c.ctx:
|
case c.mod.Context() == c.ctx:
|
||||||
// this is correct
|
// this is correct
|
||||||
case c.mod.Context() == llvm.GlobalContext():
|
case c.mod.Context() == llvm.GlobalContext():
|
||||||
// somewhere we accidentally used the global context instead of a real context
|
// somewhere we accidentally used the global context instead of a real context
|
||||||
return errors.New("module uses global context")
|
errs = append(errs, errors.New("module uses global context"))
|
||||||
default:
|
default:
|
||||||
// we used some other context by accident
|
// we used some other context by accident
|
||||||
return fmt.Errorf("module uses context %v instead of the main context %v", c.mod.Context(), c.ctx)
|
errs = append(errs, fmt.Errorf("module uses context %v instead of the main context %v", c.mod.Context(), c.ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
types := map[llvm.Type]struct{}{}
|
types := map[llvm.Type]struct{}{}
|
||||||
specials := map[llvm.TypeKind]llvm.Type{}
|
specials := map[llvm.TypeKind]llvm.Type{}
|
||||||
for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||||
if err := c.checkFunction(fn, types, specials); err != nil {
|
errs = append(errs, c.checkFunction(fn, types, specials)...)
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for g := c.mod.FirstGlobal(); !g.IsNil(); g = llvm.NextGlobal(g) {
|
for g := c.mod.FirstGlobal(); !g.IsNil(); g = llvm.NextGlobal(g) {
|
||||||
if err := c.checkValue(g, types, specials); err != nil {
|
if err := c.checkValue(g, types, specials); err != nil {
|
||||||
return fmt.Errorf("failed to verify global %s of module: %s", g.Name(), err.Error())
|
errs = append(errs, fmt.Errorf("failed to verify global %s of module: %s", g.Name(), err.Error()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return errs
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-168
@@ -193,7 +193,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
GOOS: c.GOOS(),
|
GOOS: c.GOOS(),
|
||||||
GOROOT: goenv.Get("GOROOT"),
|
GOROOT: goenv.Get("GOROOT"),
|
||||||
GOPATH: goenv.Get("GOPATH"),
|
GOPATH: goenv.Get("GOPATH"),
|
||||||
CgoEnabled: true,
|
CgoEnabled: c.CgoEnabled(),
|
||||||
UseAllFiles: false,
|
UseAllFiles: false,
|
||||||
Compiler: "gc", // must be one of the recognized compilers
|
Compiler: "gc", // must be one of the recognized compilers
|
||||||
BuildTags: c.BuildTags(),
|
BuildTags: c.BuildTags(),
|
||||||
@@ -203,7 +203,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
GOOS: c.GOOS(),
|
GOOS: c.GOOS(),
|
||||||
GOROOT: goenv.Get("TINYGOROOT"),
|
GOROOT: goenv.Get("TINYGOROOT"),
|
||||||
GOPATH: overlayGopath,
|
GOPATH: overlayGopath,
|
||||||
CgoEnabled: true,
|
CgoEnabled: c.CgoEnabled(),
|
||||||
UseAllFiles: false,
|
UseAllFiles: false,
|
||||||
Compiler: "gc", // must be one of the recognized compilers
|
Compiler: "gc", // must be one of the recognized compilers
|
||||||
BuildTags: c.BuildTags(),
|
BuildTags: c.BuildTags(),
|
||||||
@@ -217,7 +217,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
path = path[len(tinygoPath+"/src/"):]
|
path = path[len(tinygoPath+"/src/"):]
|
||||||
}
|
}
|
||||||
switch path {
|
switch path {
|
||||||
case "machine", "os", "reflect", "runtime", "runtime/volatile", "sync", "testing", "internal/reflectlite":
|
case "machine", "os", "reflect", "runtime", "runtime/interrupt", "runtime/volatile", "sync", "testing", "internal/reflectlite":
|
||||||
return path
|
return path
|
||||||
default:
|
default:
|
||||||
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
||||||
@@ -251,13 +251,17 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
return []error{err}
|
return []error{err}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err = lprogram.Import(mainPath, wd)
|
_, err = lprogram.Import(mainPath, wd, token.Position{
|
||||||
|
Filename: "build command-line-arguments",
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []error{err}
|
return []error{err}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = lprogram.Import("runtime", "")
|
_, err = lprogram.Import("runtime", "", token.Position{
|
||||||
|
Filename: "build default import",
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []error{err}
|
return []error{err}
|
||||||
}
|
}
|
||||||
@@ -767,14 +771,6 @@ func (c *Compiler) attachDebugInfo(f *ir.Function) llvm.Metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata {
|
func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata {
|
||||||
if _, ok := c.difiles[filename]; !ok {
|
|
||||||
dir, file := filepath.Split(filename)
|
|
||||||
if dir != "" {
|
|
||||||
dir = dir[:len(dir)-1]
|
|
||||||
}
|
|
||||||
c.difiles[filename] = c.dibuilder.CreateFile(file, dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug info for this function.
|
// Debug info for this function.
|
||||||
diparams := make([]llvm.Metadata, 0, len(f.Params))
|
diparams := make([]llvm.Metadata, 0, len(f.Params))
|
||||||
for _, param := range f.Params {
|
for _, param := range f.Params {
|
||||||
@@ -788,7 +784,7 @@ func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix,
|
|||||||
difunc := c.dibuilder.CreateFunction(c.difiles[filename], llvm.DIFunction{
|
difunc := c.dibuilder.CreateFunction(c.difiles[filename], llvm.DIFunction{
|
||||||
Name: f.RelString(nil) + suffix,
|
Name: f.RelString(nil) + suffix,
|
||||||
LinkageName: f.LinkName() + suffix,
|
LinkageName: f.LinkName() + suffix,
|
||||||
File: c.difiles[filename],
|
File: c.getDIFile(filename),
|
||||||
Line: line,
|
Line: line,
|
||||||
Type: diFuncType,
|
Type: diFuncType,
|
||||||
LocalToUnit: true,
|
LocalToUnit: true,
|
||||||
@@ -801,21 +797,37 @@ func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix,
|
|||||||
return difunc
|
return difunc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getDIFile returns a DIFile metadata node for the given filename. It tries to
|
||||||
|
// use one that was already created, otherwise it falls back to creating a new
|
||||||
|
// one.
|
||||||
|
func (c *Compiler) getDIFile(filename string) llvm.Metadata {
|
||||||
|
if _, ok := c.difiles[filename]; !ok {
|
||||||
|
dir, file := filepath.Split(filename)
|
||||||
|
if dir != "" {
|
||||||
|
dir = dir[:len(dir)-1]
|
||||||
|
}
|
||||||
|
c.difiles[filename] = c.dibuilder.CreateFile(file, dir)
|
||||||
|
}
|
||||||
|
return c.difiles[filename]
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseFunc(frame *Frame) {
|
func (c *Compiler) parseFunc(frame *Frame) {
|
||||||
if c.DumpSSA() {
|
if c.DumpSSA() {
|
||||||
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
||||||
}
|
}
|
||||||
if !frame.fn.LLVMFn.IsDeclaration() {
|
if !frame.fn.LLVMFn.IsDeclaration() {
|
||||||
c.addError(frame.fn.Pos(), "function is already defined:"+frame.fn.LLVMFn.Name())
|
errValue := frame.fn.LLVMFn.Name() + " redeclared in this program"
|
||||||
|
fnPos := getPosition(frame.fn.LLVMFn)
|
||||||
|
if fnPos.IsValid() {
|
||||||
|
errValue += "\n\tprevious declaration at " + fnPos.String()
|
||||||
|
}
|
||||||
|
c.addError(frame.fn.Pos(), errValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !frame.fn.IsExported() {
|
if !frame.fn.IsExported() {
|
||||||
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
||||||
frame.fn.LLVMFn.SetUnnamedAddr(true)
|
frame.fn.LLVMFn.SetUnnamedAddr(true)
|
||||||
}
|
}
|
||||||
if frame.fn.IsInterrupt() && strings.HasPrefix(c.Triple(), "avr") {
|
|
||||||
frame.fn.LLVMFn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some functions have a pragma controlling the inlining level.
|
// Some functions have a pragma controlling the inlining level.
|
||||||
switch frame.fn.Inline() {
|
switch frame.fn.Inline() {
|
||||||
@@ -1291,12 +1303,16 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
|||||||
return c.emitAsmFull(frame, instr)
|
return c.emitAsmFull(frame, instr)
|
||||||
case strings.HasPrefix(name, "device/arm.SVCall"):
|
case strings.HasPrefix(name, "device/arm.SVCall"):
|
||||||
return c.emitSVCall(frame, instr.Args)
|
return c.emitSVCall(frame, instr.Args)
|
||||||
|
case strings.HasPrefix(name, "(device/riscv.CSR)."):
|
||||||
|
return c.emitCSROperation(frame, instr)
|
||||||
case strings.HasPrefix(name, "syscall.Syscall"):
|
case strings.HasPrefix(name, "syscall.Syscall"):
|
||||||
return c.emitSyscall(frame, instr)
|
return c.emitSyscall(frame, instr)
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||||
return c.emitVolatileLoad(frame, instr)
|
return c.emitVolatileLoad(frame, instr)
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||||
return c.emitVolatileStore(frame, instr)
|
return c.emitVolatileStore(frame, instr)
|
||||||
|
case name == "runtime/interrupt.New":
|
||||||
|
return c.emitInterruptGlobal(frame, instr)
|
||||||
}
|
}
|
||||||
|
|
||||||
targetFunc := c.ir.GetFunction(fn)
|
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)
|
val := c.getValue(frame, expr.X)
|
||||||
return c.parseMakeInterface(val, expr.X.Type(), expr.Pos()), nil
|
return c.parseMakeInterface(val, expr.X.Type(), expr.Pos()), nil
|
||||||
case *ssa.MakeMap:
|
case *ssa.MakeMap:
|
||||||
mapType := expr.Type().Underlying().(*types.Map)
|
return c.emitMakeMap(frame, expr)
|
||||||
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
|
|
||||||
case *ssa.MakeSlice:
|
case *ssa.MakeSlice:
|
||||||
sliceLen := c.getValue(frame, expr.Len)
|
sliceLen := c.getValue(frame, expr.Len)
|
||||||
sliceCap := c.getValue(frame, expr.Cap)
|
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).
|
// Emit object file (.o).
|
||||||
func (c *Compiler) EmitObject(path string) error {
|
func (c *Compiler) EmitObject(path string) error {
|
||||||
llvmBuf, err := c.machine.EmitToMemoryBuffer(c.mod, llvm.ObjectFile)
|
llvmBuf, err := c.machine.EmitToMemoryBuffer(c.mod, llvm.ObjectFile)
|
||||||
|
|||||||
+48
-3
@@ -14,6 +14,7 @@ package compiler
|
|||||||
// frames.
|
// frames.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"github.com/tinygo-org/tinygo/ir"
|
"github.com/tinygo-org/tinygo/ir"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
@@ -34,6 +35,40 @@ func (c *Compiler) deferInitFunc(frame *Frame) {
|
|||||||
c.builder.CreateStore(llvm.ConstPointerNull(deferType), frame.deferPtr)
|
c.builder.CreateStore(llvm.ConstPointerNull(deferType), frame.deferPtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isInLoop checks if there is a path from a basic block to itself.
|
||||||
|
func isInLoop(start *ssa.BasicBlock) bool {
|
||||||
|
// Use a breadth-first search to scan backwards through the block graph.
|
||||||
|
queue := []*ssa.BasicBlock{start}
|
||||||
|
checked := map[*ssa.BasicBlock]struct{}{}
|
||||||
|
|
||||||
|
for len(queue) > 0 {
|
||||||
|
// pop a block off of the queue
|
||||||
|
block := queue[len(queue)-1]
|
||||||
|
queue = queue[:len(queue)-1]
|
||||||
|
|
||||||
|
// Search through predecessors.
|
||||||
|
// Searching backwards means that this is pretty fast when the block is close to the start of the function.
|
||||||
|
// Defers are often placed near the start of the function.
|
||||||
|
for _, pred := range block.Preds {
|
||||||
|
if pred == start {
|
||||||
|
// cycle found
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := checked[pred]; ok {
|
||||||
|
// block already checked
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to queue and checked map
|
||||||
|
queue = append(queue, pred)
|
||||||
|
checked[pred] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// emitDefer emits a single defer instruction, to be run when this function
|
// emitDefer emits a single defer instruction, to be run when this function
|
||||||
// returns.
|
// returns.
|
||||||
func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) {
|
func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) {
|
||||||
@@ -127,12 +162,22 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) {
|
|||||||
deferFrame = c.builder.CreateInsertValue(deferFrame, value, i, "")
|
deferFrame = c.builder.CreateInsertValue(deferFrame, value, i, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put this struct in an alloca.
|
// Put this struct in an allocation.
|
||||||
alloca := c.builder.CreateAlloca(deferFrameType, "defer.alloca")
|
var alloca llvm.Value
|
||||||
c.builder.CreateStore(deferFrame, alloca)
|
if !isInLoop(instr.Block()) {
|
||||||
|
// This can safely use a stack allocation.
|
||||||
|
alloca = llvmutil.CreateEntryBlockAlloca(c.builder, deferFrameType, "defer.alloca")
|
||||||
|
} else {
|
||||||
|
// This may be hit a variable number of times, so use a heap allocation.
|
||||||
|
size := c.targetData.TypeAllocSize(deferFrameType)
|
||||||
|
sizeValue := llvm.ConstInt(c.uintptrType, size, false)
|
||||||
|
allocCall := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call")
|
||||||
|
alloca = c.builder.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc")
|
||||||
|
}
|
||||||
if c.NeedsStackObjects() {
|
if c.NeedsStackObjects() {
|
||||||
c.trackPointer(alloca)
|
c.trackPointer(alloca)
|
||||||
}
|
}
|
||||||
|
c.builder.CreateStore(deferFrame, alloca)
|
||||||
|
|
||||||
// Push it on top of the linked list by replacing deferPtr.
|
// Push it on top of the linked list by replacing deferPtr.
|
||||||
allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast")
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package compiler
|
package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go/scanner"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Compiler) makeError(pos token.Pos, msg string) types.Error {
|
func (c *Compiler) makeError(pos token.Pos, msg string) types.Error {
|
||||||
@@ -16,3 +20,42 @@ func (c *Compiler) makeError(pos token.Pos, msg string) types.Error {
|
|||||||
func (c *Compiler) addError(pos token.Pos, msg string) {
|
func (c *Compiler) addError(pos token.Pos, msg string) {
|
||||||
c.diagnostics = append(c.diagnostics, c.makeError(pos, msg))
|
c.diagnostics = append(c.diagnostics, c.makeError(pos, msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// errorAt returns an error value at the location of the instruction.
|
||||||
|
// The location information may not be complete as it depends on debug
|
||||||
|
// information in the IR.
|
||||||
|
func errorAt(inst llvm.Value, msg string) scanner.Error {
|
||||||
|
return scanner.Error{
|
||||||
|
Pos: getPosition(inst),
|
||||||
|
Msg: msg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPosition returns the position information for the given value, as far as
|
||||||
|
// it is available.
|
||||||
|
func getPosition(val llvm.Value) token.Position {
|
||||||
|
if !val.IsAInstruction().IsNil() {
|
||||||
|
loc := val.InstructionDebugLoc()
|
||||||
|
if loc.IsNil() {
|
||||||
|
return token.Position{}
|
||||||
|
}
|
||||||
|
file := loc.LocationScope().ScopeFile()
|
||||||
|
return token.Position{
|
||||||
|
Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
|
||||||
|
Line: int(loc.LocationLine()),
|
||||||
|
Column: int(loc.LocationColumn()),
|
||||||
|
}
|
||||||
|
} else if !val.IsAFunction().IsNil() {
|
||||||
|
loc := val.Subprogram()
|
||||||
|
if loc.IsNil() {
|
||||||
|
return token.Position{}
|
||||||
|
}
|
||||||
|
file := loc.ScopeFile()
|
||||||
|
return token.Position{
|
||||||
|
Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
|
||||||
|
Line: int(loc.SubprogramLine()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return token.Position{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ package compiler
|
|||||||
// scheduler, which runs in the background scheduling all coroutines.
|
// scheduler, which runs in the background scheduling all coroutines.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -283,7 +282,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
if use.IsConstant() && use.Opcode() == llvm.PtrToInt {
|
if use.IsConstant() && use.Opcode() == llvm.PtrToInt {
|
||||||
for _, call := range getUses(use) {
|
for _, call := range getUses(use) {
|
||||||
if call.IsACallInst().IsNil() || call.CalledValue().Name() != "runtime.makeGoroutine" {
|
if call.IsACallInst().IsNil() || call.CalledValue().Name() != "runtime.makeGoroutine" {
|
||||||
return false, errors.New("async function " + f.Name() + " incorrectly used in ptrtoint, expected runtime.makeGoroutine")
|
return false, errorAt(call, "async function incorrectly used in ptrtoint, expected runtime.makeGoroutine")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// This is a go statement. Do not mark the parent as async, as
|
// This is a go statement. Do not mark the parent as async, as
|
||||||
@@ -303,12 +302,19 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
// Not a call instruction. Maybe a store to a global? In any
|
// Not a call instruction. Maybe a store to a global? In any
|
||||||
// case, this requires support for async calls across function
|
// case, this requires support for async calls across function
|
||||||
// pointers which is not yet supported.
|
// pointers which is not yet supported.
|
||||||
return false, errors.New("async function " + f.Name() + " used as function pointer")
|
at := use
|
||||||
|
if use.IsAInstruction().IsNil() {
|
||||||
|
// The use might not be an instruction (for example, in the
|
||||||
|
// case of a const bitcast). Fall back to reporting the
|
||||||
|
// location of the function instead.
|
||||||
|
at = f
|
||||||
|
}
|
||||||
|
return false, errorAt(at, "async function "+f.Name()+" used as function pointer")
|
||||||
}
|
}
|
||||||
parent := use.InstructionParent().Parent()
|
parent := use.InstructionParent().Parent()
|
||||||
for i := 0; i < use.OperandsCount()-1; i++ {
|
for i := 0; i < use.OperandsCount()-1; i++ {
|
||||||
if use.Operand(i) == f {
|
if use.Operand(i) == f {
|
||||||
return false, errors.New("async function " + f.Name() + " used as function pointer in " + parent.Name())
|
return false, errorAt(use, "async function "+f.Name()+" used as function pointer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
worklist = append(worklist, parent)
|
worklist = append(worklist, parent)
|
||||||
@@ -805,7 +811,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||||||
llvm.ConstNull(c.ctx.TokenType()),
|
llvm.ConstNull(c.ctx.TokenType()),
|
||||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||||
}, "")
|
}, "")
|
||||||
wakeup := c.splitBasicBlock(inst, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup")
|
wakeup := llvmutil.SplitBasicBlock(c.builder, inst, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup")
|
||||||
c.builder.SetInsertPointBefore(inst)
|
c.builder.SetInsertPointBefore(inst)
|
||||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
|
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
|
||||||
@@ -906,12 +912,12 @@ func (c *Compiler) lowerMakeGoroutineCalls(sched bool) error {
|
|||||||
origFunc := ptrtointIn.Operand(0)
|
origFunc := ptrtointIn.Operand(0)
|
||||||
uses := getUses(goroutine)
|
uses := getUses(goroutine)
|
||||||
if len(uses) != 1 || uses[0].IsAIntToPtrInst().IsNil() {
|
if len(uses) != 1 || uses[0].IsAIntToPtrInst().IsNil() {
|
||||||
return errors.New("expected exactly 1 inttoptr use of runtime.makeGoroutine")
|
return errorAt(makeGoroutine, "expected exactly 1 inttoptr use of runtime.makeGoroutine")
|
||||||
}
|
}
|
||||||
inttoptrOut := uses[0]
|
inttoptrOut := uses[0]
|
||||||
uses = getUses(inttoptrOut)
|
uses = getUses(inttoptrOut)
|
||||||
if len(uses) != 1 || uses[0].IsACallInst().IsNil() {
|
if len(uses) != 1 || uses[0].IsACallInst().IsNil() {
|
||||||
return errors.New("expected exactly 1 call use of runtime.makeGoroutine bitcast")
|
return errorAt(inttoptrOut, "expected exactly 1 call use of runtime.makeGoroutine bitcast")
|
||||||
}
|
}
|
||||||
realCall := uses[0]
|
realCall := uses[0]
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0)
|
||||||
return c.builder.CreateCall(target, llvmArgs, ""), nil
|
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})
|
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every method is a *i16 reference indicating the signature of this method.
|
// Every method is a *i8 reference indicating the signature of this method.
|
||||||
methods := make([]llvm.Value, typ.Underlying().(*types.Interface).NumMethods())
|
methods := make([]llvm.Value, typ.Underlying().(*types.Interface).NumMethods())
|
||||||
for i := range methods {
|
for i := range methods {
|
||||||
method := typ.Underlying().(*types.Interface).Method(i)
|
method := typ.Underlying().(*types.Interface).Method(i)
|
||||||
methods[i] = c.getMethodSignature(method)
|
methods[i] = c.getMethodSignature(method)
|
||||||
}
|
}
|
||||||
|
|
||||||
value := llvm.ConstArray(methods[0].Type(), methods)
|
value := llvm.ConstArray(c.i8ptrType, methods)
|
||||||
global = llvm.AddGlobal(c.mod, value.Type(), typ.String()+"$interface")
|
global = llvm.AddGlobal(c.mod, value.Type(), typ.String()+"$interface")
|
||||||
global.SetInitializer(value)
|
global.SetInitializer(value)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
@@ -295,7 +295,7 @@ func (c *Compiler) getInterfaceMethodSet(typ *types.Named) llvm.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getMethodSignature returns a global variable which is a reference to an
|
// getMethodSignature returns a global variable which is a reference to an
|
||||||
// external *i16 indicating the indicating the signature of this method. It is
|
// external *i8 indicating the indicating the signature of this method. It is
|
||||||
// used during the interface lowering pass.
|
// used during the interface lowering pass.
|
||||||
func (c *Compiler) getMethodSignature(method *types.Func) llvm.Value {
|
func (c *Compiler) getMethodSignature(method *types.Func) llvm.Value {
|
||||||
signature := ir.MethodSignature(method)
|
signature := ir.MethodSignature(method)
|
||||||
|
|||||||
@@ -0,0 +1,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)
|
return llvmutil.EmitPointerUnpack(c.builder, c.mod, ptr, valueTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// splitBasicBlock splits a LLVM basic block into two parts. All instructions
|
|
||||||
// after afterInst are moved into a new basic block (created right after the
|
|
||||||
// current one) with the given name.
|
|
||||||
func (c *Compiler) splitBasicBlock(afterInst llvm.Value, insertAfter llvm.BasicBlock, name string) llvm.BasicBlock {
|
|
||||||
oldBlock := afterInst.InstructionParent()
|
|
||||||
newBlock := c.ctx.InsertBasicBlock(insertAfter, name)
|
|
||||||
var nextInstructions []llvm.Value // values to move
|
|
||||||
|
|
||||||
// Collect to-be-moved instructions.
|
|
||||||
inst := afterInst
|
|
||||||
for {
|
|
||||||
inst = llvm.NextInstruction(inst)
|
|
||||||
if inst.IsNil() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
nextInstructions = append(nextInstructions, inst)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move instructions.
|
|
||||||
c.builder.SetInsertPointAtEnd(newBlock)
|
|
||||||
for _, inst := range nextInstructions {
|
|
||||||
inst.RemoveFromParentAsInstruction()
|
|
||||||
c.builder.Insert(inst)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find PHI nodes to update.
|
|
||||||
var phiNodes []llvm.Value // PHI nodes to update
|
|
||||||
for bb := insertAfter.Parent().FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
|
||||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
|
||||||
if inst.IsAPHINode().IsNil() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
needsUpdate := false
|
|
||||||
incomingCount := inst.IncomingCount()
|
|
||||||
for i := 0; i < incomingCount; i++ {
|
|
||||||
if inst.IncomingBlock(i) == oldBlock {
|
|
||||||
needsUpdate = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !needsUpdate {
|
|
||||||
// PHI node has no incoming edge from the old block.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
phiNodes = append(phiNodes, inst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update PHI nodes.
|
|
||||||
for _, phi := range phiNodes {
|
|
||||||
c.builder.SetInsertPointBefore(phi)
|
|
||||||
newPhi := c.builder.CreatePHI(phi.Type(), "")
|
|
||||||
incomingCount := phi.IncomingCount()
|
|
||||||
incomingVals := make([]llvm.Value, incomingCount)
|
|
||||||
incomingBlocks := make([]llvm.BasicBlock, incomingCount)
|
|
||||||
for i := 0; i < incomingCount; i++ {
|
|
||||||
value := phi.IncomingValue(i)
|
|
||||||
block := phi.IncomingBlock(i)
|
|
||||||
if block == oldBlock {
|
|
||||||
block = newBlock
|
|
||||||
}
|
|
||||||
incomingVals[i] = value
|
|
||||||
incomingBlocks[i] = block
|
|
||||||
}
|
|
||||||
newPhi.AddIncoming(incomingVals, incomingBlocks)
|
|
||||||
phi.ReplaceAllUsesWith(newPhi)
|
|
||||||
phi.EraseFromParentAsInstruction()
|
|
||||||
}
|
|
||||||
|
|
||||||
return newBlock
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeGlobalArray creates a new LLVM global with the given name and integers as
|
// makeGlobalArray creates a new LLVM global with the given name and integers as
|
||||||
// contents, and returns the global.
|
// contents, and returns the global.
|
||||||
// Note that it is left with the default linkage etc., you should set
|
// Note that it is left with the default linkage etc., you should set
|
||||||
|
|||||||
@@ -94,3 +94,75 @@ func getLifetimeEndFunc(mod llvm.Module) llvm.Value {
|
|||||||
}
|
}
|
||||||
return fn
|
return fn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SplitBasicBlock splits a LLVM basic block into two parts. All instructions
|
||||||
|
// after afterInst are moved into a new basic block (created right after the
|
||||||
|
// current one) with the given name.
|
||||||
|
func SplitBasicBlock(builder llvm.Builder, afterInst llvm.Value, insertAfter llvm.BasicBlock, name string) llvm.BasicBlock {
|
||||||
|
oldBlock := afterInst.InstructionParent()
|
||||||
|
newBlock := afterInst.Type().Context().InsertBasicBlock(insertAfter, name)
|
||||||
|
var nextInstructions []llvm.Value // values to move
|
||||||
|
|
||||||
|
// Collect to-be-moved instructions.
|
||||||
|
inst := afterInst
|
||||||
|
for {
|
||||||
|
inst = llvm.NextInstruction(inst)
|
||||||
|
if inst.IsNil() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
nextInstructions = append(nextInstructions, inst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move instructions.
|
||||||
|
builder.SetInsertPointAtEnd(newBlock)
|
||||||
|
for _, inst := range nextInstructions {
|
||||||
|
inst.RemoveFromParentAsInstruction()
|
||||||
|
builder.Insert(inst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find PHI nodes to update.
|
||||||
|
var phiNodes []llvm.Value // PHI nodes to update
|
||||||
|
for bb := insertAfter.Parent().FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||||
|
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||||
|
if inst.IsAPHINode().IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
needsUpdate := false
|
||||||
|
incomingCount := inst.IncomingCount()
|
||||||
|
for i := 0; i < incomingCount; i++ {
|
||||||
|
if inst.IncomingBlock(i) == oldBlock {
|
||||||
|
needsUpdate = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !needsUpdate {
|
||||||
|
// PHI node has no incoming edge from the old block.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
phiNodes = append(phiNodes, inst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update PHI nodes.
|
||||||
|
for _, phi := range phiNodes {
|
||||||
|
builder.SetInsertPointBefore(phi)
|
||||||
|
newPhi := builder.CreatePHI(phi.Type(), "")
|
||||||
|
incomingCount := phi.IncomingCount()
|
||||||
|
incomingVals := make([]llvm.Value, incomingCount)
|
||||||
|
incomingBlocks := make([]llvm.BasicBlock, incomingCount)
|
||||||
|
for i := 0; i < incomingCount; i++ {
|
||||||
|
value := phi.IncomingValue(i)
|
||||||
|
block := phi.IncomingBlock(i)
|
||||||
|
if block == oldBlock {
|
||||||
|
block = newBlock
|
||||||
|
}
|
||||||
|
incomingVals[i] = value
|
||||||
|
incomingBlocks[i] = block
|
||||||
|
}
|
||||||
|
newPhi.AddIncoming(incomingVals, incomingBlocks)
|
||||||
|
phi.ReplaceAllUsesWith(newPhi)
|
||||||
|
phi.EraseFromParentAsInstruction()
|
||||||
|
}
|
||||||
|
|
||||||
|
return newBlock
|
||||||
|
}
|
||||||
|
|||||||
+62
-25
@@ -6,9 +6,45 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/ssa"
|
||||||
"tinygo.org/x/go-llvm"
|
"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) {
|
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
||||||
llvmValueType := c.getLLVMType(valueType)
|
llvmValueType := c.getLLVMType(valueType)
|
||||||
|
|
||||||
@@ -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.
|
// Do the lookup. How it is done depends on the key type.
|
||||||
var commaOkValue llvm.Value
|
var commaOkValue llvm.Value
|
||||||
|
keyType = keyType.Underlying()
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
params := []llvm.Value{m, key, mapValuePtr}
|
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, "")
|
commaOkValue = c.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||||
c.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
c.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
||||||
} else {
|
} else {
|
||||||
// Not trivially comparable using memcmp.
|
// Not trivially comparable using memcmp. Make it an interface instead.
|
||||||
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())
|
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
|
// 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.createRuntimeCall("hashmapBinarySet", params, "")
|
||||||
c.emitLifetimeEnd(keyPtr, keySize)
|
c.emitLifetimeEnd(keyPtr, keySize)
|
||||||
} else {
|
} 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)
|
c.emitLifetimeEnd(valuePtr, valueSize)
|
||||||
}
|
}
|
||||||
@@ -89,30 +139,17 @@ func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos toke
|
|||||||
c.emitLifetimeEnd(keyPtr, keySize)
|
c.emitLifetimeEnd(keyPtr, keySize)
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} 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
|
// Returns true if this key type does not contain strings, interfaces etc., so
|
||||||
|
|||||||
+27
-12
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
// Run the LLVM optimizer over the module.
|
// Run the LLVM optimizer over the module.
|
||||||
// The inliner can be disabled (if necessary) by passing 0 to the inlinerThreshold.
|
// The inliner can be disabled (if necessary) by passing 0 to the inlinerThreshold.
|
||||||
func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) error {
|
func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) []error {
|
||||||
builder := llvm.NewPassManagerBuilder()
|
builder := llvm.NewPassManagerBuilder()
|
||||||
defer builder.Dispose()
|
defer builder.Dispose()
|
||||||
builder.SetOptLevel(optLevel)
|
builder.SetOptLevel(optLevel)
|
||||||
@@ -25,9 +25,9 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
|
|
||||||
// run a check of all of our code
|
// run a check of all of our code
|
||||||
if c.VerifyIR() {
|
if c.VerifyIR() {
|
||||||
err := c.checkModule()
|
errs := c.checkModule()
|
||||||
if err != nil {
|
if errs != nil {
|
||||||
return err
|
return errs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +45,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
// Run some preparatory passes for the Go optimizer.
|
// Run some preparatory passes for the Go optimizer.
|
||||||
goPasses := llvm.NewPassManager()
|
goPasses := llvm.NewPassManager()
|
||||||
defer goPasses.Dispose()
|
defer goPasses.Dispose()
|
||||||
|
goPasses.AddGlobalDCEPass()
|
||||||
goPasses.AddGlobalOptimizerPass()
|
goPasses.AddGlobalOptimizerPass()
|
||||||
goPasses.AddConstantPropagationPass()
|
goPasses.AddConstantPropagationPass()
|
||||||
goPasses.AddAggressiveDCEPass()
|
goPasses.AddAggressiveDCEPass()
|
||||||
@@ -56,7 +57,15 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
transform.OptimizeStringToBytes(c.mod)
|
transform.OptimizeStringToBytes(c.mod)
|
||||||
transform.OptimizeAllocs(c.mod)
|
transform.OptimizeAllocs(c.mod)
|
||||||
transform.LowerInterfaces(c.mod)
|
transform.LowerInterfaces(c.mod)
|
||||||
c.LowerFuncValues()
|
|
||||||
|
errs := transform.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
|
// After interfaces are lowered, there are many more opportunities for
|
||||||
// interprocedural optimizations. To get them to work, function
|
// interprocedural optimizations. To get them to work, function
|
||||||
@@ -85,24 +94,30 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
|
|
||||||
err := c.LowerGoroutines()
|
err := c.LowerGoroutines()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return []error{err}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Must be run at any optimization level.
|
// Must be run at any optimization level.
|
||||||
transform.LowerInterfaces(c.mod)
|
transform.LowerInterfaces(c.mod)
|
||||||
c.LowerFuncValues()
|
if c.funcImplementation() == funcValueSwitch {
|
||||||
|
transform.LowerFuncValues(c.mod)
|
||||||
|
}
|
||||||
err := c.LowerGoroutines()
|
err := c.LowerGoroutines()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return []error{err}
|
||||||
|
}
|
||||||
|
errs := transform.LowerInterrupts(c.mod)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.VerifyIR() {
|
if c.VerifyIR() {
|
||||||
if err := c.checkModule(); err != nil {
|
if errs := c.checkModule(); errs != nil {
|
||||||
return err
|
return errs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := c.Verify(); err != nil {
|
if err := c.Verify(); err != nil {
|
||||||
return errors.New("optimizations caused a verification failure")
|
return []error{errors.New("optimizations caused a verification failure")}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sizeLevel >= 2 {
|
if sizeLevel >= 2 {
|
||||||
@@ -141,7 +156,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
hasGCPass = transform.MakeGCStackSlots(c.mod) || hasGCPass
|
hasGCPass = transform.MakeGCStackSlots(c.mod) || hasGCPass
|
||||||
if hasGCPass {
|
if hasGCPass {
|
||||||
if err := c.Verify(); err != nil {
|
if err := c.Verify(); err != nil {
|
||||||
return errors.New("GC pass caused a verification failure")
|
return []error{errors.New("GC pass caused a verification failure")}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,9 @@ func (s *StdSizes) Sizeof(T types.Type) int64 {
|
|||||||
if k == types.UnsafePointer {
|
if k == types.UnsafePointer {
|
||||||
return s.PtrSize
|
return s.PtrSize
|
||||||
}
|
}
|
||||||
|
if k == types.Invalid {
|
||||||
|
return 0 // only relevant when there is a type error somewhere
|
||||||
|
}
|
||||||
panic("unknown basic type: " + t.String())
|
panic("unknown basic type: " + t.String())
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
n := t.Len()
|
n := t.Len()
|
||||||
|
|||||||
+31
-1
@@ -60,16 +60,46 @@ func (c *Compiler) getGlobal(g *ssa.Global) llvm.Value {
|
|||||||
info := c.getGlobalInfo(g)
|
info := c.getGlobalInfo(g)
|
||||||
llvmGlobal := c.mod.NamedGlobal(info.linkName)
|
llvmGlobal := c.mod.NamedGlobal(info.linkName)
|
||||||
if llvmGlobal.IsNil() {
|
if llvmGlobal.IsNil() {
|
||||||
llvmType := c.getLLVMType(g.Type().(*types.Pointer).Elem())
|
typ := g.Type().(*types.Pointer).Elem()
|
||||||
|
llvmType := c.getLLVMType(typ)
|
||||||
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
||||||
if !info.extern {
|
if !info.extern {
|
||||||
llvmGlobal.SetInitializer(llvm.ConstNull(llvmType))
|
llvmGlobal.SetInitializer(llvm.ConstNull(llvmType))
|
||||||
llvmGlobal.SetLinkage(llvm.InternalLinkage)
|
llvmGlobal.SetLinkage(llvm.InternalLinkage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set alignment from the //go:align comment.
|
||||||
|
var alignInBits uint32
|
||||||
|
if info.align < 0 || info.align&(info.align-1) != 0 {
|
||||||
|
// Check for power-of-two (or 0).
|
||||||
|
// See: https://stackoverflow.com/a/108360
|
||||||
|
c.addError(g.Pos(), "global variable alignment must be a positive power of two")
|
||||||
|
} else {
|
||||||
|
// Set the alignment only when it is a power of two.
|
||||||
|
alignInBits = uint32(info.align) ^ uint32(info.align-1)
|
||||||
if info.align > c.targetData.ABITypeAlignment(llvmType) {
|
if info.align > c.targetData.ABITypeAlignment(llvmType) {
|
||||||
llvmGlobal.SetAlignment(info.align)
|
llvmGlobal.SetAlignment(info.align)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Debug() && !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
|
return llvmGlobal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -152,7 +152,7 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
|||||||
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH())
|
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH())
|
||||||
}
|
}
|
||||||
switch c.GOOS() {
|
switch c.GOOS() {
|
||||||
case "linux":
|
case "linux", "freebsd":
|
||||||
// Return values: r0, r1 uintptr, err Errno
|
// Return values: r0, r1 uintptr, err Errno
|
||||||
// Pseudocode:
|
// Pseudocode:
|
||||||
// var err uintptr
|
// var err uintptr
|
||||||
|
|||||||
@@ -10,5 +10,5 @@ require (
|
|||||||
go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45
|
go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45
|
||||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect
|
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect
|
||||||
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef
|
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef
|
||||||
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257
|
tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,3 +30,7 @@ tinygo.org/x/go-llvm v0.0.0-20191113125529-bad6d01809e8 h1:9Bfvso+tTVQg16UzOA614
|
|||||||
tinygo.org/x/go-llvm v0.0.0-20191113125529-bad6d01809e8/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
tinygo.org/x/go-llvm v0.0.0-20191113125529-bad6d01809e8/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257 h1:o8VDylrMN7gWemBMu8rEyuogKPhcLTdx5KrUAp9macc=
|
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257 h1:o8VDylrMN7gWemBMu8rEyuogKPhcLTdx5KrUAp9macc=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||||
|
tinygo.org/x/go-llvm v0.0.0-20191215173731-ad71f3d24aae h1:s8J5EyxCkHxXB08UI3gk9W9IS/ekizRvSX+PfZxnAB0=
|
||||||
|
tinygo.org/x/go-llvm v0.0.0-20191215173731-ad71f3d24aae/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||||
|
tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566 h1:a4y30bTf7U0zDA75v2PTL+XQ2OzJetj19gK8XwQpUNY=
|
||||||
|
tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ var Keys = []string{
|
|||||||
"GOROOT",
|
"GOROOT",
|
||||||
"GOPATH",
|
"GOPATH",
|
||||||
"GOCACHE",
|
"GOCACHE",
|
||||||
|
"CGO_ENABLED",
|
||||||
"TINYGOROOT",
|
"TINYGOROOT",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +58,13 @@ func Get(name string) string {
|
|||||||
panic("could not find cache dir: " + err.Error())
|
panic("could not find cache dir: " + err.Error())
|
||||||
}
|
}
|
||||||
return filepath.Join(dir, "tinygo")
|
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":
|
case "TINYGOROOT":
|
||||||
return sourceDir()
|
return sourceDir()
|
||||||
default:
|
default:
|
||||||
|
|||||||
+67
-16
@@ -125,7 +125,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
}
|
}
|
||||||
indices[i] = uint32(operand.Value().ZExtValue())
|
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() {
|
if result.Type() != inst.Type() {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: gep: type does not match")
|
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 {
|
if elementCount == 1 {
|
||||||
fr.locals[resultInst] = result
|
fr.locals[resultInst] = result
|
||||||
} else {
|
} 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":
|
case callee.Name() == "runtime.hashmapMake":
|
||||||
// create a map
|
// create a map
|
||||||
@@ -278,24 +285,49 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
}
|
}
|
||||||
case callee.Name() == "runtime.hashmapStringSet":
|
case callee.Name() == "runtime.hashmapStringSet":
|
||||||
// set a string key in the map
|
// set a string key in the map
|
||||||
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil, fr.errorAt(inst, "could not update map with string key")
|
|
||||||
}
|
|
||||||
// "key" is a Go string value, which in the TinyGo calling convention is split up
|
|
||||||
// into separate pointer and length parameters.
|
|
||||||
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
||||||
keyLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
keyLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
||||||
valPtr := fr.getLocal(inst.Operand(3)).(*LocalValue)
|
valPtr := fr.getLocal(inst.Operand(3)).(*LocalValue)
|
||||||
|
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
||||||
|
if !ok || !keyBuf.IsConstant() || !keyLen.IsConstant() || !valPtr.IsConstant() {
|
||||||
|
// The mapassign operation could not be done at compile
|
||||||
|
// time. Do it at runtime instead.
|
||||||
|
m := fr.getLocal(inst.Operand(0)).Value()
|
||||||
|
fr.markDirty(m)
|
||||||
|
llvmParams := []llvm.Value{
|
||||||
|
m, // *runtime.hashmap
|
||||||
|
fr.getLocal(inst.Operand(1)).Value(), // key.ptr
|
||||||
|
fr.getLocal(inst.Operand(2)).Value(), // key.len
|
||||||
|
fr.getLocal(inst.Operand(3)).Value(), // value (unsafe.Pointer)
|
||||||
|
fr.getLocal(inst.Operand(4)).Value(), // context
|
||||||
|
fr.getLocal(inst.Operand(5)).Value(), // parentHandle
|
||||||
|
}
|
||||||
|
fr.builder.CreateCall(callee, llvmParams, "")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// "key" is a Go string value, which in the TinyGo calling convention is split up
|
||||||
|
// into separate pointer and length parameters.
|
||||||
m.PutString(keyBuf, keyLen, valPtr)
|
m.PutString(keyBuf, keyLen, valPtr)
|
||||||
case callee.Name() == "runtime.hashmapBinarySet":
|
case callee.Name() == "runtime.hashmapBinarySet":
|
||||||
// set a binary (int etc.) key in the map
|
// set a binary (int etc.) key in the map
|
||||||
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil, fr.errorAt(inst, "could not update map")
|
|
||||||
}
|
|
||||||
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
||||||
valPtr := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
valPtr := fr.getLocal(inst.Operand(2)).(*LocalValue)
|
||||||
|
m, ok := fr.getLocal(inst.Operand(0)).(*MapValue)
|
||||||
|
if !ok || !keyBuf.IsConstant() || !valPtr.IsConstant() {
|
||||||
|
// The mapassign operation could not be done at compile
|
||||||
|
// time. Do it at runtime instead.
|
||||||
|
m := fr.getLocal(inst.Operand(0)).Value()
|
||||||
|
fr.markDirty(m)
|
||||||
|
llvmParams := []llvm.Value{
|
||||||
|
m, // *runtime.hashmap
|
||||||
|
fr.getLocal(inst.Operand(1)).Value(), // key
|
||||||
|
fr.getLocal(inst.Operand(2)).Value(), // value
|
||||||
|
fr.getLocal(inst.Operand(3)).Value(), // context
|
||||||
|
fr.getLocal(inst.Operand(4)).Value(), // parentHandle
|
||||||
|
}
|
||||||
|
fr.builder.CreateCall(callee, llvmParams, "")
|
||||||
|
continue
|
||||||
|
}
|
||||||
m.PutBinary(keyBuf, valPtr)
|
m.PutBinary(keyBuf, valPtr)
|
||||||
case callee.Name() == "runtime.stringConcat":
|
case callee.Name() == "runtime.stringConcat":
|
||||||
// adding two strings together
|
// adding two strings together
|
||||||
@@ -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,
|
// a bitcast of the original array instead of the GEP,
|
||||||
// which breaks our assumptions.
|
// which breaks our assumptions.
|
||||||
// Re-add this GEP, in the hope that it it is then of the correct type...
|
// Re-add this GEP, in the hope that it it is then of the correct type...
|
||||||
dstArray = dstArray.GetElementPtr([]uint32{0, 0}).(*LocalValue)
|
dstArrayValue, err := dstArray.GetElementPtr([]uint32{0, 0})
|
||||||
srcArray = srcArray.GetElementPtr([]uint32{0, 0}).(*LocalValue)
|
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 {
|
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")
|
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?")
|
return nil, nil, fr.errorAt(inst, "interp: trying to copy a slice with negative length?")
|
||||||
}
|
}
|
||||||
for i := int64(0); i < length; i++ {
|
for i := int64(0); i < length; i++ {
|
||||||
|
var err error
|
||||||
// *dst = *src
|
// *dst = *src
|
||||||
dstArray.Store(srcArray.Load())
|
dstArray.Store(srcArray.Load())
|
||||||
// dst++
|
// 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++
|
// 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":
|
case callee.Name() == "runtime.stringToBytes":
|
||||||
// convert a string to a []byte
|
// 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)}
|
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
|
||||||
case callee.Name() == "llvm.dbg.value":
|
case callee.Name() == "llvm.dbg.value":
|
||||||
// do nothing
|
// do nothing
|
||||||
|
case strings.HasPrefix(callee.Name(), "llvm.lifetime."):
|
||||||
|
// do nothing
|
||||||
case callee.Name() == "runtime.trackPointer":
|
case callee.Name() == "runtime.trackPointer":
|
||||||
// do nothing
|
// do nothing
|
||||||
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ func TestInterp(t *testing.T) {
|
|||||||
"basic",
|
"basic",
|
||||||
"slice-copy",
|
"slice-copy",
|
||||||
"consteval",
|
"consteval",
|
||||||
|
"map",
|
||||||
} {
|
} {
|
||||||
name := name // make tc local to this closure
|
name := name // make tc local to this closure
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
|||||||
+13
-8
@@ -1,6 +1,8 @@
|
|||||||
package interp
|
package interp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,22 +41,25 @@ type sideEffectResult struct {
|
|||||||
// returns whether this function has side effects and if it does, which globals
|
// returns whether this function has side effects and if it does, which globals
|
||||||
// it mentions anywhere in this function or any called functions.
|
// it mentions anywhere in this function or any called functions.
|
||||||
func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) {
|
func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) {
|
||||||
switch fn.Name() {
|
name := fn.Name()
|
||||||
case "runtime.alloc":
|
switch {
|
||||||
|
case name == "runtime.alloc":
|
||||||
// Cannot be scanned but can be interpreted.
|
// Cannot be scanned but can be interpreted.
|
||||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||||
case "runtime.nanotime":
|
case name == "runtime.nanotime":
|
||||||
// Fixed value at compile time.
|
// Fixed value at compile time.
|
||||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||||
case "runtime._panic":
|
case name == "runtime._panic":
|
||||||
return &sideEffectResult{severity: sideEffectLimited}, nil
|
return &sideEffectResult{severity: sideEffectLimited}, nil
|
||||||
case "runtime.interfaceImplements":
|
case name == "runtime.interfaceImplements":
|
||||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||||
case "runtime.sliceCopy":
|
case name == "runtime.sliceCopy":
|
||||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||||
case "runtime.trackPointer":
|
case name == "runtime.trackPointer":
|
||||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||||
case "llvm.dbg.value":
|
case name == "llvm.dbg.value":
|
||||||
|
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||||
|
case strings.HasPrefix(name, "llvm.lifetime."):
|
||||||
return &sideEffectResult{severity: sideEffectNone}, nil
|
return &sideEffectResult{severity: sideEffectNone}, nil
|
||||||
}
|
}
|
||||||
if fn.IsDeclaration() {
|
if fn.IsDeclaration() {
|
||||||
|
|||||||
Vendored
+76
@@ -0,0 +1,76 @@
|
|||||||
|
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||||
|
target triple = "armv6m-none-eabi"
|
||||||
|
|
||||||
|
%runtime._string = type { i8*, i32 }
|
||||||
|
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
|
||||||
|
|
||||||
|
@main.m = global %runtime.hashmap* null
|
||||||
|
@main.binaryMap = global %runtime.hashmap* null
|
||||||
|
@main.stringMap = global %runtime.hashmap* null
|
||||||
|
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
|
||||||
|
|
||||||
|
declare %runtime.hashmap* @runtime.hashmapMake(i8, i8, i32, i8* %context, i8* %parentHandle)
|
||||||
|
declare void @runtime.hashmapBinarySet(%runtime.hashmap*, i8*, i8*, i8* %context, i8* %parentHandle)
|
||||||
|
declare void @runtime.hashmapStringSet(%runtime.hashmap*, i8*, i32, i8*, i8* %context, i8* %parentHandle)
|
||||||
|
declare void @llvm.lifetime.end.p0i8(i64, i8*)
|
||||||
|
declare void @llvm.lifetime.start.p0i8(i64, i8*)
|
||||||
|
|
||||||
|
define void @runtime.initAll() unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @main.init(i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
; Test that hashmap optimizations generally work (even with lifetimes).
|
||||||
|
%hashmap.key = alloca i8
|
||||||
|
%hashmap.value = alloca %runtime._string
|
||||||
|
%0 = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 8, i32 1, i8* undef, i8* null)
|
||||||
|
%hashmap.value.bitcast = bitcast %runtime._string* %hashmap.value to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 8, i8* %hashmap.value.bitcast)
|
||||||
|
store %runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string* %hashmap.value
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 1, i8* %hashmap.key)
|
||||||
|
store i8 1, i8* %hashmap.key
|
||||||
|
call void @runtime.hashmapBinarySet(%runtime.hashmap* %0, i8* %hashmap.key, i8* %hashmap.value.bitcast, i8* undef, i8* null)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 1, i8* %hashmap.key)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 8, i8* %hashmap.value.bitcast)
|
||||||
|
store %runtime.hashmap* %0, %runtime.hashmap** @main.m
|
||||||
|
|
||||||
|
; Other tests, that can be done in a separate function.
|
||||||
|
call void @main.testNonConstantBinarySet()
|
||||||
|
call void @main.testNonConstantStringSet()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Test that a map loaded from a global can still be used for mapassign
|
||||||
|
; operations (with binary keys).
|
||||||
|
define internal void @main.testNonConstantBinarySet() {
|
||||||
|
%hashmap.key = alloca i8
|
||||||
|
%hashmap.value = alloca i8
|
||||||
|
; Create hashmap from global. This breaks the normal hashmapBinarySet
|
||||||
|
; optimization, to test the fallback.
|
||||||
|
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 1, i8 1, i32 1, i8* undef, i8* null)
|
||||||
|
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.binaryMap
|
||||||
|
%map = load %runtime.hashmap*, %runtime.hashmap** @main.binaryMap
|
||||||
|
; Do the binary set to the newly loaded map.
|
||||||
|
store i8 1, i8* %hashmap.key
|
||||||
|
store i8 2, i8* %hashmap.value
|
||||||
|
call void @runtime.hashmapBinarySet(%runtime.hashmap* %map, i8* %hashmap.key, i8* %hashmap.value, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Test that a map loaded from a global can still be used for mapassign
|
||||||
|
; operations (with string keys).
|
||||||
|
define internal void @main.testNonConstantStringSet() {
|
||||||
|
%hashmap.value = alloca i8
|
||||||
|
; Create hashmap from global. This breaks the normal hashmapStringSet
|
||||||
|
; optimization, to test the fallback.
|
||||||
|
%map.new = call %runtime.hashmap* @runtime.hashmapMake(i8 8, i8 1, i32 1, i8* undef, i8* null)
|
||||||
|
store %runtime.hashmap* %map.new, %runtime.hashmap** @main.stringMap
|
||||||
|
%map = load %runtime.hashmap*, %runtime.hashmap** @main.stringMap
|
||||||
|
; Do the string set to the newly loaded map.
|
||||||
|
store i8 2, i8* %hashmap.value
|
||||||
|
call void @runtime.hashmapStringSet(%runtime.hashmap* %map, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7, i8* %hashmap.value, i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
Vendored
+28
@@ -0,0 +1,28 @@
|
|||||||
|
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||||
|
target triple = "armv6m-none-eabi"
|
||||||
|
|
||||||
|
%runtime.hashmap = type { %runtime.hashmap*, i8*, i32, i8, i8, i8 }
|
||||||
|
%runtime._string = type { i8*, i32 }
|
||||||
|
|
||||||
|
@main.m = local_unnamed_addr global %runtime.hashmap* @"main$map"
|
||||||
|
@main.binaryMap = local_unnamed_addr global %runtime.hashmap* @"main$map.4"
|
||||||
|
@main.stringMap = local_unnamed_addr global %runtime.hashmap* @"main$map.6"
|
||||||
|
@main.init.string = internal unnamed_addr constant [7 x i8] c"CONNECT"
|
||||||
|
@"main$mapbucket" = internal unnamed_addr global { [8 x i8], i8*, [8 x i8], [8 x %runtime._string] } { [8 x i8] c"\04\00\00\00\00\00\00\00", i8* null, [8 x i8] c"\01\00\00\00\00\00\00\00", [8 x %runtime._string] [%runtime._string { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7 }, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer, %runtime._string zeroinitializer] }
|
||||||
|
@"main$map" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* getelementptr inbounds ({ [8 x i8], i8*, [8 x i8], [8 x %runtime._string] }, { [8 x i8], i8*, [8 x i8], [8 x %runtime._string] }* @"main$mapbucket", i32 0, i32 0, i32 0), i32 1, i8 1, i8 8, i8 0 }
|
||||||
|
@"main$alloca.2" = internal global i8 1
|
||||||
|
@"main$alloca.3" = internal global i8 2
|
||||||
|
@"main$map.4" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* null, i32 0, i8 1, i8 1, i8 0 }
|
||||||
|
@"main$alloca.5" = internal global i8 2
|
||||||
|
@"main$map.6" = internal unnamed_addr global %runtime.hashmap { %runtime.hashmap* null, i8* null, i32 0, i8 8, i8 1, i8 0 }
|
||||||
|
|
||||||
|
declare void @runtime.hashmapBinarySet(%runtime.hashmap*, i8*, i8*, i8*, i8*) local_unnamed_addr
|
||||||
|
|
||||||
|
declare void @runtime.hashmapStringSet(%runtime.hashmap*, i8*, i32, i8*, i8*, i8*) local_unnamed_addr
|
||||||
|
|
||||||
|
define void @runtime.initAll() unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @runtime.hashmapBinarySet(%runtime.hashmap* @"main$map.4", i8* @"main$alloca.2", i8* @"main$alloca.3", i8* undef, i8* null)
|
||||||
|
call void @runtime.hashmapStringSet(%runtime.hashmap* @"main$map.6", i8* getelementptr inbounds ([7 x i8], [7 x i8]* @main.init.string, i32 0, i32 0), i32 7, i8* @"main$alloca.5", i8* undef, i8* null)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
+5
-1
@@ -24,7 +24,11 @@ func getStringBytes(strPtr Value, strLen llvm.Value) []byte {
|
|||||||
}
|
}
|
||||||
buf := make([]byte, strLen.ZExtValue())
|
buf := make([]byte, strLen.ZExtValue())
|
||||||
for i := range buf {
|
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())
|
buf[i] = byte(c.ZExtValue())
|
||||||
}
|
}
|
||||||
return buf
|
return buf
|
||||||
|
|||||||
+10
-7
@@ -3,6 +3,7 @@ package interp
|
|||||||
// This file provides a litte bit of abstraction around LLVM values.
|
// This file provides a litte bit of abstraction around LLVM values.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
@@ -16,7 +17,7 @@ type Value interface {
|
|||||||
IsConstant() bool // returns true if this value is a constant value
|
IsConstant() bool // returns true if this value is a constant value
|
||||||
Load() llvm.Value // dereference a pointer
|
Load() llvm.Value // dereference a pointer
|
||||||
Store(llvm.Value) // store to a pointer
|
Store(llvm.Value) // store to a pointer
|
||||||
GetElementPtr([]uint32) Value // returns an interior pointer
|
GetElementPtr([]uint32) (Value, error) // returns an interior pointer
|
||||||
String() string // string representation, for debugging
|
String() string // string representation, for debugging
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,21 +98,21 @@ func (v *LocalValue) Store(value llvm.Value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetElementPtr returns a GEP when the underlying value is of pointer type.
|
// 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() {
|
if !v.Underlying.IsAGlobalVariable().IsNil() {
|
||||||
int32Type := v.Underlying.Type().Context().Int32Type()
|
int32Type := v.Underlying.Type().Context().Int32Type()
|
||||||
gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices))
|
gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices))
|
||||||
return &LocalValue{v.Eval, gep}
|
return &LocalValue{v.Eval, gep}, nil
|
||||||
}
|
}
|
||||||
if !v.Underlying.IsAConstantExpr().IsNil() {
|
if !v.Underlying.IsAConstantExpr().IsNil() {
|
||||||
switch v.Underlying.Opcode() {
|
switch v.Underlying.Opcode() {
|
||||||
case llvm.GetElementPtr, llvm.IntToPtr, llvm.BitCast:
|
case llvm.GetElementPtr, llvm.IntToPtr, llvm.BitCast:
|
||||||
int32Type := v.Underlying.Type().Context().Int32Type()
|
int32Type := v.Underlying.Type().Context().Int32Type()
|
||||||
llvmIndices := getLLVMIndices(int32Type, indices)
|
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
|
// 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)
|
// GetElementPtr panics: maps are of reference type so their (interior)
|
||||||
// addresses cannot be calculated.
|
// addresses cannot be calculated.
|
||||||
func (v *MapValue) GetElementPtr(indices []uint32) Value {
|
func (v *MapValue) GetElementPtr(indices []uint32) (Value, error) {
|
||||||
panic("interp: GEP on a map")
|
return nil, errors.New("interp: GEP on a map")
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutString does a map assign operation, assuming that the map is of type
|
// PutString does a map assign operation, assuming that the map is of type
|
||||||
@@ -367,11 +368,13 @@ func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !keyPtr.Underlying.IsAConstantExpr().IsNil() {
|
||||||
if keyPtr.Underlying.Opcode() == llvm.BitCast {
|
if keyPtr.Underlying.Opcode() == llvm.BitCast {
|
||||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||||
} else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr {
|
} else if keyPtr.Underlying.Opcode() == llvm.GetElementPtr {
|
||||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
key := keyPtr.Load()
|
key := keyPtr.Load()
|
||||||
if v.KeyType.IsNil() {
|
if v.KeyType.IsNil() {
|
||||||
v.KeyType = key.Type()
|
v.KeyType = key.Type()
|
||||||
|
|||||||
@@ -29,11 +29,10 @@ type Function struct {
|
|||||||
*ssa.Function
|
*ssa.Function
|
||||||
LLVMFn llvm.Value
|
LLVMFn llvm.Value
|
||||||
module string // go:wasm-module
|
module string // go:wasm-module
|
||||||
linkName string // go:linkname, go:export, go:interrupt
|
linkName string // go:linkname, go:export
|
||||||
exported bool // go:export
|
exported bool // go:export
|
||||||
nobounds bool // go:nobounds
|
nobounds bool // go:nobounds
|
||||||
flag bool // used by dead code elimination
|
flag bool // used by dead code elimination
|
||||||
interrupt bool // go:interrupt
|
|
||||||
inline InlineType // go:inline
|
inline InlineType // go:inline
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,18 +242,6 @@ func (f *Function) parsePragmas() {
|
|||||||
f.inline = InlineHint
|
f.inline = InlineHint
|
||||||
case "//go:noinline":
|
case "//go:noinline":
|
||||||
f.inline = InlineNone
|
f.inline = InlineNone
|
||||||
case "//go:interrupt":
|
|
||||||
if len(parts) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
name := parts[1]
|
|
||||||
if strings.HasSuffix(name, "_vect") {
|
|
||||||
// AVR vector naming
|
|
||||||
name = "__vector_" + name[:len(name)-5]
|
|
||||||
}
|
|
||||||
f.linkName = name
|
|
||||||
f.exported = true
|
|
||||||
f.interrupt = true
|
|
||||||
case "//go:linkname":
|
case "//go:linkname":
|
||||||
if len(parts) != 3 || parts[1] != f.Name() {
|
if len(parts) != 3 || parts[1] != f.Name() {
|
||||||
continue
|
continue
|
||||||
@@ -288,14 +275,6 @@ func (f *Function) IsExported() bool {
|
|||||||
return f.exported || f.CName() != ""
|
return f.exported || f.CName() != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true for functions annotated with //go:interrupt. The function name is
|
|
||||||
// already customized in LinkName() to hook up in the interrupt vector.
|
|
||||||
//
|
|
||||||
// On some platforms (like AVR), interrupts need a special compiler flag.
|
|
||||||
func (f *Function) IsInterrupt() bool {
|
|
||||||
return f.interrupt
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the inline directive of this function.
|
// Return the inline directive of this function.
|
||||||
func (f *Function) Inline() InlineType {
|
func (f *Function) Inline() InlineType {
|
||||||
return f.inline
|
return f.inline
|
||||||
|
|||||||
Submodule
+1
Submodule lib/wasi-libc added at a280fead2a
+14
-3
@@ -6,6 +6,7 @@ import (
|
|||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
"go/build"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
|
"go/scanner"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"os"
|
"os"
|
||||||
@@ -46,7 +47,7 @@ type Package struct {
|
|||||||
|
|
||||||
// Import loads the given package relative to srcDir (for the vendor directory).
|
// Import loads the given package relative to srcDir (for the vendor directory).
|
||||||
// It only loads the current package without recursion.
|
// It only loads the current package without recursion.
|
||||||
func (p *Program) Import(path, srcDir string) (*Package, error) {
|
func (p *Program) Import(path, srcDir string, pos token.Position) (*Package, error) {
|
||||||
if p.Packages == nil {
|
if p.Packages == nil {
|
||||||
p.Packages = make(map[string]*Package)
|
p.Packages = make(map[string]*Package)
|
||||||
}
|
}
|
||||||
@@ -59,7 +60,10 @@ func (p *Program) Import(path, srcDir string) (*Package, error) {
|
|||||||
}
|
}
|
||||||
buildPkg, err := ctx.Import(path, srcDir, build.ImportComment)
|
buildPkg, err := ctx.Import(path, srcDir, build.ImportComment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, scanner.Error{
|
||||||
|
Pos: pos,
|
||||||
|
Msg: err.Error(), // TODO: define a new error type that will wrap the inner error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if existingPkg, ok := p.Packages[buildPkg.ImportPath]; ok {
|
if existingPkg, ok := p.Packages[buildPkg.ImportPath]; ok {
|
||||||
// Already imported, or at least started the import.
|
// Already imported, or at least started the import.
|
||||||
@@ -467,7 +471,14 @@ func (p *Package) importRecursively(includeTests bool) error {
|
|||||||
if _, ok := p.Imports[to]; ok {
|
if _, ok := p.Imports[to]; ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
importedPkg, err := p.Program.Import(to, p.Package.Dir)
|
// Find error location.
|
||||||
|
var pos token.Position
|
||||||
|
if len(p.Package.ImportPos[to]) > 0 {
|
||||||
|
pos = p.Package.ImportPos[to][0]
|
||||||
|
} else {
|
||||||
|
pos = token.Position{Filename: p.Package.ImportPath}
|
||||||
|
}
|
||||||
|
importedPkg, err := p.Program.Import(to, p.Package.Dir, pos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err, ok := err.(*ImportCycleError); ok {
|
if err, ok := err.(*ImportCycleError); ok {
|
||||||
err.Packages = append([]string{p.ImportPath}, err.Packages...)
|
err.Packages = append([]string{p.ImportPath}, err.Packages...)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -183,6 +184,14 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
return builder.Build(pkgName, fileExt, config, func(tmppath string) error {
|
return builder.Build(pkgName, fileExt, config, func(tmppath string) error {
|
||||||
// do we need port reset to put MCU into bootloader mode?
|
// do we need port reset to put MCU into bootloader mode?
|
||||||
if config.Target.PortReset == "true" {
|
if config.Target.PortReset == "true" {
|
||||||
|
if port == "" {
|
||||||
|
var err error
|
||||||
|
port, err = getDefaultPort()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := touchSerialPortAt1200bps(port)
|
err := touchSerialPortAt1200bps(port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to reset port", tmppath, err}
|
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
|
flashCmd := config.Target.FlashCommand
|
||||||
fileToken := "{" + fileExt[1:] + "}"
|
fileToken := "{" + fileExt[1:] + "}"
|
||||||
flashCmd = strings.Replace(flashCmd, fileToken, tmppath, -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)
|
flashCmd = strings.Replace(flashCmd, "{port}", port, -1)
|
||||||
|
|
||||||
// Execute the command.
|
// Execute the command.
|
||||||
cmd := exec.Command("/bin/sh", "-c", flashCmd)
|
var cmd *exec.Cmd
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
command := strings.Split(flashCmd, " ")
|
||||||
|
if len(command) < 2 {
|
||||||
|
return errors.New("invalid flash command")
|
||||||
|
}
|
||||||
|
cmd = exec.Command(command[0], command[1:]...)
|
||||||
|
default:
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", flashCmd)
|
||||||
|
}
|
||||||
|
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Dir = goenv.Get("TINYGOROOT")
|
cmd.Dir = goenv.Get("TINYGOROOT")
|
||||||
@@ -254,7 +283,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
//
|
//
|
||||||
// Note: this command is expected to execute just before exiting, as it
|
// Note: this command is expected to execute just before exiting, as it
|
||||||
// modifies global state.
|
// modifies global state.
|
||||||
func FlashGDB(pkgName, port string, ocdOutput bool, options *compileopts.Options) error {
|
func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) error {
|
||||||
config, err := builder.NewConfig(options)
|
config, err := builder.NewConfig(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -268,11 +297,13 @@ func FlashGDB(pkgName, port string, ocdOutput bool, options *compileopts.Options
|
|||||||
gdbInterface, openocdInterface := config.Programmer()
|
gdbInterface, openocdInterface := config.Programmer()
|
||||||
switch gdbInterface {
|
switch gdbInterface {
|
||||||
case "msd", "command", "":
|
case "msd", "command", "":
|
||||||
if gdbInterface == "" {
|
if len(config.Target.Emulator) != 0 {
|
||||||
gdbInterface = "command"
|
// Assume QEMU as an emulator.
|
||||||
}
|
gdbInterface = "qemu"
|
||||||
if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
|
} else if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
|
||||||
gdbInterface = "openocd"
|
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
|
// Make sure the daemon doesn't receive Ctrl-C that is intended for
|
||||||
// GDB (to break the currently executing program).
|
// GDB (to break the currently executing program).
|
||||||
setCommandAsDaemon(daemon)
|
setCommandAsDaemon(daemon)
|
||||||
|
// Start now, and kill it on exit.
|
||||||
|
daemon.Start()
|
||||||
|
defer func() {
|
||||||
|
daemon.Process.Signal(os.Interrupt)
|
||||||
|
// Maybe we should send a .Kill() after x seconds?
|
||||||
|
daemon.Wait()
|
||||||
|
}()
|
||||||
|
case "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.
|
// Start now, and kill it on exit.
|
||||||
daemon.Start()
|
daemon.Start()
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -401,9 +477,18 @@ func touchSerialPortAt1200bps(port string) error {
|
|||||||
|
|
||||||
func flashUF2UsingMSD(volume, tmppath string) error {
|
func flashUF2UsingMSD(volume, tmppath string) error {
|
||||||
// find standard UF2 info path
|
// find standard UF2 info path
|
||||||
infoPath := "/media/*/" + volume + "/INFO_UF2.TXT"
|
var infoPath string
|
||||||
if runtime.GOOS == "darwin" {
|
switch runtime.GOOS {
|
||||||
|
case "linux", "freebsd":
|
||||||
|
infoPath = "/media/*/" + volume + "/INFO_UF2.TXT"
|
||||||
|
case "darwin":
|
||||||
infoPath = "/Volumes/" + volume + "/INFO_UF2.TXT"
|
infoPath = "/Volumes/" + volume + "/INFO_UF2.TXT"
|
||||||
|
case "windows":
|
||||||
|
path, err := windowsFindUSBDrive(volume)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
infoPath = path + "/INFO_UF2.TXT"
|
||||||
}
|
}
|
||||||
|
|
||||||
d, err := filepath.Glob(infoPath)
|
d, err := filepath.Glob(infoPath)
|
||||||
@@ -419,9 +504,18 @@ func flashUF2UsingMSD(volume, tmppath string) error {
|
|||||||
|
|
||||||
func flashHexUsingMSD(volume, tmppath string) error {
|
func flashHexUsingMSD(volume, tmppath string) error {
|
||||||
// find expected volume path
|
// find expected volume path
|
||||||
destPath := "/media/*/" + volume
|
var destPath string
|
||||||
if runtime.GOOS == "darwin" {
|
switch runtime.GOOS {
|
||||||
|
case "linux", "freebsd":
|
||||||
|
destPath = "/media/*/" + volume
|
||||||
|
case "darwin":
|
||||||
destPath = "/Volumes/" + volume
|
destPath = "/Volumes/" + volume
|
||||||
|
case "windows":
|
||||||
|
path, err := windowsFindUSBDrive(volume)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
destPath = path + "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
d, err := filepath.Glob(destPath)
|
d, err := filepath.Glob(destPath)
|
||||||
@@ -435,6 +529,29 @@ func flashHexUsingMSD(volume, tmppath string) error {
|
|||||||
return moveFile(tmppath, d[0]+"/flash.hex")
|
return moveFile(tmppath, d[0]+"/flash.hex")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func windowsFindUSBDrive(volume string) (string, error) {
|
||||||
|
cmd := exec.Command("wmic",
|
||||||
|
"PATH", "Win32_LogicalDisk", "WHERE", "VolumeName = '"+volume+"'",
|
||||||
|
"get", "DeviceID,VolumeName,FileSystem,DriveType")
|
||||||
|
|
||||||
|
var out bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range strings.Split(out.String(), "\n") {
|
||||||
|
words := strings.Fields(line)
|
||||||
|
if len(words) >= 3 {
|
||||||
|
if words[1] == "2" && words[2] == "FAT" {
|
||||||
|
return words[0], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("unable to locate a USB device to be flashed")
|
||||||
|
}
|
||||||
|
|
||||||
// parseSize converts a human-readable size (with k/m/g suffix) into a plain
|
// parseSize converts a human-readable size (with k/m/g suffix) into a plain
|
||||||
// number.
|
// number.
|
||||||
func parseSize(s string) (int64, error) {
|
func parseSize(s string) (int64, error) {
|
||||||
@@ -459,6 +576,55 @@ func parseSize(s string) (int64, error) {
|
|||||||
return n, err
|
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() {
|
func usage() {
|
||||||
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
||||||
fmt.Fprintln(os.Stderr, "version:", version)
|
fmt.Fprintln(os.Stderr, "version:", version)
|
||||||
@@ -526,7 +692,7 @@ func main() {
|
|||||||
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
printSize := flag.String("size", "", "print sizes (none, short, full)")
|
||||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
||||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||||
port := flag.String("port", "/dev/ttyACM0", "flash port")
|
port := flag.String("port", "", "flash port")
|
||||||
programmer := flag.String("programmer", "", "which hardware programmer to use")
|
programmer := flag.String("programmer", "", "which hardware programmer to use")
|
||||||
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
cFlags := flag.String("cflags", "", "additional cflags for compiler")
|
||||||
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
ldFlags := flag.String("ldflags", "", "additional ldflags for linker")
|
||||||
@@ -629,7 +795,7 @@ func main() {
|
|||||||
usage()
|
usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
err := FlashGDB(flag.Arg(0), *port, *ocdOutput, options)
|
err := FlashGDB(flag.Arg(0), *ocdOutput, options)
|
||||||
handleCompilerError(err)
|
handleCompilerError(err)
|
||||||
}
|
}
|
||||||
case "run":
|
case "run":
|
||||||
|
|||||||
+48
-25
@@ -12,7 +12,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/loader"
|
"github.com/tinygo-org/tinygo/loader"
|
||||||
@@ -50,7 +52,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run("EmulatedCortexM3", func(t *testing.T) {
|
t.Run("EmulatedCortexM3", func(t *testing.T) {
|
||||||
runPlatTests("qemu", matches, t)
|
runPlatTests("cortex-m-qemu", matches, t)
|
||||||
})
|
})
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
@@ -67,41 +69,38 @@ func TestCompiler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runPlatTests(target string, matches []string, t *testing.T) {
|
func runPlatTests(target string, matches []string, t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
for _, path := range matches {
|
for _, path := range matches {
|
||||||
switch {
|
path := path // redefine to avoid race condition
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run(filepath.Base(path), func(t *testing.T) {
|
t.Run(filepath.Base(path), func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
runTest(path, target, t)
|
runTest(path, target, t)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Due to some problems with LLD, we cannot run links in parallel, or in parallel with compiles.
|
||||||
|
// Therefore, we put a lock around builds and run everything else in parallel.
|
||||||
|
var buildLock sync.Mutex
|
||||||
|
|
||||||
|
// runBuild is a thread-safe wrapper around Build.
|
||||||
|
func runBuild(src, out string, opts *compileopts.Options) error {
|
||||||
|
buildLock.Lock()
|
||||||
|
defer buildLock.Unlock()
|
||||||
|
|
||||||
|
return Build(src, out, opts)
|
||||||
|
}
|
||||||
|
|
||||||
func runTest(path, target string, t *testing.T) {
|
func runTest(path, target string, t *testing.T) {
|
||||||
// Get the expected output for this test.
|
// Get the expected output for this test.
|
||||||
txtpath := path[:len(path)-3] + ".txt"
|
txtpath := path[:len(path)-3] + ".txt"
|
||||||
if path[len(path)-1] == os.PathSeparator {
|
if path[len(path)-1] == os.PathSeparator {
|
||||||
txtpath = path + "out.txt"
|
txtpath = path + "out.txt"
|
||||||
}
|
}
|
||||||
f, err := os.Open(txtpath)
|
expected, err := ioutil.ReadFile(txtpath)
|
||||||
if err != nil {
|
|
||||||
t.Fatal("could not open expected output file:", err)
|
|
||||||
}
|
|
||||||
expected, err := ioutil.ReadAll(f)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("could not read expected output file:", err)
|
t.Fatal("could not read expected output file:", err)
|
||||||
}
|
}
|
||||||
@@ -130,7 +129,7 @@ func runTest(path, target string, t *testing.T) {
|
|||||||
WasmAbi: "js",
|
WasmAbi: "js",
|
||||||
}
|
}
|
||||||
binary := filepath.Join(tmpdir, "test")
|
binary := filepath.Join(tmpdir, "test")
|
||||||
err = Build("./"+path, binary, config)
|
err = runBuild("./"+path, binary, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errLoader, ok := err.(loader.Errors); ok {
|
if errLoader, ok := err.(loader.Errors); ok {
|
||||||
for _, err := range errLoader.Errs {
|
for _, err := range errLoader.Errs {
|
||||||
@@ -144,7 +143,9 @@ func runTest(path, target string, t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run the test.
|
// Run the test.
|
||||||
|
runComplete := make(chan struct{})
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
|
ranTooLong := false
|
||||||
if target == "" {
|
if target == "" {
|
||||||
cmd = exec.Command(binary)
|
cmd = exec.Command(binary)
|
||||||
} else {
|
} else {
|
||||||
@@ -160,13 +161,35 @@ func runTest(path, target string, t *testing.T) {
|
|||||||
}
|
}
|
||||||
stdout := &bytes.Buffer{}
|
stdout := &bytes.Buffer{}
|
||||||
cmd.Stdout = stdout
|
cmd.Stdout = stdout
|
||||||
if target != "" {
|
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
err = cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("failed to start:", err)
|
||||||
}
|
}
|
||||||
err = cmd.Run()
|
go func() {
|
||||||
|
// Terminate the process if it runs too long.
|
||||||
|
timer := time.NewTimer(1 * time.Second)
|
||||||
|
select {
|
||||||
|
case <-runComplete:
|
||||||
|
timer.Stop()
|
||||||
|
case <-timer.C:
|
||||||
|
ranTooLong = true
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
cmd.Process.Signal(os.Kill) // Windows doesn't support SIGINT.
|
||||||
|
} else {
|
||||||
|
cmd.Process.Signal(os.Interrupt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
err = cmd.Wait()
|
||||||
if _, ok := err.(*exec.ExitError); ok && target != "" {
|
if _, ok := err.(*exec.ExitError); ok && target != "" {
|
||||||
err = nil // workaround for QEMU
|
err = nil // workaround for QEMU
|
||||||
}
|
}
|
||||||
|
close(runComplete)
|
||||||
|
|
||||||
|
if ranTooLong {
|
||||||
|
stdout.WriteString("--- test ran too long, terminating...\n")
|
||||||
|
}
|
||||||
|
|
||||||
// putchar() prints CRLF, convert it to LF.
|
// putchar() prints CRLF, convert it to LF.
|
||||||
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
|
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
|
||||||
|
|||||||
@@ -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
|
.type _start,@function
|
||||||
|
|
||||||
_start:
|
_start:
|
||||||
// Workaround for missing support of the la pseudo-instruction in Clang 8:
|
// Load the stack pointer.
|
||||||
// https://reviews.llvm.org/D55325
|
la sp, _stack_top
|
||||||
lui sp, %hi(_stack_top)
|
|
||||||
addi sp, sp, %lo(_stack_top)
|
// Load the globals pointer. The program will load pointers relative to this
|
||||||
// see https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register
|
// register, so it must be set to the right value on startup.
|
||||||
lui gp, %hi(__global_pointer$)
|
// See: https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register
|
||||||
addi gp, gp, %lo(__global_pointer$)
|
la gp, __global_pointer$
|
||||||
|
|
||||||
|
// Jump to runtime.main
|
||||||
call 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.
|
// Hand created file. DO NOT DELETE.
|
||||||
// STM32F103XX bitfield definitions that are not auto-generated by gen-device-svd.py
|
// STM32F103XX bitfield definitions that are not auto-generated by gen-device-svd.go
|
||||||
|
|
||||||
// +build stm32,stm32f103xx
|
// +build stm32,stm32f103xx
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ func main() {
|
|||||||
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
|
|
||||||
// timer fires 10 times per second
|
// timer fires 10 times per second
|
||||||
arm.SetupSystemTimer(machine.CPU_FREQUENCY / 10)
|
arm.SetupSystemTimer(machine.CPUFrequency() / 10)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
const CPU_FREQUENCY = 16000000
|
// Return the current CPU frequency in hertz.
|
||||||
|
func CPUFrequency() uint32 {
|
||||||
|
return 16000000
|
||||||
|
}
|
||||||
|
|
||||||
// LED on the Arduino
|
// LED on the Arduino
|
||||||
const LED Pin = 13
|
const LED Pin = 13
|
||||||
|
|||||||
@@ -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.
|
// This contains the pin mappings for the Arduino Nano33 IoT board.
|
||||||
//
|
//
|
||||||
@@ -6,8 +6,6 @@
|
|||||||
//
|
//
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import "device/sam"
|
|
||||||
|
|
||||||
// used to reset into bootloader
|
// used to reset into bootloader
|
||||||
const RESET_MAGIC_VALUE = 0x07738135
|
const RESET_MAGIC_VALUE = 0x07738135
|
||||||
|
|
||||||
@@ -54,69 +52,23 @@ const (
|
|||||||
USBCDC_DP_PIN Pin = PA25
|
USBCDC_DP_PIN Pin = PA25
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART1 on the Arduino Nano 33 connects to the onboard NINA-W102 WiFi chip.
|
|
||||||
var (
|
|
||||||
UART1 = UART{
|
|
||||||
Buffer: NewRingBuffer(),
|
|
||||||
Bus: sam.SERCOM5_USART,
|
|
||||||
SERCOM: 5,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// UART1 pins
|
// UART1 pins
|
||||||
const (
|
const (
|
||||||
UART_TX_PIN Pin = PA22
|
UART_TX_PIN Pin = PA22
|
||||||
UART_RX_PIN Pin = PA23
|
UART_RX_PIN Pin = PA23
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:export SERCOM5_IRQHandler
|
|
||||||
func handleUART1() {
|
|
||||||
defaultUART1Handler()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UART2 on the Arduino Nano 33 connects to the normal TX/RX pins.
|
|
||||||
var (
|
|
||||||
UART2 = UART{
|
|
||||||
Buffer: NewRingBuffer(),
|
|
||||||
Bus: sam.SERCOM3_USART,
|
|
||||||
SERCOM: 3,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:export SERCOM3_IRQHandler
|
|
||||||
func handleUART2() {
|
|
||||||
// should reset IRQ
|
|
||||||
UART2.Receive(byte((UART2.Bus.DATA.Get() & 0xFF)))
|
|
||||||
UART2.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN Pin = A4 // SDA: SERCOM4/PAD[1]
|
SDA_PIN Pin = A4 // SDA: SERCOM4/PAD[1]
|
||||||
SCL_PIN Pin = A5 // SCL: SERCOM4/PAD[1]
|
SCL_PIN Pin = A5 // SCL: SERCOM4/PAD[1]
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2C on the Arduino Nano 33.
|
|
||||||
var (
|
|
||||||
I2C0 = I2C{
|
|
||||||
Bus: sam.SERCOM4_I2CM,
|
|
||||||
SERCOM: 4,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
const (
|
const (
|
||||||
SPI0_SCK_PIN Pin = A2 // SCK: SERCOM0/PAD[3]
|
SPI0_SCK_PIN Pin = D13 // SCK: SERCOM1/PAD[1]
|
||||||
SPI0_MOSI_PIN Pin = A3 // MOSI: SERCOM0/PAD[2]
|
SPI0_MOSI_PIN Pin = D11 // MOSI: SERCOM1/PAD[0]
|
||||||
SPI0_MISO_PIN Pin = A6 // MISO: SERCOM0/PAD[1]
|
SPI0_MISO_PIN Pin = D12 // MISO: SERCOM1/PAD[3]
|
||||||
)
|
|
||||||
|
|
||||||
// SPI on the Arduino Nano 33.
|
|
||||||
var (
|
|
||||||
SPI0 = SPI{
|
|
||||||
Bus: sam.SERCOM0_SPI,
|
|
||||||
SERCOM: 0,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NINA-W102 Pins
|
// NINA-W102 Pins
|
||||||
@@ -132,23 +84,9 @@ const (
|
|||||||
NINA_RX Pin = PA23
|
NINA_RX Pin = PA23
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI1 is connected to the NINA-W102 chip on the Arduino Nano 33.
|
|
||||||
var (
|
|
||||||
SPI1 = SPI{
|
|
||||||
Bus: sam.SERCOM2_SPI,
|
|
||||||
SERCOM: 2,
|
|
||||||
}
|
|
||||||
NINA_SPI = SPI1
|
|
||||||
)
|
|
||||||
|
|
||||||
// I2S pins
|
// I2S pins
|
||||||
const (
|
const (
|
||||||
I2S_SCK_PIN Pin = PA10
|
I2S_SCK_PIN Pin = PA10
|
||||||
I2S_SD_PIN Pin = PA08
|
I2S_SD_PIN Pin = PA08
|
||||||
I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino Nano 33.
|
I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino Nano 33.
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2S on the Arduino Nano 33.
|
|
||||||
var (
|
|
||||||
I2S0 = I2S{Bus: sam.I2S}
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -0,0 +1,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
|
package machine
|
||||||
|
|
||||||
import "device/stm32"
|
import (
|
||||||
|
"device/stm32"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
// https://wiki.stm32duino.com/index.php?title=File:Bluepillpinout.gif
|
// https://wiki.stm32duino.com/index.php?title=File:Bluepillpinout.gif
|
||||||
const (
|
const (
|
||||||
@@ -61,14 +64,12 @@ var (
|
|||||||
UART0 = UART{
|
UART0 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART1,
|
Bus: stm32.USART1,
|
||||||
IRQVal: stm32.IRQ_USART1,
|
|
||||||
}
|
}
|
||||||
UART1 = &UART0
|
UART1 = &UART0
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:export USART1_IRQHandler
|
func init() {
|
||||||
func handleUART1() {
|
UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, UART0.handleInterrupt)
|
||||||
UART1.Receive(byte((UART1.Bus.DR.Get() & 0xFF)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
|
|||||||
@@ -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
|
package machine
|
||||||
|
|
||||||
import "device/sam"
|
|
||||||
|
|
||||||
// used to reset into bootloader
|
// used to reset into bootloader
|
||||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||||
|
|
||||||
@@ -68,20 +66,6 @@ const (
|
|||||||
UART_RX_PIN = PB09 // PORTB
|
UART_RX_PIN = PB09 // PORTB
|
||||||
)
|
)
|
||||||
|
|
||||||
// UART1 on the Circuit Playground Express.
|
|
||||||
var (
|
|
||||||
UART1 = UART{
|
|
||||||
Buffer: NewRingBuffer(),
|
|
||||||
Bus: sam.SERCOM4_USART,
|
|
||||||
SERCOM: 4,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:export SERCOM1_IRQHandler
|
|
||||||
func handleUART1() {
|
|
||||||
defaultUART1Handler()
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN = PB02 // I2C0 external
|
SDA_PIN = PB02 // I2C0 external
|
||||||
@@ -91,20 +75,6 @@ const (
|
|||||||
SCL1_PIN = PA01 // I2C1 internal
|
SCL1_PIN = PA01 // I2C1 internal
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2C on the Circuit Playground Express.
|
|
||||||
var (
|
|
||||||
// external device
|
|
||||||
I2C0 = I2C{
|
|
||||||
Bus: sam.SERCOM5_I2CM,
|
|
||||||
SERCOM: 5,
|
|
||||||
}
|
|
||||||
// internal device
|
|
||||||
I2C1 = I2C{
|
|
||||||
Bus: sam.SERCOM1_I2CM,
|
|
||||||
SERCOM: 1,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// SPI pins (internal flash)
|
// SPI pins (internal flash)
|
||||||
const (
|
const (
|
||||||
SPI0_SCK_PIN = PA21 // SCK: SERCOM3/PAD[3]
|
SPI0_SCK_PIN = PA21 // SCK: SERCOM3/PAD[3]
|
||||||
@@ -112,22 +82,9 @@ const (
|
|||||||
SPI0_MISO_PIN = PA16 // MISO: SERCOM3/PAD[0]
|
SPI0_MISO_PIN = PA16 // MISO: SERCOM3/PAD[0]
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI on the Circuit Playground Express.
|
|
||||||
var (
|
|
||||||
SPI0 = SPI{
|
|
||||||
Bus: sam.SERCOM3_SPI,
|
|
||||||
SERCOM: 3,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// I2S pins
|
// I2S pins
|
||||||
const (
|
const (
|
||||||
I2S_SCK_PIN = PA10
|
I2S_SCK_PIN = PA10
|
||||||
I2S_SD_PIN = PA08
|
I2S_SD_PIN = PA08
|
||||||
I2S_WS_PIN = NoPin // no WS, instead uses SCK to sync
|
I2S_WS_PIN = NoPin // no WS, instead uses SCK to sync
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2S on the Circuit Playground Express.
|
|
||||||
var (
|
|
||||||
I2S0 = I2S{Bus: sam.I2S}
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// +build sam,atsamd21,circuitplay_express
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/sam"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART1 on the Circuit Playground Express.
|
||||||
|
var (
|
||||||
|
UART1 = UART{
|
||||||
|
Buffer: NewRingBuffer(),
|
||||||
|
Bus: sam.SERCOM4_USART,
|
||||||
|
SERCOM: 4,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM4, UART1.handleInterrupt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// I2C on the Circuit Playground Express.
|
||||||
|
var (
|
||||||
|
// external device
|
||||||
|
I2C0 = I2C{
|
||||||
|
Bus: sam.SERCOM5_I2CM,
|
||||||
|
SERCOM: 5,
|
||||||
|
}
|
||||||
|
// internal device
|
||||||
|
I2C1 = I2C{
|
||||||
|
Bus: sam.SERCOM1_I2CM,
|
||||||
|
SERCOM: 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI on the Circuit Playground Express.
|
||||||
|
var (
|
||||||
|
SPI0 = SPI{
|
||||||
|
Bus: sam.SERCOM3_SPI,
|
||||||
|
SERCOM: 3,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2S on the Circuit Playground Express.
|
||||||
|
var (
|
||||||
|
I2S0 = I2S{Bus: sam.I2S}
|
||||||
|
)
|
||||||
@@ -2,6 +2,18 @@
|
|||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
// Return the current CPU frequency in hertz.
|
||||||
|
func CPUFrequency() uint32 {
|
||||||
|
return 16000000
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
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
|
package machine
|
||||||
|
|
||||||
import "device/sam"
|
import (
|
||||||
|
"device/sam"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
// used to reset into bootloader
|
// used to reset into bootloader
|
||||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||||
@@ -60,9 +63,8 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:export SERCOM1_IRQHandler
|
func init() {
|
||||||
func handleUART1() {
|
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
|
||||||
defaultUART1Handler()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
|
|||||||
@@ -67,10 +67,10 @@ const (
|
|||||||
|
|
||||||
// I2C on the Feather M4.
|
// I2C on the Feather M4.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{Bus: sam.SERCOM2_I2CM,
|
I2C0 = I2C{
|
||||||
SDA: SDA_PIN,
|
Bus: sam.SERCOM2_I2CM,
|
||||||
SCL: SCL_PIN,
|
SERCOM: 2,
|
||||||
PinMode: PinSERCOM}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
@@ -82,14 +82,8 @@ const (
|
|||||||
|
|
||||||
// SPI on the Feather M4.
|
// SPI on the Feather M4.
|
||||||
var (
|
var (
|
||||||
SPI0 = SPI{Bus: sam.SERCOM1_SPIM,
|
SPI0 = SPI{
|
||||||
SCK: SPI0_SCK_PIN,
|
Bus: sam.SERCOM1_SPIM,
|
||||||
MOSI: SPI0_MOSI_PIN,
|
SERCOM: 1,
|
||||||
MISO: SPI0_MISO_PIN,
|
|
||||||
DOpad: spiTXPad3SCK1,
|
|
||||||
DIpad: sercomRXPad2,
|
|
||||||
MISOPinMode: PinSERCOM,
|
|
||||||
MOSIPinMode: PinSERCOM,
|
|
||||||
SCKPinMode: PinSERCOM,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/sam"
|
"device/sam"
|
||||||
|
"runtime/interrupt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// used to reset into bootloader
|
// used to reset into bootloader
|
||||||
@@ -62,9 +63,8 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:export SERCOM1_IRQHandler
|
func init() {
|
||||||
func handleUART1() {
|
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
|
||||||
defaultUART1Handler()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
|
|||||||
@@ -69,10 +69,10 @@ const (
|
|||||||
|
|
||||||
// I2C on the ItsyBitsy M4.
|
// I2C on the ItsyBitsy M4.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{Bus: sam.SERCOM2_I2CM,
|
I2C0 = I2C{
|
||||||
SDA: SDA_PIN,
|
Bus: sam.SERCOM2_I2CM,
|
||||||
SCL: SCL_PIN,
|
SERCOM: 2,
|
||||||
PinMode: PinSERCOM}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
@@ -84,12 +84,8 @@ const (
|
|||||||
|
|
||||||
// SPI on the ItsyBitsy M4.
|
// SPI on the ItsyBitsy M4.
|
||||||
var (
|
var (
|
||||||
SPI0 = SPI{Bus: sam.SERCOM1_SPIM,
|
SPI0 = SPI{
|
||||||
SCK: SPI0_SCK_PIN,
|
Bus: sam.SERCOM1_SPIM,
|
||||||
MOSI: SPI0_MOSI_PIN,
|
SERCOM: 1,
|
||||||
MISO: SPI0_MISO_PIN,
|
|
||||||
DOpad: spiTXPad2SCK3,
|
|
||||||
DIpad: sercomRXPad3,
|
|
||||||
MISOPinMode: PinSERCOM,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -77,10 +77,10 @@ const (
|
|||||||
|
|
||||||
// I2C on the Metro M4.
|
// I2C on the Metro M4.
|
||||||
var (
|
var (
|
||||||
I2C0 = I2C{Bus: sam.SERCOM5_I2CM,
|
I2C0 = I2C{
|
||||||
SDA: SDA_PIN,
|
Bus: sam.SERCOM5_I2CM,
|
||||||
SCL: SCL_PIN,
|
SERCOM: 5,
|
||||||
PinMode: PinSERCOMAlt}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
@@ -98,14 +98,7 @@ const (
|
|||||||
var (
|
var (
|
||||||
SPI0 = SPI{
|
SPI0 = SPI{
|
||||||
Bus: sam.SERCOM2_SPIM,
|
Bus: sam.SERCOM2_SPIM,
|
||||||
SCK: SPI0_SCK_PIN,
|
SERCOM: 2,
|
||||||
MOSI: SPI0_MOSI_PIN,
|
|
||||||
MISO: SPI0_MISO_PIN,
|
|
||||||
DOpad: spiTXPad0SCK1,
|
|
||||||
DIpad: sercomRXPad2,
|
|
||||||
MISOPinMode: PinSERCOM,
|
|
||||||
MOSIPinMode: PinSERCOM,
|
|
||||||
SCKPinMode: PinSERCOM,
|
|
||||||
}
|
}
|
||||||
NINA_SPI = SPI0
|
NINA_SPI = SPI0
|
||||||
)
|
)
|
||||||
@@ -120,13 +113,6 @@ const (
|
|||||||
var (
|
var (
|
||||||
SPI1 = SPI{
|
SPI1 = SPI{
|
||||||
Bus: sam.SERCOM1_SPIM,
|
Bus: sam.SERCOM1_SPIM,
|
||||||
SCK: SPI1_SCK_PIN,
|
SERCOM: 1,
|
||||||
MOSI: SPI1_MOSI_PIN,
|
|
||||||
MISO: SPI1_MISO_PIN,
|
|
||||||
DOpad: spiTXPad3SCK1,
|
|
||||||
DIpad: sercomRXPad0,
|
|
||||||
MISOPinMode: PinSERCOM,
|
|
||||||
MOSIPinMode: PinSERCOM,
|
|
||||||
SCKPinMode: PinSERCOM,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import "device/stm32"
|
import (
|
||||||
|
"device/stm32"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PA0 = portA + 0
|
PA0 = portA + 0
|
||||||
@@ -100,14 +103,12 @@ var (
|
|||||||
UART0 = UART{
|
UART0 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART2,
|
Bus: stm32.USART2,
|
||||||
IRQVal: stm32.IRQ_USART2,
|
|
||||||
}
|
}
|
||||||
UART2 = &UART0
|
UART2 = &UART0
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:export USART2_IRQHandler
|
func init() {
|
||||||
func handleUART2() {
|
UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
|
||||||
UART2.Receive(byte((UART2.Bus.DR.Get() & 0xFF)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
|
|||||||
@@ -0,0 +1,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
|
package machine
|
||||||
|
|
||||||
import "device/sam"
|
import (
|
||||||
|
"device/sam"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
// used to reset into bootloader
|
// used to reset into bootloader
|
||||||
const RESET_MAGIC_VALUE = 0xf01669ef
|
const RESET_MAGIC_VALUE = 0xf01669ef
|
||||||
@@ -51,9 +54,8 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:export SERCOM1_IRQHandler
|
func init() {
|
||||||
func handleUART1() {
|
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM0, UART1.handleInterrupt)
|
||||||
defaultUART1Handler()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/avr"
|
"device/avr"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -140,7 +141,7 @@ func (i2c I2C) Configure(config I2CConfig) {
|
|||||||
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
||||||
// NOTE: TWBR should be 10 or higher for master mode.
|
// NOTE: TWBR should be 10 or higher for master mode.
|
||||||
// It is 72 for a 16mhz board with 100kHz TWI
|
// It is 72 for a 16mhz board with 100kHz TWI
|
||||||
avr.TWBR.Set(uint8(((CPU_FREQUENCY / config.Frequency) - 16) / 2))
|
avr.TWBR.Set(uint8(((CPUFrequency() / config.Frequency) - 16) / 2))
|
||||||
|
|
||||||
// Enable twi module.
|
// Enable twi module.
|
||||||
avr.TWCR.Set(avr.TWCR_TWEN)
|
avr.TWCR.Set(avr.TWCR_TWEN)
|
||||||
@@ -232,10 +233,22 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
config.BaudRate = 9600
|
config.BaudRate = 9600
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register the UART interrupt.
|
||||||
|
interrupt.New(avr.IRQ_USART_RX, func(intr interrupt.Interrupt) {
|
||||||
|
// Read register to clear it.
|
||||||
|
data := avr.UDR0.Get()
|
||||||
|
|
||||||
|
// Ensure no error.
|
||||||
|
if !avr.UCSR0A.HasBits(avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0) {
|
||||||
|
// Put data from UDR register into buffer.
|
||||||
|
UART0.Receive(byte(data))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Set baud rate based on prescale formula from
|
// Set baud rate based on prescale formula from
|
||||||
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
||||||
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
||||||
ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
ps := ((CPUFrequency()+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
||||||
avr.UBRR0H.Set(uint8(ps >> 8))
|
avr.UBRR0H.Set(uint8(ps >> 8))
|
||||||
avr.UBRR0L.Set(uint8(ps & 0xff))
|
avr.UBRR0L.Set(uint8(ps & 0xff))
|
||||||
|
|
||||||
@@ -254,15 +267,3 @@ func (uart UART) WriteByte(c byte) error {
|
|||||||
avr.UDR0.Set(c) // send char
|
avr.UDR0.Set(c) // send char
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:interrupt USART_RX_vect
|
|
||||||
func handleUSART_RX() {
|
|
||||||
// Read register to clear it.
|
|
||||||
data := avr.UDR0.Get()
|
|
||||||
|
|
||||||
// Ensure no error.
|
|
||||||
if !avr.UCSR0A.HasBits(avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0) {
|
|
||||||
// Put data from UDR register into buffer.
|
|
||||||
UART0.Receive(byte(data))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,11 +11,10 @@ import (
|
|||||||
"device/arm"
|
"device/arm"
|
||||||
"device/sam"
|
"device/sam"
|
||||||
"errors"
|
"errors"
|
||||||
|
"runtime/interrupt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 48000000
|
|
||||||
|
|
||||||
type PinMode uint8
|
type PinMode uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -35,74 +34,6 @@ const (
|
|||||||
PinInputPulldown PinMode = 12
|
PinInputPulldown PinMode = 12
|
||||||
)
|
)
|
||||||
|
|
||||||
// Hardware pins
|
|
||||||
const (
|
|
||||||
PA00 Pin = 0
|
|
||||||
PA01 Pin = 1
|
|
||||||
PA02 Pin = 2
|
|
||||||
PA03 Pin = 3
|
|
||||||
PA04 Pin = 4
|
|
||||||
PA05 Pin = 5
|
|
||||||
PA06 Pin = 6
|
|
||||||
PA07 Pin = 7
|
|
||||||
PA08 Pin = 8
|
|
||||||
PA09 Pin = 9
|
|
||||||
PA10 Pin = 10
|
|
||||||
PA11 Pin = 11
|
|
||||||
PA12 Pin = 12
|
|
||||||
PA13 Pin = 13
|
|
||||||
PA14 Pin = 14
|
|
||||||
PA15 Pin = 15
|
|
||||||
PA16 Pin = 16
|
|
||||||
PA17 Pin = 17
|
|
||||||
PA18 Pin = 18
|
|
||||||
PA19 Pin = 19
|
|
||||||
PA20 Pin = 20
|
|
||||||
PA21 Pin = 21
|
|
||||||
PA22 Pin = 22
|
|
||||||
PA23 Pin = 23
|
|
||||||
PA24 Pin = 24
|
|
||||||
PA25 Pin = 25
|
|
||||||
PA26 Pin = 26
|
|
||||||
PA27 Pin = 27
|
|
||||||
PA28 Pin = 28
|
|
||||||
PA29 Pin = 29
|
|
||||||
PA30 Pin = 30
|
|
||||||
PA31 Pin = 31
|
|
||||||
PB00 Pin = 32
|
|
||||||
PB01 Pin = 33
|
|
||||||
PB02 Pin = 34
|
|
||||||
PB03 Pin = 35
|
|
||||||
PB04 Pin = 36
|
|
||||||
PB05 Pin = 37
|
|
||||||
PB06 Pin = 38
|
|
||||||
PB07 Pin = 39
|
|
||||||
PB08 Pin = 40
|
|
||||||
PB09 Pin = 41
|
|
||||||
PB10 Pin = 42
|
|
||||||
PB11 Pin = 43
|
|
||||||
PB12 Pin = 44
|
|
||||||
PB13 Pin = 45
|
|
||||||
PB14 Pin = 46
|
|
||||||
PB15 Pin = 47
|
|
||||||
PB16 Pin = 48
|
|
||||||
PB17 Pin = 49
|
|
||||||
PB18 Pin = 50
|
|
||||||
PB19 Pin = 51
|
|
||||||
PB20 Pin = 52
|
|
||||||
PB21 Pin = 53
|
|
||||||
PB22 Pin = 54
|
|
||||||
PB23 Pin = 55
|
|
||||||
PB24 Pin = 56
|
|
||||||
PB25 Pin = 57
|
|
||||||
PB26 Pin = 58
|
|
||||||
PB27 Pin = 59
|
|
||||||
PB28 Pin = 60
|
|
||||||
PB29 Pin = 61
|
|
||||||
PB30 Pin = 62
|
|
||||||
PB31 Pin = 63
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
pinPadMapSERCOM0Pad0 byte = (0x10 << 1) | 0x00
|
pinPadMapSERCOM0Pad0 byte = (0x10 << 1) | 0x00
|
||||||
pinPadMapSERCOM1Pad0 byte = (0x20 << 1) | 0x00
|
pinPadMapSERCOM1Pad0 byte = (0x20 << 1) | 0x00
|
||||||
@@ -365,6 +296,7 @@ type UART struct {
|
|||||||
Buffer *RingBuffer
|
Buffer *RingBuffer
|
||||||
Bus *sam.SERCOM_USART_Type
|
Bus *sam.SERCOM_USART_Type
|
||||||
SERCOM uint8
|
SERCOM uint8
|
||||||
|
Interrupt interrupt.Interrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -471,10 +403,7 @@ func (uart UART) Configure(config UARTConfig) error {
|
|||||||
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INTENSET_RXC)
|
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INTENSET_RXC)
|
||||||
|
|
||||||
// Enable RX IRQ.
|
// Enable RX IRQ.
|
||||||
// IRQ lines are in the same order as SERCOM instance numbers on SAMD21
|
uart.Interrupt.Enable()
|
||||||
// chips, so the IRQ number can be trivially determined from the SERCOM
|
|
||||||
// number.
|
|
||||||
arm.EnableIRQ(sam.IRQ_SERCOM0 + uint32(uart.SERCOM))
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -485,7 +414,7 @@ func (uart UART) SetBaudRate(br uint32) {
|
|||||||
// BAUD = fref / (sampleRateValue * fbaud)
|
// BAUD = fref / (sampleRateValue * fbaud)
|
||||||
// (multiply by 8, to calculate fractional piece)
|
// (multiply by 8, to calculate fractional piece)
|
||||||
// uint32_t baudTimes8 = (SystemCoreClock * 8) / (16 * baudrate);
|
// uint32_t baudTimes8 = (SystemCoreClock * 8) / (16 * baudrate);
|
||||||
baud := (CPU_FREQUENCY * 8) / (sampleRate16X * br)
|
baud := (CPUFrequency() * 8) / (sampleRate16X * br)
|
||||||
|
|
||||||
// sercom->USART.BAUD.FRAC.FP = (baudTimes8 % 8);
|
// sercom->USART.BAUD.FRAC.FP = (baudTimes8 % 8);
|
||||||
// sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8);
|
// sercom->USART.BAUD.FRAC.BAUD = (baudTimes8 / 8);
|
||||||
@@ -502,11 +431,12 @@ func (uart UART) WriteByte(c byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultUART1Handler handles the UART1 IRQ.
|
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||||
func defaultUART1Handler() {
|
// this UART instance.
|
||||||
|
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||||
// should reset IRQ
|
// should reset IRQ
|
||||||
UART1.Receive(byte((UART1.Bus.DATA.Get() & 0xFF)))
|
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
|
||||||
UART1.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C on the SAMD21.
|
// I2C on the SAMD21.
|
||||||
@@ -601,7 +531,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
|||||||
func (i2c I2C) SetBaudRate(br uint32) {
|
func (i2c I2C) SetBaudRate(br uint32) {
|
||||||
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
|
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
|
||||||
// SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
|
// SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
|
||||||
baud := CPU_FREQUENCY/(2*br) - 5 - (((CPU_FREQUENCY / 1000000) * riseTimeNanoseconds) / (2 * 1000))
|
baud := CPUFrequency()/(2*br) - 5 - (((CPUFrequency() / 1000000) * riseTimeNanoseconds) / (2 * 1000))
|
||||||
i2c.Bus.BAUD.Set(baud)
|
i2c.Bus.BAUD.Set(baud)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -793,7 +723,7 @@ func (i2s I2S) Configure(config I2SConfig) {
|
|||||||
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_I2S_)
|
sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_I2S_)
|
||||||
|
|
||||||
// setting clock rate for sample.
|
// setting clock rate for sample.
|
||||||
division_factor := CPU_FREQUENCY / (config.AudioFrequency * uint32(config.DataFormat))
|
division_factor := CPUFrequency() / (config.AudioFrequency * uint32(config.DataFormat))
|
||||||
|
|
||||||
// Switch Generic Clock Generator 3 to DFLL48M.
|
// Switch Generic Clock Generator 3 to DFLL48M.
|
||||||
sam.GCLK.GENDIV.Set((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENDIV_ID_Pos) |
|
sam.GCLK.GENDIV.Set((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENDIV_ID_Pos) |
|
||||||
@@ -1113,7 +1043,7 @@ func (spi SPI) Configure(config SPIConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set synch speed for SPI
|
// Set synch speed for SPI
|
||||||
baudRate := (CPU_FREQUENCY / (2 * config.Frequency)) - 1
|
baudRate := (CPUFrequency() / (2 * config.Frequency)) - 1
|
||||||
spi.Bus.BAUD.Set(uint8(baudRate))
|
spi.Bus.BAUD.Set(uint8(baudRate))
|
||||||
|
|
||||||
// Enable SPI port.
|
// Enable SPI port.
|
||||||
@@ -1438,7 +1368,8 @@ func (usbcdc USBCDC) Configure(config UARTConfig) {
|
|||||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||||
|
|
||||||
// enable IRQ
|
// enable IRQ
|
||||||
arm.EnableIRQ(sam.IRQ_USB)
|
intr := interrupt.New(sam.IRQ_USB, handleUSB)
|
||||||
|
intr.Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePadCalibration() {
|
func handlePadCalibration() {
|
||||||
@@ -1484,8 +1415,7 @@ func handlePadCalibration() {
|
|||||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export USB_IRQHandler
|
func handleUSB(intr interrupt.Interrupt) {
|
||||||
func handleUSB() {
|
|
||||||
// reset all interrupt flags
|
// reset all interrupt flags
|
||||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||||
|
|||||||
+308
-214
@@ -11,10 +11,13 @@ import (
|
|||||||
"device/arm"
|
"device/arm"
|
||||||
"device/sam"
|
"device/sam"
|
||||||
"errors"
|
"errors"
|
||||||
|
"runtime/interrupt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 120000000
|
func CPUFrequency() uint32 {
|
||||||
|
return 120000000
|
||||||
|
}
|
||||||
|
|
||||||
type PinMode uint8
|
type PinMode uint8
|
||||||
|
|
||||||
@@ -110,6 +113,162 @@ const (
|
|||||||
PB31 Pin = 63
|
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
|
// Return the register and mask to enable a given GPIO pin. This can be used to
|
||||||
// implement bit-banged drivers.
|
// implement bit-banged drivers.
|
||||||
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
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)
|
sam.ADC0.CTRLA.SetBits(sam.ADC_CTRLA_PRESCALER_DIV32 << sam.ADC_CTRLA_PRESCALER_Pos)
|
||||||
// adcs[i]->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_10BIT_Val;
|
// 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
|
// wait for sync
|
||||||
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_CTRLB) {
|
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_CTRLB) {
|
||||||
@@ -349,7 +508,7 @@ func InitADC() {
|
|||||||
|
|
||||||
// same for ADC1, as for ADC0
|
// same for ADC1, as for ADC0
|
||||||
sam.ADC1.CTRLA.SetBits(sam.ADC_CTRLA_PRESCALER_DIV32 << sam.ADC_CTRLA_PRESCALER_Pos)
|
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) {
|
for sam.ADC1.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_CTRLB) {
|
||||||
}
|
}
|
||||||
sam.ADC1.SAMPCTRL.Set(5)
|
sam.ADC1.SAMPCTRL.Set(5)
|
||||||
@@ -387,6 +546,9 @@ func (a ADC) Get() uint16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Selection for the positive ADC input channel
|
// 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)
|
bus.INPUTCTRL.SetBits((uint16(ch) & sam.ADC_INPUTCTRL_MUXPOS_Msk) << sam.ADC_INPUTCTRL_MUXPOS_Pos)
|
||||||
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
|
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
|
||||||
}
|
}
|
||||||
@@ -402,7 +564,7 @@ func (a ADC) Get() uint16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear the Data Ready flag
|
// 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) {
|
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,16 +606,20 @@ func (a ADC) getADCChannel() uint8 {
|
|||||||
return 6
|
return 6
|
||||||
case PA07:
|
case PA07:
|
||||||
return 7
|
return 7
|
||||||
|
case PB00:
|
||||||
|
return 12
|
||||||
|
case PB01:
|
||||||
|
return 13
|
||||||
case PB02:
|
case PB02:
|
||||||
return 10
|
return 14
|
||||||
case PB03:
|
case PB03:
|
||||||
return 11
|
return 15
|
||||||
case PA09:
|
case PA09:
|
||||||
return 17
|
return 17
|
||||||
case PA11:
|
case PA11:
|
||||||
return 19
|
return 19
|
||||||
default:
|
default:
|
||||||
return 0
|
panic("Invalid ADC pin")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,7 +627,8 @@ func (a ADC) getADCChannel() uint8 {
|
|||||||
type UART struct {
|
type UART struct {
|
||||||
Buffer *RingBuffer
|
Buffer *RingBuffer
|
||||||
Bus *sam.SERCOM_USART_INT_Type
|
Bus *sam.SERCOM_USART_INT_Type
|
||||||
Mode PinMode
|
SERCOM uint8
|
||||||
|
IRQVal uint32 // RXC interrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -469,89 +636,71 @@ var (
|
|||||||
UART0 = USBCDC{Buffer: NewRingBuffer()}
|
UART0 = USBCDC{Buffer: NewRingBuffer()}
|
||||||
|
|
||||||
// The first hardware serial port on the SAMD51. Uses the SERCOM3 interface.
|
// The first hardware serial port on the SAMD51. Uses the SERCOM3 interface.
|
||||||
UART1 = UART{Bus: sam.SERCOM3_USART_INT,
|
UART1 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
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.
|
// The second hardware serial port on the SAMD51. Uses the SERCOM0 interface.
|
||||||
UART2 = UART{
|
UART2 = UART{
|
||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: sam.SERCOM0_USART_INT,
|
Bus: sam.SERCOM0_USART_INT,
|
||||||
Mode: PinSERCOMAlt,
|
SERCOM: 0,
|
||||||
|
IRQVal: sam.IRQ_SERCOM0_2, // RXC interrupt
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sampleRate16X = 16
|
sampleRate16X = 16
|
||||||
lsbFirst = 1
|
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
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Configure the UART.
|
// Configure the UART.
|
||||||
func (uart UART) Configure(config UARTConfig) {
|
func (uart UART) Configure(config UARTConfig) error {
|
||||||
// Default baud rate to 115200.
|
// Default baud rate to 115200.
|
||||||
if config.BaudRate == 0 {
|
if config.BaudRate == 0 {
|
||||||
config.BaudRate = 115200
|
config.BaudRate = 115200
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine pins
|
// determine pins
|
||||||
if config.TX == 0 {
|
if config.TX == 0 && config.RX == 0 {
|
||||||
// use default pins
|
// use default pins
|
||||||
config.TX = UART_TX_PIN
|
config.TX = UART_TX_PIN
|
||||||
config.RX = UART_RX_PIN
|
config.RX = UART_RX_PIN
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine pads
|
// Determine transmit pinout.
|
||||||
var txpad, rxpad int
|
txPinMode, txPad, ok := findPinPadMapping(uart.SERCOM, config.TX)
|
||||||
switch config.TX {
|
if !ok {
|
||||||
case PA04:
|
return ErrInvalidOutputPin
|
||||||
txpad = sercomTXPad0
|
}
|
||||||
case PA10:
|
var txPinOut uint32
|
||||||
txpad = sercomTXPad2
|
// See CTRLA.RXPO bits of the SERCOM USART peripheral (page 945-946) for how
|
||||||
case PA18:
|
// pads are mapped to pinout values.
|
||||||
txpad = sercomTXPad2
|
switch txPad {
|
||||||
case PA16:
|
case 0:
|
||||||
txpad = sercomTXPad0
|
txPinOut = 0
|
||||||
default:
|
default:
|
||||||
panic("Invalid TX pin for UART")
|
// TODO: flow control (RTS/CTS)
|
||||||
|
return ErrInvalidOutputPin
|
||||||
}
|
}
|
||||||
|
|
||||||
switch config.RX {
|
// Determine receive pinout.
|
||||||
case PA06:
|
rxPinMode, rxPad, ok := findPinPadMapping(uart.SERCOM, config.RX)
|
||||||
rxpad = sercomRXPad2
|
if !ok {
|
||||||
case PA07:
|
return ErrInvalidInputPin
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
|
// 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
|
// configure pins
|
||||||
config.TX.Configure(PinConfig{Mode: uart.Mode})
|
config.TX.Configure(PinConfig{Mode: txPinMode})
|
||||||
config.RX.Configure(PinConfig{Mode: uart.Mode})
|
config.RX.Configure(PinConfig{Mode: rxPinMode})
|
||||||
|
|
||||||
// reset SERCOM0
|
// reset SERCOM
|
||||||
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_INT_CTRLA_SWRST)
|
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_INT_CTRLA_SWRST)
|
||||||
for uart.Bus.CTRLA.HasBits(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) {
|
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...
|
// set UART pads. This is not same as pins...
|
||||||
// SERCOM_USART_CTRLA_TXPO(txPad) |
|
// SERCOM_USART_CTRLA_TXPO(txPad) |
|
||||||
// SERCOM_USART_CTRLA_RXPO(rxPad);
|
// SERCOM_USART_CTRLA_RXPO(rxPad);
|
||||||
uart.Bus.CTRLA.SetBits(uint32((txpad << sam.SERCOM_USART_INT_CTRLA_TXPO_Pos) |
|
uart.Bus.CTRLA.SetBits((txPinOut << sam.SERCOM_USART_INT_CTRLA_TXPO_Pos) |
|
||||||
(rxpad << sam.SERCOM_USART_INT_CTRLA_RXPO_Pos)))
|
(rxPinOut << sam.SERCOM_USART_INT_CTRLA_RXPO_Pos))
|
||||||
|
|
||||||
// Enable Transceiver and Receiver
|
// Enable Transceiver and Receiver
|
||||||
//sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ;
|
//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)
|
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INT_INTENSET_RXC)
|
||||||
|
|
||||||
// Enable RX IRQ.
|
// Enable RX IRQ.
|
||||||
switch uart.Bus {
|
// This is a small note at the bottom of the NVIC section of the datasheet:
|
||||||
case sam.SERCOM0_USART_INT:
|
// > The integer number specified in the source refers to the respective bit
|
||||||
arm.EnableIRQ(sam.IRQ_SERCOM0_0)
|
// > position in the INTFLAG register of respective peripheral.
|
||||||
arm.EnableIRQ(sam.IRQ_SERCOM0_1)
|
// Therefore, if we only need to listen to the RXC interrupt source (in bit
|
||||||
arm.EnableIRQ(sam.IRQ_SERCOM0_2)
|
// position 2), we only need interrupt source 2 for this SERCOM device.
|
||||||
arm.EnableIRQ(sam.IRQ_SERCOM0_OTHER)
|
arm.EnableIRQ(uart.IRQVal)
|
||||||
default:
|
|
||||||
// Currently assumes SERCOM3
|
return nil
|
||||||
arm.EnableIRQ(sam.IRQ_SERCOM3_0)
|
|
||||||
arm.EnableIRQ(sam.IRQ_SERCOM3_1)
|
|
||||||
arm.EnableIRQ(sam.IRQ_SERCOM3_2)
|
|
||||||
arm.EnableIRQ(sam.IRQ_SERCOM3_OTHER)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBaudRate sets the communication speed for the UART.
|
// SetBaudRate sets the communication speed for the UART.
|
||||||
@@ -639,53 +783,15 @@ func (uart UART) WriteByte(c byte) error {
|
|||||||
return nil
|
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
|
//go:export SERCOM3_2_IRQHandler
|
||||||
func handleSERCOM3_2() {
|
func handleSERCOM3_2() {
|
||||||
handleUART1()
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:export SERCOM3_OTHER_IRQHandler
|
|
||||||
func handleSERCOM3_OTHER() {
|
|
||||||
handleUART1()
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleUART1() {
|
|
||||||
// should reset IRQ
|
// should reset IRQ
|
||||||
UART1.Receive(byte((UART1.Bus.DATA.Get() & 0xFF)))
|
UART1.Receive(byte((UART1.Bus.DATA.Get() & 0xFF)))
|
||||||
UART1.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
|
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
|
//go:export SERCOM0_2_IRQHandler
|
||||||
func handleSERCOM0_2() {
|
func handleSERCOM0_2() {
|
||||||
handleUART2()
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:export SERCOM0_OTHER_IRQHandler
|
|
||||||
func handleSERCOM0_OTHER() {
|
|
||||||
handleUART2()
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleUART2() {
|
|
||||||
// should reset IRQ
|
// should reset IRQ
|
||||||
UART2.Receive(byte((UART2.Bus.DATA.Get() & 0xFF)))
|
UART2.Receive(byte((UART2.Bus.DATA.Get() & 0xFF)))
|
||||||
UART2.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
|
UART2.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
|
||||||
@@ -694,9 +800,7 @@ func handleUART2() {
|
|||||||
// I2C on the SAMD51.
|
// I2C on the SAMD51.
|
||||||
type I2C struct {
|
type I2C struct {
|
||||||
Bus *sam.SERCOM_I2CM_Type
|
Bus *sam.SERCOM_I2CM_Type
|
||||||
SCL Pin
|
SERCOM uint8
|
||||||
SDA Pin
|
|
||||||
PinMode PinMode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2CConfig is used to store config info for I2C.
|
// I2CConfig is used to store config info for I2C.
|
||||||
@@ -729,12 +833,33 @@ const (
|
|||||||
const i2cTimeout = 1000
|
const i2cTimeout = 1000
|
||||||
|
|
||||||
// Configure is intended to setup the I2C interface.
|
// 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.
|
// Default I2C bus speed is 100 kHz.
|
||||||
if config.Frequency == 0 {
|
if config.Frequency == 0 {
|
||||||
config.Frequency = TWI_FREQ_100KHZ
|
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
|
// reset SERCOM
|
||||||
i2c.Bus.CTRLA.SetBits(sam.SERCOM_I2CM_CTRLA_SWRST)
|
i2c.Bus.CTRLA.SetBits(sam.SERCOM_I2CM_CTRLA_SWRST)
|
||||||
for i2c.Bus.CTRLA.HasBits(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
|
// enable pins
|
||||||
i2c.SDA.Configure(PinConfig{Mode: i2c.PinMode})
|
config.SDA.Configure(PinConfig{Mode: sdaPinMode})
|
||||||
i2c.SCL.Configure(PinConfig{Mode: i2c.PinMode})
|
config.SCL.Configure(PinConfig{Mode: sclPinMode})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBaudRate sets the communication speed for the I2C.
|
// SetBaudRate sets the communication speed for the I2C.
|
||||||
@@ -860,7 +987,7 @@ func (i2c I2C) WriteByte(data byte) error {
|
|||||||
timeout := i2cTimeout
|
timeout := i2cTimeout
|
||||||
for !i2c.Bus.INTFLAG.HasBits(sam.SERCOM_I2CM_INTFLAG_MB) {
|
for !i2c.Bus.INTFLAG.HasBits(sam.SERCOM_I2CM_INTFLAG_MB) {
|
||||||
// check for bus error
|
// 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")
|
return errors.New("I2C bus error")
|
||||||
}
|
}
|
||||||
timeout--
|
timeout--
|
||||||
@@ -930,14 +1057,7 @@ func (i2c I2C) readByte() byte {
|
|||||||
// SPI
|
// SPI
|
||||||
type SPI struct {
|
type SPI struct {
|
||||||
Bus *sam.SERCOM_SPIM_Type
|
Bus *sam.SERCOM_SPIM_Type
|
||||||
SCK Pin
|
SERCOM uint8
|
||||||
MOSI Pin
|
|
||||||
MISO Pin
|
|
||||||
DOpad int
|
|
||||||
DIpad int
|
|
||||||
SCKPinMode PinMode
|
|
||||||
MOSIPinMode PinMode
|
|
||||||
MISOPinMode PinMode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPIConfig is used to store config info for SPI.
|
// SPIConfig is used to store config info for SPI.
|
||||||
@@ -951,34 +1071,61 @@ type SPIConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configure is intended to setup the SPI interface.
|
// Configure is intended to setup the SPI interface.
|
||||||
func (spi SPI) Configure(config SPIConfig) {
|
func (spi SPI) Configure(config SPIConfig) error {
|
||||||
doPad := spi.DOpad
|
// Use default pins if not set.
|
||||||
diPad := spi.DIpad
|
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
|
// set default frequency
|
||||||
if config.Frequency == 0 {
|
if config.Frequency == 0 {
|
||||||
config.Frequency = 4000000
|
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.
|
// Disable SPI port.
|
||||||
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPIM_CTRLA_ENABLE)
|
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPIM_CTRLA_ENABLE)
|
||||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_ENABLE) {
|
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_ENABLE) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable pins
|
// enable pins
|
||||||
if spi.SCKPinMode == 0 {
|
config.SCK.Configure(PinConfig{Mode: sckPinMode})
|
||||||
spi.SCKPinMode = PinSERCOMAlt
|
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
|
// reset SERCOM
|
||||||
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_SWRST)
|
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_SWRST)
|
||||||
@@ -987,17 +1134,17 @@ func (spi SPI) Configure(config SPIConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set bit transfer order
|
// set bit transfer order
|
||||||
dataOrder := 0
|
dataOrder := uint32(0)
|
||||||
if config.LSBFirst {
|
if config.LSBFirst {
|
||||||
dataOrder = 1
|
dataOrder = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set SPI master
|
// Set SPI master
|
||||||
// SERCOM_SPIM_CTRLA_MODE_SPI_MASTER = 3
|
// SERCOM_SPIM_CTRLA_MODE_SPI_MASTER = 3
|
||||||
spi.Bus.CTRLA.Set(uint32((3 << sam.SERCOM_SPIM_CTRLA_MODE_Pos) |
|
spi.Bus.CTRLA.Set((3 << sam.SERCOM_SPIM_CTRLA_MODE_Pos) |
|
||||||
(doPad << sam.SERCOM_SPIM_CTRLA_DOPO_Pos) |
|
(dataOutPinout << sam.SERCOM_SPIM_CTRLA_DOPO_Pos) |
|
||||||
(diPad << sam.SERCOM_SPIM_CTRLA_DIPO_Pos) |
|
(dataInPinout << sam.SERCOM_SPIM_CTRLA_DIPO_Pos) |
|
||||||
(dataOrder << sam.SERCOM_SPIM_CTRLA_DORD_Pos)))
|
(dataOrder << sam.SERCOM_SPIM_CTRLA_DORD_Pos))
|
||||||
|
|
||||||
spi.Bus.CTRLB.SetBits((0 << sam.SERCOM_SPIM_CTRLB_CHSIZE_Pos) | // 8bit char size
|
spi.Bus.CTRLB.SetBits((0 << sam.SERCOM_SPIM_CTRLB_CHSIZE_Pos) | // 8bit char size
|
||||||
sam.SERCOM_SPIM_CTRLB_RXEN) // receive enable
|
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)
|
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_ENABLE)
|
||||||
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_ENABLE) {
|
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_ENABLE) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer writes/reads a single byte using the SPI interface.
|
// Transfer writes/reads a single byte using the SPI interface.
|
||||||
@@ -1049,19 +1198,6 @@ func (spi SPI) Transfer(w byte) (byte, error) {
|
|||||||
// PWM
|
// PWM
|
||||||
const period = 0xFFFF
|
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.
|
// Configure configures a PWM pin for output.
|
||||||
func (pwm PWM) Configure() {
|
func (pwm PWM) Configure() {
|
||||||
// Set pin as output
|
// Set pin as output
|
||||||
@@ -1176,34 +1312,6 @@ func (pwm PWM) setPinCfg(val uint8) {
|
|||||||
pwm.Pin.setPinCfg(val)
|
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
|
// setChannel sets the value for the correct channel for PWM on this pin
|
||||||
func (pwm PWM) setChannel(val uint32) {
|
func (pwm PWM) setChannel(val uint32) {
|
||||||
switch pwm.Pin {
|
switch pwm.Pin {
|
||||||
@@ -1227,6 +1335,8 @@ func (pwm PWM) setChannel(val uint32) {
|
|||||||
pwm.getTimer().CC[3].Set(val)
|
pwm.getTimer().CC[3].Set(val)
|
||||||
case PA22:
|
case PA22:
|
||||||
pwm.getTimer().CC[2].Set(val)
|
pwm.getTimer().CC[2].Set(val)
|
||||||
|
case PB31:
|
||||||
|
pwm.getTimer().CC[1].Set(val)
|
||||||
default:
|
default:
|
||||||
return // not supported on this pin
|
return // not supported on this pin
|
||||||
}
|
}
|
||||||
@@ -1255,6 +1365,8 @@ func (pwm PWM) setChannelBuffer(val uint32) {
|
|||||||
pwm.getTimer().CCBUF[3].Set(val)
|
pwm.getTimer().CCBUF[3].Set(val)
|
||||||
case PA22:
|
case PA22:
|
||||||
pwm.getTimer().CCBUF[2].Set(val)
|
pwm.getTimer().CCBUF[2].Set(val)
|
||||||
|
case PB31:
|
||||||
|
pwm.getTimer().CCBUF[1].Set(val)
|
||||||
default:
|
default:
|
||||||
return // not supported on this pin
|
return // not supported on this pin
|
||||||
}
|
}
|
||||||
@@ -1283,6 +1395,8 @@ func (pwm PWM) getMux() PinMode {
|
|||||||
return PinPWMG
|
return PinPWMG
|
||||||
case PA22:
|
case PA22:
|
||||||
return PinPWMG
|
return PinPWMG
|
||||||
|
case PB31:
|
||||||
|
return PinPWMF
|
||||||
default:
|
default:
|
||||||
return 0 // not supported on this pin
|
return 0 // not supported on this pin
|
||||||
}
|
}
|
||||||
@@ -1400,11 +1514,11 @@ func (usbcdc USBCDC) Configure(config UARTConfig) {
|
|||||||
// enable USB
|
// enable USB
|
||||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||||
|
|
||||||
// enable IRQ
|
// enable IRQ at highest priority
|
||||||
arm.EnableIRQ(sam.IRQ_USB_OTHER)
|
interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable()
|
||||||
arm.EnableIRQ(sam.IRQ_USB_SOF_HSOF)
|
interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable()
|
||||||
arm.EnableIRQ(sam.IRQ_USB_TRCPT0)
|
interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable()
|
||||||
arm.EnableIRQ(sam.IRQ_USB_TRCPT1)
|
interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePadCalibration() {
|
func handlePadCalibration() {
|
||||||
@@ -1450,27 +1564,7 @@ func handlePadCalibration() {
|
|||||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export USB_OTHER_IRQHandler
|
func handleUSBIRQ(interrupt.Interrupt) {
|
||||||
func handleUSBOther() {
|
|
||||||
handleUSBIRQ()
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:export USB_SOF_HSOF_IRQHandler
|
|
||||||
func handleUSBSOFHSOF() {
|
|
||||||
handleUSBIRQ()
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:export USB_TRCPT0_IRQHandler
|
|
||||||
func handleUSBTRCPT0() {
|
|
||||||
handleUSBIRQ()
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:export USB_TRCPT1_IRQHandler
|
|
||||||
func handleUSBTRCPT1() {
|
|
||||||
handleUSBIRQ()
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleUSBIRQ() {
|
|
||||||
// reset all interrupt flags
|
// reset all interrupt flags
|
||||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||||
@@ -2044,14 +2138,14 @@ func setEPINTENSET(ep uint32, val uint8) {
|
|||||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val)
|
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.
|
// to switch to the bootloader to flash new firmware.
|
||||||
func ResetProcessor() {
|
func ResetProcessor() {
|
||||||
arm.DisableInterrupts()
|
arm.DisableInterrupts()
|
||||||
|
|
||||||
// Perform magic reset into bootloader, as mentioned in
|
// Perform magic reset into bootloader, as mentioned in
|
||||||
// https://github.com/arduino/ArduinoCore-samd/issues/197
|
// 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()
|
arm.SystemReset()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,3 +6,48 @@
|
|||||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
|
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
|
||||||
//
|
//
|
||||||
package machine
|
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
|
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
|
||||||
//
|
//
|
||||||
package machine
|
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 (
|
import (
|
||||||
"device/sifive"
|
"device/sifive"
|
||||||
|
"runtime/interrupt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 16000000
|
func CPUFrequency() uint32 {
|
||||||
|
return 16000000
|
||||||
|
}
|
||||||
|
|
||||||
type PinMode uint8
|
type PinMode uint8
|
||||||
|
|
||||||
@@ -63,6 +66,23 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
// 115200 baud rate is 138.
|
// 115200 baud rate is 138.
|
||||||
sifive.UART0.DIV.Set(138)
|
sifive.UART0.DIV.Set(138)
|
||||||
sifive.UART0.TXCTRL.Set(sifive.UART_TXCTRL_ENABLE)
|
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) {
|
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 = (SPI_CFG(dev)->f_sys / (2 * frequency)) - 1;
|
||||||
div := CPU_FREQUENCY/(2*config.Frequency) - 1
|
div := CPUFrequency()/(2*config.Frequency) - 1
|
||||||
spi.Bus.DIV.Set(div)
|
spi.Bus.DIV.Set(div)
|
||||||
|
|
||||||
// set mode
|
// set mode
|
||||||
|
|||||||
@@ -8,6 +8,25 @@ import (
|
|||||||
"unsafe"
|
"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.
|
// Make it easier to directly write to I/O RAM.
|
||||||
var ioram = (*[0x400]volatile.Register8)(unsafe.Pointer(uintptr(0x04000000)))
|
var ioram = (*[0x400]volatile.Register8)(unsafe.Pointer(uintptr(0x04000000)))
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/arm"
|
|
||||||
"device/nrf"
|
"device/nrf"
|
||||||
"errors"
|
"errors"
|
||||||
|
"runtime/interrupt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -88,8 +88,9 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
nrf.UART0.INTENSET.Set(nrf.UART_INTENSET_RXDRDY_Msk)
|
nrf.UART0.INTENSET.Set(nrf.UART_INTENSET_RXDRDY_Msk)
|
||||||
|
|
||||||
// Enable RX IRQ.
|
// Enable RX IRQ.
|
||||||
arm.SetPriority(nrf.IRQ_UART0, 0xc0) // low priority
|
intr := interrupt.New(nrf.IRQ_UART0, UART0.handleInterrupt)
|
||||||
arm.EnableIRQ(nrf.IRQ_UART0)
|
intr.SetPriority(0xc0) // low priority
|
||||||
|
intr.Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBaudRate sets the communication speed for the UART.
|
// SetBaudRate sets the communication speed for the UART.
|
||||||
@@ -116,7 +117,7 @@ func (uart UART) WriteByte(c byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uart UART) handleInterrupt() {
|
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||||
if nrf.UART0.EVENTS_RXDRDY.Get() != 0 {
|
if nrf.UART0.EVENTS_RXDRDY.Get() != 0 {
|
||||||
uart.Receive(byte(nrf.UART0.RXD.Get()))
|
uart.Receive(byte(nrf.UART0.RXD.Get()))
|
||||||
nrf.UART0.EVENTS_RXDRDY.Set(0x0)
|
nrf.UART0.EVENTS_RXDRDY.Set(0x0)
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import (
|
|||||||
"device/nrf"
|
"device/nrf"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 16000000
|
func CPUFrequency() uint32 {
|
||||||
|
return 16000000
|
||||||
|
}
|
||||||
|
|
||||||
// Get peripheral and pin number for this GPIO pin.
|
// Get peripheral and pin number for this GPIO pin.
|
||||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||||
@@ -18,11 +20,6 @@ func (uart UART) setPins(tx, rx Pin) {
|
|||||||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export UART0_IRQHandler
|
|
||||||
func handleUART0() {
|
|
||||||
UART0.handleInterrupt()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i2c I2C) setPins(scl, sda Pin) {
|
func (i2c I2C) setPins(scl, sda Pin) {
|
||||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 64000000
|
func CPUFrequency() uint32 {
|
||||||
|
return 64000000
|
||||||
|
}
|
||||||
|
|
||||||
// Get peripheral and pin number for this GPIO pin.
|
// Get peripheral and pin number for this GPIO pin.
|
||||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||||
@@ -19,11 +21,6 @@ func (uart UART) setPins(tx, rx Pin) {
|
|||||||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export UARTE0_UART0_IRQHandler
|
|
||||||
func handleUART0() {
|
|
||||||
UART0.handleInterrupt()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i2c I2C) setPins(scl, sda Pin) {
|
func (i2c I2C) setPins(scl, sda Pin) {
|
||||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||||
|
|||||||
@@ -7,7 +7,61 @@ import (
|
|||||||
"unsafe"
|
"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.
|
// Get peripheral and pin number for this GPIO pin.
|
||||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||||
@@ -23,11 +77,6 @@ func (uart UART) setPins(tx, rx Pin) {
|
|||||||
nrf.UART0.PSEL.RXD.Set(uint32(rx))
|
nrf.UART0.PSEL.RXD.Set(uint32(rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export UARTE0_UART0_IRQHandler
|
|
||||||
func handleUART0() {
|
|
||||||
UART0.handleInterrupt()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i2c I2C) setPins(scl, sda Pin) {
|
func (i2c I2C) setPins(scl, sda Pin) {
|
||||||
i2c.Bus.PSEL.SCL.Set(uint32(scl))
|
i2c.Bus.PSEL.SCL.Set(uint32(scl))
|
||||||
i2c.Bus.PSEL.SDA.Set(uint32(sda))
|
i2c.Bus.PSEL.SDA.Set(uint32(sda))
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ package machine
|
|||||||
// Peripheral abstraction layer for the stm32.
|
// Peripheral abstraction layer for the stm32.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/arm"
|
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
"errors"
|
"errors"
|
||||||
|
"runtime/interrupt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 72000000
|
func CPUFrequency() uint32 {
|
||||||
|
return 72000000
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PinInput PinMode = 0 // Input mode
|
PinInput PinMode = 0 // Input mode
|
||||||
@@ -111,7 +113,7 @@ func (p Pin) Get() bool {
|
|||||||
type UART struct {
|
type UART struct {
|
||||||
Buffer *RingBuffer
|
Buffer *RingBuffer
|
||||||
Bus *stm32.USART_Type
|
Bus *stm32.USART_Type
|
||||||
IRQVal uint32
|
Interrupt interrupt.Interrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the UART.
|
// Configure the UART.
|
||||||
@@ -153,8 +155,8 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
uart.Bus.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
uart.Bus.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
||||||
|
|
||||||
// Enable RX IRQ
|
// Enable RX IRQ
|
||||||
arm.SetPriority(uart.IRQVal, 0xc0)
|
uart.Interrupt.SetPriority(0xc0)
|
||||||
arm.EnableIRQ(uart.IRQVal)
|
uart.Interrupt.Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBaudRate sets the communication speed for the UART.
|
// SetBaudRate sets the communication speed for the UART.
|
||||||
@@ -163,10 +165,10 @@ func (uart UART) SetBaudRate(br uint32) {
|
|||||||
var divider uint32
|
var divider uint32
|
||||||
if uart.Bus == stm32.USART1 {
|
if uart.Bus == stm32.USART1 {
|
||||||
// first divide by PCLK2 prescaler (div 1) and then desired baudrate
|
// first divide by PCLK2 prescaler (div 1) and then desired baudrate
|
||||||
divider = CPU_FREQUENCY / br
|
divider = CPUFrequency() / br
|
||||||
} else {
|
} else {
|
||||||
// first divide by PCLK1 prescaler (div 2) and then desired baudrate
|
// first divide by PCLK1 prescaler (div 2) and then desired baudrate
|
||||||
divider = CPU_FREQUENCY / 2 / br
|
divider = CPUFrequency() / 2 / br
|
||||||
}
|
}
|
||||||
uart.Bus.BRR.Set(divider)
|
uart.Bus.BRR.Set(divider)
|
||||||
}
|
}
|
||||||
@@ -180,6 +182,12 @@ func (uart UART) WriteByte(c byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||||
|
// this UART instance.
|
||||||
|
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||||
|
uart.Receive(byte((uart.Bus.DR.Get() & 0xFF)))
|
||||||
|
}
|
||||||
|
|
||||||
// SPI on the STM32.
|
// SPI on the STM32.
|
||||||
type SPI struct {
|
type SPI struct {
|
||||||
Bus *stm32.SPI_Type
|
Bus *stm32.SPI_Type
|
||||||
@@ -360,7 +368,7 @@ func (i2c I2C) Configure(config I2CConfig) {
|
|||||||
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_PE)
|
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_PE)
|
||||||
|
|
||||||
// pclk1 clock speed is main frequency divided by PCLK1 prescaler (div 2)
|
// pclk1 clock speed is main frequency divided by PCLK1 prescaler (div 2)
|
||||||
pclk1 := uint32(CPU_FREQUENCY / 2)
|
pclk1 := CPUFrequency() / 2
|
||||||
|
|
||||||
// set freqency range to PCLK1 clock speed in MHz
|
// set freqency range to PCLK1 clock speed in MHz
|
||||||
// aka setting the value 36 means to use 36 MHz clock
|
// aka setting the value 36 means to use 36 MHz clock
|
||||||
|
|||||||
@@ -5,11 +5,13 @@ package machine
|
|||||||
// Peripheral abstraction layer for the stm32.
|
// Peripheral abstraction layer for the stm32.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/arm"
|
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
|
"runtime/interrupt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 168000000
|
func CPUFrequency() uint32 {
|
||||||
|
return 168000000
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Mode Flag
|
// Mode Flag
|
||||||
@@ -201,8 +203,11 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
stm32.USART2.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
stm32.USART2.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
||||||
|
|
||||||
// Enable RX IRQ.
|
// Enable RX IRQ.
|
||||||
arm.SetPriority(stm32.IRQ_USART2, 0xc0)
|
intr := interrupt.New(stm32.IRQ_USART2, func(interrupt.Interrupt) {
|
||||||
arm.EnableIRQ(stm32.IRQ_USART2)
|
UART1.Receive(byte((stm32.USART2.DR.Get() & 0xFF)))
|
||||||
|
})
|
||||||
|
intr.SetPriority(0xc0)
|
||||||
|
intr.Enable()
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteByte writes a byte of data to the UART.
|
// WriteByte writes a byte of data to the UART.
|
||||||
@@ -213,8 +218,3 @@ func (uart UART) WriteByte(c byte) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export USART2_IRQHandler
|
|
||||||
func handleUSART2() {
|
|
||||||
UART1.Receive(byte((stm32.USART2.DR.Get() & 0xFF)))
|
|
||||||
}
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build sam stm32,!stm32f407 fe310
|
// +build !baremetal sam stm32,!stm32f407 fe310
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
+12
-13
@@ -3,7 +3,6 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
@@ -348,18 +347,18 @@ const cdcSize = iadDescriptorSize +
|
|||||||
|
|
||||||
// Bytes returns CDCDescriptor data.
|
// Bytes returns CDCDescriptor data.
|
||||||
func (d CDCDescriptor) Bytes() []byte {
|
func (d CDCDescriptor) Bytes() []byte {
|
||||||
buf := bytes.NewBuffer(make([]byte, 0))
|
var buf []byte
|
||||||
buf.Write(d.iad.Bytes())
|
buf = append(buf, d.iad.Bytes()...)
|
||||||
buf.Write(d.cif.Bytes())
|
buf = append(buf, d.cif.Bytes()...)
|
||||||
buf.Write(d.header.Bytes())
|
buf = append(buf, d.header.Bytes()...)
|
||||||
buf.Write(d.controlManagement.Bytes())
|
buf = append(buf, d.controlManagement.Bytes()...)
|
||||||
buf.Write(d.functionalDescriptor.Bytes())
|
buf = append(buf, d.functionalDescriptor.Bytes()...)
|
||||||
buf.Write(d.callManagement.Bytes())
|
buf = append(buf, d.callManagement.Bytes()...)
|
||||||
buf.Write(d.cifin.Bytes())
|
buf = append(buf, d.cifin.Bytes()...)
|
||||||
buf.Write(d.dif.Bytes())
|
buf = append(buf, d.dif.Bytes()...)
|
||||||
buf.Write(d.out.Bytes())
|
buf = append(buf, d.out.Bytes()...)
|
||||||
buf.Write(d.in.Bytes())
|
buf = append(buf, d.in.Bytes()...)
|
||||||
return buf.Bytes()
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
// MSCDescriptor is not used yet.
|
// MSCDescriptor is not used yet.
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build darwin linux,!baremetal
|
// +build darwin linux,!baremetal freebsd,!baremetal
|
||||||
|
|
||||||
package os
|
package os
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const GOARCH = "wasm"
|
|||||||
const TargetBits = 32
|
const TargetBits = 32
|
||||||
|
|
||||||
//go:extern __heap_base
|
//go:extern __heap_base
|
||||||
var heapStartSymbol unsafe.Pointer
|
var heapStartSymbol [0]byte
|
||||||
|
|
||||||
//go:export llvm.wasm.memory.size.i32
|
//go:export llvm.wasm.memory.size.i32
|
||||||
func wasm_memory_size(index int32) int32
|
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