mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-10 05:53:39 +00:00
Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8615d1007 | |||
| 9245337ecf | |||
| 8170f59440 | |||
| 622de53854 | |||
| 6cd9e3c348 | |||
| 33e2411b6a | |||
| fb1fc267ab | |||
| 490e377bba | |||
| fcedf0beaa | |||
| 4dadb31e18 | |||
| 5b81b835ba | |||
| a9a6d0ee63 | |||
| 19d5e05e37 | |||
| 9e599bac49 | |||
| 81e325205f | |||
| 71cbb1495e | |||
| efc824a103 | |||
| cb4f3f12e6 | |||
| d1b470f04e | |||
| 7aaa9e45a6 | |||
| 5c9930df3f | |||
| cf6b544cd9 | |||
| 097f628955 | |||
| 20a1c730a1 | |||
| 04f65f1189 | |||
| 2ce17a1892 | |||
| 0b9b293651 | |||
| f20c932bb9 | |||
| e7227df80d | |||
| b99175a436 | |||
| de3ffe0af7 | |||
| 1e47d9efac | |||
| 8a2e7bac04 | |||
| ae13db917f | |||
| 98dbbbfda6 | |||
| 475135f546 | |||
| 88fd2823df | |||
| 57a5b833b2 | |||
| c810628a20 | |||
| 51238fba50 | |||
| 7f829fe153 | |||
| 946184b8ba | |||
| 753162f4e0 | |||
| 0e6d2af028 | |||
| 0df4a7a35f | |||
| 9a17698d6a | |||
| 3ee47a9c1b | |||
| da7db81087 | |||
| 098fb5f39c | |||
| 47a975a44f | |||
| 83252448b0 | |||
| 222977a642 | |||
| 58565fa46d | |||
| ecaf9461ce | |||
| ae01904ab0 | |||
| 2194d447e9 | |||
| a21a039ac7 | |||
| a761f556ff | |||
| 29d65cb637 | |||
| 63005622ae | |||
| 510f145a3a | |||
| e5e324f93e | |||
| 4fa1fc6e72 | |||
| ccb803e35d | |||
| d1ac0138e6 | |||
| b132b5bc60 | |||
| 743254d5bc | |||
| 9d625a1ccb | |||
| b75f042c23 | |||
| 32c7f3baf9 | |||
| 1dc85ded47 | |||
| 26a0819119 | |||
| b59a46eef0 | |||
| 8a410b993b | |||
| e412a63a1c | |||
| a81face618 | |||
| 0ac216b093 | |||
| 9575fe628f | |||
| f4e8ea0d23 | |||
| 2d03f65d67 |
+53
-20
@@ -44,28 +44,40 @@ commands:
|
|||||||
command: |
|
command: |
|
||||||
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||||
sudo apt install ./google-chrome-stable_current_amd64.deb
|
sudo apt install ./google-chrome-stable_current_amd64.deb
|
||||||
|
install-xtensa-toolchain:
|
||||||
|
parameters:
|
||||||
|
variant:
|
||||||
|
type: string
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: "Install Xtensa toolchain"
|
||||||
|
command: |
|
||||||
|
curl -L https://github.com/espressif/crosstool-NG/releases/download/esp-2020r2/xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz -o xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
|
||||||
|
sudo tar -C /usr/local -xf xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
|
||||||
|
sudo ln -s /usr/local/xtensa-esp32-elf/bin/xtensa-esp32-elf-ld /usr/local/bin/xtensa-esp32-elf-ld
|
||||||
|
rm xtensa-esp32-elf-gcc8_2_0-esp-2020r2-<<parameters.variant>>.tar.gz
|
||||||
llvm-source-linux:
|
llvm-source-linux:
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-source-10-v0
|
- llvm-source-10-v1
|
||||||
- run:
|
- run:
|
||||||
name: "Fetch LLVM source"
|
name: "Fetch LLVM source"
|
||||||
command: make llvm-source
|
command: make llvm-source
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-source-10-v0
|
key: llvm-source-10-v1
|
||||||
paths:
|
paths:
|
||||||
- llvm-project
|
- llvm-project
|
||||||
build-wasi-libc:
|
build-wasi-libc:
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- wasi-libc-sysroot-v2
|
- wasi-libc-sysroot-v3
|
||||||
- run:
|
- run:
|
||||||
name: "Build wasi-libc"
|
name: "Build wasi-libc"
|
||||||
command: make wasi-libc
|
command: make wasi-libc
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: wasi-libc-sysroot-v2
|
key: wasi-libc-sysroot-v3
|
||||||
paths:
|
paths:
|
||||||
- lib/wasi-libc/sysroot
|
- lib/wasi-libc/sysroot
|
||||||
test-linux:
|
test-linux:
|
||||||
@@ -87,15 +99,16 @@ commands:
|
|||||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- wasi-libc-sysroot-systemclang-v1
|
- wasi-libc-sysroot-systemclang-v2
|
||||||
- run: make wasi-libc
|
- run: make wasi-libc
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: wasi-libc-sysroot-systemclang-v1
|
key: wasi-libc-sysroot-systemclang-v2
|
||||||
paths:
|
paths:
|
||||||
- lib/wasi-libc/sysroot
|
- lib/wasi-libc/sysroot
|
||||||
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./interp ./transform .
|
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./interp ./transform .
|
||||||
- run: make gen-device -j4
|
- run: make gen-device -j4
|
||||||
- run: make smoketest
|
- run: make smoketest XTENSA=0
|
||||||
|
- run: make tinygo-test
|
||||||
- run: make wasmtest
|
- run: make wasmtest
|
||||||
- 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 }}
|
||||||
@@ -121,6 +134,8 @@ commands:
|
|||||||
gcc-avr \
|
gcc-avr \
|
||||||
avr-libc
|
avr-libc
|
||||||
- install-node
|
- install-node
|
||||||
|
- install-xtensa-toolchain:
|
||||||
|
variant: "linux-amd64"
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||||
@@ -128,7 +143,7 @@ commands:
|
|||||||
- llvm-source-linux
|
- llvm-source-linux
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-build-10-linux-v0-assert
|
- llvm-build-10-linux-v1-assert
|
||||||
- run:
|
- run:
|
||||||
name: "Build LLVM"
|
name: "Build LLVM"
|
||||||
command: |
|
command: |
|
||||||
@@ -146,7 +161,7 @@ commands:
|
|||||||
make ASSERT=1 llvm-build
|
make ASSERT=1 llvm-build
|
||||||
fi
|
fi
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-build-10-linux-v0-assert
|
key: llvm-build-10-linux-v1-assert
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- run: make ASSERT=1
|
- run: make ASSERT=1
|
||||||
@@ -179,6 +194,8 @@ commands:
|
|||||||
gcc-avr \
|
gcc-avr \
|
||||||
avr-libc
|
avr-libc
|
||||||
- install-node
|
- install-node
|
||||||
|
- install-xtensa-toolchain:
|
||||||
|
variant: "linux-amd64"
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||||
@@ -186,7 +203,7 @@ commands:
|
|||||||
- llvm-source-linux
|
- llvm-source-linux
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-build-10-linux-v0
|
- llvm-build-10-linux-v1
|
||||||
- run:
|
- run:
|
||||||
name: "Build LLVM"
|
name: "Build LLVM"
|
||||||
command: |
|
command: |
|
||||||
@@ -204,7 +221,7 @@ commands:
|
|||||||
make llvm-build
|
make llvm-build
|
||||||
fi
|
fi
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-build-10-linux-v0
|
key: llvm-build-10-linux-v1
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- build-wasi-libc
|
- build-wasi-libc
|
||||||
@@ -250,23 +267,25 @@ commands:
|
|||||||
sudo tar -C /usr/local -xzf go1.14.darwin-amd64.tar.gz
|
sudo tar -C /usr/local -xzf go1.14.darwin-amd64.tar.gz
|
||||||
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
|
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu
|
||||||
|
- install-xtensa-toolchain:
|
||||||
|
variant: "macos"
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
- go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||||
- go-cache-macos-v2-{{ checksum "go.mod" }}
|
- go-cache-macos-v2-{{ checksum "go.mod" }}
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-source-10-macos-v0
|
- llvm-source-10-macos-v1
|
||||||
- run:
|
- run:
|
||||||
name: "Fetch LLVM source"
|
name: "Fetch LLVM source"
|
||||||
command: make llvm-source
|
command: make llvm-source
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-source-10-macos-v0
|
key: llvm-source-10-macos-v1
|
||||||
paths:
|
paths:
|
||||||
- llvm-project
|
- llvm-project
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- llvm-build-10-macos-v0
|
- llvm-build-10-macos-v1
|
||||||
- run:
|
- run:
|
||||||
name: "Build LLVM"
|
name: "Build LLVM"
|
||||||
command: |
|
command: |
|
||||||
@@ -278,17 +297,17 @@ commands:
|
|||||||
make llvm-build
|
make llvm-build
|
||||||
fi
|
fi
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: llvm-build-10-macos-v0
|
key: llvm-build-10-macos-v1
|
||||||
paths:
|
paths:
|
||||||
llvm-build
|
llvm-build
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- wasi-libc-sysroot-macos-v1
|
- wasi-libc-sysroot-macos-v2
|
||||||
- run:
|
- run:
|
||||||
name: "Build wasi-libc"
|
name: "Build wasi-libc"
|
||||||
command: make wasi-libc
|
command: make wasi-libc
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: wasi-libc-sysroot-macos-v1
|
key: wasi-libc-sysroot-macos-v2
|
||||||
paths:
|
paths:
|
||||||
- lib/wasi-libc/sysroot
|
- lib/wasi-libc/sysroot
|
||||||
- run:
|
- run:
|
||||||
@@ -322,41 +341,48 @@ commands:
|
|||||||
- run:
|
- run:
|
||||||
name: Create TinyGo user
|
name: Create TinyGo user
|
||||||
command: useradd -m tinygo
|
command: useradd -m tinygo
|
||||||
- run:
|
|
||||||
name: Become TinyGo user
|
|
||||||
command: su tinygo
|
|
||||||
- run:
|
- run:
|
||||||
name: Start SSH Agent
|
name: Start SSH Agent
|
||||||
|
user: tinygo
|
||||||
command: eval $(ssh-agent -s)
|
command: eval $(ssh-agent -s)
|
||||||
- run:
|
- run:
|
||||||
name: Add ARCH_RELEASE_SSH_PRIVATE_KEY identity
|
name: Add ARCH_RELEASE_SSH_PRIVATE_KEY identity
|
||||||
|
user: tinygo
|
||||||
command: echo "${ARCH_RELEASE_SSH_PRIVATE_KEY}" | tr -d '\r' | ssh-add -
|
command: echo "${ARCH_RELEASE_SSH_PRIVATE_KEY}" | tr -d '\r' | ssh-add -
|
||||||
- run:
|
- run:
|
||||||
name: Create SSH directory
|
name: Create SSH directory
|
||||||
|
user: tinygo
|
||||||
command: mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
command: mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
||||||
- run:
|
- run:
|
||||||
name: Add aur.archlinux.org to known hosts
|
name: Add aur.archlinux.org to known hosts
|
||||||
|
user: tinygo
|
||||||
command: ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts
|
command: ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts
|
||||||
- run:
|
- run:
|
||||||
name: Clone tinygo-bin repo
|
name: Clone tinygo-bin repo
|
||||||
|
user: tinygo
|
||||||
command: git clone ssh://aur@aur.archlinux.org/tinygo-bin.git ~/tinygo-bin
|
command: git clone ssh://aur@aur.archlinux.org/tinygo-bin.git ~/tinygo-bin
|
||||||
- run:
|
- run:
|
||||||
name: Update package version
|
name: Update package version
|
||||||
|
user: tinygo
|
||||||
command: sed -i -E "s/(pkgver=)(.*)$/\1${CIRCLE_TAG}/" ~/tinygo-bin/PKGBUILD
|
command: sed -i -E "s/(pkgver=)(.*)$/\1${CIRCLE_TAG}/" ~/tinygo-bin/PKGBUILD
|
||||||
- run:
|
- run:
|
||||||
name: Update file checksums
|
name: Update file checksums
|
||||||
|
user: tinygo
|
||||||
command: cd ~/tinygo-bin && updpkgsums
|
command: cd ~/tinygo-bin && updpkgsums
|
||||||
- run:
|
- run:
|
||||||
name: Update .SRCINFO
|
name: Update .SRCINFO
|
||||||
|
user: tinygo
|
||||||
command: cd ~/tinygo-bin && makepkg --printsrcinfo > .SRCINFO
|
command: cd ~/tinygo-bin && makepkg --printsrcinfo > .SRCINFO
|
||||||
# Commit the update
|
# Commit the update
|
||||||
- run:
|
- run:
|
||||||
name: Set git commit config
|
name: Set git commit config
|
||||||
|
user: tinygo
|
||||||
command: |
|
command: |
|
||||||
git config --global user.email "tinygo-bot@tinygo.org" &&
|
git config --global user.email "tinygo-bot@tinygo.org" &&
|
||||||
git config --global user.name "TinyGo Release Bot"
|
git config --global user.name "TinyGo Release Bot"
|
||||||
- run:
|
- run:
|
||||||
name: Commit and push changes
|
name: Commit and push changes
|
||||||
|
user: tinygo
|
||||||
command: |
|
command: |
|
||||||
cd ~/tinygo-bin &&
|
cd ~/tinygo-bin &&
|
||||||
git commit -a -m "Update tinygo-bin to v${CIRCLE_TAG}" &&
|
git commit -a -m "Update tinygo-bin to v${CIRCLE_TAG}" &&
|
||||||
@@ -387,6 +413,12 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- test-linux:
|
- test-linux:
|
||||||
llvm: "10"
|
llvm: "10"
|
||||||
|
test-llvm10-go115:
|
||||||
|
docker:
|
||||||
|
- image: circleci/golang:1.15-buster
|
||||||
|
steps:
|
||||||
|
- test-linux:
|
||||||
|
llvm: "10"
|
||||||
assert-test-linux:
|
assert-test-linux:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.14-stretch
|
- image: circleci/golang:1.14-stretch
|
||||||
@@ -418,6 +450,7 @@ workflows:
|
|||||||
- test-llvm10-go112
|
- test-llvm10-go112
|
||||||
- test-llvm10-go113
|
- test-llvm10-go113
|
||||||
- test-llvm10-go114
|
- test-llvm10-go114
|
||||||
|
- test-llvm10-go115
|
||||||
- build-linux
|
- build-linux
|
||||||
- build-macos
|
- build-macos
|
||||||
- assert-test-linux
|
- assert-test-linux
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ docs/_build
|
|||||||
src/device/avr/*.go
|
src/device/avr/*.go
|
||||||
src/device/avr/*.ld
|
src/device/avr/*.ld
|
||||||
src/device/avr/*.s
|
src/device/avr/*.s
|
||||||
|
src/device/esp/*.go
|
||||||
src/device/nrf/*.go
|
src/device/nrf/*.go
|
||||||
src/device/nrf/*.s
|
src/device/nrf/*.s
|
||||||
src/device/nxp/*.go
|
src/device/nxp/*.go
|
||||||
|
|||||||
@@ -1,3 +1,83 @@
|
|||||||
|
0.15.0
|
||||||
|
---
|
||||||
|
|
||||||
|
* **command-line**
|
||||||
|
- add cached GOROOT to info subcommand
|
||||||
|
- embed git-hash in tinygo-dev executable
|
||||||
|
- implement tinygo targets to list usable targets
|
||||||
|
- use simpler file copy instead of file renaming to avoid issues on nrf52840 UF2 bootloaders
|
||||||
|
- use ToSlash() to specify program path
|
||||||
|
- support flashing esp32/esp8266 directly from tinygo
|
||||||
|
- when flashing call PortReset only on other than openocd
|
||||||
|
* **compiler**
|
||||||
|
- `compileopts`: add support for custom binary formats
|
||||||
|
- `compiler`: improve display of goroutine wrappers
|
||||||
|
- `interp`: don't panic in the Store method
|
||||||
|
- `interp`: replace some panics with error messages
|
||||||
|
- `interp`: show error line in first line of the traceback
|
||||||
|
- `loader`: be more robust when creating the cached GOROOT
|
||||||
|
- `loader`: rewrite/refactor much of the code to use go list directly
|
||||||
|
- `loader`: use ioutil.TempDir to create a temporary directory
|
||||||
|
- `stacksize`: deal with DW_CFA_advance_loc1
|
||||||
|
* **standard library**
|
||||||
|
- `runtime`: use waitForEvents when appropriate
|
||||||
|
* **wasm**
|
||||||
|
- `wasm`: Remove --no-threads from wasm-ld calls.
|
||||||
|
- `wasm`: update wasi-libc dependency
|
||||||
|
* **targets**
|
||||||
|
- `arduino-mega2560`: fix flashing on Windows
|
||||||
|
- `arm`: automatically determine stack sizes
|
||||||
|
- `arm64`: make dynamic loader structs and constants private
|
||||||
|
- `avr`: configure emulator in board files
|
||||||
|
- `cortexm`: fix stack size calculation with interrupts
|
||||||
|
- `flash`: add openocd settings to atsamd21 / atsamd51
|
||||||
|
- `flash`: add openocd settings to nrf5
|
||||||
|
- `microbit`: reelboard: flash using OpenOCD when needed
|
||||||
|
- `nintendoswitch`: Add dynamic loader for runtime loading PIE sections
|
||||||
|
- `nintendoswitch`: fix import cycle on dynamic_arm64.go
|
||||||
|
- `nintendoswitch`: Fix invalid memory read / write in print calls
|
||||||
|
- `nintendoswitch`: simplified assembly code
|
||||||
|
- `nintendoswitch`: support outputting .nro files directly
|
||||||
|
* **boards**
|
||||||
|
- `arduino-zero`: Adding support for the Arduino Zero (#1365)
|
||||||
|
- `atsamd2x`: fix BAUD value
|
||||||
|
- `atsamd5x`: fix BAUD value
|
||||||
|
- `bluepill`: Enable stm32's USART2 for the board and map it to UART1 tinygo's device
|
||||||
|
- `device/atsamd51x`: add all remaining bitfield values for PCHCTRLm Mapping
|
||||||
|
- `esp32`: add libgcc ROM functions to linker script
|
||||||
|
- `esp32`: add SPI support
|
||||||
|
- `esp32`: add support for basic GPIO
|
||||||
|
- `esp32`: add support for the Espressif ESP32 chip
|
||||||
|
- `esp32`: configure the I/O matrix for GPIO pins
|
||||||
|
- `esp32`: export machine.PortMask* for bitbanging implementations
|
||||||
|
- `esp8266`: add support for this chip
|
||||||
|
- `machine/atsamd51x,runtime/atsamd51x`: fixes needed for full support for all PWM pins. Also adds some useful constants to clarify peripheral clock usage
|
||||||
|
- `machine/itsybitsy-nrf52840`: add support for Adafruit Itsybitsy nrf52840 (#1243)
|
||||||
|
- `machine/stm32f4`: refactor common code and add new build tag stm32f4 (#1332)
|
||||||
|
- `nrf`: add SoftDevice support for the Circuit Playground Bluefruit
|
||||||
|
- `nrf`: call sd_app_evt_wait when the SoftDevice is enabled
|
||||||
|
- `nrf52840`: add build tags for SoftDevice support
|
||||||
|
- `nrf52840`: use higher priority for USB-CDC code
|
||||||
|
- `runtime/atsamd51x`: use PCHCTRL_GCLK_SERCOMX_SLOW for setting clocks on all SERCOM ports
|
||||||
|
- `stm32f405`: add basic UART handler
|
||||||
|
- `stm32f405`: add STM32F405 machine/runtime, and new board/target feather-stm32f405
|
||||||
|
* **build**
|
||||||
|
- `all`: run test binaries in the correct directory
|
||||||
|
- `build`: Fix arch release job
|
||||||
|
- `ci`: run `tinygo test` for known-working packages
|
||||||
|
- `ci`: set git-fetch-depth to 1
|
||||||
|
- `docker`: fix the problem with the wasm build (#1357)
|
||||||
|
- `Makefile`: check whether submodules have been downloaded in some common cases
|
||||||
|
* **docs**
|
||||||
|
- add ESP32, ESP8266, and Adafruit Feather STM32F405 to list of supported boards
|
||||||
|
|
||||||
|
0.14.1
|
||||||
|
---
|
||||||
|
* **command-line**
|
||||||
|
- support for Go 1.15
|
||||||
|
* **compiler**
|
||||||
|
- loader: work around Windows symlink limitation
|
||||||
|
|
||||||
0.14.0
|
0.14.0
|
||||||
---
|
---
|
||||||
* **command-line**
|
* **command-line**
|
||||||
|
|||||||
+1
-1
@@ -15,4 +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>
|
Nia Weiss <niaow1234@gmail.com>
|
||||||
|
|||||||
+4
-1
@@ -27,7 +27,10 @@ COPY --from=tinygo-base /go/bin/tinygo /go/bin/tinygo
|
|||||||
COPY --from=tinygo-base /tinygo/src /tinygo/src
|
COPY --from=tinygo-base /tinygo/src /tinygo/src
|
||||||
COPY --from=tinygo-base /tinygo/targets /tinygo/targets
|
COPY --from=tinygo-base /tinygo/targets /tinygo/targets
|
||||||
|
|
||||||
RUN apt-get install -y libllvm10 lld-10
|
RUN cd /tinygo/ && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y make clang-10 libllvm10 lld-10 && \
|
||||||
|
make wasi-libc
|
||||||
|
|
||||||
# tinygo-avr stage installs the needed dependencies to compile TinyGo programs for AVR microcontrollers.
|
# tinygo-avr stage installs the needed dependencies to compile TinyGo programs for AVR microcontrollers.
|
||||||
FROM tinygo-base AS tinygo-avr
|
FROM tinygo-base AS tinygo-avr
|
||||||
|
|||||||
@@ -118,9 +118,10 @@ fmt-check:
|
|||||||
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
|
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
|
||||||
|
|
||||||
|
|
||||||
gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32 gen-device-kendryte gen-device-nxp
|
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32 gen-device-kendryte gen-device-nxp
|
||||||
|
|
||||||
gen-device-avr:
|
gen-device-avr:
|
||||||
|
@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||||
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
|
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
|
||||||
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
|
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
|
||||||
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
|
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
|
||||||
@@ -129,6 +130,10 @@ gen-device-avr:
|
|||||||
build/gen-device-svd: ./tools/gen-device-svd/*.go
|
build/gen-device-svd: ./tools/gen-device-svd/*.go
|
||||||
$(GO) build -o $@ ./tools/gen-device-svd/
|
$(GO) build -o $@ ./tools/gen-device-svd/
|
||||||
|
|
||||||
|
gen-device-esp: build/gen-device-svd
|
||||||
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif-Community -interrupts=software lib/cmsis-svd/data/Espressif-Community/ src/device/esp/
|
||||||
|
GO111MODULE=off $(GO) fmt ./src/device/esp
|
||||||
|
|
||||||
gen-device-nrf: build/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/
|
./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
|
||||||
@@ -156,13 +161,13 @@ gen-device-stm32: build/gen-device-svd
|
|||||||
|
|
||||||
# Get LLVM sources.
|
# Get LLVM sources.
|
||||||
$(LLVM_PROJECTDIR)/README.md:
|
$(LLVM_PROJECTDIR)/README.md:
|
||||||
git clone -b release/10.x --depth=1 https://github.com/llvm/llvm-project $(LLVM_PROJECTDIR)
|
git clone -b xtensa_release_10.0.1 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
|
||||||
llvm-source: $(LLVM_PROJECTDIR)/README.md
|
llvm-source: $(LLVM_PROJECTDIR)/README.md
|
||||||
|
|
||||||
# Configure LLVM.
|
# Configure LLVM.
|
||||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||||
$(LLVM_BUILDDIR)/build.ninja: llvm-source
|
$(LLVM_BUILDDIR)/build.ninja: llvm-source
|
||||||
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF $(LLVM_OPTION)
|
mkdir -p $(LLVM_BUILDDIR); cd $(LLVM_BUILDDIR); cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF $(LLVM_OPTION)
|
||||||
|
|
||||||
# Build LLVM.
|
# Build LLVM.
|
||||||
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||||
@@ -173,19 +178,26 @@ $(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
|||||||
.PHONY: wasi-libc
|
.PHONY: wasi-libc
|
||||||
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
||||||
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||||
|
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||||
cd lib/wasi-libc && make -j4 WASM_CC=$(CLANG) WASM_AR=$(LLVM_AR) WASM_NM=$(LLVM_NM)
|
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 -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
|
||||||
|
|
||||||
test: wasi-libc
|
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 -buildmode exe -tags byollvm ./cgo ./compileopts ./interp ./transform .
|
||||||
|
|
||||||
|
# Test known-working standard library packages.
|
||||||
|
# TODO: do this in one command, parallelize, and only show failing tests (no
|
||||||
|
# implied -v flag).
|
||||||
|
.PHONY: tinygo-test
|
||||||
tinygo-test:
|
tinygo-test:
|
||||||
cd tests/tinygotest && tinygo test
|
$(TINYGO) test container/list
|
||||||
|
$(TINYGO) test container/ring
|
||||||
|
$(TINYGO) test text/scanner
|
||||||
|
|
||||||
.PHONY: smoketest
|
.PHONY: smoketest
|
||||||
smoketest:
|
smoketest:
|
||||||
@@ -267,7 +279,9 @@ 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=stm32f4disco-1 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=feather-stm32f405 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -315,6 +329,17 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=itsybitsy-nrf52840 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
# test pwm
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m4 examples/pwm
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=pyportal examples/pwm
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
ifneq ($(AVR), 0)
|
ifneq ($(AVR), 0)
|
||||||
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -330,6 +355,12 @@ ifneq ($(AVR), 0)
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
endif
|
||||||
|
ifneq ($(XTENSA), 0)
|
||||||
|
$(TINYGO) build -size short -o test.bin -target=esp32-wroom-32 examples/blinky1
|
||||||
|
@$(MD5SUM) test.bin
|
||||||
|
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
|
||||||
|
@$(MD5SUM) test.bin
|
||||||
endif
|
endif
|
||||||
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
@@ -342,8 +373,8 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=1 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=1 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -o test.elf -target=nintendoswitch examples/serial
|
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
|
||||||
@$(MD5SUM) test.elf
|
@$(MD5SUM) test.nro
|
||||||
|
|
||||||
wasmtest:
|
wasmtest:
|
||||||
$(GO) test ./tests/wasm
|
$(GO) test ./tests/wasm
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ 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 39 microcontroller boards/devices are currently supported:
|
The following 44 microcontroller boards are currently supported:
|
||||||
|
|
||||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
* [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)
|
||||||
@@ -51,8 +51,10 @@ The following 39 microcontroller boards/devices are currently supported:
|
|||||||
* [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 Feather nRF52840 Express](https://www.adafruit.com/product/4062)
|
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
|
||||||
|
* [Adafruit Feather STM32F405 Express](https://www.adafruit.com/product/4382)
|
||||||
* [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 ItsyBitsy nRF52840](https://www.adafruit.com/product/4481)
|
||||||
* [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 PyBadge](https://www.adafruit.com/product/4200)
|
||||||
* [Adafruit PyGamer](https://www.adafruit.com/product/4242)
|
* [Adafruit PyGamer](https://www.adafruit.com/product/4242)
|
||||||
@@ -62,8 +64,11 @@ The following 39 microcontroller boards/devices are currently supported:
|
|||||||
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
|
* [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)
|
||||||
|
* [Arduino Zero](https://store.arduino.cc/usa/arduino-zero)
|
||||||
* [BBC micro:bit](https://microbit.org/)
|
* [BBC micro:bit](https://microbit.org/)
|
||||||
* [Digispark](http://digistump.com/products/1)
|
* [Digispark](http://digistump.com/products/1)
|
||||||
|
* [ESP32](https://www.espressif.com/en/products/socs/esp32)
|
||||||
|
* [ESP8266](https://www.espressif.com/en/products/socs/esp8266)
|
||||||
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
|
||||||
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
|
* [Makerdiary nRF52840-MDK](https://wiki.makerdiary.com/nrf52840-mdk/)
|
||||||
* [Nintendo Switch](https://www.nintendo.com/switch/)
|
* [Nintendo Switch](https://www.nintendo.com/switch/)
|
||||||
|
|||||||
+12
-7
@@ -12,22 +12,27 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- task: GoTool@0
|
- task: GoTool@0
|
||||||
inputs:
|
inputs:
|
||||||
version: '1.14.1'
|
version: '1.15'
|
||||||
- checkout: self
|
- checkout: self
|
||||||
- task: CacheBeta@0
|
fetchDepth: 1
|
||||||
|
- task: Cache@2
|
||||||
displayName: Cache LLVM source
|
displayName: Cache LLVM source
|
||||||
inputs:
|
inputs:
|
||||||
key: llvm-source-10-windows-v0
|
key: llvm-source-10-windows-v1
|
||||||
path: llvm-project
|
path: llvm-project
|
||||||
- task: Bash@3
|
- task: Bash@3
|
||||||
displayName: Download LLVM source
|
displayName: Download LLVM source
|
||||||
inputs:
|
inputs:
|
||||||
targetType: inline
|
targetType: inline
|
||||||
script: make llvm-source
|
script: |
|
||||||
|
make llvm-source
|
||||||
|
# Workaround for bad symlinks:
|
||||||
|
# https://github.com/microsoft/azure-pipelines-tasks/issues/13418
|
||||||
|
rm -f llvm-project/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/bad_symlink
|
||||||
- task: CacheBeta@0
|
- task: CacheBeta@0
|
||||||
displayName: Cache LLVM build
|
displayName: Cache LLVM build
|
||||||
inputs:
|
inputs:
|
||||||
key: llvm-build-10-windows-v0
|
key: llvm-build-10-windows-v1
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- task: Bash@3
|
- task: Bash@3
|
||||||
displayName: Build LLVM
|
displayName: Build LLVM
|
||||||
@@ -47,7 +52,7 @@ jobs:
|
|||||||
- task: CacheBeta@0
|
- task: CacheBeta@0
|
||||||
displayName: Cache wasi-libc sysroot
|
displayName: Cache wasi-libc sysroot
|
||||||
inputs:
|
inputs:
|
||||||
key: wasi-libc-sysroot-v2
|
key: wasi-libc-sysroot-v3
|
||||||
path: lib/wasi-libc/sysroot
|
path: lib/wasi-libc/sysroot
|
||||||
- task: Bash@3
|
- task: Bash@3
|
||||||
displayName: Build wasi-libc
|
displayName: Build wasi-libc
|
||||||
@@ -80,4 +85,4 @@ jobs:
|
|||||||
script: |
|
script: |
|
||||||
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
|
||||||
unset GOROOT
|
unset GOROOT
|
||||||
make smoketest TINYGO=build/tinygo AVR=0
|
make smoketest TINYGO=build/tinygo AVR=0 XTENSA=0
|
||||||
|
|||||||
+287
-80
@@ -5,6 +5,7 @@ package builder
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -23,22 +24,35 @@ import (
|
|||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BuildResult is the output of a build. This includes the binary itself and
|
||||||
|
// some other metadata that is obtained while building the binary.
|
||||||
|
type BuildResult struct {
|
||||||
|
// A path to the output binary. It will be removed after Build returns, so
|
||||||
|
// if it should be kept it must be copied or moved away.
|
||||||
|
Binary string
|
||||||
|
|
||||||
|
// The directory of the main package. This is useful for testing as the test
|
||||||
|
// binary must be run in the directory of the tested package.
|
||||||
|
MainDir string
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
// name, an output path, and set of compile options and from that it manages the
|
// name, an output path, and set of compile options and from that it manages the
|
||||||
// whole compilation process.
|
// whole compilation process.
|
||||||
//
|
//
|
||||||
// The error value may be of type *MultiError. Callers will likely want to check
|
// The error value may be of type *MultiError. Callers will likely want to check
|
||||||
// for this case and print such errors individually.
|
// for this case and print such errors individually.
|
||||||
func Build(pkgName, outpath string, config *compileopts.Config, action func(string) error) error {
|
func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
|
||||||
// Compile Go code to IR.
|
// Compile Go code to IR.
|
||||||
machine, err := compiler.NewTargetMachine(config)
|
machine, err := compiler.NewTargetMachine(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mod, extraFiles, extraLDFlags, errs := compiler.Compile(pkgName, machine, config)
|
buildOutput, errs := compiler.Compile(pkgName, machine, config)
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
return newMultiError(errs)
|
return newMultiError(errs)
|
||||||
}
|
}
|
||||||
|
mod := buildOutput.Mod
|
||||||
|
|
||||||
if config.Options.PrintIR {
|
if config.Options.PrintIR {
|
||||||
fmt.Println("; Generated LLVM IR:")
|
fmt.Println("; Generated LLVM IR:")
|
||||||
@@ -115,6 +129,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure stack sizes are loaded from a separate section so they can be
|
||||||
|
// modified after linking.
|
||||||
|
var stackSizeLoads []string
|
||||||
|
if config.AutomaticStackSize() {
|
||||||
|
stackSizeLoads = transform.CreateStackSizeLoads(mod, config)
|
||||||
|
}
|
||||||
|
|
||||||
// Generate output.
|
// Generate output.
|
||||||
outext := filepath.Ext(outpath)
|
outext := filepath.Ext(outpath)
|
||||||
switch outext {
|
switch outext {
|
||||||
@@ -188,7 +209,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compile C files in packages.
|
// Compile C files in packages.
|
||||||
for i, file := range extraFiles {
|
for i, file := range buildOutput.ExtraFiles {
|
||||||
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+filepath.Base(file)+".o")
|
outpath := filepath.Join(dir, "pkg"+strconv.Itoa(i)+"-"+filepath.Base(file)+".o")
|
||||||
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
|
err := runCCompiler(config.Target.Compiler, append(config.CFlags(), "-c", "-o", outpath, file)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -197,8 +218,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
|||||||
ldflags = append(ldflags, outpath)
|
ldflags = append(ldflags, outpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(extraLDFlags) > 0 {
|
if len(buildOutput.ExtraLDFlags) > 0 {
|
||||||
ldflags = append(ldflags, extraLDFlags...)
|
ldflags = append(ldflags, buildOutput.ExtraLDFlags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link the object files together.
|
// Link the object files together.
|
||||||
@@ -207,6 +228,26 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
|||||||
return &commandError{"failed to link", executable, err}
|
return &commandError{"failed to link", executable, err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var calculatedStacks []string
|
||||||
|
var stackSizes map[string]functionStackSize
|
||||||
|
if config.Options.PrintStacks || config.AutomaticStackSize() {
|
||||||
|
// Try to determine stack sizes at compile time.
|
||||||
|
// Don't do this by default as it usually doesn't work on
|
||||||
|
// unsupported architectures.
|
||||||
|
calculatedStacks, stackSizes, err = determineStackSizes(mod, executable)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if config.AutomaticStackSize() {
|
||||||
|
// Modify the .tinygo_stacksizes section that contains a stack size
|
||||||
|
// for each goroutine.
|
||||||
|
err = modifyStackSizes(executable, stackSizeLoads, stackSizes)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not modify stack sizes: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
|
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
|
||||||
sizes, err := loadProgramSize(executable)
|
sizes, err := loadProgramSize(executable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -228,28 +269,250 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
|||||||
|
|
||||||
// Print goroutine stack sizes, as far as possible.
|
// Print goroutine stack sizes, as far as possible.
|
||||||
if config.Options.PrintStacks {
|
if config.Options.PrintStacks {
|
||||||
printStacks(mod, executable)
|
printStacks(calculatedStacks, stackSizes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get an Intel .hex file or .bin file from the .elf file.
|
// Get an Intel .hex file or .bin file from the .elf file.
|
||||||
if outext == ".hex" || outext == ".bin" || outext == ".gba" {
|
outputBinaryFormat := config.BinaryFormat(outext)
|
||||||
|
switch outputBinaryFormat {
|
||||||
|
case "elf":
|
||||||
|
// do nothing, file is already in ELF format
|
||||||
|
case "hex", "bin":
|
||||||
|
// Extract raw binary, either encoding it as a hex file or as a raw
|
||||||
|
// firmware file.
|
||||||
tmppath = filepath.Join(dir, "main"+outext)
|
tmppath = filepath.Join(dir, "main"+outext)
|
||||||
err := objcopy(executable, tmppath)
|
err := objcopy(executable, tmppath, outputBinaryFormat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if outext == ".uf2" {
|
case "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, config.Target.UF2FamilyID)
|
err := convertELFFileToUF2File(executable, tmppath, config.Target.UF2FamilyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case "esp32", "esp8266":
|
||||||
|
// Special format for the ESP family of chips (parsed by the ROM
|
||||||
|
// bootloader).
|
||||||
|
tmppath = filepath.Join(dir, "main"+outext)
|
||||||
|
err := makeESPFirmareImage(executable, tmppath, outputBinaryFormat)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
|
||||||
}
|
}
|
||||||
return action(tmppath)
|
return action(BuildResult{
|
||||||
|
Binary: tmppath,
|
||||||
|
MainDir: buildOutput.MainDir,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// functionStackSizes keeps stack size information about a single function
|
||||||
|
// (usually a goroutine).
|
||||||
|
type functionStackSize struct {
|
||||||
|
humanName string
|
||||||
|
stackSize uint64
|
||||||
|
stackSizeType stacksize.SizeType
|
||||||
|
missingStackSize *stacksize.CallNode
|
||||||
|
}
|
||||||
|
|
||||||
|
// determineStackSizes tries to determine the stack sizes of all started
|
||||||
|
// goroutines and of the reset vector. The LLVM module is necessary to find
|
||||||
|
// functions that call a function pointer.
|
||||||
|
func determineStackSizes(mod llvm.Module, executable string) ([]string, map[string]functionStackSize, error) {
|
||||||
|
var callsIndirectFunction []string
|
||||||
|
gowrappers := []string{}
|
||||||
|
gowrapperNames := make(map[string]string)
|
||||||
|
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||||
|
// Determine which functions call a function pointer.
|
||||||
|
for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
||||||
|
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||||
|
if inst.IsACallInst().IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if callee := inst.CalledValue(); callee.IsAFunction().IsNil() && callee.IsAInlineAsm().IsNil() {
|
||||||
|
callsIndirectFunction = append(callsIndirectFunction, fn.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a list of "go wrappers", small wrapper functions that decode
|
||||||
|
// parameters when starting a new goroutine.
|
||||||
|
attr := fn.GetStringAttributeAtIndex(-1, "tinygo-gowrapper")
|
||||||
|
if !attr.IsNil() {
|
||||||
|
gowrappers = append(gowrappers, fn.Name())
|
||||||
|
gowrapperNames[fn.Name()] = attr.GetStringValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(gowrappers)
|
||||||
|
|
||||||
|
// Load the ELF binary.
|
||||||
|
f, err := elf.Open(executable)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("could not load executable for stack size analysis: %w", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// Determine the frame size of each function (if available) and the callgraph.
|
||||||
|
functions, err := stacksize.CallGraph(f, callsIndirectFunction)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("could not parse executable for stack size analysis: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Goroutines need to be started and finished and take up some stack space
|
||||||
|
// that way. This can be measured by measuing the stack size of
|
||||||
|
// tinygo_startTask.
|
||||||
|
if numFuncs := len(functions["tinygo_startTask"]); numFuncs != 1 {
|
||||||
|
return nil, nil, fmt.Errorf("expected exactly one definition of tinygo_startTask, got %d", numFuncs)
|
||||||
|
}
|
||||||
|
baseStackSize, baseStackSizeType, baseStackSizeFailedAt := functions["tinygo_startTask"][0].StackSize()
|
||||||
|
|
||||||
|
sizes := make(map[string]functionStackSize)
|
||||||
|
|
||||||
|
// Add the reset handler function, for convenience. The reset handler runs
|
||||||
|
// startup code and the scheduler. The listed stack size is not the full
|
||||||
|
// stack size: interrupts are not counted.
|
||||||
|
var resetFunction string
|
||||||
|
switch f.Machine {
|
||||||
|
case elf.EM_ARM:
|
||||||
|
// Note: all interrupts happen on this stack so the real size is bigger.
|
||||||
|
resetFunction = "Reset_Handler"
|
||||||
|
}
|
||||||
|
if resetFunction != "" {
|
||||||
|
funcs := functions[resetFunction]
|
||||||
|
if len(funcs) != 1 {
|
||||||
|
return nil, nil, fmt.Errorf("expected exactly one definition of %s in the callgraph, found %d", resetFunction, len(funcs))
|
||||||
|
}
|
||||||
|
stackSize, stackSizeType, missingStackSize := funcs[0].StackSize()
|
||||||
|
sizes[resetFunction] = functionStackSize{
|
||||||
|
stackSize: stackSize,
|
||||||
|
stackSizeType: stackSizeType,
|
||||||
|
missingStackSize: missingStackSize,
|
||||||
|
humanName: resetFunction,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all goroutine wrapper functions.
|
||||||
|
for _, name := range gowrappers {
|
||||||
|
funcs := functions[name]
|
||||||
|
if len(funcs) != 1 {
|
||||||
|
return nil, nil, fmt.Errorf("expected exactly one definition of %s in the callgraph, found %d", name, len(funcs))
|
||||||
|
}
|
||||||
|
humanName := gowrapperNames[name]
|
||||||
|
if humanName == "" {
|
||||||
|
humanName = name // fallback
|
||||||
|
}
|
||||||
|
stackSize, stackSizeType, missingStackSize := funcs[0].StackSize()
|
||||||
|
if baseStackSizeType != stacksize.Bounded {
|
||||||
|
// It was not possible to determine the stack size at compile time
|
||||||
|
// because tinygo_startTask does not have a fixed stack size. This
|
||||||
|
// can happen when using -opt=1.
|
||||||
|
stackSizeType = baseStackSizeType
|
||||||
|
missingStackSize = baseStackSizeFailedAt
|
||||||
|
} else if stackSize < baseStackSize {
|
||||||
|
// This goroutine has a very small stack, but still needs to fit all
|
||||||
|
// registers to start and suspend the goroutine. Otherwise a stack
|
||||||
|
// overflow will occur even before the goroutine is started.
|
||||||
|
stackSize = baseStackSize
|
||||||
|
}
|
||||||
|
sizes[name] = functionStackSize{
|
||||||
|
stackSize: stackSize,
|
||||||
|
stackSizeType: stackSizeType,
|
||||||
|
missingStackSize: missingStackSize,
|
||||||
|
humanName: humanName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resetFunction != "" {
|
||||||
|
return append([]string{resetFunction}, gowrappers...), sizes, nil
|
||||||
|
}
|
||||||
|
return gowrappers, sizes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// modifyStackSizes modifies the .tinygo_stacksizes section with the updated
|
||||||
|
// stack size information. Before this modification, all stack sizes in the
|
||||||
|
// section assume the default stack size (which is relatively big).
|
||||||
|
func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map[string]functionStackSize) error {
|
||||||
|
fp, err := os.OpenFile(executable, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fp.Close()
|
||||||
|
|
||||||
|
elfFile, err := elf.NewFile(fp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
section := elfFile.Section(".tinygo_stacksizes")
|
||||||
|
if section == nil {
|
||||||
|
return errors.New("could not find .tinygo_stacksizes section")
|
||||||
|
}
|
||||||
|
|
||||||
|
if section.Size != section.FileSize {
|
||||||
|
// Sanity check.
|
||||||
|
return fmt.Errorf("expected .tinygo_stacksizes to have identical size and file size, got %d and %d", section.Size, section.FileSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read all goroutine stack sizes.
|
||||||
|
data := make([]byte, section.Size)
|
||||||
|
_, err = fp.ReadAt(data, int64(section.Offset))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(stackSizeLoads)*4 != len(data) {
|
||||||
|
// Note: while AVR should use 2 byte stack sizes, even 64-bit platforms
|
||||||
|
// should probably stick to 4 byte stack sizes as a larger than 4GB
|
||||||
|
// stack doesn't make much sense.
|
||||||
|
return errors.New("expected 4 byte stack sizes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify goroutine stack sizes with a compile-time known worst case stack
|
||||||
|
// size.
|
||||||
|
for i, name := range stackSizeLoads {
|
||||||
|
fn, ok := stackSizes[name]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("could not find symbol %s in ELF file", name)
|
||||||
|
}
|
||||||
|
if fn.stackSizeType == stacksize.Bounded {
|
||||||
|
stackSize := uint32(fn.stackSize)
|
||||||
|
|
||||||
|
// Adding 4 for the stack canary. Even though the size may be
|
||||||
|
// automatically determined, stack overflow checking is still
|
||||||
|
// important as the stack size cannot be determined for all
|
||||||
|
// goroutines.
|
||||||
|
stackSize += 4
|
||||||
|
|
||||||
|
// Add stack size used by interrupts.
|
||||||
|
switch elfFile.Machine {
|
||||||
|
case elf.EM_ARM:
|
||||||
|
// On Cortex-M (assumed here), this stack size is 8 words or 32
|
||||||
|
// bytes. This is only to store the registers that the interrupt
|
||||||
|
// may modify, the interrupt will switch to the interrupt stack
|
||||||
|
// (MSP).
|
||||||
|
// Some background:
|
||||||
|
// https://interrupt.memfault.com/blog/cortex-m-rtos-context-switching
|
||||||
|
stackSize += 32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally write the stack size to the binary.
|
||||||
|
binary.LittleEndian.PutUint32(data[i*4:], stackSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write back the modified stack sizes.
|
||||||
|
_, err = fp.WriteAt(data, int64(section.Offset))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// printStacks prints the maximum stack depth for functions that are started as
|
// printStacks prints the maximum stack depth for functions that are started as
|
||||||
// goroutines. Stack sizes cannot always be determined statically, in particular
|
// goroutines. Stack sizes cannot always be determined statically, in particular
|
||||||
// recursive functions and functions that call interface methods or function
|
// recursive functions and functions that call interface methods or function
|
||||||
@@ -260,78 +523,22 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
|
|||||||
//
|
//
|
||||||
// function stack usage (in bytes)
|
// function stack usage (in bytes)
|
||||||
// Reset_Handler 316
|
// Reset_Handler 316
|
||||||
// .Lexamples/blinky2.led1 92
|
// examples/blinky2.led1 92
|
||||||
// .Lruntime.run$1 300
|
// runtime.run$1 300
|
||||||
func printStacks(mod llvm.Module, executable string) {
|
func printStacks(calculatedStacks []string, stackSizes map[string]functionStackSize) {
|
||||||
// Determine which functions call a function pointer.
|
|
||||||
var callsIndirectFunction []string
|
|
||||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
|
||||||
for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
|
|
||||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
|
||||||
if inst.IsACallInst().IsNil() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if callee := inst.CalledValue(); callee.IsAFunction().IsNil() && callee.IsAInlineAsm().IsNil() {
|
|
||||||
callsIndirectFunction = append(callsIndirectFunction, fn.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the ELF binary.
|
|
||||||
f, err := elf.Open(executable)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "could not load executable for stack size analysis:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
// Determine the frame size of each function (if available) and the callgraph.
|
|
||||||
functions, err := stacksize.CallGraph(f, callsIndirectFunction)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "could not parse executable for stack size analysis:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a list of "go wrappers", small wrapper functions that decode
|
|
||||||
// parameters when starting a new goroutine.
|
|
||||||
var gowrappers []string
|
|
||||||
for name := range functions {
|
|
||||||
if strings.HasSuffix(name, "$gowrapper") {
|
|
||||||
gowrappers = append(gowrappers, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(gowrappers)
|
|
||||||
|
|
||||||
switch f.Machine {
|
|
||||||
case elf.EM_ARM:
|
|
||||||
// Add the reset handler, which runs startup code and is the
|
|
||||||
// interrupt/scheduler stack with -scheduler=tasks.
|
|
||||||
// Note that because interrupts happen on this stack, the stack needed
|
|
||||||
// by just the Reset_Handler is not enough. Stacks needed by interrupt
|
|
||||||
// handlers should also be taken into account.
|
|
||||||
gowrappers = append([]string{"Reset_Handler"}, gowrappers...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print the sizes of all stacks.
|
// Print the sizes of all stacks.
|
||||||
fmt.Printf("%-32s %s\n", "function", "stack usage (in bytes)")
|
fmt.Printf("%-32s %s\n", "function", "stack usage (in bytes)")
|
||||||
for _, name := range gowrappers {
|
for _, name := range calculatedStacks {
|
||||||
for _, fn := range functions[name] {
|
fn := stackSizes[name]
|
||||||
stackSize, stackSizeType, missingStackSize := fn.StackSize()
|
switch fn.stackSizeType {
|
||||||
strippedName := name
|
case stacksize.Bounded:
|
||||||
if strings.HasSuffix(name, "$gowrapper") {
|
fmt.Printf("%-32s %d\n", fn.humanName, fn.stackSize)
|
||||||
strippedName = name[:len(name)-len("$gowrapper")]
|
case stacksize.Unknown:
|
||||||
}
|
fmt.Printf("%-32s unknown, %s does not have stack frame information\n", fn.humanName, fn.missingStackSize)
|
||||||
switch stackSizeType {
|
case stacksize.Recursive:
|
||||||
case stacksize.Bounded:
|
fmt.Printf("%-32s recursive, %s may call itself\n", fn.humanName, fn.missingStackSize)
|
||||||
fmt.Printf("%-32s %d\n", strippedName, stackSize)
|
case stacksize.IndirectCall:
|
||||||
case stacksize.Unknown:
|
fmt.Printf("%-32s unknown, %s calls a function pointer\n", fn.humanName, fn.missingStackSize)
|
||||||
fmt.Printf("%-32s unknown, %s does not have stack frame information\n", strippedName, missingStackSize)
|
|
||||||
case stacksize.Recursive:
|
|
||||||
fmt.Printf("%-32s recursive, %s may call itself\n", strippedName, missingStackSize)
|
|
||||||
case stacksize.IndirectCall:
|
|
||||||
fmt.Printf("%-32s unknown, %s calls a function pointer\n", strippedName, missingStackSize)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -25,8 +25,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
|
||||||
}
|
}
|
||||||
if major != 1 || minor < 11 || minor > 14 {
|
if major != 1 || minor < 11 || minor > 15 {
|
||||||
return nil, fmt.Errorf("requires go version 1.11, 1.12, 1.13, or 1.14, got go%d.%d", major, minor)
|
return nil, fmt.Errorf("requires go version 1.11 through 1.15, got go%d.%d", major, minor)
|
||||||
}
|
}
|
||||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||||
return &compileopts.Config{
|
return &compileopts.Config{
|
||||||
|
|||||||
+153
@@ -0,0 +1,153 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
// This file implements support for writing ESP image files. These image files
|
||||||
|
// are read by the ROM bootloader so have to be in a particular format.
|
||||||
|
//
|
||||||
|
// In the future, it may be necessary to implement support for other image
|
||||||
|
// formats, such as the ESP8266 image formats (again, used by the ROM bootloader
|
||||||
|
// to load the firmware).
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"debug/elf"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type espImageSegment struct {
|
||||||
|
addr uint32
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeESPFirmare converts an input ELF file to an image file for an ESP32 or
|
||||||
|
// ESP8266 chip. This is a special purpose image format just for the ESP chip
|
||||||
|
// family, and is parsed by the on-chip mask ROM bootloader.
|
||||||
|
//
|
||||||
|
// The following documentation has been used:
|
||||||
|
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format
|
||||||
|
// https://github.com/espressif/esp-idf/blob/8fbb63c2a701c22ccf4ce249f43aded73e134a34/components/bootloader_support/include/esp_image_format.h#L58
|
||||||
|
// https://github.com/espressif/esptool/blob/master/esptool.py
|
||||||
|
func makeESPFirmareImage(infile, outfile, format string) error {
|
||||||
|
inf, err := elf.Open(infile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer inf.Close()
|
||||||
|
|
||||||
|
// Load all segments to be written to the image. These are actually ELF
|
||||||
|
// sections, not true ELF segments (similar to how esptool does it).
|
||||||
|
var segments []*espImageSegment
|
||||||
|
for _, section := range inf.Sections {
|
||||||
|
if section.Type != elf.SHT_PROGBITS || section.Size == 0 || section.Flags&elf.SHF_ALLOC == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data, err := section.Data()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read section data: %w", err)
|
||||||
|
}
|
||||||
|
for len(data)%4 != 0 {
|
||||||
|
// Align segment to 4 bytes.
|
||||||
|
data = append(data, 0)
|
||||||
|
}
|
||||||
|
if uint64(uint32(section.Addr)) != section.Addr {
|
||||||
|
return fmt.Errorf("section address too big: 0x%x", section.Addr)
|
||||||
|
}
|
||||||
|
segments = append(segments, &espImageSegment{
|
||||||
|
addr: uint32(section.Addr),
|
||||||
|
data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the segments by address. This is what esptool does too.
|
||||||
|
sort.SliceStable(segments, func(i, j int) bool { return segments[i].addr < segments[j].addr })
|
||||||
|
|
||||||
|
// Calculate checksum over the segment data. This is used in the image
|
||||||
|
// footer.
|
||||||
|
checksum := uint8(0xef)
|
||||||
|
for _, segment := range segments {
|
||||||
|
for _, b := range segment.data {
|
||||||
|
checksum ^= b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write first to an in-memory buffer, primarily so that we can easily
|
||||||
|
// calculate a hash over the entire image.
|
||||||
|
// An added benefit is that we don't need to check for errors all the time.
|
||||||
|
outf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
// Image header.
|
||||||
|
switch format {
|
||||||
|
case "esp32":
|
||||||
|
// Header format:
|
||||||
|
// https://github.com/espressif/esp-idf/blob/8fbb63c2/components/bootloader_support/include/esp_image_format.h#L58
|
||||||
|
binary.Write(outf, binary.LittleEndian, struct {
|
||||||
|
magic uint8
|
||||||
|
segment_count uint8
|
||||||
|
spi_mode uint8
|
||||||
|
spi_speed_size uint8
|
||||||
|
entry_addr uint32
|
||||||
|
wp_pin uint8
|
||||||
|
spi_pin_drv [3]uint8
|
||||||
|
reserved [11]uint8
|
||||||
|
hash_appended bool
|
||||||
|
}{
|
||||||
|
magic: 0xE9,
|
||||||
|
segment_count: byte(len(segments)),
|
||||||
|
spi_mode: 0, // irrelevant, replaced by esptool when flashing
|
||||||
|
spi_speed_size: 0, // spi_speed, spi_size: replaced by esptool when flashing
|
||||||
|
entry_addr: uint32(inf.Entry),
|
||||||
|
wp_pin: 0xEE, // disable WP pin
|
||||||
|
hash_appended: true, // add a SHA256 hash
|
||||||
|
})
|
||||||
|
case "esp8266":
|
||||||
|
// Header format:
|
||||||
|
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format
|
||||||
|
// Basically a truncated version of the ESP32 header.
|
||||||
|
binary.Write(outf, binary.LittleEndian, struct {
|
||||||
|
magic uint8
|
||||||
|
segment_count uint8
|
||||||
|
spi_mode uint8
|
||||||
|
spi_speed_size uint8
|
||||||
|
entry_addr uint32
|
||||||
|
}{
|
||||||
|
magic: 0xE9,
|
||||||
|
segment_count: byte(len(segments)),
|
||||||
|
spi_mode: 0, // irrelevant, replaced by esptool when flashing
|
||||||
|
spi_speed_size: 0x20, // spi_speed, spi_size: replaced by esptool when flashing
|
||||||
|
entry_addr: uint32(inf.Entry),
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("builder: unknown binary format %#v, expected esp32 or esp8266", format)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write all segments to the image.
|
||||||
|
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format#segment
|
||||||
|
for _, segment := range segments {
|
||||||
|
binary.Write(outf, binary.LittleEndian, struct {
|
||||||
|
addr uint32
|
||||||
|
length uint32
|
||||||
|
}{
|
||||||
|
addr: segment.addr,
|
||||||
|
length: uint32(len(segment.data)),
|
||||||
|
})
|
||||||
|
outf.Write(segment.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Footer, including checksum.
|
||||||
|
// The entire image size must be a multiple of 16, so pad the image to one
|
||||||
|
// byte less than that before writing the checksum.
|
||||||
|
outf.Write(make([]byte, 15-outf.Len()%16))
|
||||||
|
outf.WriteByte(checksum)
|
||||||
|
|
||||||
|
if format == "esp32" {
|
||||||
|
// SHA256 hash (to protect against image corruption, not for security).
|
||||||
|
hash := sha256.Sum256(outf.Bytes())
|
||||||
|
outf.Write(hash[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the image to the output file.
|
||||||
|
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
|
||||||
|
}
|
||||||
+19
-14
@@ -4,12 +4,15 @@ import (
|
|||||||
"debug/elf"
|
"debug/elf"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/marcinbor85/gohex"
|
"github.com/marcinbor85/gohex"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// maxPadBytes is the maximum allowed bytes to be padded in a rom extraction
|
||||||
|
// this value is currently defined by Nintendo Switch Page Alignment (4096 bytes)
|
||||||
|
const maxPadBytes = 4095
|
||||||
|
|
||||||
// objcopyError is an error returned by functions that act like objcopy.
|
// objcopyError is an error returned by functions that act like objcopy.
|
||||||
type objcopyError struct {
|
type objcopyError struct {
|
||||||
Op string
|
Op string
|
||||||
@@ -71,7 +74,12 @@ func extractROM(path string) (uint64, []byte, error) {
|
|||||||
var rom []byte
|
var rom []byte
|
||||||
for _, prog := range progs {
|
for _, prog := range progs {
|
||||||
if prog.Paddr != progs[0].Paddr+uint64(len(rom)) {
|
if prog.Paddr != progs[0].Paddr+uint64(len(rom)) {
|
||||||
return 0, nil, objcopyError{"ROM segments are non-contiguous: " + path, nil}
|
diff := prog.Paddr - (progs[0].Paddr + uint64(len(rom)))
|
||||||
|
if diff > maxPadBytes {
|
||||||
|
return 0, nil, objcopyError{"ROM segments are non-contiguous: " + path, nil}
|
||||||
|
}
|
||||||
|
// Pad the difference
|
||||||
|
rom = append(rom, make([]byte, diff)...)
|
||||||
}
|
}
|
||||||
data, err := ioutil.ReadAll(prog.Open())
|
data, err := ioutil.ReadAll(prog.Open())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -93,7 +101,7 @@ func extractROM(path string) (uint64, []byte, error) {
|
|||||||
|
|
||||||
// objcopy converts an ELF file to a different (simpler) output file format:
|
// objcopy converts an ELF file to a different (simpler) output file format:
|
||||||
// .bin or .hex. It extracts only the .text section.
|
// .bin or .hex. It extracts only the .text section.
|
||||||
func objcopy(infile, outfile string) error {
|
func objcopy(infile, outfile, binaryFormat string) error {
|
||||||
f, err := os.OpenFile(outfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
|
f, err := os.OpenFile(outfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -107,23 +115,20 @@ func objcopy(infile, outfile string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write to the file, in the correct format.
|
// Write to the file, in the correct format.
|
||||||
switch filepath.Ext(outfile) {
|
switch binaryFormat {
|
||||||
case ".gba":
|
case "hex":
|
||||||
// The address is not stored in a .gba file.
|
// Intel hex file, includes the firmware start address.
|
||||||
_, err := f.Write(data)
|
|
||||||
return err
|
|
||||||
case ".bin":
|
|
||||||
// The address is not stored in a .bin file (therefore you
|
|
||||||
// should use .hex files in most cases).
|
|
||||||
_, err := f.Write(data)
|
|
||||||
return err
|
|
||||||
case ".hex":
|
|
||||||
mem := gohex.NewMemory()
|
mem := gohex.NewMemory()
|
||||||
err := mem.AddBinary(uint32(addr), data)
|
err := mem.AddBinary(uint32(addr), data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return objcopyError{"failed to create .hex file", err}
|
return objcopyError{"failed to create .hex file", err}
|
||||||
}
|
}
|
||||||
return mem.DumpIntelHex(f, 16)
|
return mem.DumpIntelHex(f, 16)
|
||||||
|
case "bin":
|
||||||
|
// The start address is not stored in raw firmware files (therefore you
|
||||||
|
// should use .hex files in most cases).
|
||||||
|
_, err := f.Write(data)
|
||||||
|
return err
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,6 +164,16 @@ func (c *Config) PanicStrategy() string {
|
|||||||
return c.Options.PanicStrategy
|
return c.Options.PanicStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AutomaticStackSize returns whether goroutine stack sizes should be determined
|
||||||
|
// automatically at compile time, if possible. If it is false, no attempt is
|
||||||
|
// made.
|
||||||
|
func (c *Config) AutomaticStackSize() bool {
|
||||||
|
if c.Target.AutoStackSize != nil && c.Scheduler() == "tasks" {
|
||||||
|
return *c.Target.AutoStackSize
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
||||||
// preprocessing.
|
// preprocessing.
|
||||||
func (c *Config) CFlags() []string {
|
func (c *Config) CFlags() []string {
|
||||||
@@ -229,6 +239,31 @@ func (c *Config) Debug() bool {
|
|||||||
return c.Options.Debug
|
return c.Options.Debug
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BinaryFormat returns an appropriate binary format, based on the file
|
||||||
|
// extension and the configured binary format in the target JSON file.
|
||||||
|
func (c *Config) BinaryFormat(ext string) string {
|
||||||
|
switch ext {
|
||||||
|
case ".bin", ".gba", ".nro":
|
||||||
|
// The simplest format possible: dump everything in a raw binary file.
|
||||||
|
if c.Target.BinaryFormat != "" {
|
||||||
|
return c.Target.BinaryFormat
|
||||||
|
}
|
||||||
|
return "bin"
|
||||||
|
case ".hex":
|
||||||
|
// Similar to bin, but includes the start address and is thus usually a
|
||||||
|
// better format.
|
||||||
|
return "hex"
|
||||||
|
case ".uf2":
|
||||||
|
// Special purpose firmware format, mainly used on Adafruit boards.
|
||||||
|
// More information:
|
||||||
|
// https://github.com/Microsoft/uf2
|
||||||
|
return "uf2"
|
||||||
|
default:
|
||||||
|
// Use the ELF format for unrecognized file formats.
|
||||||
|
return "elf"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Programmer returns the flash method and OpenOCD interface name given a
|
// Programmer returns the flash method and OpenOCD interface name given a
|
||||||
// particular configuration. It may either be all configured in the target JSON
|
// particular configuration. It may either be all configured in the target JSON
|
||||||
// file or be modified using the -programmmer command-line option.
|
// file or be modified using the -programmmer command-line option.
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ type TargetSpec struct {
|
|||||||
Linker string `json:"linker"`
|
Linker string `json:"linker"`
|
||||||
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
|
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
|
||||||
Libc string `json:"libc"`
|
Libc string `json:"libc"`
|
||||||
|
AutoStackSize *bool `json:"automatic-stack-size"` // Determine stack size automatically at compile time.
|
||||||
|
DefaultStackSize uint64 `json:"default-stack-size"` // Default stack size if the size couldn't be determined at compile time.
|
||||||
CFlags []string `json:"cflags"`
|
CFlags []string `json:"cflags"`
|
||||||
LDFlags []string `json:"ldflags"`
|
LDFlags []string `json:"ldflags"`
|
||||||
LinkerScript string `json:"linkerscript"`
|
LinkerScript string `json:"linkerscript"`
|
||||||
@@ -45,6 +47,7 @@ type TargetSpec struct {
|
|||||||
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"`
|
UF2FamilyID string `json:"uf2-family-id"`
|
||||||
|
BinaryFormat string `json:"binary-format"`
|
||||||
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"`
|
||||||
@@ -90,6 +93,12 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
|
|||||||
if spec2.Libc != "" {
|
if spec2.Libc != "" {
|
||||||
spec.Libc = spec2.Libc
|
spec.Libc = spec2.Libc
|
||||||
}
|
}
|
||||||
|
if spec2.AutoStackSize != nil {
|
||||||
|
spec.AutoStackSize = spec2.AutoStackSize
|
||||||
|
}
|
||||||
|
if spec2.DefaultStackSize != 0 {
|
||||||
|
spec.DefaultStackSize = spec2.DefaultStackSize
|
||||||
|
}
|
||||||
spec.CFlags = append(spec.CFlags, spec2.CFlags...)
|
spec.CFlags = append(spec.CFlags, spec2.CFlags...)
|
||||||
spec.LDFlags = append(spec.LDFlags, spec2.LDFlags...)
|
spec.LDFlags = append(spec.LDFlags, spec2.LDFlags...)
|
||||||
if spec2.LinkerScript != "" {
|
if spec2.LinkerScript != "" {
|
||||||
@@ -120,6 +129,9 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
|
|||||||
if spec2.UF2FamilyID != "" {
|
if spec2.UF2FamilyID != "" {
|
||||||
spec.UF2FamilyID = spec2.UF2FamilyID
|
spec.UF2FamilyID = spec2.UF2FamilyID
|
||||||
}
|
}
|
||||||
|
if spec2.BinaryFormat != "" {
|
||||||
|
spec.BinaryFormat = spec2.BinaryFormat
|
||||||
|
}
|
||||||
if spec2.OpenOCDInterface != "" {
|
if spec2.OpenOCDInterface != "" {
|
||||||
spec.OpenOCDInterface = spec2.OpenOCDInterface
|
spec.OpenOCDInterface = spec2.OpenOCDInterface
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-48
@@ -5,18 +5,15 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
|
||||||
"github.com/tinygo-org/tinygo/ir"
|
"github.com/tinygo-org/tinygo/ir"
|
||||||
"github.com/tinygo-org/tinygo/loader"
|
"github.com/tinygo-org/tinygo/loader"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
@@ -130,6 +127,28 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
|
|||||||
return machine, nil
|
return machine, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CompilerOutput is returned from the Compile() call. It contains the compile
|
||||||
|
// output and information necessary to continue to compile and link the program.
|
||||||
|
type CompilerOutput struct {
|
||||||
|
// The LLVM module that contains the compiled but not optimized LLVM module
|
||||||
|
// for all the Go code in the program.
|
||||||
|
Mod llvm.Module
|
||||||
|
|
||||||
|
// ExtraFiles is a list of C source files included in packages that should
|
||||||
|
// be built and linked together with the main executable to form one
|
||||||
|
// program. They can be used from CGo, for example.
|
||||||
|
ExtraFiles []string
|
||||||
|
|
||||||
|
// ExtraLDFlags are linker flags obtained during CGo processing. These flags
|
||||||
|
// must be passed to the linker which links the entire executable.
|
||||||
|
ExtraLDFlags []string
|
||||||
|
|
||||||
|
// MainDir is the absolute directory path to the directory of the main
|
||||||
|
// package. This is useful for testing: tests must be run in the package
|
||||||
|
// directory that is being tested.
|
||||||
|
MainDir string
|
||||||
|
}
|
||||||
|
|
||||||
// Compile the given package path or .go file path. Return an error when this
|
// Compile the given package path or .go file path. Return an error when this
|
||||||
// fails (in any stage). If successful it returns the LLVM module and a list of
|
// fails (in any stage). If successful it returns the LLVM module and a list of
|
||||||
// extra C files to be compiled. If not, one or more errors will be returned.
|
// extra C files to be compiled. If not, one or more errors will be returned.
|
||||||
@@ -138,7 +157,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
|
|||||||
// violation. Eventually, this Compile function should only compile a single
|
// violation. Eventually, this Compile function should only compile a single
|
||||||
// package and not the whole program, and loading of the program (including CGo
|
// package and not the whole program, and loading of the program (including CGo
|
||||||
// processing) should be moved outside the compiler package.
|
// processing) should be moved outside the compiler package.
|
||||||
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (mod llvm.Module, extrafiles []string, extraldflags []string, errors []error) {
|
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (output CompilerOutput, errors []error) {
|
||||||
c := &compilerContext{
|
c := &compilerContext{
|
||||||
Config: config,
|
Config: config,
|
||||||
difiles: make(map[string]llvm.Metadata),
|
difiles: make(map[string]llvm.Metadata),
|
||||||
@@ -154,6 +173,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
|||||||
if c.Debug() {
|
if c.Debug() {
|
||||||
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
c.dibuilder = llvm.NewDIBuilder(c.mod)
|
||||||
}
|
}
|
||||||
|
output.Mod = c.mod
|
||||||
|
|
||||||
c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8)
|
c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8)
|
||||||
if c.targetData.PointerSize() <= 4 {
|
if c.targetData.PointerSize() <= 4 {
|
||||||
@@ -172,55 +192,29 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
|||||||
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
|
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
|
||||||
dummyFunc.EraseFromParentAsFunction()
|
dummyFunc.EraseFromParentAsFunction()
|
||||||
|
|
||||||
wd, err := os.Getwd()
|
lprogram, err := loader.Load(c.Config, []string{pkgName}, c.ClangHeaders, types.Config{
|
||||||
|
Sizes: &stdSizes{
|
||||||
|
IntSize: int64(c.targetData.TypeAllocSize(c.intType)),
|
||||||
|
PtrSize: int64(c.targetData.PointerSize()),
|
||||||
|
MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)),
|
||||||
|
}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.mod, nil, nil, []error{err}
|
return output, []error{err}
|
||||||
}
|
|
||||||
goroot, err := loader.GetCachedGoroot(c.Config)
|
|
||||||
if err != nil {
|
|
||||||
return c.mod, nil, nil, []error{err}
|
|
||||||
}
|
|
||||||
lprogram := &loader.Program{
|
|
||||||
Build: &build.Context{
|
|
||||||
GOARCH: c.GOARCH(),
|
|
||||||
GOOS: c.GOOS(),
|
|
||||||
GOROOT: goroot,
|
|
||||||
GOPATH: goenv.Get("GOPATH"),
|
|
||||||
CgoEnabled: c.CgoEnabled(),
|
|
||||||
UseAllFiles: false,
|
|
||||||
Compiler: "gc", // must be one of the recognized compilers
|
|
||||||
BuildTags: c.BuildTags(),
|
|
||||||
},
|
|
||||||
Tests: c.TestConfig.CompileTestBinary,
|
|
||||||
TypeChecker: types.Config{
|
|
||||||
Sizes: &stdSizes{
|
|
||||||
IntSize: int64(c.targetData.TypeAllocSize(c.intType)),
|
|
||||||
PtrSize: int64(c.targetData.PointerSize()),
|
|
||||||
MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Dir: wd,
|
|
||||||
TINYGOROOT: goenv.Get("TINYGOROOT"),
|
|
||||||
CFlags: c.CFlags(),
|
|
||||||
ClangHeaders: c.ClangHeaders,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = lprogram.Load(pkgName)
|
|
||||||
if err != nil {
|
|
||||||
return c.mod, nil, nil, []error{err}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = lprogram.Parse()
|
err = lprogram.Parse()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.mod, nil, nil, []error{err}
|
return output, []error{err}
|
||||||
}
|
}
|
||||||
|
output.ExtraLDFlags = lprogram.LDFlags
|
||||||
|
output.MainDir = lprogram.MainPkg().Dir
|
||||||
|
|
||||||
c.ir = ir.NewProgram(lprogram)
|
c.ir = ir.NewProgram(lprogram)
|
||||||
|
|
||||||
// Run a simple dead code elimination pass.
|
// Run a simple dead code elimination pass.
|
||||||
err = c.ir.SimpleDCE()
|
err = c.ir.SimpleDCE()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.mod, nil, nil, []error{err}
|
return output, []error{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize debug information.
|
// Initialize debug information.
|
||||||
@@ -359,17 +353,13 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gather the list of (C) file paths that should be included in the build.
|
// Gather the list of (C) file paths that should be included in the build.
|
||||||
var extraFiles []string
|
|
||||||
for _, pkg := range c.ir.LoaderProgram.Sorted() {
|
for _, pkg := range c.ir.LoaderProgram.Sorted() {
|
||||||
for _, file := range pkg.OtherFiles {
|
for _, filename := range pkg.CFiles {
|
||||||
switch strings.ToLower(filepath.Ext(file)) {
|
output.ExtraFiles = append(output.ExtraFiles, filepath.Join(pkg.Dir, filename))
|
||||||
case ".c":
|
|
||||||
extraFiles = append(extraFiles, file)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.mod, extraFiles, lprogram.LDFlags, c.diagnostics
|
return output, c.diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
// getLLVMRuntimeType obtains a named type from the runtime package and returns
|
||||||
|
|||||||
+19
-3
@@ -19,16 +19,30 @@ import (
|
|||||||
// Because a go statement doesn't return anything, return undef.
|
// Because a go statement doesn't return anything, return undef.
|
||||||
func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, prefix string, pos token.Pos) llvm.Value {
|
func (b *builder) createGoInstruction(funcPtr llvm.Value, params []llvm.Value, prefix string, pos token.Pos) llvm.Value {
|
||||||
paramBundle := b.emitPointerPack(params)
|
paramBundle := b.emitPointerPack(params)
|
||||||
var callee llvm.Value
|
var callee, stackSize llvm.Value
|
||||||
switch b.Scheduler() {
|
switch b.Scheduler() {
|
||||||
case "none", "tasks":
|
case "none", "tasks":
|
||||||
callee = b.createGoroutineStartWrapper(funcPtr, prefix, pos)
|
callee = b.createGoroutineStartWrapper(funcPtr, prefix, pos)
|
||||||
|
if b.AutomaticStackSize() {
|
||||||
|
// The stack size is not known until after linking. Call a dummy
|
||||||
|
// function that will be replaced with a load from a special ELF
|
||||||
|
// section that contains the stack size (and is modified after
|
||||||
|
// linking).
|
||||||
|
stackSize = b.createCall(b.mod.NamedFunction("internal/task.getGoroutineStackSize"), []llvm.Value{callee, llvm.Undef(b.i8ptrType), llvm.Undef(b.i8ptrType)}, "stacksize")
|
||||||
|
} else {
|
||||||
|
// The stack size is fixed at compile time. By emitting it here as a
|
||||||
|
// constant, it can be optimized.
|
||||||
|
stackSize = llvm.ConstInt(b.uintptrType, b.Target.DefaultStackSize, false)
|
||||||
|
}
|
||||||
case "coroutines":
|
case "coroutines":
|
||||||
callee = b.CreatePtrToInt(funcPtr, b.uintptrType, "")
|
callee = b.CreatePtrToInt(funcPtr, b.uintptrType, "")
|
||||||
|
// There is no goroutine stack size: coroutines are used instead of
|
||||||
|
// stacks.
|
||||||
|
stackSize = llvm.Undef(b.uintptrType)
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
b.createCall(b.mod.NamedFunction("internal/task.start"), []llvm.Value{callee, paramBundle, llvm.Undef(b.i8ptrType), llvm.ConstPointerNull(b.i8ptrType)}, "")
|
b.createCall(b.mod.NamedFunction("internal/task.start"), []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.i8ptrType), llvm.ConstPointerNull(b.i8ptrType)}, "")
|
||||||
return llvm.Undef(funcPtr.Type().ElementType().ReturnType())
|
return llvm.Undef(funcPtr.Type().ElementType().ReturnType())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,8 +81,9 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
// Create the wrapper.
|
// Create the wrapper.
|
||||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
||||||
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
|
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
|
||||||
wrapper.SetLinkage(llvm.PrivateLinkage)
|
wrapper.SetLinkage(llvm.InternalLinkage)
|
||||||
wrapper.SetUnnamedAddr(true)
|
wrapper.SetUnnamedAddr(true)
|
||||||
|
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
|
||||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
builder.SetInsertPointAtEnd(entry)
|
builder.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
@@ -125,6 +140,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
|||||||
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
|
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
|
||||||
wrapper.SetLinkage(llvm.InternalLinkage)
|
wrapper.SetLinkage(llvm.InternalLinkage)
|
||||||
wrapper.SetUnnamedAddr(true)
|
wrapper.SetUnnamedAddr(true)
|
||||||
|
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
|
||||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||||
builder.SetInsertPointAtEnd(entry)
|
builder.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
// Version of TinyGo.
|
// Version of TinyGo.
|
||||||
// Update this value before release of new version of software.
|
// Update this value before release of new version of software.
|
||||||
const Version = "0.14.0"
|
const Version = "0.15.0"
|
||||||
|
|
||||||
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
||||||
// If the goroot cannot be determined, (0, 0) is returned.
|
// If the goroot cannot be determined, (0, 0) is returned.
|
||||||
|
|||||||
+3
-1
@@ -47,10 +47,12 @@ func (e *Error) Error() string {
|
|||||||
// location of the instruction. The location information may not be complete as
|
// location of the instruction. The location information may not be complete as
|
||||||
// it depends on debug information in the IR.
|
// it depends on debug information in the IR.
|
||||||
func (e *evalPackage) errorAt(inst llvm.Value, err error) *Error {
|
func (e *evalPackage) errorAt(inst llvm.Value, err error) *Error {
|
||||||
|
pos := getPosition(inst)
|
||||||
return &Error{
|
return &Error{
|
||||||
ImportPath: e.packagePath,
|
ImportPath: e.packagePath,
|
||||||
Pos: getPosition(inst),
|
Pos: pos,
|
||||||
Err: err,
|
Err: err,
|
||||||
|
Traceback: []ErrorLine{{pos, inst}},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+37
-9
@@ -95,7 +95,11 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
if !operand.IsConstant() || inst.IsVolatile() || (!operand.Underlying.IsAConstantExpr().IsNil() && operand.Underlying.Opcode() == llvm.BitCast) {
|
if !operand.IsConstant() || inst.IsVolatile() || (!operand.Underlying.IsAConstantExpr().IsNil() && operand.Underlying.Opcode() == llvm.BitCast) {
|
||||||
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
|
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
|
||||||
} else {
|
} else {
|
||||||
value = operand.Load()
|
var err error
|
||||||
|
value, err = operand.Load()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if value.Type() != inst.Type() {
|
if value.Type() != inst.Type() {
|
||||||
return nil, nil, fr.errorAt(inst, errors.New("interp: load: type does not match"))
|
return nil, nil, fr.errorAt(inst, errors.New("interp: load: type does not match"))
|
||||||
@@ -107,7 +111,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
if inst.IsVolatile() {
|
if inst.IsVolatile() {
|
||||||
fr.builder.CreateStore(value.Value(), ptr.Value())
|
fr.builder.CreateStore(value.Value(), ptr.Value())
|
||||||
} else {
|
} else {
|
||||||
ptr.Store(value.Value())
|
err := ptr.Store(value.Value())
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case !inst.IsAGetElementPtrInst().IsNil():
|
case !inst.IsAGetElementPtrInst().IsNil():
|
||||||
value := fr.getLocal(inst.Operand(0))
|
value := fr.getLocal(inst.Operand(0))
|
||||||
@@ -308,7 +315,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
}
|
}
|
||||||
// "key" is a Go string value, which in the TinyGo calling convention is split up
|
// "key" is a Go string value, which in the TinyGo calling convention is split up
|
||||||
// into separate pointer and length parameters.
|
// into separate pointer and length parameters.
|
||||||
m.PutString(keyBuf, keyLen, valPtr)
|
err := m.PutString(keyBuf, keyLen, valPtr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
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
|
||||||
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
||||||
@@ -329,15 +339,24 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
fr.builder.CreateCall(callee, llvmParams, "")
|
fr.builder.CreateCall(callee, llvmParams, "")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
m.PutBinary(keyBuf, valPtr)
|
err := m.PutBinary(keyBuf, valPtr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
case callee.Name() == "runtime.stringConcat":
|
case callee.Name() == "runtime.stringConcat":
|
||||||
// adding two strings together
|
// adding two strings together
|
||||||
buf1Ptr := fr.getLocal(inst.Operand(0))
|
buf1Ptr := fr.getLocal(inst.Operand(0))
|
||||||
buf1Len := fr.getLocal(inst.Operand(1))
|
buf1Len := fr.getLocal(inst.Operand(1))
|
||||||
buf2Ptr := fr.getLocal(inst.Operand(2))
|
buf2Ptr := fr.getLocal(inst.Operand(2))
|
||||||
buf2Len := fr.getLocal(inst.Operand(3))
|
buf2Len := fr.getLocal(inst.Operand(3))
|
||||||
buf1 := getStringBytes(buf1Ptr, buf1Len.Value())
|
buf1, err := getStringBytes(buf1Ptr, buf1Len.Value())
|
||||||
buf2 := getStringBytes(buf2Ptr, buf2Len.Value())
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
buf2, err := getStringBytes(buf2Ptr, buf2Len.Value())
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
result := []byte(string(buf1) + string(buf2))
|
result := []byte(string(buf1) + string(buf2))
|
||||||
vals := make([]llvm.Value, len(result))
|
vals := make([]llvm.Value, len(result))
|
||||||
for i := range vals {
|
for i := range vals {
|
||||||
@@ -401,9 +420,15 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
return nil, nil, fr.errorAt(inst, errors.New("interp: trying to copy a slice with negative length?"))
|
return nil, nil, fr.errorAt(inst, errors.New("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())
|
val, err := srcArray.Load()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
err = dstArray.Store(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
// dst++
|
// dst++
|
||||||
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
|
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -421,7 +446,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
// convert a string to a []byte
|
// convert a string to a []byte
|
||||||
bufPtr := fr.getLocal(inst.Operand(0))
|
bufPtr := fr.getLocal(inst.Operand(0))
|
||||||
bufLen := fr.getLocal(inst.Operand(1))
|
bufLen := fr.getLocal(inst.Operand(1))
|
||||||
result := getStringBytes(bufPtr, bufLen.Value())
|
result, err := getStringBytes(bufPtr, bufLen.Value())
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
vals := make([]llvm.Value, len(result))
|
vals := make([]llvm.Value, len(result))
|
||||||
for i := range vals {
|
for i := range vals {
|
||||||
vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)
|
vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)
|
||||||
|
|||||||
+10
-5
@@ -1,6 +1,8 @@
|
|||||||
package interp
|
package interp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,20 +20,23 @@ func getUses(value llvm.Value) []llvm.Value {
|
|||||||
|
|
||||||
// getStringBytes loads the byte slice of a Go string represented as a
|
// getStringBytes loads the byte slice of a Go string represented as a
|
||||||
// {ptr, len} pair.
|
// {ptr, len} pair.
|
||||||
func getStringBytes(strPtr Value, strLen llvm.Value) []byte {
|
func getStringBytes(strPtr Value, strLen llvm.Value) ([]byte, error) {
|
||||||
if !strLen.IsConstant() {
|
if !strLen.IsConstant() {
|
||||||
panic("getStringBytes with a non-constant length")
|
return nil, errors.New("getStringBytes with a non-constant length")
|
||||||
}
|
}
|
||||||
buf := make([]byte, strLen.ZExtValue())
|
buf := make([]byte, strLen.ZExtValue())
|
||||||
for i := range buf {
|
for i := range buf {
|
||||||
gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
|
gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // TODO
|
return nil, err
|
||||||
|
}
|
||||||
|
c, err := gep.Load()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
c := gep.Load()
|
|
||||||
buf[i] = byte(c.ZExtValue())
|
buf[i] = byte(c.ZExtValue())
|
||||||
}
|
}
|
||||||
return buf
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLLVMIndices converts an []uint32 into an []llvm.Value, for use in
|
// getLLVMIndices converts an []uint32 into an []llvm.Value, for use in
|
||||||
|
|||||||
+60
-36
@@ -15,8 +15,8 @@ type Value interface {
|
|||||||
Value() llvm.Value // returns a LLVM value
|
Value() llvm.Value // returns a LLVM value
|
||||||
Type() llvm.Type // equal to Value().Type()
|
Type() llvm.Type // equal to Value().Type()
|
||||||
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, error) // dereference a pointer
|
||||||
Store(llvm.Value) // store to a pointer
|
Store(llvm.Value) error // store to a pointer
|
||||||
GetElementPtr([]uint32) (Value, error) // returns an interior pointer
|
GetElementPtr([]uint32) (Value, error) // returns an interior pointer
|
||||||
String() string // string representation, for debugging
|
String() string // string representation, for debugging
|
||||||
}
|
}
|
||||||
@@ -44,29 +44,32 @@ func (v *LocalValue) IsConstant() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load loads a constant value if this is a constant pointer.
|
// Load loads a constant value if this is a constant pointer.
|
||||||
func (v *LocalValue) Load() llvm.Value {
|
func (v *LocalValue) Load() (llvm.Value, error) {
|
||||||
if !v.Underlying.IsAGlobalVariable().IsNil() {
|
if !v.Underlying.IsAGlobalVariable().IsNil() {
|
||||||
return v.Underlying.Initializer()
|
return v.Underlying.Initializer(), nil
|
||||||
}
|
}
|
||||||
switch v.Underlying.Opcode() {
|
switch v.Underlying.Opcode() {
|
||||||
case llvm.GetElementPtr:
|
case llvm.GetElementPtr:
|
||||||
indices := v.getConstGEPIndices()
|
indices := v.getConstGEPIndices()
|
||||||
if indices[0] != 0 {
|
if indices[0] != 0 {
|
||||||
panic("invalid GEP")
|
return llvm.Value{}, errors.New("invalid GEP")
|
||||||
}
|
}
|
||||||
global := v.Eval.getValue(v.Underlying.Operand(0))
|
global := v.Eval.getValue(v.Underlying.Operand(0))
|
||||||
agg := global.Load()
|
agg, err := global.Load()
|
||||||
return llvm.ConstExtractValue(agg, indices[1:])
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
|
return llvm.ConstExtractValue(agg, indices[1:]), nil
|
||||||
case llvm.BitCast:
|
case llvm.BitCast:
|
||||||
panic("interp: load from a bitcast")
|
return llvm.Value{}, errors.New("interp: load from a bitcast")
|
||||||
default:
|
default:
|
||||||
panic("interp: load from a constant")
|
return llvm.Value{}, errors.New("interp: load from a constant")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store stores to the underlying value if the value type is a pointer type,
|
// Store stores to the underlying value if the value type is a pointer type,
|
||||||
// otherwise it panics.
|
// otherwise it returns an error.
|
||||||
func (v *LocalValue) Store(value llvm.Value) {
|
func (v *LocalValue) Store(value llvm.Value) error {
|
||||||
if !v.Underlying.IsAGlobalVariable().IsNil() {
|
if !v.Underlying.IsAGlobalVariable().IsNil() {
|
||||||
if !value.IsConstant() {
|
if !value.IsConstant() {
|
||||||
v.MarkDirty()
|
v.MarkDirty()
|
||||||
@@ -74,26 +77,28 @@ func (v *LocalValue) Store(value llvm.Value) {
|
|||||||
} else {
|
} else {
|
||||||
v.Underlying.SetInitializer(value)
|
v.Underlying.SetInitializer(value)
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
if !value.IsConstant() {
|
if !value.IsConstant() {
|
||||||
v.MarkDirty()
|
v.MarkDirty()
|
||||||
v.Eval.builder.CreateStore(value, v.Underlying)
|
v.Eval.builder.CreateStore(value, v.Underlying)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
switch v.Underlying.Opcode() {
|
switch v.Underlying.Opcode() {
|
||||||
case llvm.GetElementPtr:
|
case llvm.GetElementPtr:
|
||||||
indices := v.getConstGEPIndices()
|
indices := v.getConstGEPIndices()
|
||||||
if indices[0] != 0 {
|
if indices[0] != 0 {
|
||||||
panic("invalid GEP")
|
return errors.New("invalid GEP")
|
||||||
}
|
}
|
||||||
global := &LocalValue{v.Eval, v.Underlying.Operand(0)}
|
global := &LocalValue{v.Eval, v.Underlying.Operand(0)}
|
||||||
agg := global.Load()
|
agg, err := global.Load()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
agg = llvm.ConstInsertValue(agg, value, indices[1:])
|
agg = llvm.ConstInsertValue(agg, value, indices[1:])
|
||||||
global.Store(agg)
|
return global.Store(agg)
|
||||||
return
|
|
||||||
default:
|
default:
|
||||||
panic("interp: store on a constant")
|
return errors.New("interp: store on a constant")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +230,11 @@ func (v *MapValue) Value() llvm.Value {
|
|||||||
keyPtr := llvm.ConstExtractValue(llvmKey, []uint32{0})
|
keyPtr := llvm.ConstExtractValue(llvmKey, []uint32{0})
|
||||||
keyLen := llvm.ConstExtractValue(llvmKey, []uint32{1})
|
keyLen := llvm.ConstExtractValue(llvmKey, []uint32{1})
|
||||||
keyPtrVal := v.Eval.getValue(keyPtr)
|
keyPtrVal := v.Eval.getValue(keyPtr)
|
||||||
keyBuf = getStringBytes(keyPtrVal, keyLen)
|
var err error
|
||||||
|
keyBuf, err = getStringBytes(keyPtrVal, keyLen)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // TODO
|
||||||
|
}
|
||||||
} else if key.Type().TypeKind() == llvm.IntegerTypeKind {
|
} else if key.Type().TypeKind() == llvm.IntegerTypeKind {
|
||||||
keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
|
keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
|
||||||
n := key.Value().ZExtValue()
|
n := key.Value().ZExtValue()
|
||||||
@@ -299,13 +308,15 @@ func (v *MapValue) IsConstant() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load panics: maps are of reference type so cannot be dereferenced.
|
// Load panics: maps are of reference type so cannot be dereferenced.
|
||||||
func (v *MapValue) Load() llvm.Value {
|
func (v *MapValue) Load() (llvm.Value, error) {
|
||||||
panic("interp: load from a map")
|
panic("interp: load from a map")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store panics: maps are of reference type so cannot be stored to.
|
// Store returns an error: maps are of reference type so cannot be stored to.
|
||||||
func (v *MapValue) Store(value llvm.Value) {
|
func (v *MapValue) Store(value llvm.Value) error {
|
||||||
panic("interp: store on a map")
|
// This must be a bug, but it might be helpful to indicate the location
|
||||||
|
// anyway.
|
||||||
|
return errors.New("interp: store on a map")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetElementPtr panics: maps are of reference type so their (interior)
|
// GetElementPtr panics: maps are of reference type so their (interior)
|
||||||
@@ -316,23 +327,26 @@ func (v *MapValue) GetElementPtr(indices []uint32) (Value, error) {
|
|||||||
|
|
||||||
// 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
|
||||||
// map[string]T.
|
// map[string]T.
|
||||||
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
|
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) error {
|
||||||
if !v.Underlying.IsNil() {
|
if !v.Underlying.IsNil() {
|
||||||
panic("map already created")
|
return errors.New("map already created")
|
||||||
}
|
}
|
||||||
|
|
||||||
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
||||||
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
||||||
}
|
}
|
||||||
value := valPtr.Load()
|
value, err := valPtr.Load()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if v.ValueType.IsNil() {
|
if v.ValueType.IsNil() {
|
||||||
v.ValueType = value.Type()
|
v.ValueType = value.Type()
|
||||||
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
||||||
panic("interp: map store value type has the wrong size")
|
return errors.New("interp: map store value type has the wrong size")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if value.Type() != v.ValueType {
|
if value.Type() != v.ValueType {
|
||||||
panic("interp: map store value type is inconsistent")
|
return errors.New("interp: map store value type is inconsistent")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,26 +359,31 @@ func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
|
|||||||
// TODO: avoid duplicate keys
|
// TODO: avoid duplicate keys
|
||||||
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
||||||
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutBinary does a map assign operation.
|
// PutBinary does a map assign operation.
|
||||||
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
|
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) error {
|
||||||
if !v.Underlying.IsNil() {
|
if !v.Underlying.IsNil() {
|
||||||
panic("map already created")
|
return errors.New("map already created")
|
||||||
}
|
}
|
||||||
|
|
||||||
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
||||||
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
||||||
}
|
}
|
||||||
value := valPtr.Load()
|
value, err := valPtr.Load()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if v.ValueType.IsNil() {
|
if v.ValueType.IsNil() {
|
||||||
v.ValueType = value.Type()
|
v.ValueType = value.Type()
|
||||||
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
||||||
panic("interp: map store value type has the wrong size")
|
return errors.New("interp: map store value type has the wrong size")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if value.Type() != v.ValueType {
|
if value.Type() != v.ValueType {
|
||||||
panic("interp: map store value type is inconsistent")
|
return errors.New("interp: map store value type is inconsistent")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,21 +394,26 @@ func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
|
|||||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key := keyPtr.Load()
|
key, err := keyPtr.Load()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if v.KeyType.IsNil() {
|
if v.KeyType.IsNil() {
|
||||||
v.KeyType = key.Type()
|
v.KeyType = key.Type()
|
||||||
if int(v.Eval.TargetData.TypeAllocSize(v.KeyType)) != v.KeySize {
|
if int(v.Eval.TargetData.TypeAllocSize(v.KeyType)) != v.KeySize {
|
||||||
panic("interp: map store key type has the wrong size")
|
return errors.New("interp: map store key type has the wrong size")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if key.Type() != v.KeyType {
|
if key.Type() != v.KeyType {
|
||||||
panic("interp: map store key type is inconsistent")
|
return errors.New("interp: map store key type is inconsistent")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: avoid duplicate keys
|
// TODO: avoid duplicate keys
|
||||||
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
||||||
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get FNV-1a hash of this string.
|
// Get FNV-1a hash of this string.
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ func NewProgram(lprogram *loader.Program) *Program {
|
|||||||
program := lprogram.LoadSSA()
|
program := lprogram.LoadSSA()
|
||||||
program.Build()
|
program.Build()
|
||||||
|
|
||||||
mainPkg := program.ImportedPackage(lprogram.MainPkg.PkgPath)
|
mainPkg := program.ImportedPackage(lprogram.MainPkg().ImportPath)
|
||||||
if mainPkg == nil {
|
if mainPkg == nil {
|
||||||
panic("could not find main package")
|
panic("could not find main package")
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ func NewProgram(lprogram *loader.Program) *Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
p.AddPackage(program.ImportedPackage(pkg.PkgPath))
|
p.AddPackage(program.ImportedPackage(pkg.ImportPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
|||||||
+1
-1
Submodule lib/cmsis-svd updated: a155cfd832...d9b58694ce
+1
-1
Submodule lib/wasi-libc updated: a280fead2a...215adc8ac9
@@ -1,5 +1,7 @@
|
|||||||
package loader
|
package loader
|
||||||
|
|
||||||
|
import "go/scanner"
|
||||||
|
|
||||||
// Errors contains a list of parser errors or a list of typechecker errors for
|
// Errors contains a list of parser errors or a list of typechecker errors for
|
||||||
// the given package.
|
// the given package.
|
||||||
type Errors struct {
|
type Errors struct {
|
||||||
@@ -10,3 +12,14 @@ type Errors struct {
|
|||||||
func (e Errors) Error() string {
|
func (e Errors) Error() string {
|
||||||
return "could not compile: " + e.Errs[0].Error()
|
return "could not compile: " + e.Errs[0].Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error is a regular error but with an added import stack. This is especially
|
||||||
|
// useful for debugging import cycle errors.
|
||||||
|
type Error struct {
|
||||||
|
ImportStack []string
|
||||||
|
Err scanner.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Error) Error() string {
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
|||||||
+49
-8
@@ -8,19 +8,21 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"sync"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var gorootCreateMutex sync.Mutex
|
||||||
|
|
||||||
// GetCachedGoroot creates a new GOROOT by merging both the standard GOROOT and
|
// GetCachedGoroot creates a new GOROOT by merging both the standard GOROOT and
|
||||||
// the GOROOT from TinyGo using lots of symbolic links.
|
// the GOROOT from TinyGo using lots of symbolic links.
|
||||||
func GetCachedGoroot(config *compileopts.Config) (string, error) {
|
func GetCachedGoroot(config *compileopts.Config) (string, error) {
|
||||||
@@ -49,16 +51,29 @@ func GetCachedGoroot(config *compileopts.Config) (string, error) {
|
|||||||
fmt.Fprintln(hash, tinygoroot)
|
fmt.Fprintln(hash, tinygoroot)
|
||||||
gorootsHash := hash.Sum(nil)
|
gorootsHash := hash.Sum(nil)
|
||||||
gorootsHashHex := hex.EncodeToString(gorootsHash[:])
|
gorootsHashHex := hex.EncodeToString(gorootsHash[:])
|
||||||
cachedgoroot := filepath.Join(goenv.Get("GOCACHE"), "goroot-"+version+"-"+gorootsHashHex)
|
cachedgorootName := "goroot-" + version + "-" + gorootsHashHex
|
||||||
|
cachedgoroot := filepath.Join(goenv.Get("GOCACHE"), cachedgorootName)
|
||||||
if needsSyscallPackage(config.BuildTags()) {
|
if needsSyscallPackage(config.BuildTags()) {
|
||||||
cachedgoroot += "-syscall"
|
cachedgoroot += "-syscall"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not try to create the cached GOROOT in parallel, that's only a waste
|
||||||
|
// of I/O bandwidth and thus speed. Instead, use a mutex to make sure only
|
||||||
|
// one goroutine does it at a time.
|
||||||
|
// This is not a way to ensure atomicity (a different TinyGo invocation
|
||||||
|
// could be creating the same directory), but instead a way to avoid
|
||||||
|
// creating it many times in parallel when running tests in parallel.
|
||||||
|
gorootCreateMutex.Lock()
|
||||||
|
defer gorootCreateMutex.Unlock()
|
||||||
|
|
||||||
if _, err := os.Stat(cachedgoroot); err == nil {
|
if _, err := os.Stat(cachedgoroot); err == nil {
|
||||||
return cachedgoroot, nil
|
return cachedgoroot, nil
|
||||||
}
|
}
|
||||||
tmpgoroot := cachedgoroot + ".tmp" + strconv.Itoa(rand.Int())
|
err = os.MkdirAll(goenv.Get("GOCACHE"), 0777)
|
||||||
err = os.MkdirAll(tmpgoroot, 0777)
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
tmpgoroot, err := ioutil.TempDir(goenv.Get("GOCACHE"), cachedgorootName+".tmp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -85,6 +100,15 @@ func GetCachedGoroot(config *compileopts.Config) (string, error) {
|
|||||||
// deleted by the defer above.
|
// deleted by the defer above.
|
||||||
return cachedgoroot, nil
|
return cachedgoroot, nil
|
||||||
}
|
}
|
||||||
|
if runtime.GOOS == "windows" && os.IsPermission(err) {
|
||||||
|
// On Windows, a rename with a destination directory that already
|
||||||
|
// exists does not result in an IsExist error, but rather in an
|
||||||
|
// access denied error. To be sure, check for this case by checking
|
||||||
|
// whether the target directory exists.
|
||||||
|
if _, err := os.Stat(cachedgoroot); err == nil {
|
||||||
|
return cachedgoroot, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return cachedgoroot, nil
|
return cachedgoroot, nil
|
||||||
@@ -188,7 +212,7 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool {
|
|||||||
"reflect/": false,
|
"reflect/": false,
|
||||||
"runtime/": false,
|
"runtime/": false,
|
||||||
"sync/": true,
|
"sync/": true,
|
||||||
"testing/": false,
|
"testing/": true,
|
||||||
}
|
}
|
||||||
if needsSyscallPackage {
|
if needsSyscallPackage {
|
||||||
paths["syscall/"] = true // include syscall/js
|
paths["syscall/"] = true // include syscall/js
|
||||||
@@ -228,10 +252,27 @@ func symlink(oldname, newname string) error {
|
|||||||
return symlinkErr
|
return symlinkErr
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Make a hard link.
|
// Try making a hard link.
|
||||||
err := os.Link(oldname, newname)
|
err := os.Link(oldname, newname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return symlinkErr
|
// Making a hardlink failed. Try copying the file as a last
|
||||||
|
// fallback.
|
||||||
|
inf, err := os.Open(oldname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer inf.Close()
|
||||||
|
outf, err := os.Create(newname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer outf.Close()
|
||||||
|
_, err = io.Copy(outf, inf)
|
||||||
|
if err != nil {
|
||||||
|
os.Remove(newname)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// File was copied.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil // success
|
return nil // success
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List returns a ready-to-run *exec.Cmd for running the `go list` command with
|
||||||
|
// the configuration used for TinyGo.
|
||||||
|
func List(config *compileopts.Config, extraArgs, pkgs []string) (*exec.Cmd, error) {
|
||||||
|
goroot, err := GetCachedGoroot(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
args := append([]string{"list"}, extraArgs...)
|
||||||
|
if len(config.BuildTags()) != 0 {
|
||||||
|
args = append(args, "-tags", strings.Join(config.BuildTags(), " "))
|
||||||
|
}
|
||||||
|
args = append(args, pkgs...)
|
||||||
|
cgoEnabled := "0"
|
||||||
|
if config.CgoEnabled() {
|
||||||
|
cgoEnabled = "1"
|
||||||
|
}
|
||||||
|
cmd := exec.Command("go", args...)
|
||||||
|
cmd.Env = append(os.Environ(), "GOROOT="+goroot, "GOOS="+config.GOOS(), "GOARCH="+config.GOARCH(), "CGO_ENABLED="+cgoEnabled)
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
+186
-255
@@ -2,165 +2,178 @@ package loader
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/scanner"
|
"go/scanner"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"syscall"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/cgo"
|
"github.com/tinygo-org/tinygo/cgo"
|
||||||
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
"golang.org/x/tools/go/packages"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Program holds all packages and some metadata about the program as a whole.
|
// Program holds all packages and some metadata about the program as a whole.
|
||||||
type Program struct {
|
type Program struct {
|
||||||
Build *build.Context
|
config *compileopts.Config
|
||||||
Tests bool
|
clangHeaders string
|
||||||
Packages map[string]*Package
|
typeChecker types.Config
|
||||||
MainPkg *Package
|
goroot string // synthetic GOROOT
|
||||||
sorted []*Package
|
workingDir string
|
||||||
fset *token.FileSet
|
|
||||||
TypeChecker types.Config
|
Packages map[string]*Package
|
||||||
Dir string // current working directory (for error reporting)
|
sorted []*Package
|
||||||
TINYGOROOT string // root of the TinyGo installation or root of the source code
|
fset *token.FileSet
|
||||||
CFlags []string
|
|
||||||
LDFlags []string
|
// Information obtained during parsing.
|
||||||
ClangHeaders string
|
LDFlags []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackageJSON is a subset of the JSON struct returned from `go list`.
|
||||||
|
type PackageJSON struct {
|
||||||
|
Dir string
|
||||||
|
ImportPath string
|
||||||
|
ForTest string
|
||||||
|
|
||||||
|
// Source files
|
||||||
|
GoFiles []string
|
||||||
|
CgoFiles []string
|
||||||
|
CFiles []string
|
||||||
|
|
||||||
|
// Dependency information
|
||||||
|
Imports []string
|
||||||
|
ImportMap map[string]string
|
||||||
|
|
||||||
|
// Error information
|
||||||
|
Error *struct {
|
||||||
|
ImportStack []string
|
||||||
|
Pos string
|
||||||
|
Err string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Package holds a loaded package, its imports, and its parsed files.
|
// Package holds a loaded package, its imports, and its parsed files.
|
||||||
type Package struct {
|
type Package struct {
|
||||||
*Program
|
PackageJSON
|
||||||
*packages.Package
|
|
||||||
Files []*ast.File
|
program *Program
|
||||||
Pkg *types.Package
|
Files []*ast.File
|
||||||
types.Info
|
Pkg *types.Package
|
||||||
|
info types.Info
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads the given package with all dependencies (including the runtime
|
// Load loads the given package with all dependencies (including the runtime
|
||||||
// package). Call .Parse() afterwards to parse all Go files (including CGo
|
// package). Call .Parse() afterwards to parse all Go files (including CGo
|
||||||
// processing, if necessary).
|
// processing, if necessary).
|
||||||
func (p *Program) Load(importPath string) error {
|
func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, typeChecker types.Config) (*Program, error) {
|
||||||
if p.Packages == nil {
|
goroot, err := GetCachedGoroot(config)
|
||||||
p.Packages = make(map[string]*Package)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := &Program{
|
||||||
|
config: config,
|
||||||
|
clangHeaders: clangHeaders,
|
||||||
|
typeChecker: typeChecker,
|
||||||
|
goroot: goroot,
|
||||||
|
workingDir: wd,
|
||||||
|
Packages: make(map[string]*Package),
|
||||||
|
fset: token.NewFileSet(),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := p.loadPackage(importPath)
|
// List the dependencies of this package, in raw JSON format.
|
||||||
|
extraArgs := []string{"-json", "-deps"}
|
||||||
|
if config.TestConfig.CompileTestBinary {
|
||||||
|
extraArgs = append(extraArgs, "-test")
|
||||||
|
}
|
||||||
|
cmd, err := List(config, extraArgs, inputPkgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
p.MainPkg = p.sorted[len(p.sorted)-1]
|
buf := &bytes.Buffer{}
|
||||||
if _, ok := p.Packages["runtime"]; !ok {
|
cmd.Stdout = buf
|
||||||
// The runtime package wasn't loaded. Although `go list -deps` seems to
|
cmd.Stderr = os.Stderr
|
||||||
// return the full dependency list, there is no way to get those
|
err = cmd.Run()
|
||||||
// packages from the go/packages package. Therefore load the runtime
|
if err != nil {
|
||||||
// manually and add it to the list of to-be-compiled packages
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
// (duplicates are already filtered).
|
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
||||||
return p.loadPackage("runtime")
|
os.Exit(status.ExitStatus())
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("failed to run `go list`: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Program) loadPackage(importPath string) error {
|
// Parse the returned json from `go list`.
|
||||||
cgoEnabled := "0"
|
decoder := json.NewDecoder(buf)
|
||||||
if p.Build.CgoEnabled {
|
for {
|
||||||
cgoEnabled = "1"
|
pkg := &Package{
|
||||||
}
|
program: p,
|
||||||
pkgs, err := packages.Load(&packages.Config{
|
info: types.Info{
|
||||||
Mode: packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps,
|
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||||
Env: append(os.Environ(), "GOROOT="+p.Build.GOROOT, "GOOS="+p.Build.GOOS, "GOARCH="+p.Build.GOARCH, "CGO_ENABLED="+cgoEnabled),
|
Defs: make(map[*ast.Ident]types.Object),
|
||||||
BuildFlags: []string{"-tags", strings.Join(p.Build.BuildTags, " ")},
|
Uses: make(map[*ast.Ident]types.Object),
|
||||||
Tests: p.Tests,
|
Implicits: make(map[ast.Node]types.Object),
|
||||||
}, importPath)
|
Scopes: make(map[ast.Node]*types.Scope),
|
||||||
if err != nil {
|
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||||
return err
|
},
|
||||||
}
|
|
||||||
var pkg *packages.Package
|
|
||||||
if p.Tests {
|
|
||||||
// We need the second package. Quoting from the docs:
|
|
||||||
// > For example, when using the go command, loading "fmt" with Tests=true
|
|
||||||
// > returns four packages, with IDs "fmt" (the standard package),
|
|
||||||
// > "fmt [fmt.test]" (the package as compiled for the test),
|
|
||||||
// > "fmt_test" (the test functions from source files in package fmt_test),
|
|
||||||
// > and "fmt.test" (the test binary).
|
|
||||||
pkg = pkgs[1]
|
|
||||||
} else {
|
|
||||||
if len(pkgs) != 1 {
|
|
||||||
return fmt.Errorf("expected exactly one package while importing %s, got %d", importPath, len(pkgs))
|
|
||||||
}
|
}
|
||||||
pkg = pkgs[0]
|
err := decoder.Decode(&pkg.PackageJSON)
|
||||||
}
|
if err != nil {
|
||||||
var importError *Errors
|
if err == io.EOF {
|
||||||
var addPackages func(pkg *packages.Package)
|
break
|
||||||
addPackages = func(pkg *packages.Package) {
|
}
|
||||||
if _, ok := p.Packages[pkg.PkgPath]; ok {
|
return nil, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
pkg2 := p.newPackage(pkg)
|
if pkg.Error != nil {
|
||||||
p.Packages[pkg.PkgPath] = pkg2
|
// There was an error while importing (for example, a circular
|
||||||
if len(pkg.Errors) != 0 {
|
// dependency).
|
||||||
if importError != nil {
|
pos := token.Position{}
|
||||||
// There was another error reported already. Do not report
|
fields := strings.Split(pkg.Error.Pos, ":")
|
||||||
// errors from multiple packages at once.
|
if len(fields) >= 2 {
|
||||||
return
|
// There is some file/line/column information.
|
||||||
}
|
if n, err := strconv.Atoi(fields[len(fields)-2]); err == nil {
|
||||||
importError = &Errors{
|
// Format: filename.go:line:colum
|
||||||
Pkg: pkg2,
|
pos.Filename = strings.Join(fields[:len(fields)-2], ":")
|
||||||
}
|
pos.Line = n
|
||||||
for _, err := range pkg.Errors {
|
pos.Column, _ = strconv.Atoi(fields[len(fields)-1])
|
||||||
pos := token.Position{}
|
} else {
|
||||||
fields := strings.Split(err.Pos, ":")
|
// Format: filename.go:line
|
||||||
if len(fields) >= 2 {
|
pos.Filename = strings.Join(fields[:len(fields)-1], ":")
|
||||||
// There is some file/line/column information.
|
pos.Line, _ = strconv.Atoi(fields[len(fields)-1])
|
||||||
if n, err := strconv.Atoi(fields[len(fields)-2]); err == nil {
|
|
||||||
// Format: filename.go:line:colum
|
|
||||||
pos.Filename = strings.Join(fields[:len(fields)-2], ":")
|
|
||||||
pos.Line = n
|
|
||||||
pos.Column, _ = strconv.Atoi(fields[len(fields)-1])
|
|
||||||
} else {
|
|
||||||
// Format: filename.go:line
|
|
||||||
pos.Filename = strings.Join(fields[:len(fields)-1], ":")
|
|
||||||
pos.Line, _ = strconv.Atoi(fields[len(fields)-1])
|
|
||||||
}
|
|
||||||
pos.Filename = p.getOriginalPath(pos.Filename)
|
|
||||||
}
|
}
|
||||||
importError.Errs = append(importError.Errs, scanner.Error{
|
pos.Filename = p.getOriginalPath(pos.Filename)
|
||||||
Pos: pos,
|
|
||||||
Msg: err.Msg,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return
|
err := scanner.Error{
|
||||||
|
Pos: pos,
|
||||||
|
Msg: pkg.Error.Err,
|
||||||
|
}
|
||||||
|
if len(pkg.Error.ImportStack) != 0 {
|
||||||
|
return nil, Error{
|
||||||
|
ImportStack: pkg.Error.ImportStack,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
p.sorted = append(p.sorted, pkg)
|
||||||
// Get the list of imports (sorted alphabetically).
|
p.Packages[pkg.ImportPath] = pkg
|
||||||
names := make([]string, 0, len(pkg.Imports))
|
|
||||||
for name := range pkg.Imports {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
|
|
||||||
// Add all the imports.
|
|
||||||
for _, name := range names {
|
|
||||||
addPackages(pkg.Imports[name])
|
|
||||||
}
|
|
||||||
|
|
||||||
p.sorted = append(p.sorted, pkg2)
|
|
||||||
}
|
}
|
||||||
addPackages(pkg)
|
|
||||||
if importError != nil {
|
return p, nil
|
||||||
return *importError
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOriginalPath looks whether this path is in the generated GOROOT and if so,
|
// getOriginalPath looks whether this path is in the generated GOROOT and if so,
|
||||||
@@ -168,23 +181,23 @@ func (p *Program) loadPackage(importPath string) error {
|
|||||||
// the input path is returned.
|
// the input path is returned.
|
||||||
func (p *Program) getOriginalPath(path string) string {
|
func (p *Program) getOriginalPath(path string) string {
|
||||||
originalPath := path
|
originalPath := path
|
||||||
if strings.HasPrefix(path, p.Build.GOROOT+string(filepath.Separator)) {
|
if strings.HasPrefix(path, p.goroot+string(filepath.Separator)) {
|
||||||
// If this file is part of the synthetic GOROOT, try to infer the
|
// If this file is part of the synthetic GOROOT, try to infer the
|
||||||
// original path.
|
// original path.
|
||||||
relpath := path[len(filepath.Join(p.Build.GOROOT, "src"))+1:]
|
relpath := path[len(filepath.Join(p.goroot, "src"))+1:]
|
||||||
realgorootPath := filepath.Join(goenv.Get("GOROOT"), "src", relpath)
|
realgorootPath := filepath.Join(goenv.Get("GOROOT"), "src", relpath)
|
||||||
if _, err := os.Stat(realgorootPath); err == nil {
|
if _, err := os.Stat(realgorootPath); err == nil {
|
||||||
originalPath = realgorootPath
|
originalPath = realgorootPath
|
||||||
}
|
}
|
||||||
maybeInTinyGoRoot := false
|
maybeInTinyGoRoot := false
|
||||||
for prefix := range pathsToOverride(needsSyscallPackage(p.Build.BuildTags)) {
|
for prefix := range pathsToOverride(needsSyscallPackage(p.config.BuildTags())) {
|
||||||
if !strings.HasPrefix(relpath, prefix) {
|
if !strings.HasPrefix(relpath, prefix) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
maybeInTinyGoRoot = true
|
maybeInTinyGoRoot = true
|
||||||
}
|
}
|
||||||
if maybeInTinyGoRoot {
|
if maybeInTinyGoRoot {
|
||||||
tinygoPath := filepath.Join(p.TINYGOROOT, "src", relpath)
|
tinygoPath := filepath.Join(goenv.Get("TINYGOROOT"), "src", relpath)
|
||||||
if _, err := os.Stat(tinygoPath); err == nil {
|
if _, err := os.Stat(tinygoPath); err == nil {
|
||||||
originalPath = tinygoPath
|
originalPath = tinygoPath
|
||||||
}
|
}
|
||||||
@@ -193,28 +206,18 @@ func (p *Program) getOriginalPath(path string) string {
|
|||||||
return originalPath
|
return originalPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPackage instantiates a new *Package object with initialized members.
|
|
||||||
func (p *Program) newPackage(pkg *packages.Package) *Package {
|
|
||||||
return &Package{
|
|
||||||
Program: p,
|
|
||||||
Package: pkg,
|
|
||||||
Info: types.Info{
|
|
||||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
|
||||||
Defs: make(map[*ast.Ident]types.Object),
|
|
||||||
Uses: make(map[*ast.Ident]types.Object),
|
|
||||||
Implicits: make(map[ast.Node]types.Object),
|
|
||||||
Scopes: make(map[ast.Node]*types.Scope),
|
|
||||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sorted returns a list of all packages, sorted in a way that no packages come
|
// Sorted returns a list of all packages, sorted in a way that no packages come
|
||||||
// before the packages they depend upon.
|
// before the packages they depend upon.
|
||||||
func (p *Program) Sorted() []*Package {
|
func (p *Program) Sorted() []*Package {
|
||||||
return p.sorted
|
return p.sorted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MainPkg returns the last package in the Sorted() slice. This is the main
|
||||||
|
// package of the program.
|
||||||
|
func (p *Program) MainPkg() *Package {
|
||||||
|
return p.sorted[len(p.sorted)-1]
|
||||||
|
}
|
||||||
|
|
||||||
// Parse parses all packages and typechecks them.
|
// Parse parses all packages and typechecks them.
|
||||||
//
|
//
|
||||||
// The returned error may be an Errors error, which contains a list of errors.
|
// The returned error may be an Errors error, which contains a list of errors.
|
||||||
@@ -222,22 +225,16 @@ func (p *Program) Sorted() []*Package {
|
|||||||
// Idempotent.
|
// Idempotent.
|
||||||
func (p *Program) Parse() error {
|
func (p *Program) Parse() error {
|
||||||
// Parse all packages.
|
// Parse all packages.
|
||||||
for _, pkg := range p.Sorted() {
|
// TODO: do this in parallel.
|
||||||
|
for _, pkg := range p.sorted {
|
||||||
err := pkg.Parse()
|
err := pkg.Parse()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.Tests {
|
|
||||||
err := p.swapTestMain()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typecheck all packages.
|
// Typecheck all packages.
|
||||||
for _, pkg := range p.Sorted() {
|
for _, pkg := range p.sorted {
|
||||||
err := pkg.Check()
|
err := pkg.Check()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -247,82 +244,6 @@ func (p *Program) Parse() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Program) swapTestMain() error {
|
|
||||||
var tests []string
|
|
||||||
|
|
||||||
isTestFunc := func(f *ast.FuncDecl) bool {
|
|
||||||
// TODO: improve signature check
|
|
||||||
if strings.HasPrefix(f.Name.Name, "Test") && f.Name.Name != "TestMain" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, f := range p.MainPkg.Files {
|
|
||||||
for i, d := range f.Decls {
|
|
||||||
switch v := d.(type) {
|
|
||||||
case *ast.FuncDecl:
|
|
||||||
if isTestFunc(v) {
|
|
||||||
tests = append(tests, v.Name.Name)
|
|
||||||
}
|
|
||||||
if v.Name.Name == "main" {
|
|
||||||
// Remove main
|
|
||||||
if len(f.Decls) == 1 {
|
|
||||||
f.Decls = make([]ast.Decl, 0)
|
|
||||||
} else {
|
|
||||||
f.Decls[i] = f.Decls[len(f.Decls)-1]
|
|
||||||
f.Decls = f.Decls[:len(f.Decls)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Check if they defined a TestMain and call it instead of testing.TestMain
|
|
||||||
const mainBody = `package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main () {
|
|
||||||
m := &testing.M{
|
|
||||||
Tests: []testing.TestToCall{
|
|
||||||
{{range .TestFunctions}}
|
|
||||||
{Name: "{{.}}", Func: {{.}}},
|
|
||||||
{{end}}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
testing.TestMain(m)
|
|
||||||
}
|
|
||||||
`
|
|
||||||
tmpl := template.Must(template.New("testmain").Parse(mainBody))
|
|
||||||
b := bytes.Buffer{}
|
|
||||||
tmplData := struct {
|
|
||||||
TestFunctions []string
|
|
||||||
}{
|
|
||||||
TestFunctions: tests,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := tmpl.Execute(&b, tmplData)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
path := filepath.Join(p.MainPkg.Dir, "$testmain.go")
|
|
||||||
|
|
||||||
if p.fset == nil {
|
|
||||||
p.fset = token.NewFileSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
newMain, err := parser.ParseFile(p.fset, path, b.Bytes(), parser.AllErrors)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
p.MainPkg.Files = append(p.MainPkg.Files, newMain)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseFile is a wrapper around parser.ParseFile.
|
// parseFile is a wrapper around parser.ParseFile.
|
||||||
func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
||||||
if p.fset == nil {
|
if p.fset == nil {
|
||||||
@@ -342,14 +263,13 @@ func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
|||||||
// Idempotent.
|
// Idempotent.
|
||||||
func (p *Package) Parse() error {
|
func (p *Package) Parse() error {
|
||||||
if len(p.Files) != 0 {
|
if len(p.Files) != 0 {
|
||||||
return nil
|
return nil // nothing to do (?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the AST.
|
// Load the AST.
|
||||||
// TODO: do this in parallel.
|
if p.ImportPath == "unsafe" {
|
||||||
if p.PkgPath == "unsafe" {
|
// Special case for the unsafe package, which is defined internally by
|
||||||
// Special case for the unsafe package. Don't even bother loading
|
// the types package.
|
||||||
// the files.
|
|
||||||
p.Pkg = types.Unsafe
|
p.Pkg = types.Unsafe
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -369,11 +289,11 @@ func (p *Package) Parse() error {
|
|||||||
// Idempotent.
|
// Idempotent.
|
||||||
func (p *Package) Check() error {
|
func (p *Package) Check() error {
|
||||||
if p.Pkg != nil {
|
if p.Pkg != nil {
|
||||||
return nil
|
return nil // already typechecked
|
||||||
}
|
}
|
||||||
|
|
||||||
var typeErrors []error
|
var typeErrors []error
|
||||||
checker := p.TypeChecker
|
checker := p.program.typeChecker // make a copy, because it will be modified
|
||||||
checker.Error = func(err error) {
|
checker.Error = func(err error) {
|
||||||
typeErrors = append(typeErrors, err)
|
typeErrors = append(typeErrors, err)
|
||||||
}
|
}
|
||||||
@@ -381,7 +301,7 @@ func (p *Package) Check() error {
|
|||||||
// Do typechecking of the package.
|
// Do typechecking of the package.
|
||||||
checker.Importer = p
|
checker.Importer = p
|
||||||
|
|
||||||
typesPkg, err := checker.Check(p.PkgPath, p.fset, p.Files, &p.Info)
|
typesPkg, err := checker.Check(p.ImportPath, p.program.fset, p.Files, &p.info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err, ok := err.(Errors); ok {
|
if err, ok := err.(Errors); ok {
|
||||||
return err
|
return err
|
||||||
@@ -394,40 +314,46 @@ func (p *Package) Check() error {
|
|||||||
|
|
||||||
// parseFiles parses the loaded list of files and returns this list.
|
// parseFiles parses the loaded list of files and returns this list.
|
||||||
func (p *Package) parseFiles() ([]*ast.File, error) {
|
func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||||
// TODO: do this concurrently.
|
|
||||||
var files []*ast.File
|
var files []*ast.File
|
||||||
var fileErrs []error
|
var fileErrs []error
|
||||||
|
|
||||||
var cgoFiles []*ast.File
|
// Parse all files (incuding CgoFiles).
|
||||||
for _, file := range p.GoFiles {
|
parseFile := func(file string) {
|
||||||
f, err := p.parseFile(file, parser.ParseComments)
|
if !filepath.IsAbs(file) {
|
||||||
|
file = filepath.Join(p.Dir, file)
|
||||||
|
}
|
||||||
|
f, err := p.program.parseFile(file, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fileErrs = append(fileErrs, err)
|
fileErrs = append(fileErrs, err)
|
||||||
continue
|
return
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
fileErrs = append(fileErrs, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, importSpec := range f.Imports {
|
|
||||||
if importSpec.Path.Value == `"C"` {
|
|
||||||
cgoFiles = append(cgoFiles, f)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
}
|
}
|
||||||
if len(cgoFiles) != 0 {
|
for _, file := range p.GoFiles {
|
||||||
cflags := append(p.CFlags, "-I"+filepath.Dir(p.GoFiles[0]))
|
parseFile(file)
|
||||||
if p.ClangHeaders != "" {
|
}
|
||||||
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.ClangHeaders)
|
for _, file := range p.CgoFiles {
|
||||||
|
parseFile(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do CGo processing.
|
||||||
|
if len(p.CgoFiles) != 0 {
|
||||||
|
var cflags []string
|
||||||
|
cflags = append(cflags, p.program.config.CFlags()...)
|
||||||
|
cflags = append(cflags, "-I"+p.Dir)
|
||||||
|
if p.program.clangHeaders != "" {
|
||||||
|
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
|
||||||
}
|
}
|
||||||
generated, ldflags, errs := cgo.Process(files, p.Program.Dir, p.fset, cflags)
|
generated, ldflags, errs := cgo.Process(files, p.program.workingDir, p.program.fset, cflags)
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
fileErrs = append(fileErrs, errs...)
|
fileErrs = append(fileErrs, errs...)
|
||||||
}
|
}
|
||||||
files = append(files, generated)
|
files = append(files, generated)
|
||||||
p.LDFlags = append(p.LDFlags, ldflags...)
|
p.program.LDFlags = append(p.program.LDFlags, ldflags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only return an error after CGo processing, so that errors in parsing and
|
||||||
|
// CGo can be reported together.
|
||||||
if len(fileErrs) != 0 {
|
if len(fileErrs) != 0 {
|
||||||
return nil, Errors{p, fileErrs}
|
return nil, Errors{p, fileErrs}
|
||||||
}
|
}
|
||||||
@@ -441,8 +367,13 @@ func (p *Package) Import(to string) (*types.Package, error) {
|
|||||||
if to == "unsafe" {
|
if to == "unsafe" {
|
||||||
return types.Unsafe, nil
|
return types.Unsafe, nil
|
||||||
}
|
}
|
||||||
if _, ok := p.Imports[to]; ok {
|
if replace, ok := p.ImportMap[to]; ok {
|
||||||
return p.Packages[p.Imports[to].PkgPath].Pkg, nil
|
// This import path should be replaced by another import path, according
|
||||||
|
// to `go list`.
|
||||||
|
to = replace
|
||||||
|
}
|
||||||
|
if imported, ok := p.program.Packages[to]; ok {
|
||||||
|
return imported.Pkg, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("package not imported: " + to)
|
return nil, errors.New("package not imported: " + to)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -10,8 +10,8 @@ import (
|
|||||||
func (p *Program) LoadSSA() *ssa.Program {
|
func (p *Program) LoadSSA() *ssa.Program {
|
||||||
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
|
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
|
||||||
|
|
||||||
for _, pkg := range p.Sorted() {
|
for _, pkg := range p.sorted {
|
||||||
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.Info, true)
|
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.info, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return prog
|
return prog
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"go/scanner"
|
"go/scanner"
|
||||||
"go/types"
|
"go/types"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -28,6 +29,12 @@ import (
|
|||||||
"go.bug.st/serial"
|
"go.bug.st/serial"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// This variable is set at build time using -ldflags parameters.
|
||||||
|
// See: https://stackoverflow.com/a/11355611
|
||||||
|
gitSha1 string
|
||||||
|
)
|
||||||
|
|
||||||
// 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 {
|
||||||
@@ -58,33 +65,23 @@ func moveFile(src, dst string) error {
|
|||||||
return os.Remove(src)
|
return os.Remove(src)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyFile copies the given file from src to dst. It copies first to a .tmp
|
// copyFile copies the given file from src to dst. It can copy over
|
||||||
// file which is then moved over a possibly already existing file at the
|
// a possibly already existing file at the destination.
|
||||||
// destination.
|
|
||||||
func copyFile(src, dst string) error {
|
func copyFile(src, dst string) error {
|
||||||
inf, err := os.Open(src)
|
source, err := os.Open(src)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer inf.Close()
|
|
||||||
outpath := dst + ".tmp"
|
|
||||||
outf, err := os.Create(outpath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer source.Close()
|
||||||
|
|
||||||
_, err = io.Copy(outf, inf)
|
destination, err := os.Create(dst)
|
||||||
if err != nil {
|
|
||||||
os.Remove(outpath)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = outf.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer destination.Close()
|
||||||
|
|
||||||
return os.Rename(dst+".tmp", dst)
|
_, err = io.Copy(destination, source)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build compiles and links the given package and writes it to outpath.
|
// Build compiles and links the given package and writes it to outpath.
|
||||||
@@ -94,10 +91,10 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, outpath, config, func(tmppath string) error {
|
return builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
|
||||||
if err := os.Rename(tmppath, outpath); err != nil {
|
if err := os.Rename(result.Binary, outpath); err != nil {
|
||||||
// Moving failed. Do a file copy.
|
// Moving failed. Do a file copy.
|
||||||
inf, err := os.Open(tmppath)
|
inf, err := os.Open(result.Binary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -130,16 +127,11 @@ func Test(pkgName string, options *compileopts.Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add test build tag. This is incorrect: `go test` only looks at the
|
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
|
||||||
// _test.go file suffix but does not add the test build tag in the process.
|
cmd := exec.Command(result.Binary)
|
||||||
// However, it's a simple fix right now.
|
|
||||||
// For details: https://github.com/golang/go/issues/21360
|
|
||||||
config.Target.BuildTags = append(config.Target.BuildTags, "test")
|
|
||||||
|
|
||||||
return builder.Build(pkgName, ".elf", config, func(tmppath string) error {
|
|
||||||
cmd := exec.Command(tmppath)
|
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Dir = result.MainDir
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Propagate the exit code
|
// Propagate the exit code
|
||||||
@@ -149,7 +141,7 @@ func Test(pkgName string, options *compileopts.Options) error {
|
|||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
return &commandError{"failed to run compiled binary", tmppath, err}
|
return &commandError{"failed to run compiled binary", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -193,9 +185,9 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
return errors.New("unknown flash method: " + flashMethod)
|
return errors.New("unknown flash method: " + flashMethod)
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, fileExt, config, func(tmppath string) error {
|
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) 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" && flashMethod != "openocd" {
|
||||||
if port == "" {
|
if port == "" {
|
||||||
var err error
|
var err error
|
||||||
port, err = getDefaultPort()
|
port, err = getDefaultPort()
|
||||||
@@ -206,7 +198,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
|
|
||||||
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", result.Binary, err}
|
||||||
}
|
}
|
||||||
// give the target MCU a chance to restart into bootloader
|
// give the target MCU a chance to restart into bootloader
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
@@ -218,7 +210,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
// Create the command.
|
// Create the command.
|
||||||
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, result.Binary, -1)
|
||||||
|
|
||||||
if port == "" && strings.Contains(flashCmd, "{port}") {
|
if port == "" && strings.Contains(flashCmd, "{port}") {
|
||||||
var err error
|
var err error
|
||||||
@@ -248,21 +240,21 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
cmd.Dir = goenv.Get("TINYGOROOT")
|
cmd.Dir = goenv.Get("TINYGOROOT")
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to flash", tmppath, err}
|
return &commandError{"failed to flash", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case "msd":
|
case "msd":
|
||||||
switch fileExt {
|
switch fileExt {
|
||||||
case ".uf2":
|
case ".uf2":
|
||||||
err := flashUF2UsingMSD(config.Target.FlashVolume, tmppath)
|
err := flashUF2UsingMSD(config.Target.FlashVolume, result.Binary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to flash", tmppath, err}
|
return &commandError{"failed to flash", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case ".hex":
|
case ".hex":
|
||||||
err := flashHexUsingMSD(config.Target.FlashVolume, tmppath)
|
err := flashHexUsingMSD(config.Target.FlashVolume, result.Binary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to flash", tmppath, err}
|
return &commandError{"failed to flash", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
@@ -273,13 +265,13 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
args = append(args, "-c", "program "+tmppath+" reset exit")
|
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+" reset exit")
|
||||||
cmd := exec.Command("openocd", args...)
|
cmd := exec.Command("openocd", args...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to flash", tmppath, err}
|
return &commandError{"failed to flash", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
@@ -304,7 +296,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
|||||||
return errors.New("gdb not configured in the target specification")
|
return errors.New("gdb not configured in the target specification")
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, "", config, func(tmppath string) error {
|
return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
|
||||||
// Find a good way to run GDB.
|
// Find a good way to run GDB.
|
||||||
gdbInterface, openocdInterface := config.Programmer()
|
gdbInterface, openocdInterface := config.Programmer()
|
||||||
switch gdbInterface {
|
switch gdbInterface {
|
||||||
@@ -367,7 +359,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
|||||||
gdbCommands = append(gdbCommands, "target remote :1234")
|
gdbCommands = append(gdbCommands, "target remote :1234")
|
||||||
|
|
||||||
// Run in an emulator.
|
// Run in an emulator.
|
||||||
args := append(config.Target.Emulator[1:], tmppath, "-s", "-S")
|
args := append(config.Target.Emulator[1:], result.Binary, "-s", "-S")
|
||||||
daemon = exec.Command(config.Target.Emulator[0], args...)
|
daemon = exec.Command(config.Target.Emulator[0], args...)
|
||||||
daemon.Stdout = os.Stdout
|
daemon.Stdout = os.Stdout
|
||||||
daemon.Stderr = os.Stderr
|
daemon.Stderr = os.Stderr
|
||||||
@@ -375,7 +367,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
|||||||
gdbCommands = append(gdbCommands, "target remote :2345")
|
gdbCommands = append(gdbCommands, "target remote :2345")
|
||||||
|
|
||||||
// Run in an emulator.
|
// Run in an emulator.
|
||||||
args := append(config.Target.Emulator[1:], tmppath, "-g")
|
args := append(config.Target.Emulator[1:], result.Binary, "-g")
|
||||||
daemon = exec.Command(config.Target.Emulator[0], args...)
|
daemon = exec.Command(config.Target.Emulator[0], args...)
|
||||||
daemon.Stdout = os.Stdout
|
daemon.Stdout = os.Stdout
|
||||||
daemon.Stderr = os.Stderr
|
daemon.Stderr = os.Stderr
|
||||||
@@ -413,7 +405,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
|||||||
// Construct and execute a gdb command.
|
// Construct and execute a gdb command.
|
||||||
// By default: gdb -ex run <binary>
|
// By default: gdb -ex run <binary>
|
||||||
// Exit GDB with Ctrl-D.
|
// Exit GDB with Ctrl-D.
|
||||||
params := []string{tmppath}
|
params := []string{result.Binary}
|
||||||
for _, cmd := range gdbCommands {
|
for _, cmd := range gdbCommands {
|
||||||
params = append(params, "-ex", cmd)
|
params = append(params, "-ex", cmd)
|
||||||
}
|
}
|
||||||
@@ -423,7 +415,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
|||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &commandError{"failed to run gdb with", tmppath, err}
|
return &commandError{"failed to run gdb with", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -439,10 +431,10 @@ func Run(pkgName string, options *compileopts.Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.Build(pkgName, ".elf", config, func(tmppath string) error {
|
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
|
||||||
if len(config.Target.Emulator) == 0 {
|
if len(config.Target.Emulator) == 0 {
|
||||||
// Run directly.
|
// Run directly.
|
||||||
cmd := exec.Command(tmppath)
|
cmd := exec.Command(result.Binary)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
@@ -451,12 +443,12 @@ func Run(pkgName string, options *compileopts.Options) error {
|
|||||||
// Workaround for QEMU which always exits with an error.
|
// Workaround for QEMU which always exits with an error.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &commandError{"failed to run compiled binary", tmppath, err}
|
return &commandError{"failed to run compiled binary", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
// Run in an emulator.
|
// Run in an emulator.
|
||||||
args := append(config.Target.Emulator[1:], tmppath)
|
args := append(config.Target.Emulator[1:], result.Binary)
|
||||||
cmd := exec.Command(config.Target.Emulator[0], args...)
|
cmd := exec.Command(config.Target.Emulator[0], args...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
@@ -466,7 +458,7 @@ func Run(pkgName string, options *compileopts.Options) error {
|
|||||||
// Workaround for QEMU which always exits with an error.
|
// Workaround for QEMU which always exits with an error.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &commandError{"failed to run emulator with", tmppath, err}
|
return &commandError{"failed to run emulator with", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -652,36 +644,6 @@ func getDefaultPort() (port string, err error) {
|
|||||||
return d[0], nil
|
return d[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runGoList runs the `go list` command but using the configuration used for
|
|
||||||
// TinyGo.
|
|
||||||
func runGoList(config *compileopts.Config, flagJSON, flagDeps bool, pkgs []string) error {
|
|
||||||
goroot, err := loader.GetCachedGoroot(config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
args := []string{"list"}
|
|
||||||
if flagJSON {
|
|
||||||
args = append(args, "-json")
|
|
||||||
}
|
|
||||||
if flagDeps {
|
|
||||||
args = append(args, "-deps")
|
|
||||||
}
|
|
||||||
if len(config.BuildTags()) != 0 {
|
|
||||||
args = append(args, "-tags", strings.Join(config.BuildTags(), " "))
|
|
||||||
}
|
|
||||||
args = append(args, pkgs...)
|
|
||||||
cgoEnabled := "0"
|
|
||||||
if config.CgoEnabled() {
|
|
||||||
cgoEnabled = "1"
|
|
||||||
}
|
|
||||||
cmd := exec.Command("go", args...)
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
cmd.Env = append(os.Environ(), "GOROOT="+goroot, "GOOS="+config.GOOS(), "GOARCH="+config.GOARCH(), "CGO_ENABLED="+cgoEnabled)
|
|
||||||
cmd.Run()
|
|
||||||
return 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:", goenv.Version)
|
fmt.Fprintln(os.Stderr, "version:", goenv.Version)
|
||||||
@@ -755,10 +717,16 @@ func printCompilerError(logln func(...interface{}), err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case loader.Errors:
|
case loader.Errors:
|
||||||
logln("#", err.Pkg.PkgPath)
|
logln("#", err.Pkg.ImportPath)
|
||||||
for _, err := range err.Errs {
|
for _, err := range err.Errs {
|
||||||
printCompilerError(logln, err)
|
printCompilerError(logln, err)
|
||||||
}
|
}
|
||||||
|
case loader.Error:
|
||||||
|
logln(err.Err.Error())
|
||||||
|
logln("package", err.ImportStack[0])
|
||||||
|
for _, pkgPath := range err.ImportStack[1:] {
|
||||||
|
logln("\timports", pkgPath)
|
||||||
|
}
|
||||||
case *builder.MultiError:
|
case *builder.MultiError:
|
||||||
for _, err := range err.Errs {
|
for _, err := range err.Errs {
|
||||||
printCompilerError(logln, err)
|
printCompilerError(logln, err)
|
||||||
@@ -954,6 +922,35 @@ func main() {
|
|||||||
}
|
}
|
||||||
err := Test(pkgName, options)
|
err := Test(pkgName, options)
|
||||||
handleCompilerError(err)
|
handleCompilerError(err)
|
||||||
|
case "targets":
|
||||||
|
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
|
||||||
|
entries, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "could not list targets:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.Mode().IsRegular() || !strings.HasSuffix(entry.Name(), ".json") {
|
||||||
|
// Only inspect JSON files.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
path := filepath.Join(dir, entry.Name())
|
||||||
|
spec, err := compileopts.LoadTarget(path)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "could not list target:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == nil {
|
||||||
|
// This doesn't look like a regular target file, but rather like
|
||||||
|
// a parent target (such as targets/cortex-m.json).
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := entry.Name()
|
||||||
|
name = name[:len(name)-5]
|
||||||
|
fmt.Println(name)
|
||||||
|
}
|
||||||
case "info":
|
case "info":
|
||||||
if flag.NArg() == 1 {
|
if flag.NArg() == 1 {
|
||||||
options.Target = flag.Arg(0)
|
options.Target = flag.Arg(0)
|
||||||
@@ -973,12 +970,18 @@ func main() {
|
|||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
cachedGOROOT, err := loader.GetCachedGoroot(config)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
fmt.Printf("LLVM triple: %s\n", config.Triple())
|
fmt.Printf("LLVM triple: %s\n", config.Triple())
|
||||||
fmt.Printf("GOOS: %s\n", config.GOOS())
|
fmt.Printf("GOOS: %s\n", config.GOOS())
|
||||||
fmt.Printf("GOARCH: %s\n", config.GOARCH())
|
fmt.Printf("GOARCH: %s\n", config.GOARCH())
|
||||||
fmt.Printf("build tags: %s\n", strings.Join(config.BuildTags(), " "))
|
fmt.Printf("build tags: %s\n", strings.Join(config.BuildTags(), " "))
|
||||||
fmt.Printf("garbage collector: %s\n", config.GC())
|
fmt.Printf("garbage collector: %s\n", config.GC())
|
||||||
fmt.Printf("scheduler: %s\n", config.Scheduler())
|
fmt.Printf("scheduler: %s\n", config.Scheduler())
|
||||||
|
fmt.Printf("cached GOROOT: %s\n", cachedGOROOT)
|
||||||
case "list":
|
case "list":
|
||||||
config, err := builder.NewConfig(options)
|
config, err := builder.NewConfig(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -986,11 +989,31 @@ func main() {
|
|||||||
usage()
|
usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
err = runGoList(config, *flagJSON, *flagDeps, flag.Args())
|
var extraArgs []string
|
||||||
|
if *flagJSON {
|
||||||
|
extraArgs = append(extraArgs, "-json")
|
||||||
|
}
|
||||||
|
if *flagDeps {
|
||||||
|
extraArgs = append(extraArgs, "-deps")
|
||||||
|
}
|
||||||
|
cmd, err := loader.List(config, extraArgs, flag.Args())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
|
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
|
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
||||||
|
os.Exit(status.ExitStatus())
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
case "clean":
|
case "clean":
|
||||||
// remove cache directory
|
// remove cache directory
|
||||||
err := os.RemoveAll(goenv.Get("GOCACHE"))
|
err := os.RemoveAll(goenv.Get("GOCACHE"))
|
||||||
@@ -1005,7 +1028,11 @@ func main() {
|
|||||||
if s, err := goenv.GorootVersionString(goenv.Get("GOROOT")); err == nil {
|
if s, err := goenv.GorootVersionString(goenv.Get("GOROOT")); err == nil {
|
||||||
goversion = s
|
goversion = s
|
||||||
}
|
}
|
||||||
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", goenv.Version, runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
|
version := goenv.Version
|
||||||
|
if strings.HasSuffix(goenv.Version, "-dev") && gitSha1 != "" {
|
||||||
|
version += "-" + gitSha1
|
||||||
|
}
|
||||||
|
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", version, runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
|
||||||
case "env":
|
case "env":
|
||||||
if flag.NArg() == 0 {
|
if flag.NArg() == 0 {
|
||||||
// Show all environment variables.
|
// Show all environment variables.
|
||||||
|
|||||||
+1
-1
@@ -157,7 +157,7 @@ func runTest(path, target string, t *testing.T) {
|
|||||||
PrintIR: false,
|
PrintIR: false,
|
||||||
DumpSSA: false,
|
DumpSSA: false,
|
||||||
VerifyIR: true,
|
VerifyIR: true,
|
||||||
Debug: false,
|
Debug: true,
|
||||||
PrintSizes: "",
|
PrintSizes: "",
|
||||||
WasmAbi: "js",
|
WasmAbi: "js",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
.syntax unified
|
.syntax unified
|
||||||
|
.cfi_sections .debug_frame
|
||||||
|
|
||||||
.section .text.HardFault_Handler
|
.section .text.HardFault_Handler
|
||||||
.global HardFault_Handler
|
.global HardFault_Handler
|
||||||
.type HardFault_Handler, %function
|
.type HardFault_Handler, %function
|
||||||
HardFault_Handler:
|
HardFault_Handler:
|
||||||
|
.cfi_startproc
|
||||||
// Put the old stack pointer in the first argument, for easy debugging. This
|
// Put the old stack pointer in the first argument, for easy debugging. This
|
||||||
// is especially useful on Cortex-M0, which supports far fewer debug
|
// is especially useful on Cortex-M0, which supports far fewer debug
|
||||||
// facilities.
|
// facilities.
|
||||||
@@ -19,6 +21,7 @@ HardFault_Handler:
|
|||||||
|
|
||||||
// Continue handling this error in Go.
|
// Continue handling this error in Go.
|
||||||
bl handleHardFault
|
bl handleHardFault
|
||||||
|
.cfi_endproc
|
||||||
.size HardFault_Handler, .-HardFault_Handler
|
.size HardFault_Handler, .-HardFault_Handler
|
||||||
|
|
||||||
// This is a convenience function for semihosting support.
|
// This is a convenience function for semihosting support.
|
||||||
@@ -27,5 +30,8 @@ HardFault_Handler:
|
|||||||
.global SemihostingCall
|
.global SemihostingCall
|
||||||
.type SemihostingCall, %function
|
.type SemihostingCall, %function
|
||||||
SemihostingCall:
|
SemihostingCall:
|
||||||
|
.cfi_startproc
|
||||||
bkpt 0xab
|
bkpt 0xab
|
||||||
bx lr
|
bx lr
|
||||||
|
.cfi_endproc
|
||||||
|
.size SemihostingCall, .-SemihostingCall
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
// The following definitions were copied from:
|
||||||
|
// esp-idf/components/xtensa/include/xtensa/corebits.h
|
||||||
|
#define PS_WOE_MASK 0x00040000
|
||||||
|
#define PS_OWB_MASK 0x00000F00
|
||||||
|
#define PS_CALLINC_MASK 0x00030000
|
||||||
|
#define PS_WOE PS_WOE_MASK
|
||||||
|
|
||||||
|
// Only calling it call_start_cpu0 for consistency with ESP-IDF.
|
||||||
|
.section .text.call_start_cpu0
|
||||||
|
1:
|
||||||
|
.long _stack_top
|
||||||
|
.global call_start_cpu0
|
||||||
|
call_start_cpu0:
|
||||||
|
// We need to set the stack pointer to a different value. This is somewhat
|
||||||
|
// complicated in the Xtensa architecture. The code below is a modified
|
||||||
|
// version of the following code:
|
||||||
|
// https://github.com/espressif/esp-idf/blob/c77c4ccf/components/xtensa/include/xt_instr_macros.h#L47
|
||||||
|
|
||||||
|
// Disable WOE.
|
||||||
|
rsr.ps a2
|
||||||
|
movi a3, ~(PS_WOE_MASK)
|
||||||
|
and a2, a2, a3
|
||||||
|
wsr.ps a2
|
||||||
|
rsync
|
||||||
|
|
||||||
|
// Set WINDOWBASE to 1 << WINDOWSTART.
|
||||||
|
rsr.windowbase a2
|
||||||
|
ssl a2
|
||||||
|
movi a2, 1
|
||||||
|
sll a2, a2
|
||||||
|
wsr.windowstart a2
|
||||||
|
rsync
|
||||||
|
|
||||||
|
// Load new stack pointer.
|
||||||
|
l32r sp, 1b
|
||||||
|
|
||||||
|
// Re-enable WOE.
|
||||||
|
rsr.ps a2
|
||||||
|
movi a3, PS_WOE
|
||||||
|
or a2, a2, a3
|
||||||
|
wsr.ps a2
|
||||||
|
rsync
|
||||||
|
|
||||||
|
// Jump to the runtime start function written in Go.
|
||||||
|
j main
|
||||||
|
|
||||||
|
.section .text.tinygo_scanCurrentStack
|
||||||
|
.global tinygo_scanCurrentStack
|
||||||
|
tinygo_scanCurrentStack:
|
||||||
|
// TODO: save callee saved registers on the stack
|
||||||
|
j tinygo_scanstack
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
.section .text.tinygo_scanCurrentStack
|
||||||
|
.global tinygo_scanCurrentStack
|
||||||
|
tinygo_scanCurrentStack:
|
||||||
|
// TODO: save callee saved registers on the stack
|
||||||
|
j tinygo_scanstack
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
// Hand created file. DO NOT DELETE.
|
||||||
|
// atsamd51x bitfield definitions that are not auto-generated by gen-device-svd.go
|
||||||
|
|
||||||
|
// +build sam,atsamd51
|
||||||
|
|
||||||
|
// These are the supported pchctrl function numberings on the atsamd51x
|
||||||
|
// See http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf
|
||||||
|
// table 14-9
|
||||||
|
|
||||||
|
package sam
|
||||||
|
|
||||||
|
const (
|
||||||
|
PCHCTRL_GCLK_OSCCTRL_DFLL48 = 0 // DFLL48 input clock source
|
||||||
|
PCHCTRL_GCLK_OSCCTRL_FDPLL0 = 1 // Reference clock for FDPLL0
|
||||||
|
PCHCTRL_GCLK_OSCCTRL_FDPLL1 = 2 // Reference clock for FDPLL1
|
||||||
|
PCHCTRL_GCLK_OSCCTRL_FDPLL0_32K = 3 // FDPLL0 = 3 // 32KHz clock for internal lock timer
|
||||||
|
PCHCTRL_GCLK_OSCCTRL_FDPLL1_32K = 3 // FDPLL1 = 3 // 32KHz clock for internal lock timer
|
||||||
|
PCHCTRL_GCLK_SDHC0_SLOW = 3 // SDHC0 = 3 // Slow
|
||||||
|
PCHCTRL_GCLK_SDHC1_SLOW = 3 // SDHC1 = 3 // Slow
|
||||||
|
PCHCTRL_GCLK_SERCOMX_SLOW = 3 // GCLK_SERCOM[0..7]_SLOW = 3
|
||||||
|
PCHCTRL_GCLK_EIC = 4
|
||||||
|
PCHCTRL_GCLK_FREQM_MSR = 5 // FREQM Measure
|
||||||
|
PCHCTRL_GCLK_FREQM_REF = 6 // FREQM Reference
|
||||||
|
PCHCTRL_GCLK_SERCOM0_CORE = 7 // SERCOM0 Core
|
||||||
|
PCHCTRL_GCLK_SERCOM1_CORE = 8 // SERCOM1 Core
|
||||||
|
PCHCTRL_GCLK_TC0 = 9
|
||||||
|
PCHCTRL_GCLK_TC1 = 9 // TC0, TC1
|
||||||
|
PCHCTRL_GCLK_USB = 10 // USB
|
||||||
|
PCHCTRL_GCLK_EVSYS0 = 11
|
||||||
|
PCHCTRL_GCLK_EVSYS1 = 12
|
||||||
|
PCHCTRL_GCLK_EVSYS2 = 13
|
||||||
|
PCHCTRL_GCLK_EVSYS3 = 14
|
||||||
|
PCHCTRL_GCLK_EVSYS4 = 15
|
||||||
|
PCHCTRL_GCLK_EVSYS5 = 16
|
||||||
|
PCHCTRL_GCLK_EVSYS6 = 17
|
||||||
|
PCHCTRL_GCLK_EVSYS7 = 18
|
||||||
|
PCHCTRL_GCLK_EVSYS8 = 19
|
||||||
|
PCHCTRL_GCLK_EVSYS9 = 20
|
||||||
|
PCHCTRL_GCLK_EVSYS10 = 21
|
||||||
|
PCHCTRL_GCLK_EVSYS11 = 22
|
||||||
|
PCHCTRL_GCLK_SERCOM2_CORE = 23 // SERCOM2 Core
|
||||||
|
PCHCTRL_GCLK_SERCOM3_CORE = 24 // SERCOM3 Core
|
||||||
|
PCHCTRL_GCLK_TCC0 = 25
|
||||||
|
PCHCTRL_GCLK_TCC1 = 25 // TCC0, TCC1
|
||||||
|
PCHCTRL_GCLK_TC2 = 26
|
||||||
|
PCHCTRL_GCLK_TC3 = 26 // TC2, TC3
|
||||||
|
PCHCTRL_GCLK_CAN0 = 27 // CAN0
|
||||||
|
PCHCTRL_GCLK_CAN1 = 28 // CAN1
|
||||||
|
PCHCTRL_GCLK_TCC2 = 29
|
||||||
|
PCHCTRL_GCLK_TCC3 = 29 // TCC2, TCC3
|
||||||
|
PCHCTRL_GCLK_TC4 = 30
|
||||||
|
PCHCTRL_GCLK_TC5 = 30 // TC4, TC5
|
||||||
|
PCHCTRL_GCLK_PDEC = 31 // PDEC
|
||||||
|
PCHCTRL_GCLK_AC = 32 // AC
|
||||||
|
PCHCTRL_GCLK_CCL = 33 // CCL
|
||||||
|
PCHCTRL_GCLK_SERCOM4_CORE = 34 // SERCOM4 Core
|
||||||
|
PCHCTRL_GCLK_SERCOM5_CORE = 35 // SERCOM5 Core
|
||||||
|
PCHCTRL_GCLK_SERCOM6_CORE = 36 // SERCOM6 Core
|
||||||
|
PCHCTRL_GCLK_SERCOM7_CORE = 37 // SERCOM7 Core
|
||||||
|
PCHCTRL_GCLK_TCC4 = 38 // TCC4
|
||||||
|
PCHCTRL_GCLK_TC6 = 39
|
||||||
|
PCHCTRL_GCLK_TC7 = 39 // TC6, TC7
|
||||||
|
PCHCTRL_GCLK_ADC0 = 40 // ADC0
|
||||||
|
PCHCTRL_GCLK_ADC1 = 41 // ADC1
|
||||||
|
PCHCTRL_GCLK_DAC = 42 // DAC
|
||||||
|
PCHCTRL_GCLK_I2S0 = 43
|
||||||
|
PCHCTRL_GCLK_I2S1 = 44
|
||||||
|
PCHCTRL_GCLK_SDHC0 = 45 // SDHC0
|
||||||
|
PCHCTRL_GCLK_SDHC1 = 46 // SDHC1
|
||||||
|
PCHCTRL_GCLK_CM4_TRACE = 47 // CM4 Trace
|
||||||
|
)
|
||||||
+5
-3
@@ -1,7 +1,9 @@
|
|||||||
// These are the supported alternate function numberings on the stm32f407
|
// Hand created file. DO NOT DELETE.
|
||||||
// +build stm32,stm32f407
|
// STM32FXXX (except stm32f1xx) bitfield definitions that are not
|
||||||
|
// auto-generated by gen-device-svd.go
|
||||||
|
// +build stm32f4
|
||||||
|
|
||||||
// Alternate function settings on the stm32f4xx series
|
// Alternate function settings on the stm32f4 series
|
||||||
|
|
||||||
package stm32
|
package stm32
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
// This example assumes that an RGB LED is connected to pins 3, 5 and 6 on an Arduino.
|
// This example assumes that an RGB LED is connected to pins 3, 5 and 6 on an Arduino.
|
||||||
// Change the values below to use different pins.
|
// Change the values below to use different pins.
|
||||||
const (
|
const (
|
||||||
redPin = machine.D3
|
redPin = machine.D4
|
||||||
greenPin = machine.D5
|
greenPin = machine.D5
|
||||||
bluePin = machine.D6
|
bluePin = machine.D6
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -124,3 +124,130 @@ func IndexString(str, sub string) int {
|
|||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// The following code has been copied from the Go 1.15 release tree.
|
||||||
|
|
||||||
|
// PrimeRK is the prime base used in Rabin-Karp algorithm.
|
||||||
|
const PrimeRK = 16777619
|
||||||
|
|
||||||
|
// HashStrBytes returns the hash and the appropriate multiplicative
|
||||||
|
// factor for use in Rabin-Karp algorithm.
|
||||||
|
func HashStrBytes(sep []byte) (uint32, uint32) {
|
||||||
|
hash := uint32(0)
|
||||||
|
for i := 0; i < len(sep); i++ {
|
||||||
|
hash = hash*PrimeRK + uint32(sep[i])
|
||||||
|
}
|
||||||
|
var pow, sq uint32 = 1, PrimeRK
|
||||||
|
for i := len(sep); i > 0; i >>= 1 {
|
||||||
|
if i&1 != 0 {
|
||||||
|
pow *= sq
|
||||||
|
}
|
||||||
|
sq *= sq
|
||||||
|
}
|
||||||
|
return hash, pow
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashStr returns the hash and the appropriate multiplicative
|
||||||
|
// factor for use in Rabin-Karp algorithm.
|
||||||
|
func HashStr(sep string) (uint32, uint32) {
|
||||||
|
hash := uint32(0)
|
||||||
|
for i := 0; i < len(sep); i++ {
|
||||||
|
hash = hash*PrimeRK + uint32(sep[i])
|
||||||
|
}
|
||||||
|
var pow, sq uint32 = 1, PrimeRK
|
||||||
|
for i := len(sep); i > 0; i >>= 1 {
|
||||||
|
if i&1 != 0 {
|
||||||
|
pow *= sq
|
||||||
|
}
|
||||||
|
sq *= sq
|
||||||
|
}
|
||||||
|
return hash, pow
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashStrRevBytes returns the hash of the reverse of sep and the
|
||||||
|
// appropriate multiplicative factor for use in Rabin-Karp algorithm.
|
||||||
|
func HashStrRevBytes(sep []byte) (uint32, uint32) {
|
||||||
|
hash := uint32(0)
|
||||||
|
for i := len(sep) - 1; i >= 0; i-- {
|
||||||
|
hash = hash*PrimeRK + uint32(sep[i])
|
||||||
|
}
|
||||||
|
var pow, sq uint32 = 1, PrimeRK
|
||||||
|
for i := len(sep); i > 0; i >>= 1 {
|
||||||
|
if i&1 != 0 {
|
||||||
|
pow *= sq
|
||||||
|
}
|
||||||
|
sq *= sq
|
||||||
|
}
|
||||||
|
return hash, pow
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashStrRev returns the hash of the reverse of sep and the
|
||||||
|
// appropriate multiplicative factor for use in Rabin-Karp algorithm.
|
||||||
|
func HashStrRev(sep string) (uint32, uint32) {
|
||||||
|
hash := uint32(0)
|
||||||
|
for i := len(sep) - 1; i >= 0; i-- {
|
||||||
|
hash = hash*PrimeRK + uint32(sep[i])
|
||||||
|
}
|
||||||
|
var pow, sq uint32 = 1, PrimeRK
|
||||||
|
for i := len(sep); i > 0; i >>= 1 {
|
||||||
|
if i&1 != 0 {
|
||||||
|
pow *= sq
|
||||||
|
}
|
||||||
|
sq *= sq
|
||||||
|
}
|
||||||
|
return hash, pow
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexRabinKarpBytes uses the Rabin-Karp search algorithm to return the index of the
|
||||||
|
// first occurence of substr in s, or -1 if not present.
|
||||||
|
func IndexRabinKarpBytes(s, sep []byte) int {
|
||||||
|
// Rabin-Karp search
|
||||||
|
hashsep, pow := HashStrBytes(sep)
|
||||||
|
n := len(sep)
|
||||||
|
var h uint32
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
h = h*PrimeRK + uint32(s[i])
|
||||||
|
}
|
||||||
|
if h == hashsep && Equal(s[:n], sep) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
for i := n; i < len(s); {
|
||||||
|
h *= PrimeRK
|
||||||
|
h += uint32(s[i])
|
||||||
|
h -= pow * uint32(s[i-n])
|
||||||
|
i++
|
||||||
|
if h == hashsep && Equal(s[i-n:i], sep) {
|
||||||
|
return i - n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexRabinKarp uses the Rabin-Karp search algorithm to return the index of the
|
||||||
|
// first occurence of substr in s, or -1 if not present.
|
||||||
|
func IndexRabinKarp(s, substr string) int {
|
||||||
|
// Rabin-Karp search
|
||||||
|
hashss, pow := HashStr(substr)
|
||||||
|
n := len(substr)
|
||||||
|
var h uint32
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
h = h*PrimeRK + uint32(s[i])
|
||||||
|
}
|
||||||
|
if h == hashss && s[:n] == substr {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
for i := n; i < len(s); {
|
||||||
|
h *= PrimeRK
|
||||||
|
h += uint32(s[i])
|
||||||
|
h -= pow * uint32(s[i-n])
|
||||||
|
i++
|
||||||
|
if h == hashss && s[i-n:i] == substr {
|
||||||
|
return i - n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,3 +18,8 @@ type Task struct {
|
|||||||
// state is the underlying running state of the task.
|
// state is the underlying running state of the task.
|
||||||
state state
|
state state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getGoroutineStackSize is a compiler intrinsic that returns the stack size for
|
||||||
|
// the given function and falls back to the default stack size. It is replaced
|
||||||
|
// with a load from a special section just before codegen.
|
||||||
|
func getGoroutineStackSize(fn uintptr) uintptr
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ func createTask() *Task {
|
|||||||
// start invokes a function in a new goroutine. Calls to this are inserted by the compiler.
|
// start invokes a function in a new goroutine. Calls to this are inserted by the compiler.
|
||||||
// The created goroutine starts running immediately.
|
// The created goroutine starts running immediately.
|
||||||
// This is implemented inside the compiler.
|
// This is implemented inside the compiler.
|
||||||
func start(fn uintptr, args unsafe.Pointer)
|
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr)
|
||||||
|
|
||||||
// Current returns the current active task.
|
// Current returns the current active task.
|
||||||
// This is implemented inside the compiler.
|
// This is implemented inside the compiler.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func Current() *Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//go:noinline
|
//go:noinline
|
||||||
func start(fn uintptr, args unsafe.Pointer) {
|
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||||
// The compiler will error if this is reachable.
|
// The compiler will error if this is reachable.
|
||||||
runtimePanic("scheduler is disabled")
|
runtimePanic("scheduler is disabled")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func (t *Task) Resume() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// initialize the state and prepare to call the specified function with the specified argument bundle.
|
// initialize the state and prepare to call the specified function with the specified argument bundle.
|
||||||
func (s *state) initialize(fn uintptr, args unsafe.Pointer) {
|
func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||||
// Create a stack.
|
// Create a stack.
|
||||||
stack := make([]uintptr, stackSize/unsafe.Sizeof(uintptr(0)))
|
stack := make([]uintptr, stackSize/unsafe.Sizeof(uintptr(0)))
|
||||||
|
|
||||||
@@ -67,9 +67,9 @@ func runqueuePushBack(*Task)
|
|||||||
|
|
||||||
// start creates and starts a new goroutine with the given function and arguments.
|
// start creates and starts a new goroutine with the given function and arguments.
|
||||||
// The new goroutine is scheduled to run later.
|
// The new goroutine is scheduled to run later.
|
||||||
func start(fn uintptr, args unsafe.Pointer) {
|
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||||
t := &Task{}
|
t := &Task{}
|
||||||
t.state.initialize(fn, args)
|
t.state.initialize(fn, args, stackSize)
|
||||||
runqueuePushBack(t)
|
runqueuePushBack(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ package task
|
|||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
const stackSize = 256
|
|
||||||
|
|
||||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||||
// switching between tasks. Also see scheduler_avr.S that relies on the
|
// switching between tasks. Also see scheduler_avr.S that relies on the
|
||||||
// exact layout of this struct.
|
// exact layout of this struct.
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ package task
|
|||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
const stackSize = 1024
|
|
||||||
|
|
||||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||||
// switching between tasks. Also see scheduler_cortexm.S that relies on the
|
// switching between tasks. Also see scheduler_cortexm.S that relies on the
|
||||||
// exact layout of this struct.
|
// exact layout of this struct.
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
// +build sam,atsamd21,arduino_zero
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// used to reset into bootloader
|
||||||
|
const RESET_MAGIC_VALUE = 0x07738135
|
||||||
|
|
||||||
|
// GPIO Pins - Digital Low
|
||||||
|
const (
|
||||||
|
D0 = PA11 // RX
|
||||||
|
D1 = PA10 // TX
|
||||||
|
D2 = PA14
|
||||||
|
D3 = PA09 // PWM available
|
||||||
|
D4 = PA08 // PWM available
|
||||||
|
D5 = PA15 // PWM available
|
||||||
|
D6 = PA20 // PWM available
|
||||||
|
D7 = PA21
|
||||||
|
)
|
||||||
|
|
||||||
|
// GPIO Pins - Digital High
|
||||||
|
const (
|
||||||
|
D8 = PA06 // PWM available
|
||||||
|
D9 = PA07 // PWM available
|
||||||
|
D10 = PA18 // PWM available
|
||||||
|
D11 = PA16 // PWM available
|
||||||
|
D12 = PA19 // PWM available
|
||||||
|
D13 = PA17 // PWM available
|
||||||
|
)
|
||||||
|
|
||||||
|
// LEDs on the Arduino Zero
|
||||||
|
const (
|
||||||
|
LED = LED1
|
||||||
|
LED1 Pin = D13
|
||||||
|
LED2 Pin = PA27 // TX LED
|
||||||
|
LED3 Pin = PB03 // RX LED
|
||||||
|
)
|
||||||
|
|
||||||
|
// ADC pins
|
||||||
|
const (
|
||||||
|
AREF Pin = PA03
|
||||||
|
ADC0 Pin = PA02
|
||||||
|
ADC1 Pin = PB08
|
||||||
|
ADC2 Pin = PB09
|
||||||
|
ADC3 Pin = PA04
|
||||||
|
ADC4 Pin = PA05
|
||||||
|
ADC5 Pin = PB02
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI pins - EDBG connected
|
||||||
|
const (
|
||||||
|
SPI0_SDO_PIN Pin = PA16 // MOSI: SERCOM1/PAD[0]
|
||||||
|
SPI0_SDI_PIN Pin = PA19 // MISO: SERCOM1/PAD[2]
|
||||||
|
SPI0_SCK_PIN Pin = PA17 // SCK: SERCOM1/PAD[3]
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI pins (Legacy ICSP)
|
||||||
|
const (
|
||||||
|
SPI1_SDO_PIN Pin = PB10 // MOSI: SERCOM4/PAD[2] - Pin 4
|
||||||
|
SPI1_SDI_PIN Pin = PA12 // MISO: SERCOM4/PAD[0] - Pin 1
|
||||||
|
SPI1_SCK_PIN Pin = PB11 // SCK: SERCOM4/PAD[3] - Pin 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C pins - EDBG connected
|
||||||
|
const (
|
||||||
|
SDA_PIN Pin = PA22 // SDA: SERCOM3/PAD[0] - Pin 20
|
||||||
|
SCL_PIN Pin = PA23 // SCL: SERCOM3/PAD[1] - Pin 21
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2S pins - might not be exposed
|
||||||
|
const (
|
||||||
|
I2S_SCK_PIN Pin = PA10
|
||||||
|
I2S_SD_PIN Pin = PA07
|
||||||
|
I2S_WS_PIN Pin = PA11
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART0 pins - EDBG connected
|
||||||
|
const (
|
||||||
|
UART_RX_PIN Pin = D0
|
||||||
|
UART_TX_PIN Pin = D1
|
||||||
|
)
|
||||||
|
|
||||||
|
// 'native' USB port pins
|
||||||
|
const (
|
||||||
|
USBCDC_DM_PIN Pin = PA24
|
||||||
|
USBCDC_DP_PIN Pin = PA25
|
||||||
|
)
|
||||||
|
|
||||||
|
// USB CDC identifiers
|
||||||
|
const (
|
||||||
|
usb_STRING_PRODUCT = "Arduino Zero"
|
||||||
|
usb_STRING_MANUFACTURER = "Arduino LLC"
|
||||||
|
|
||||||
|
usb_VID uint16 = 0x2341
|
||||||
|
usb_PID uint16 = 0x804d
|
||||||
|
)
|
||||||
|
|
||||||
|
// 32.768 KHz Crystal
|
||||||
|
const (
|
||||||
|
XIN32 Pin = PA00
|
||||||
|
XOUT32 Pin = PA01
|
||||||
|
)
|
||||||
@@ -65,11 +65,15 @@ var (
|
|||||||
Buffer: NewRingBuffer(),
|
Buffer: NewRingBuffer(),
|
||||||
Bus: stm32.USART1,
|
Bus: stm32.USART1,
|
||||||
}
|
}
|
||||||
UART1 = &UART0
|
UART1 = UART{
|
||||||
|
Buffer: NewRingBuffer(),
|
||||||
|
Bus: stm32.USART2,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, UART0.handleInterrupt)
|
UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, UART0.handleInterrupt)
|
||||||
|
UART1.Interrupt = interrupt.New(stm32.IRQ_USART2, UART1.handleInterrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
const HasLowFrequencyCrystal = true
|
const HasLowFrequencyCrystal = false
|
||||||
|
|
||||||
// GPIO Pins
|
// GPIO Pins
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// +build esp32_wroom_32
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// Blue LED on the ESP32-WROOM-32 module.
|
||||||
|
const LED = Pin(2)
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
// +build feather_stm32f405
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/stm32"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NUM_DIGITAL_IO_PINS = 39
|
||||||
|
NUM_ANALOG_IO_PINS = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
// Digital pins
|
||||||
|
const (
|
||||||
|
// Arduino pin = MCU port pin // primary functions (alternate functions)
|
||||||
|
D0 = PB11 // USART3 RX, PWM TIM2_CH4 (I2C2 SDA)
|
||||||
|
D1 = PB10 // USART3 TX, PWM TIM2_CH3 (I2C2 SCL, I2S2 BCK)
|
||||||
|
D2 = PB3 // GPIO, SPI3 FLASH SCK
|
||||||
|
D3 = PB4 // GPIO, SPI3 FLASH MISO
|
||||||
|
D4 = PB5 // GPIO, SPI3 FLASH MOSI
|
||||||
|
D5 = PC7 // GPIO, PWM TIM3_CH2 (USART6 RX, I2S3 MCK)
|
||||||
|
D6 = PC6 // GPIO, PWM TIM3_CH1 (USART6 TX, I2S2 MCK)
|
||||||
|
D7 = PA15 // GPIO, SPI3 FLASH CS
|
||||||
|
D8 = PC0 // GPIO, Neopixel
|
||||||
|
D9 = PB8 // GPIO, PWM TIM4_CH3 (CAN1 RX, I2C1 SCL)
|
||||||
|
D10 = PB9 // GPIO, PWM TIM4_CH4 (CAN1 TX, I2C1 SDA, I2S2 WSL)
|
||||||
|
D11 = PC3 // GPIO (I2S2 SD, SPI2 MOSI)
|
||||||
|
D12 = PC2 // GPIO (I2S2ext SD, SPI2 MISO)
|
||||||
|
D13 = PC1 // GPIO, Builtin LED
|
||||||
|
D14 = PB7 // I2C1 SDA, PWM TIM4_CH2 (USART1 RX)
|
||||||
|
D15 = PB6 // I2C1 SCL, PWM TIM4_CH1 (USART1 TX, CAN2 TX)
|
||||||
|
D16 = PA4 // A0 (DAC OUT1)
|
||||||
|
D17 = PA5 // A1 (DAC OUT2, SPI1 SCK)
|
||||||
|
D18 = PA6 // A2, PWM TIM3_CH1 (SPI1 MISO)
|
||||||
|
D19 = PA7 // A3, PWM TIM3_CH2 (SPI1 MOSI)
|
||||||
|
D20 = PC4 // A4
|
||||||
|
D21 = PC5 // A5
|
||||||
|
D22 = PA3 // A6
|
||||||
|
D23 = PB13 // SPI2 SCK, PWM TIM1_CH1N (I2S2 BCK, CAN2 TX)
|
||||||
|
D24 = PB14 // SPI2 MISO, PWM TIM1_CH2N (I2S2ext SD)
|
||||||
|
D25 = PB15 // SPI2 MOSI, PWM TIM1_CH3N (I2S2 SD)
|
||||||
|
D26 = PC8 // SDIO
|
||||||
|
D27 = PC9 // SDIO
|
||||||
|
D28 = PC10 // SDIO
|
||||||
|
D29 = PC11 // SDIO
|
||||||
|
D30 = PC12 // SDIO
|
||||||
|
D31 = PD2 // SDIO
|
||||||
|
D32 = PB12 // SD Detect
|
||||||
|
D33 = PC14 // OSC32
|
||||||
|
D34 = PC15 // OSC32
|
||||||
|
D35 = PA11 // USB D+
|
||||||
|
D36 = PA12 // USB D-
|
||||||
|
D37 = PA13 // SWDIO
|
||||||
|
D38 = PA14 // SWCLK
|
||||||
|
)
|
||||||
|
|
||||||
|
// Analog pins
|
||||||
|
const (
|
||||||
|
A0 = D16 // ADC12 IN4
|
||||||
|
A1 = D17 // ADC12 IN5
|
||||||
|
A2 = D18 // ADC12 IN6
|
||||||
|
A3 = D19 // ADC12 IN7
|
||||||
|
A4 = D20 // ADC12 IN14
|
||||||
|
A5 = D21 // ADC12 IN15
|
||||||
|
A6 = D22 // VBAT
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initLED()
|
||||||
|
initUART()
|
||||||
|
initSPI()
|
||||||
|
initI2C()
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- LEDs ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
const (
|
||||||
|
NUM_BOARD_LED = 1
|
||||||
|
NUM_BOARD_NEOPIXEL = 1
|
||||||
|
|
||||||
|
LED_RED = D13
|
||||||
|
LED_NEOPIXEL = D8
|
||||||
|
LED_BUILTIN = LED_RED
|
||||||
|
LED = LED_BUILTIN
|
||||||
|
)
|
||||||
|
|
||||||
|
func initLED() {}
|
||||||
|
|
||||||
|
// -- UART ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
const (
|
||||||
|
// #===========#==========#==============#============#=======#=======#
|
||||||
|
// | Interface | Hardware | Bus(Freq) | RX/TX Pins | AltFn | Alias |
|
||||||
|
// #===========#==========#==============#============#=======#=======#
|
||||||
|
// | UART1 | USART3 | APB1(42 MHz) | D0/D1 | 7 | ~ |
|
||||||
|
// | UART2 | USART6 | APB2(84 MHz) | D5/D6 | 8 | ~ |
|
||||||
|
// | UART3 | USART1 | APB2(84 MHz) | D14/D15 | 7 | ~ |
|
||||||
|
// | --------- | -------- | ------------ | ---------- | ----- | ----- |
|
||||||
|
// | UART0 | USART3 | APB1(42 MHz) | D0/D1 | 7 | UART1 |
|
||||||
|
// #===========#==========#==============#============#=======#=======#
|
||||||
|
NUM_UART_INTERFACES = 3
|
||||||
|
|
||||||
|
UART1_RX_PIN = D0 // UART1 = hardware: USART3
|
||||||
|
UART1_TX_PIN = D1 //
|
||||||
|
|
||||||
|
UART2_RX_PIN = D5 // UART2 = hardware: USART6
|
||||||
|
UART2_TX_PIN = D6 //
|
||||||
|
|
||||||
|
UART3_RX_PIN = D14 // UART3 = hardware: USART1
|
||||||
|
UART3_TX_PIN = D15 //
|
||||||
|
|
||||||
|
UART0_RX_PIN = UART1_RX_PIN // UART0 = alias: UART1
|
||||||
|
UART0_TX_PIN = UART1_TX_PIN //
|
||||||
|
|
||||||
|
UART_RX_PIN = UART0_RX_PIN // default/primary UART pins
|
||||||
|
UART_TX_PIN = UART0_TX_PIN //
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
UART1 = UART{
|
||||||
|
Buffer: NewRingBuffer(),
|
||||||
|
Bus: stm32.USART3,
|
||||||
|
AltFuncSelector: stm32.AF7_USART1_2_3,
|
||||||
|
}
|
||||||
|
UART2 = UART{
|
||||||
|
Buffer: NewRingBuffer(),
|
||||||
|
Bus: stm32.USART6,
|
||||||
|
AltFuncSelector: stm32.AF8_USART4_5_6,
|
||||||
|
}
|
||||||
|
UART3 = UART{
|
||||||
|
Buffer: NewRingBuffer(),
|
||||||
|
Bus: stm32.USART1,
|
||||||
|
AltFuncSelector: stm32.AF7_USART1_2_3,
|
||||||
|
}
|
||||||
|
UART0 = UART1
|
||||||
|
)
|
||||||
|
|
||||||
|
func initUART() {
|
||||||
|
UART1.Interrupt = interrupt.New(stm32.IRQ_USART3, UART1.handleInterrupt)
|
||||||
|
UART2.Interrupt = interrupt.New(stm32.IRQ_USART6, UART2.handleInterrupt)
|
||||||
|
UART3.Interrupt = interrupt.New(stm32.IRQ_USART1, UART3.handleInterrupt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- SPI ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
const (
|
||||||
|
// #===========#==========#==============#==================#=======#=======#
|
||||||
|
// | Interface | Hardware | Bus(Freq) | SCK/SDI/SDO Pins | AltFn | Alias |
|
||||||
|
// #===========#==========#==============#==================#=======#=======#
|
||||||
|
// | SPI1 | SPI2 | APB1(42 MHz) | D23/D24/D25 | 5 | ~ |
|
||||||
|
// | SPI2 | SPI3 | APB1(42 MHz) | D2/D3/D4 | 6 | ~ |
|
||||||
|
// | SPI3 | SPI1 | APB2(84 MHz) | D17/D18/D19 | 5 | ~ |
|
||||||
|
// | --------- | -------- | ------------ | ---------------- | ----- | ----- |
|
||||||
|
// | SPI0 | SPI2 | APB1(42 MHz) | D23/D24/D25 | 5 | SPI1 |
|
||||||
|
// #===========#==========#==============#==================#=======#=======#
|
||||||
|
NUM_SPI_INTERFACES = 3
|
||||||
|
|
||||||
|
SPI1_SCK_PIN = D23 //
|
||||||
|
SPI1_SDI_PIN = D24 // SPI1 = hardware: SPI2
|
||||||
|
SPI1_SDO_PIN = D25 //
|
||||||
|
|
||||||
|
SPI2_SCK_PIN = D2 //
|
||||||
|
SPI2_SDI_PIN = D3 // SPI2 = hardware: SPI3
|
||||||
|
SPI2_SDO_PIN = D4 //
|
||||||
|
|
||||||
|
SPI3_SCK_PIN = D17 //
|
||||||
|
SPI3_SDI_PIN = D18 // SPI3 = hardware: SPI1
|
||||||
|
SPI3_SDO_PIN = D19 //
|
||||||
|
|
||||||
|
SPI0_SCK_PIN = SPI1_SCK_PIN //
|
||||||
|
SPI0_SDI_PIN = SPI1_SDI_PIN // SPI0 = alias: SPI1
|
||||||
|
SPI0_SDO_PIN = SPI1_SDO_PIN //
|
||||||
|
|
||||||
|
SPI_SCK_PIN = SPI0_SCK_PIN //
|
||||||
|
SPI_SDI_PIN = SPI0_SDI_PIN // default/primary SPI pins
|
||||||
|
SPI_SDO_PIN = SPI0_SDO_PIN //
|
||||||
|
)
|
||||||
|
|
||||||
|
func initSPI() {}
|
||||||
|
|
||||||
|
// -- I2C ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
const (
|
||||||
|
// #===========#==========#==============#==============#=======#=======#
|
||||||
|
// | Interface | Hardware | Bus(Freq) | SDA/SCL Pins | AltFn | Alias |
|
||||||
|
// #===========#==========#==============#==============#=======#=======#
|
||||||
|
// | I2C1 | I2C1 | | D14/D15 | | ~ |
|
||||||
|
// | I2C2 | I2C2 | | D0/D1 | | ~ |
|
||||||
|
// | I2C3 | I2C1 | | D9/D10 | | ~ |
|
||||||
|
// | --------- | -------- | ------------ | ------------ | ----- | ----- |
|
||||||
|
// | I2C0 | I2C1 | | D14/D15 | | I2C1 |
|
||||||
|
// #===========#==========#==============#==============#=======#=======#
|
||||||
|
NUM_I2C_INTERFACES = 3
|
||||||
|
|
||||||
|
I2C1_SDA_PIN = D14 // I2C1 = hardware: I2C1
|
||||||
|
I2C1_SCL_PIN = D15 //
|
||||||
|
|
||||||
|
I2C2_SDA_PIN = D0 // I2C2 = hardware: I2C2
|
||||||
|
I2C2_SCL_PIN = D1 //
|
||||||
|
|
||||||
|
I2C3_SDA_PIN = D9 // I2C3 = hardware: I2C1
|
||||||
|
I2C3_SCL_PIN = D10 // (interface duplicated on second pair of pins)
|
||||||
|
|
||||||
|
I2C0_SDA_PIN = I2C1_SDA_PIN // I2C0 = alias: I2C1
|
||||||
|
I2C0_SCL_PIN = I2C1_SCL_PIN //
|
||||||
|
|
||||||
|
I2C_SDA_PIN = I2C0_SDA_PIN // default/primary I2C pins
|
||||||
|
I2C_SCL_PIN = I2C0_SCL_PIN //
|
||||||
|
)
|
||||||
|
|
||||||
|
func initI2C() {}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
// +build itsybitsy_nrf52840
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
const HasLowFrequencyCrystal = true
|
||||||
|
|
||||||
|
// GPIO Pins
|
||||||
|
const (
|
||||||
|
D0 = P0_25 // UART TX
|
||||||
|
D1 = P0_24 // UART RX
|
||||||
|
D2 = P1_02
|
||||||
|
D3 = P0_06 // LED1
|
||||||
|
D4 = P0_29 // Button
|
||||||
|
D5 = P0_27
|
||||||
|
D6 = P1_09 // DotStar Clock
|
||||||
|
D7 = P1_08
|
||||||
|
D8 = P0_08 // DotStar Data
|
||||||
|
D9 = P0_07
|
||||||
|
D10 = P0_05
|
||||||
|
D11 = P0_26
|
||||||
|
D12 = P0_11
|
||||||
|
D13 = P0_12
|
||||||
|
D14 = P0_04 // A0
|
||||||
|
D15 = P0_30 // A1
|
||||||
|
D16 = P0_28 // A2
|
||||||
|
D17 = P0_31 // A3
|
||||||
|
D18 = P0_02 // A4
|
||||||
|
D19 = P0_03 // A5
|
||||||
|
D20 = P0_05 // A6
|
||||||
|
D21 = P0_16 // I2C SDA
|
||||||
|
D22 = P0_14 // I2C SCL
|
||||||
|
D23 = P0_20 // SPI SDI
|
||||||
|
D24 = P0_15 // SPI SDO
|
||||||
|
D25 = P0_13 // SPI SCK
|
||||||
|
D26 = P0_19 // QSPI SCK
|
||||||
|
D27 = P0_23 // QSPI CS
|
||||||
|
D28 = P0_21 // QSPI Data 0
|
||||||
|
D29 = P0_22 // QSPI Data 1
|
||||||
|
D30 = P1_00 // QSPI Data 2
|
||||||
|
D31 = P0_17 // QSPI Data 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Analog Pins
|
||||||
|
const (
|
||||||
|
A0 = D14
|
||||||
|
A1 = D15
|
||||||
|
A2 = D16
|
||||||
|
A3 = D17
|
||||||
|
A4 = D18
|
||||||
|
A5 = D19
|
||||||
|
A6 = D20
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LED = D3
|
||||||
|
LED1 = LED
|
||||||
|
BUTTON = D4
|
||||||
|
|
||||||
|
QSPI_SCK = D26
|
||||||
|
QSPI_CS = D27
|
||||||
|
QSPI_DATA0 = D28
|
||||||
|
QSPI_DATA1 = D29
|
||||||
|
QSPI_DATA2 = D30
|
||||||
|
QSPI_DATA3 = D31
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART0 pins (logical UART1)
|
||||||
|
const (
|
||||||
|
UART_RX_PIN = D0
|
||||||
|
UART_TX_PIN = D1
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART0 is the USB device
|
||||||
|
var (
|
||||||
|
UART0 = USB
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C pins
|
||||||
|
const (
|
||||||
|
SDA_PIN = D21 // I2C0 external
|
||||||
|
SCL_PIN = D22 // I2C0 external
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI pins
|
||||||
|
const (
|
||||||
|
SPI0_SCK_PIN = D25
|
||||||
|
SPI0_SDO_PIN = D24
|
||||||
|
SPI0_SDI_PIN = D23
|
||||||
|
)
|
||||||
|
|
||||||
|
// USB CDC identifiers
|
||||||
|
const (
|
||||||
|
usb_STRING_PRODUCT = "Adafruit ItsyBitsy nRF52840 Express"
|
||||||
|
usb_STRING_MANUFACTURER = "Adafruit"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
usb_VID uint16 = 0x239A
|
||||||
|
usb_PID uint16 = 0x8051
|
||||||
|
)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// +build nodemcu
|
||||||
|
|
||||||
|
// Pinout for the NodeMCU dev kit.
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// GPIO pins on the NodeMCU board.
|
||||||
|
const (
|
||||||
|
D0 Pin = 16
|
||||||
|
D1 Pin = 5
|
||||||
|
D2 Pin = 4
|
||||||
|
D3 Pin = 0
|
||||||
|
D4 Pin = 2
|
||||||
|
D5 Pin = 14
|
||||||
|
D6 Pin = 12
|
||||||
|
D7 Pin = 13
|
||||||
|
D8 Pin = 15
|
||||||
|
)
|
||||||
|
|
||||||
|
// Onboard blue LED (on the AI-Thinker module).
|
||||||
|
const LED = D4
|
||||||
@@ -1,16 +1,5 @@
|
|||||||
// +build bluepill nucleof103rb stm32f4disco
|
// +build bluepill nucleof103rb stm32f4
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
// Peripheral abstraction layer for the stm32.
|
// Peripheral abstraction layer for the stm32.
|
||||||
|
|
||||||
const (
|
|
||||||
portA Pin = iota * 16
|
|
||||||
portB
|
|
||||||
portC
|
|
||||||
portD
|
|
||||||
portE
|
|
||||||
portF
|
|
||||||
portG
|
|
||||||
portH
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -7,96 +7,6 @@ import (
|
|||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
PA0 = portA + 0
|
|
||||||
PA1 = portA + 1
|
|
||||||
PA2 = portA + 2
|
|
||||||
PA3 = portA + 3
|
|
||||||
PA4 = portA + 4
|
|
||||||
PA5 = portA + 5
|
|
||||||
PA6 = portA + 6
|
|
||||||
PA7 = portA + 7
|
|
||||||
PA8 = portA + 8
|
|
||||||
PA9 = portA + 9
|
|
||||||
PA10 = portA + 10
|
|
||||||
PA11 = portA + 11
|
|
||||||
PA12 = portA + 12
|
|
||||||
PA13 = portA + 13
|
|
||||||
PA14 = portA + 14
|
|
||||||
PA15 = portA + 15
|
|
||||||
|
|
||||||
PB0 = portB + 0
|
|
||||||
PB1 = portB + 1
|
|
||||||
PB2 = portB + 2
|
|
||||||
PB3 = portB + 3
|
|
||||||
PB4 = portB + 4
|
|
||||||
PB5 = portB + 5
|
|
||||||
PB6 = portB + 6
|
|
||||||
PB7 = portB + 7
|
|
||||||
PB8 = portB + 8
|
|
||||||
PB9 = portB + 9
|
|
||||||
PB10 = portB + 10
|
|
||||||
PB11 = portB + 11
|
|
||||||
PB12 = portB + 12
|
|
||||||
PB13 = portB + 13
|
|
||||||
PB14 = portB + 14
|
|
||||||
PB15 = portB + 15
|
|
||||||
|
|
||||||
PC0 = portC + 0
|
|
||||||
PC1 = portC + 1
|
|
||||||
PC2 = portC + 2
|
|
||||||
PC3 = portC + 3
|
|
||||||
PC4 = portC + 4
|
|
||||||
PC5 = portC + 5
|
|
||||||
PC6 = portC + 6
|
|
||||||
PC7 = portC + 7
|
|
||||||
PC8 = portC + 8
|
|
||||||
PC9 = portC + 9
|
|
||||||
PC10 = portC + 10
|
|
||||||
PC11 = portC + 11
|
|
||||||
PC12 = portC + 12
|
|
||||||
PC13 = portC + 13
|
|
||||||
PC14 = portC + 14
|
|
||||||
PC15 = portC + 15
|
|
||||||
|
|
||||||
PD0 = portD + 0
|
|
||||||
PD1 = portD + 1
|
|
||||||
PD2 = portD + 2
|
|
||||||
PD3 = portD + 3
|
|
||||||
PD4 = portD + 4
|
|
||||||
PD5 = portD + 5
|
|
||||||
PD6 = portD + 6
|
|
||||||
PD7 = portD + 7
|
|
||||||
PD8 = portD + 8
|
|
||||||
PD9 = portD + 9
|
|
||||||
PD10 = portD + 10
|
|
||||||
PD11 = portD + 11
|
|
||||||
PD12 = portD + 12
|
|
||||||
PD13 = portD + 13
|
|
||||||
PD14 = portD + 14
|
|
||||||
PD15 = portD + 15
|
|
||||||
|
|
||||||
PE0 = portE + 0
|
|
||||||
PE1 = portE + 1
|
|
||||||
PE2 = portE + 2
|
|
||||||
PE3 = portE + 3
|
|
||||||
PE4 = portE + 4
|
|
||||||
PE5 = portE + 5
|
|
||||||
PE6 = portE + 6
|
|
||||||
PE7 = portE + 7
|
|
||||||
PE8 = portE + 8
|
|
||||||
PE9 = portE + 9
|
|
||||||
PE10 = portE + 10
|
|
||||||
PE11 = portE + 11
|
|
||||||
PE12 = portE + 12
|
|
||||||
PE13 = portE + 13
|
|
||||||
PE14 = portE + 14
|
|
||||||
PE15 = portE + 15
|
|
||||||
|
|
||||||
PH0 = portH + 0
|
|
||||||
PH1 = portH + 1
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LED = LED_BUILTIN
|
LED = LED_BUILTIN
|
||||||
LED1 = LED_GREEN
|
LED1 = LED_GREEN
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build avr nrf sam stm32,!stm32f4disco fe310 k210
|
// +build avr nrf sam stm32,!stm32f4 fe310 k210
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -1188,7 +1188,10 @@ func (spi SPI) Configure(config SPIConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set synch speed for SPI
|
// Set synch speed for SPI
|
||||||
baudRate := (CPUFrequency() / (2 * config.Frequency)) - 1
|
baudRate := CPUFrequency() / (2 * config.Frequency)
|
||||||
|
if baudRate > 0 {
|
||||||
|
baudRate--
|
||||||
|
}
|
||||||
spi.Bus.BAUD.Set(uint8(baudRate))
|
spi.Bus.BAUD.Set(uint8(baudRate))
|
||||||
|
|
||||||
// Enable SPI port.
|
// Enable SPI port.
|
||||||
|
|||||||
@@ -1417,6 +1417,9 @@ func (spi SPI) Configure(config SPIConfig) error {
|
|||||||
|
|
||||||
// Set synch speed for SPI
|
// Set synch speed for SPI
|
||||||
baudRate := SERCOM_FREQ_REF / (2 * config.Frequency)
|
baudRate := SERCOM_FREQ_REF / (2 * config.Frequency)
|
||||||
|
if baudRate > 0 {
|
||||||
|
baudRate--
|
||||||
|
}
|
||||||
spi.Bus.BAUD.Set(uint8(baudRate))
|
spi.Bus.BAUD.Set(uint8(baudRate))
|
||||||
|
|
||||||
// Enable SPI port.
|
// Enable SPI port.
|
||||||
@@ -1580,14 +1583,14 @@ func (pwm PWM) setPinCfg(val uint8) {
|
|||||||
// 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(timer *sam.TCC_Type, val uint32) {
|
func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
|
||||||
switch pwm.Pin {
|
switch pwm.Pin {
|
||||||
case PA16:
|
|
||||||
timer.CC[0].Set(val)
|
|
||||||
case PA17:
|
|
||||||
timer.CC[1].Set(val)
|
|
||||||
case PA14:
|
case PA14:
|
||||||
timer.CC[0].Set(val)
|
timer.CC[0].Set(val)
|
||||||
case PA15:
|
case PA15:
|
||||||
timer.CC[1].Set(val)
|
timer.CC[1].Set(val)
|
||||||
|
case PA16:
|
||||||
|
timer.CC[0].Set(val)
|
||||||
|
case PA17:
|
||||||
|
timer.CC[1].Set(val)
|
||||||
case PA18:
|
case PA18:
|
||||||
timer.CC[2].Set(val)
|
timer.CC[2].Set(val)
|
||||||
case PA19:
|
case PA19:
|
||||||
@@ -1596,10 +1599,22 @@ func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
|
|||||||
timer.CC[0].Set(val)
|
timer.CC[0].Set(val)
|
||||||
case PA21:
|
case PA21:
|
||||||
timer.CC[1].Set(val)
|
timer.CC[1].Set(val)
|
||||||
case PA23:
|
|
||||||
timer.CC[3].Set(val)
|
|
||||||
case PA22:
|
case PA22:
|
||||||
timer.CC[2].Set(val)
|
timer.CC[2].Set(val)
|
||||||
|
case PA23:
|
||||||
|
timer.CC[3].Set(val)
|
||||||
|
case PB12:
|
||||||
|
timer.CC[0].Set(val)
|
||||||
|
case PB13:
|
||||||
|
timer.CC[1].Set(val)
|
||||||
|
case PB14:
|
||||||
|
timer.CC[0].Set(val)
|
||||||
|
case PB15:
|
||||||
|
timer.CC[1].Set(val)
|
||||||
|
case PB16:
|
||||||
|
timer.CC[4].Set(val)
|
||||||
|
case PB17:
|
||||||
|
timer.CC[5].Set(val)
|
||||||
case PB31:
|
case PB31:
|
||||||
timer.CC[1].Set(val)
|
timer.CC[1].Set(val)
|
||||||
default:
|
default:
|
||||||
@@ -1610,14 +1625,14 @@ func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
|
|||||||
// setChannelBuffer sets the value for the correct channel buffer for PWM on this pin
|
// setChannelBuffer sets the value for the correct channel buffer for PWM on this pin
|
||||||
func (pwm PWM) setChannelBuffer(timer *sam.TCC_Type, val uint32) {
|
func (pwm PWM) setChannelBuffer(timer *sam.TCC_Type, val uint32) {
|
||||||
switch pwm.Pin {
|
switch pwm.Pin {
|
||||||
case PA16:
|
|
||||||
timer.CCBUF[0].Set(val)
|
|
||||||
case PA17:
|
|
||||||
timer.CCBUF[1].Set(val)
|
|
||||||
case PA14:
|
case PA14:
|
||||||
timer.CCBUF[0].Set(val)
|
timer.CCBUF[0].Set(val)
|
||||||
case PA15:
|
case PA15:
|
||||||
timer.CCBUF[1].Set(val)
|
timer.CCBUF[1].Set(val)
|
||||||
|
case PA16:
|
||||||
|
timer.CCBUF[0].Set(val)
|
||||||
|
case PA17:
|
||||||
|
timer.CCBUF[1].Set(val)
|
||||||
case PA18:
|
case PA18:
|
||||||
timer.CCBUF[2].Set(val)
|
timer.CCBUF[2].Set(val)
|
||||||
case PA19:
|
case PA19:
|
||||||
@@ -1626,10 +1641,22 @@ func (pwm PWM) setChannelBuffer(timer *sam.TCC_Type, val uint32) {
|
|||||||
timer.CCBUF[0].Set(val)
|
timer.CCBUF[0].Set(val)
|
||||||
case PA21:
|
case PA21:
|
||||||
timer.CCBUF[1].Set(val)
|
timer.CCBUF[1].Set(val)
|
||||||
case PA23:
|
|
||||||
timer.CCBUF[3].Set(val)
|
|
||||||
case PA22:
|
case PA22:
|
||||||
timer.CCBUF[2].Set(val)
|
timer.CCBUF[2].Set(val)
|
||||||
|
case PA23:
|
||||||
|
timer.CCBUF[3].Set(val)
|
||||||
|
case PB12:
|
||||||
|
timer.CCBUF[0].Set(val)
|
||||||
|
case PB13:
|
||||||
|
timer.CCBUF[1].Set(val)
|
||||||
|
case PB14:
|
||||||
|
timer.CCBUF[0].Set(val)
|
||||||
|
case PB15:
|
||||||
|
timer.CCBUF[1].Set(val)
|
||||||
|
case PB16:
|
||||||
|
timer.CCBUF[4].Set(val)
|
||||||
|
case PB17:
|
||||||
|
timer.CCBUF[5].Set(val)
|
||||||
case PB31:
|
case PB31:
|
||||||
timer.CCBUF[1].Set(val)
|
timer.CCBUF[1].Set(val)
|
||||||
default:
|
default:
|
||||||
@@ -1640,14 +1667,14 @@ func (pwm PWM) setChannelBuffer(timer *sam.TCC_Type, val uint32) {
|
|||||||
// getMux returns the pin mode mux to be used for PWM on this pin.
|
// getMux returns the pin mode mux to be used for PWM on this pin.
|
||||||
func (pwm PWM) getMux() PinMode {
|
func (pwm PWM) getMux() PinMode {
|
||||||
switch pwm.Pin {
|
switch pwm.Pin {
|
||||||
case PA16:
|
|
||||||
return PinPWMF
|
|
||||||
case PA17:
|
|
||||||
return PinPWMF
|
|
||||||
case PA14:
|
case PA14:
|
||||||
return PinPWMF
|
return PinPWMF
|
||||||
case PA15:
|
case PA15:
|
||||||
return PinPWMF
|
return PinPWMF
|
||||||
|
case PA16:
|
||||||
|
return PinPWMF
|
||||||
|
case PA17:
|
||||||
|
return PinPWMF
|
||||||
case PA18:
|
case PA18:
|
||||||
return PinPWMF
|
return PinPWMF
|
||||||
case PA19:
|
case PA19:
|
||||||
@@ -1656,9 +1683,21 @@ func (pwm PWM) getMux() PinMode {
|
|||||||
return PinPWMG
|
return PinPWMG
|
||||||
case PA21:
|
case PA21:
|
||||||
return PinPWMG
|
return PinPWMG
|
||||||
|
case PA22:
|
||||||
|
return PinPWMG
|
||||||
case PA23:
|
case PA23:
|
||||||
return PinPWMG
|
return PinPWMG
|
||||||
case PA22:
|
case PB12:
|
||||||
|
return PinPWMF
|
||||||
|
case PB13:
|
||||||
|
return PinPWMF
|
||||||
|
case PB14:
|
||||||
|
return PinPWMF
|
||||||
|
case PB15:
|
||||||
|
return PinPWMF
|
||||||
|
case PB16:
|
||||||
|
return PinPWMG
|
||||||
|
case PB17:
|
||||||
return PinPWMG
|
return PinPWMG
|
||||||
case PB31:
|
case PB31:
|
||||||
return PinPWMF
|
return PinPWMF
|
||||||
|
|||||||
@@ -18,23 +18,23 @@ func InitPWM() {
|
|||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
||||||
|
|
||||||
//use clock generator 0
|
//use clock generator 0
|
||||||
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTimer returns the timer to be used for PWM on this pin
|
// getTimer returns the timer to be used for PWM on this pin
|
||||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||||
switch pwm.Pin {
|
switch pwm.Pin {
|
||||||
case PA16:
|
|
||||||
return sam.TCC1
|
|
||||||
case PA17:
|
|
||||||
return sam.TCC1
|
|
||||||
case PA14:
|
case PA14:
|
||||||
return sam.TCC2
|
return sam.TCC2
|
||||||
case PA15:
|
case PA15:
|
||||||
return sam.TCC2
|
return sam.TCC2
|
||||||
|
case PA16:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA17:
|
||||||
|
return sam.TCC1
|
||||||
case PA18:
|
case PA18:
|
||||||
return sam.TCC1
|
return sam.TCC1
|
||||||
case PA19:
|
case PA19:
|
||||||
@@ -43,10 +43,10 @@ func (pwm PWM) getTimer() *sam.TCC_Type {
|
|||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
case PA21:
|
case PA21:
|
||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
case PA23:
|
|
||||||
return sam.TCC0
|
|
||||||
case PA22:
|
case PA22:
|
||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
|
case PA23:
|
||||||
|
return sam.TCC0
|
||||||
default:
|
default:
|
||||||
return nil // not supported on this pin
|
return nil // not supported on this pin
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// Peripheral abstraction layer for the atsamd51.
|
// Peripheral abstraction layer for the atsamd51.
|
||||||
//
|
//
|
||||||
// Datasheet:
|
// Datasheet:
|
||||||
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
|
// http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5xE5x_Family_Data_Sheet_DS60001507F.pdf
|
||||||
//
|
//
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
@@ -15,29 +15,29 @@ const HSRAM_SIZE = 0x00030000
|
|||||||
func InitPWM() {
|
func InitPWM() {
|
||||||
// turn on timer clocks used for PWM
|
// turn on timer clocks used for PWM
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||||
|
|
||||||
//use clock generator 0
|
//use clock generator 0
|
||||||
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK.PCHCTRL[38].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTimer returns the timer to be used for PWM on this pin
|
// getTimer returns the timer to be used for PWM on this pin
|
||||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||||
switch pwm.Pin {
|
switch pwm.Pin {
|
||||||
case PA16:
|
|
||||||
return sam.TCC1
|
|
||||||
case PA17:
|
|
||||||
return sam.TCC1
|
|
||||||
case PA14:
|
case PA14:
|
||||||
return sam.TCC2
|
return sam.TCC2
|
||||||
case PA15:
|
case PA15:
|
||||||
return sam.TCC2
|
return sam.TCC2
|
||||||
|
case PA16:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA17:
|
||||||
|
return sam.TCC1
|
||||||
case PA18:
|
case PA18:
|
||||||
return sam.TCC1
|
return sam.TCC1
|
||||||
case PA19:
|
case PA19:
|
||||||
@@ -46,9 +46,21 @@ func (pwm PWM) getTimer() *sam.TCC_Type {
|
|||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
case PA21:
|
case PA21:
|
||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
|
case PA22:
|
||||||
|
return sam.TCC0
|
||||||
case PA23:
|
case PA23:
|
||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
case PA22:
|
case PB12:
|
||||||
|
return sam.TCC3
|
||||||
|
case PB13:
|
||||||
|
return sam.TCC3
|
||||||
|
case PB14:
|
||||||
|
return sam.TCC4
|
||||||
|
case PB15:
|
||||||
|
return sam.TCC4
|
||||||
|
case PB16:
|
||||||
|
return sam.TCC0
|
||||||
|
case PB17:
|
||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
case PB31:
|
case PB31:
|
||||||
return sam.TCC4
|
return sam.TCC4
|
||||||
|
|||||||
@@ -15,29 +15,29 @@ const HSRAM_SIZE = 0x00040000
|
|||||||
func InitPWM() {
|
func InitPWM() {
|
||||||
// turn on timer clocks used for PWM
|
// turn on timer clocks used for PWM
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||||
|
|
||||||
//use clock generator 0
|
//use clock generator 0
|
||||||
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK.PCHCTRL[38].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTimer returns the timer to be used for PWM on this pin
|
// getTimer returns the timer to be used for PWM on this pin
|
||||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||||
switch pwm.Pin {
|
switch pwm.Pin {
|
||||||
case PA16:
|
|
||||||
return sam.TCC1
|
|
||||||
case PA17:
|
|
||||||
return sam.TCC1
|
|
||||||
case PA14:
|
case PA14:
|
||||||
return sam.TCC2
|
return sam.TCC2
|
||||||
case PA15:
|
case PA15:
|
||||||
return sam.TCC2
|
return sam.TCC2
|
||||||
|
case PA16:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA17:
|
||||||
|
return sam.TCC1
|
||||||
case PA18:
|
case PA18:
|
||||||
return sam.TCC1
|
return sam.TCC1
|
||||||
case PA19:
|
case PA19:
|
||||||
@@ -46,9 +46,21 @@ func (pwm PWM) getTimer() *sam.TCC_Type {
|
|||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
case PA21:
|
case PA21:
|
||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
|
case PA22:
|
||||||
|
return sam.TCC0
|
||||||
case PA23:
|
case PA23:
|
||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
case PA22:
|
case PB12:
|
||||||
|
return sam.TCC3
|
||||||
|
case PB13:
|
||||||
|
return sam.TCC3
|
||||||
|
case PB14:
|
||||||
|
return sam.TCC4
|
||||||
|
case PB15:
|
||||||
|
return sam.TCC4
|
||||||
|
case PB16:
|
||||||
|
return sam.TCC0
|
||||||
|
case PB17:
|
||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
case PB31:
|
case PB31:
|
||||||
return sam.TCC4
|
return sam.TCC4
|
||||||
|
|||||||
@@ -15,29 +15,29 @@ const HSRAM_SIZE = 0x00030000
|
|||||||
func InitPWM() {
|
func InitPWM() {
|
||||||
// turn on timer clocks used for PWM
|
// turn on timer clocks used for PWM
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
|
||||||
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
|
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_ | sam.MCLK_APBCMASK_TCC3_)
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
|
||||||
|
|
||||||
//use clock generator 0
|
//use clock generator 0
|
||||||
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC2].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK.PCHCTRL[38].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_TCC4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTimer returns the timer to be used for PWM on this pin
|
// getTimer returns the timer to be used for PWM on this pin
|
||||||
func (pwm PWM) getTimer() *sam.TCC_Type {
|
func (pwm PWM) getTimer() *sam.TCC_Type {
|
||||||
switch pwm.Pin {
|
switch pwm.Pin {
|
||||||
case PA16:
|
|
||||||
return sam.TCC1
|
|
||||||
case PA17:
|
|
||||||
return sam.TCC1
|
|
||||||
case PA14:
|
case PA14:
|
||||||
return sam.TCC2
|
return sam.TCC2
|
||||||
case PA15:
|
case PA15:
|
||||||
return sam.TCC2
|
return sam.TCC2
|
||||||
|
case PA16:
|
||||||
|
return sam.TCC1
|
||||||
|
case PA17:
|
||||||
|
return sam.TCC1
|
||||||
case PA18:
|
case PA18:
|
||||||
return sam.TCC1
|
return sam.TCC1
|
||||||
case PA19:
|
case PA19:
|
||||||
@@ -46,9 +46,21 @@ func (pwm PWM) getTimer() *sam.TCC_Type {
|
|||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
case PA21:
|
case PA21:
|
||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
|
case PA22:
|
||||||
|
return sam.TCC0
|
||||||
case PA23:
|
case PA23:
|
||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
case PA22:
|
case PB12:
|
||||||
|
return sam.TCC3
|
||||||
|
case PB13:
|
||||||
|
return sam.TCC3
|
||||||
|
case PB14:
|
||||||
|
return sam.TCC4
|
||||||
|
case PB15:
|
||||||
|
return sam.TCC4
|
||||||
|
case PB16:
|
||||||
|
return sam.TCC0
|
||||||
|
case PB17:
|
||||||
return sam.TCC0
|
return sam.TCC0
|
||||||
case PB31:
|
case PB31:
|
||||||
return sam.TCC4
|
return sam.TCC4
|
||||||
|
|||||||
@@ -0,0 +1,503 @@
|
|||||||
|
// +build esp32
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/esp"
|
||||||
|
"errors"
|
||||||
|
"runtime/volatile"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const peripheralClock = 80000000 // 80MHz
|
||||||
|
|
||||||
|
// CPUFrequency returns the current CPU frequency of the chip.
|
||||||
|
// Currently it is a fixed frequency but it may allow changing in the future.
|
||||||
|
func CPUFrequency() uint32 {
|
||||||
|
return 160e6 // 160MHz
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidSPIBus = errors.New("machine: invalid SPI bus")
|
||||||
|
)
|
||||||
|
|
||||||
|
type PinMode uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
PinOutput PinMode = iota
|
||||||
|
PinInput
|
||||||
|
PinInputPullup
|
||||||
|
PinInputPulldown
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configure this pin with the given configuration.
|
||||||
|
func (p Pin) Configure(config PinConfig) {
|
||||||
|
// Output function 256 is a special value reserved for use as a regular GPIO
|
||||||
|
// pin. Peripherals (SPI etc) can set a custom output function by calling
|
||||||
|
// lowercase configure() instead with a signal name.
|
||||||
|
p.configure(config, 256)
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure is the same as Configure, but allows for setting a specific input
|
||||||
|
// or output signal.
|
||||||
|
// Signals are always routed through the GPIO matrix for simplicity. Output
|
||||||
|
// signals are configured in FUNCx_OUT_SEL_CFG which selects a particular signal
|
||||||
|
// to output on a given pin. Input signals are configured in FUNCy_IN_SEL_CFG,
|
||||||
|
// which sets the pin to use for a particular input signal.
|
||||||
|
func (p Pin) configure(config PinConfig, signal uint32) {
|
||||||
|
if p == NoPin {
|
||||||
|
// This simplifies pin configuration in peripherals such as SPI.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var muxConfig uint32 // The mux configuration.
|
||||||
|
|
||||||
|
// Configure this pin as a GPIO pin.
|
||||||
|
const function = 3 // function 3 is GPIO for every pin
|
||||||
|
muxConfig |= (function - 1) << esp.IO_MUX_GPIO0_MCU_SEL_Pos
|
||||||
|
|
||||||
|
// Make this pin an input pin (always).
|
||||||
|
muxConfig |= esp.IO_MUX_GPIO0_FUN_IE
|
||||||
|
|
||||||
|
// Set drive strength: 0 is lowest, 3 is highest.
|
||||||
|
muxConfig |= 2 << esp.IO_MUX_GPIO0_FUN_DRV_Pos
|
||||||
|
|
||||||
|
// Select pull mode.
|
||||||
|
if config.Mode == PinInputPullup {
|
||||||
|
muxConfig |= esp.IO_MUX_GPIO0_FUN_WPU
|
||||||
|
} else if config.Mode == PinInputPulldown {
|
||||||
|
muxConfig |= esp.IO_MUX_GPIO0_FUN_WPD
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the pad with the given IO mux configuration.
|
||||||
|
p.mux().Set(muxConfig)
|
||||||
|
|
||||||
|
switch config.Mode {
|
||||||
|
case PinOutput:
|
||||||
|
// Set the 'output enable' bit.
|
||||||
|
if p < 32 {
|
||||||
|
esp.GPIO.ENABLE_W1TS.Set(1 << p)
|
||||||
|
} else {
|
||||||
|
esp.GPIO.ENABLE1_W1TS.Set(1 << (p - 32))
|
||||||
|
}
|
||||||
|
// Set the signal to read the output value from. It can be a peripheral
|
||||||
|
// output signal, or the special value 256 which indicates regular GPIO
|
||||||
|
// usage.
|
||||||
|
p.outFunc().Set(signal)
|
||||||
|
case PinInput, PinInputPullup, PinInputPulldown:
|
||||||
|
// Clear the 'output enable' bit.
|
||||||
|
if p < 32 {
|
||||||
|
esp.GPIO.ENABLE_W1TC.Set(1 << p)
|
||||||
|
} else {
|
||||||
|
esp.GPIO.ENABLE1_W1TC.Set(1 << (p - 32))
|
||||||
|
}
|
||||||
|
if signal != 256 {
|
||||||
|
// Signal is a peripheral function (not a simple GPIO). Connect this
|
||||||
|
// signal to the pin.
|
||||||
|
// Note that outFunc and inFunc work in the opposite direction.
|
||||||
|
// outFunc configures a pin to use a given output signal, while
|
||||||
|
// inFunc specifies a pin to use to read the signal from.
|
||||||
|
inFunc(signal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(p)<<esp.GPIO_FUNC_IN_SEL_CFG_IN_SEL_Pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// outFunc returns the FUNCx_OUT_SEL_CFG register used for configuring the
|
||||||
|
// output function selection.
|
||||||
|
func (p Pin) outFunc() *volatile.Register32 {
|
||||||
|
return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.GPIO.FUNC0_OUT_SEL_CFG)) + uintptr(p)*4)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// inFunc returns the FUNCy_IN_SEL_CFG register used for configuring the input
|
||||||
|
// function selection.
|
||||||
|
func inFunc(signal uint32) *volatile.Register32 {
|
||||||
|
return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.GPIO.FUNC0_IN_SEL_CFG)) + uintptr(signal)*4)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the pin to high or low.
|
||||||
|
// Warning: only use this on an output pin!
|
||||||
|
func (p Pin) Set(value bool) {
|
||||||
|
if value {
|
||||||
|
reg, mask := p.portMaskSet()
|
||||||
|
reg.Set(mask)
|
||||||
|
} else {
|
||||||
|
reg, mask := p.portMaskClear()
|
||||||
|
reg.Set(mask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the register and mask to enable a given GPIO pin. This can be used to
|
||||||
|
// implement bit-banged drivers.
|
||||||
|
//
|
||||||
|
// Warning: only use this on an output pin!
|
||||||
|
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||||
|
reg, mask := p.portMaskSet()
|
||||||
|
return ®.Reg, mask
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the register and mask to disable a given GPIO pin. This can be used to
|
||||||
|
// implement bit-banged drivers.
|
||||||
|
//
|
||||||
|
// Warning: only use this on an output pin!
|
||||||
|
func (p Pin) PortMaskClear() (*uint32, uint32) {
|
||||||
|
reg, mask := p.portMaskClear()
|
||||||
|
return ®.Reg, mask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Pin) portMaskSet() (*volatile.Register32, uint32) {
|
||||||
|
if p < 32 {
|
||||||
|
return &esp.GPIO.OUT_W1TS, 1 << p
|
||||||
|
} else {
|
||||||
|
return &esp.GPIO.OUT1_W1TS, 1 << (p - 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Pin) portMaskClear() (*volatile.Register32, uint32) {
|
||||||
|
if p < 32 {
|
||||||
|
return &esp.GPIO.OUT_W1TC, 1 << p
|
||||||
|
} else {
|
||||||
|
return &esp.GPIO.OUT1_W1TC, 1 << (p - 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the current value of a GPIO pin when the pin is configured as an
|
||||||
|
// input.
|
||||||
|
func (p Pin) Get() bool {
|
||||||
|
if p < 32 {
|
||||||
|
return esp.GPIO.IN.Get()&(1<<p) != 0
|
||||||
|
} else {
|
||||||
|
return esp.GPIO.IN1.Get()&(1<<(p-32)) != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mux returns the I/O mux configuration register corresponding to the given
|
||||||
|
// GPIO pin.
|
||||||
|
func (p Pin) mux() *volatile.Register32 {
|
||||||
|
// I have no idea whether there is any pattern in the GPIO <-> pad mapping.
|
||||||
|
// I couldn't find it.
|
||||||
|
switch p {
|
||||||
|
case 36:
|
||||||
|
return &esp.IO_MUX.GPIO36
|
||||||
|
case 37:
|
||||||
|
return &esp.IO_MUX.GPIO37
|
||||||
|
case 38:
|
||||||
|
return &esp.IO_MUX.GPIO38
|
||||||
|
case 39:
|
||||||
|
return &esp.IO_MUX.GPIO39
|
||||||
|
case 34:
|
||||||
|
return &esp.IO_MUX.GPIO34
|
||||||
|
case 35:
|
||||||
|
return &esp.IO_MUX.GPIO35
|
||||||
|
case 32:
|
||||||
|
return &esp.IO_MUX.GPIO32
|
||||||
|
case 33:
|
||||||
|
return &esp.IO_MUX.GPIO33
|
||||||
|
case 25:
|
||||||
|
return &esp.IO_MUX.GPIO25
|
||||||
|
case 26:
|
||||||
|
return &esp.IO_MUX.GPIO26
|
||||||
|
case 27:
|
||||||
|
return &esp.IO_MUX.GPIO27
|
||||||
|
case 14:
|
||||||
|
return &esp.IO_MUX.MTMS
|
||||||
|
case 12:
|
||||||
|
return &esp.IO_MUX.MTDI
|
||||||
|
case 13:
|
||||||
|
return &esp.IO_MUX.MTCK
|
||||||
|
case 15:
|
||||||
|
return &esp.IO_MUX.MTDO
|
||||||
|
case 2:
|
||||||
|
return &esp.IO_MUX.GPIO2
|
||||||
|
case 0:
|
||||||
|
return &esp.IO_MUX.GPIO0
|
||||||
|
case 4:
|
||||||
|
return &esp.IO_MUX.GPIO4
|
||||||
|
case 16:
|
||||||
|
return &esp.IO_MUX.GPIO16
|
||||||
|
case 17:
|
||||||
|
return &esp.IO_MUX.GPIO17
|
||||||
|
case 9:
|
||||||
|
return &esp.IO_MUX.SD_DATA2
|
||||||
|
case 10:
|
||||||
|
return &esp.IO_MUX.SD_DATA3
|
||||||
|
case 11:
|
||||||
|
return &esp.IO_MUX.SD_CMD
|
||||||
|
case 6:
|
||||||
|
return &esp.IO_MUX.SD_CLK
|
||||||
|
case 7:
|
||||||
|
return &esp.IO_MUX.SD_DATA0
|
||||||
|
case 8:
|
||||||
|
return &esp.IO_MUX.SD_DATA1
|
||||||
|
case 5:
|
||||||
|
return &esp.IO_MUX.GPIO5
|
||||||
|
case 18:
|
||||||
|
return &esp.IO_MUX.GPIO18
|
||||||
|
case 19:
|
||||||
|
return &esp.IO_MUX.GPIO19
|
||||||
|
case 20:
|
||||||
|
return &esp.IO_MUX.GPIO20
|
||||||
|
case 21:
|
||||||
|
return &esp.IO_MUX.GPIO21
|
||||||
|
case 22:
|
||||||
|
return &esp.IO_MUX.GPIO22
|
||||||
|
case 3:
|
||||||
|
return &esp.IO_MUX.U0RXD
|
||||||
|
case 1:
|
||||||
|
return &esp.IO_MUX.U0TXD
|
||||||
|
case 23:
|
||||||
|
return &esp.IO_MUX.GPIO23
|
||||||
|
case 24:
|
||||||
|
return &esp.IO_MUX.GPIO24
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
|
||||||
|
UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()}
|
||||||
|
UART2 = UART{Bus: esp.UART2, Buffer: NewRingBuffer()}
|
||||||
|
)
|
||||||
|
|
||||||
|
type UART struct {
|
||||||
|
Bus *esp.UART_Type
|
||||||
|
Buffer *RingBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart UART) Configure(config UARTConfig) {
|
||||||
|
if config.BaudRate == 0 {
|
||||||
|
config.BaudRate = 115200
|
||||||
|
}
|
||||||
|
uart.Bus.CLKDIV.Set(peripheralClock / config.BaudRate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart UART) WriteByte(b byte) error {
|
||||||
|
for (uart.Bus.STATUS.Get()>>16)&0xff >= 128 {
|
||||||
|
// Read UART_TXFIFO_CNT from the status register, which indicates how
|
||||||
|
// many bytes there are in the transmit buffer. Wait until there are
|
||||||
|
// less than 128 bytes in this buffer (the default buffer size).
|
||||||
|
}
|
||||||
|
uart.Bus.TX_FIFO.Set(b)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serial Peripheral Interface on the ESP32.
|
||||||
|
type SPI struct {
|
||||||
|
Bus *esp.SPI_Type
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// SPI0 and SPI1 are reserved for use by the caching system etc.
|
||||||
|
SPI2 = SPI{esp.SPI2}
|
||||||
|
SPI3 = SPI{esp.SPI3}
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPIConfig configures a SPI peripheral on the ESP32. Make sure to set at least
|
||||||
|
// SCK, SDO and SDI (possibly to NoPin if not in use). The default for LSBFirst
|
||||||
|
// (false) and Mode (0) are good for most applications. The frequency defaults
|
||||||
|
// to 1MHz if not set but can be configured up to 40MHz. Possible values are
|
||||||
|
// 40MHz and integer divisions from 40MHz such as 20MHz, 13.3MHz, 10MHz, 8MHz,
|
||||||
|
// etc.
|
||||||
|
type SPIConfig struct {
|
||||||
|
Frequency uint32
|
||||||
|
SCK Pin
|
||||||
|
SDO Pin
|
||||||
|
SDI Pin
|
||||||
|
LSBFirst bool
|
||||||
|
Mode uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure and make the SPI peripheral ready to use.
|
||||||
|
func (spi SPI) Configure(config SPIConfig) error {
|
||||||
|
if config.Frequency == 0 {
|
||||||
|
config.Frequency = 1e6 // default to 1MHz
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the SPI clock. This assumes a peripheral clock of 80MHz.
|
||||||
|
var clockReg uint32
|
||||||
|
if config.Frequency >= 40e6 {
|
||||||
|
// Don't use a prescaler, but directly connect to the APB clock. This
|
||||||
|
// results in a SPI clock frequency of 40MHz.
|
||||||
|
clockReg |= esp.SPI_CLOCK_CLK_EQU_SYSCLK
|
||||||
|
} else {
|
||||||
|
// Use a prescaler for frequencies below 40MHz. They will get rounded
|
||||||
|
// down to the next possible frequency (20MHz, 13.3MHz, 10MHz, 8MHz,
|
||||||
|
// 6.7MHz, 5.7MHz, 5MHz, etc).
|
||||||
|
// This code is much simpler than how ESP-IDF configures the frequency,
|
||||||
|
// but should be just as accurate. The only exception is for frequencies
|
||||||
|
// below 4883Hz, which will need special support.
|
||||||
|
if config.Frequency < 4883 {
|
||||||
|
// The current lower limit is 4883Hz.
|
||||||
|
// The hardware supports lower frequencies by setting the h and n
|
||||||
|
// variables, but that's not yet implemented.
|
||||||
|
config.Frequency = 4883
|
||||||
|
}
|
||||||
|
// The prescaler value is 40e6 / config.Frequency, but rounded up so
|
||||||
|
// that the actual frequency is never higher than the frequency
|
||||||
|
// requested in config.Frequency.
|
||||||
|
var (
|
||||||
|
pre uint32 = (40e6 + config.Frequency - 1) / config.Frequency
|
||||||
|
n uint32 = 2 // this value seems to equal the number of ticks per SPI clock tick
|
||||||
|
h uint32 = 1 // must be half of n according to the formula in the reference manual
|
||||||
|
l uint32 = n // must equal n according to the reference manual
|
||||||
|
)
|
||||||
|
clockReg |= (pre - 1) << esp.SPI_CLOCK_CLKDIV_PRE_Pos
|
||||||
|
clockReg |= (n - 1) << esp.SPI_CLOCK_CLKCNT_N_Pos
|
||||||
|
clockReg |= (h - 1) << esp.SPI_CLOCK_CLKCNT_H_Pos
|
||||||
|
clockReg |= (l - 1) << esp.SPI_CLOCK_CLKCNT_L_Pos
|
||||||
|
}
|
||||||
|
spi.Bus.CLOCK.Set(clockReg)
|
||||||
|
|
||||||
|
// SPI_CTRL_REG controls bit order.
|
||||||
|
var ctrlReg uint32
|
||||||
|
if config.LSBFirst {
|
||||||
|
ctrlReg |= esp.SPI_CTRL_WR_BIT_ORDER
|
||||||
|
ctrlReg |= esp.SPI_CTRL_RD_BIT_ORDER
|
||||||
|
}
|
||||||
|
spi.Bus.CTRL.Set(ctrlReg)
|
||||||
|
|
||||||
|
// SPI_CTRL2_REG, SPI_USER_REG and SPI_PIN_REG control SPI clock polarity
|
||||||
|
// (mode), among others.
|
||||||
|
var ctrl2Reg, userReg, pinReg uint32
|
||||||
|
// For mode configuration, see table 29 in the reference manual (page 128).
|
||||||
|
var delayMode uint32
|
||||||
|
switch config.Mode {
|
||||||
|
case 0:
|
||||||
|
delayMode = 2
|
||||||
|
case 1:
|
||||||
|
delayMode = 1
|
||||||
|
userReg |= esp.SPI_USER_CK_OUT_EDGE
|
||||||
|
case 2:
|
||||||
|
delayMode = 1
|
||||||
|
userReg |= esp.SPI_USER_CK_OUT_EDGE
|
||||||
|
pinReg |= esp.SPI_PIN_CK_IDLE_EDGE
|
||||||
|
case 3:
|
||||||
|
delayMode = 2
|
||||||
|
pinReg |= esp.SPI_PIN_CK_IDLE_EDGE
|
||||||
|
}
|
||||||
|
// Extra configuration necessary for correct data input at high frequencies.
|
||||||
|
// This is only necessary when MISO goes through the GPIO matrix (which it
|
||||||
|
// currently does).
|
||||||
|
if config.Frequency >= 40e6 {
|
||||||
|
// Delay mode must be set to 0 and SPI_USR_DUMMY_CYCLELEN should be set
|
||||||
|
// to 0 (the default).
|
||||||
|
userReg |= esp.SPI_USER_USR_DUMMY
|
||||||
|
} else if config.Frequency >= 20e6 {
|
||||||
|
// Nothing to do here, delay mode should be set to 0 according to the
|
||||||
|
// datasheet.
|
||||||
|
} else {
|
||||||
|
// Follow the delay mode as given in table 29 on page 128 of the
|
||||||
|
// reference manual.
|
||||||
|
// Note that this is only specified for SPI frequency of 10MHz and
|
||||||
|
// below (≤Fapb/8), so 13.3MHz appears to be left unspecified.
|
||||||
|
ctrl2Reg |= delayMode << esp.SPI_CTRL2_MOSI_DELAY_MODE_Pos
|
||||||
|
}
|
||||||
|
// Enable full-duplex communication.
|
||||||
|
userReg |= esp.SPI_USER_DOUTDIN
|
||||||
|
userReg |= esp.SPI_USER_USR_MOSI
|
||||||
|
// Write values to registers.
|
||||||
|
spi.Bus.CTRL2.Set(ctrl2Reg)
|
||||||
|
spi.Bus.USER.Set(userReg)
|
||||||
|
spi.Bus.PIN.Set(pinReg)
|
||||||
|
|
||||||
|
// Configure pins.
|
||||||
|
// TODO: use direct output if possible, if the configured pins match the
|
||||||
|
// possible direct configurations (e.g. for SPI2, when SCK is pin 14 etc).
|
||||||
|
if spi.Bus == esp.SPI2 {
|
||||||
|
config.SCK.configure(PinConfig{Mode: PinOutput}, 8) // HSPICLK
|
||||||
|
config.SDI.configure(PinConfig{Mode: PinInput}, 9) // HSPIQ
|
||||||
|
config.SDO.configure(PinConfig{Mode: PinOutput}, 10) // HSPID
|
||||||
|
} else if spi.Bus == esp.SPI3 {
|
||||||
|
config.SCK.configure(PinConfig{Mode: PinOutput}, 63) // VSPICLK
|
||||||
|
config.SDI.configure(PinConfig{Mode: PinInput}, 64) // VSPIQ
|
||||||
|
config.SDO.configure(PinConfig{Mode: PinOutput}, 65) // VSPID
|
||||||
|
} else {
|
||||||
|
// Don't know how to configure this bus.
|
||||||
|
return ErrInvalidSPIBus
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer writes/reads a single byte using the SPI interface. If you need to
|
||||||
|
// transfer larger amounts of data, Tx will be faster.
|
||||||
|
func (spi SPI) Transfer(w byte) (byte, error) {
|
||||||
|
spi.Bus.MISO_DLEN.Set(7 << esp.SPI_MISO_DLEN_USR_MISO_DBITLEN_Pos)
|
||||||
|
spi.Bus.MOSI_DLEN.Set(7 << esp.SPI_MOSI_DLEN_USR_MOSI_DBITLEN_Pos)
|
||||||
|
|
||||||
|
spi.Bus.W0.Set(uint32(w))
|
||||||
|
|
||||||
|
// Send/receive byte.
|
||||||
|
spi.Bus.CMD.Set(esp.SPI_CMD_USR)
|
||||||
|
for spi.Bus.CMD.Get() != 0 {
|
||||||
|
}
|
||||||
|
|
||||||
|
// The received byte is stored in W0.
|
||||||
|
return byte(spi.Bus.W0.Get()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read
|
||||||
|
// interface, there must always be the same number of bytes written as bytes read.
|
||||||
|
// This is accomplished by sending zero bits if r is bigger than w or discarding
|
||||||
|
// the incoming data if w is bigger than r.
|
||||||
|
//
|
||||||
|
func (spi SPI) Tx(w, r []byte) error {
|
||||||
|
toTransfer := len(w)
|
||||||
|
if len(r) > toTransfer {
|
||||||
|
toTransfer = len(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
for toTransfer != 0 {
|
||||||
|
// Do only 64 bytes at a time.
|
||||||
|
chunkSize := toTransfer
|
||||||
|
if chunkSize > 64 {
|
||||||
|
chunkSize = 64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill tx buffer.
|
||||||
|
transferWords := (*[16]volatile.Register32)(unsafe.Pointer(uintptr(unsafe.Pointer(&spi.Bus.W0))))
|
||||||
|
var outBuf [16]uint32
|
||||||
|
txSize := 64
|
||||||
|
if txSize > len(w) {
|
||||||
|
txSize = len(w)
|
||||||
|
}
|
||||||
|
for i := 0; i < txSize; i++ {
|
||||||
|
outBuf[i/4] = outBuf[i/4] | uint32(w[i])<<((i%4)*8)
|
||||||
|
}
|
||||||
|
for i, word := range outBuf {
|
||||||
|
transferWords[i].Set(word)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the transfer.
|
||||||
|
spi.Bus.MISO_DLEN.Set((uint32(chunkSize)*8 - 1) << esp.SPI_MISO_DLEN_USR_MISO_DBITLEN_Pos)
|
||||||
|
spi.Bus.MOSI_DLEN.Set((uint32(chunkSize)*8 - 1) << esp.SPI_MOSI_DLEN_USR_MOSI_DBITLEN_Pos)
|
||||||
|
spi.Bus.CMD.Set(esp.SPI_CMD_USR)
|
||||||
|
for spi.Bus.CMD.Get() != 0 {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read rx buffer.
|
||||||
|
rxSize := 64
|
||||||
|
if rxSize > len(r) {
|
||||||
|
rxSize = len(r)
|
||||||
|
}
|
||||||
|
for i := 0; i < rxSize; i++ {
|
||||||
|
r[i] = byte(transferWords[i/4].Get() >> ((i % 4) * 8))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cut off some part of the output buffer so the next iteration we will
|
||||||
|
// only send the remaining bytes.
|
||||||
|
if len(w) < chunkSize {
|
||||||
|
w = nil
|
||||||
|
} else {
|
||||||
|
w = w[chunkSize:]
|
||||||
|
}
|
||||||
|
if len(r) < chunkSize {
|
||||||
|
r = nil
|
||||||
|
} else {
|
||||||
|
r = r[chunkSize:]
|
||||||
|
}
|
||||||
|
toTransfer -= chunkSize
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
// +build esp8266
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/esp"
|
||||||
|
"runtime/volatile"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CPUFrequency() uint32 {
|
||||||
|
return 80000000 // 80MHz
|
||||||
|
}
|
||||||
|
|
||||||
|
type PinMode uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
PinOutput PinMode = iota
|
||||||
|
PinInput
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pins that are fixed by the chip.
|
||||||
|
const (
|
||||||
|
UART_TX_PIN Pin = 1
|
||||||
|
UART_RX_PIN Pin = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pin functions are not trivial. The below array maps a pin number (GPIO
|
||||||
|
// number) to the pad as used in the IO mux.
|
||||||
|
// Tables with the mapping:
|
||||||
|
// https://www.esp8266.com/wiki/doku.php?id=esp8266_gpio_pin_allocations#pin_functions
|
||||||
|
// https://www.espressif.com/sites/default/files/documentation/ESP8266_Pin_List_0.xls
|
||||||
|
var pinPadMapping = [...]uint8{
|
||||||
|
12: 0,
|
||||||
|
13: 1,
|
||||||
|
14: 2,
|
||||||
|
15: 3,
|
||||||
|
3: 4,
|
||||||
|
1: 5,
|
||||||
|
6: 6,
|
||||||
|
7: 7,
|
||||||
|
8: 8,
|
||||||
|
9: 9,
|
||||||
|
10: 10,
|
||||||
|
11: 11,
|
||||||
|
0: 12,
|
||||||
|
2: 13,
|
||||||
|
4: 14,
|
||||||
|
5: 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPad returns the pad number and the register to configure this pad.
|
||||||
|
func (p Pin) getPad() (uint8, *volatile.Register32) {
|
||||||
|
pad := pinPadMapping[p]
|
||||||
|
var reg *volatile.Register32
|
||||||
|
switch pad {
|
||||||
|
case 0:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_MTDI
|
||||||
|
case 1:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_MTCK
|
||||||
|
case 2:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_MTMS
|
||||||
|
case 3:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_MTDO
|
||||||
|
case 4:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_U0RXD
|
||||||
|
case 5:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_U0TXD
|
||||||
|
case 6:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_SD_CLK
|
||||||
|
case 7:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_SD_DATA0
|
||||||
|
case 8:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_SD_DATA1
|
||||||
|
case 9:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_SD_DATA2
|
||||||
|
case 10:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_SD_DATA3
|
||||||
|
case 11:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_SD_CMD
|
||||||
|
case 12:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_GPIO0
|
||||||
|
case 13:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_GPIO2
|
||||||
|
case 14:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_GPIO4
|
||||||
|
case 15:
|
||||||
|
reg = &esp.IO_MUX.IO_MUX_GPIO5
|
||||||
|
}
|
||||||
|
return pad, reg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure sets the given pin as output or input pin.
|
||||||
|
func (p Pin) Configure(config PinConfig) {
|
||||||
|
switch config.Mode {
|
||||||
|
case PinInput, PinOutput:
|
||||||
|
pad, reg := p.getPad()
|
||||||
|
if pad >= 12 { // pin 0, 2, 4, 5
|
||||||
|
reg.Set(0 << 4) // function 0 at bit position 4
|
||||||
|
} else {
|
||||||
|
reg.Set(3 << 4) // function 3 at bit position 4
|
||||||
|
}
|
||||||
|
if config.Mode == PinOutput {
|
||||||
|
esp.GPIO.GPIO_ENABLE_W1TS.Set(1 << p)
|
||||||
|
} else {
|
||||||
|
esp.GPIO.GPIO_ENABLE_W1TC.Set(1 << p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the output value of this pin to high (true) or low (false).
|
||||||
|
func (p Pin) Set(value bool) {
|
||||||
|
if value {
|
||||||
|
esp.GPIO.GPIO_OUT_W1TS.Set(1 << p)
|
||||||
|
} else {
|
||||||
|
esp.GPIO.GPIO_OUT_W1TC.Set(1 << p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the register and mask to enable a given GPIO pin. This can be used to
|
||||||
|
// implement bit-banged drivers.
|
||||||
|
//
|
||||||
|
// Warning: only use this on an output pin!
|
||||||
|
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||||
|
return &esp.GPIO.GPIO_OUT_W1TS.Reg, 1 << p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the register and mask to disable a given GPIO pin. This can be used to
|
||||||
|
// implement bit-banged drivers.
|
||||||
|
//
|
||||||
|
// Warning: only use this on an output pin!
|
||||||
|
func (p Pin) PortMaskClear() (*uint32, uint32) {
|
||||||
|
return &esp.GPIO.GPIO_OUT_W1TC.Reg, 1 << p
|
||||||
|
}
|
||||||
|
|
||||||
|
// UART0 is a hardware UART that supports both TX and RX.
|
||||||
|
var UART0 = UART{Buffer: NewRingBuffer()}
|
||||||
|
|
||||||
|
type UART struct {
|
||||||
|
Buffer *RingBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the UART baud rate. TX and RX pins are fixed by the hardware so
|
||||||
|
// cannot be modified and will be ignored.
|
||||||
|
func (uart UART) Configure(config UARTConfig) {
|
||||||
|
if config.BaudRate == 0 {
|
||||||
|
config.BaudRate = 115200
|
||||||
|
}
|
||||||
|
esp.UART0.UART_CLKDIV.Set(CPUFrequency() / config.BaudRate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteByte writes a single byte to the output buffer. Note that the hardware
|
||||||
|
// includes a buffer of 128 bytes which will be used first.
|
||||||
|
func (uart UART) WriteByte(c byte) error {
|
||||||
|
for (esp.UART0.UART_STATUS.Get()>>16)&0xff >= 128 {
|
||||||
|
// Wait until the TX buffer has room.
|
||||||
|
}
|
||||||
|
esp.UART0.UART_FIFO.Set(uint32(c))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -4,6 +4,19 @@ package machine
|
|||||||
|
|
||||||
// Peripheral abstraction layer for the stm32.
|
// Peripheral abstraction layer for the stm32.
|
||||||
|
|
||||||
|
const (
|
||||||
|
portA Pin = iota * 16
|
||||||
|
portB
|
||||||
|
portC
|
||||||
|
portD
|
||||||
|
portE
|
||||||
|
portF
|
||||||
|
portG
|
||||||
|
portH
|
||||||
|
portI
|
||||||
|
portJ
|
||||||
|
)
|
||||||
|
|
||||||
type PinMode uint8
|
type PinMode uint8
|
||||||
|
|
||||||
// Peripheral operations sequence:
|
// Peripheral operations sequence:
|
||||||
|
|||||||
@@ -0,0 +1,229 @@
|
|||||||
|
// +build stm32f4
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// Peripheral abstraction layer for the stm32f4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/stm32"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PA0 = portA + 0
|
||||||
|
PA1 = portA + 1
|
||||||
|
PA2 = portA + 2
|
||||||
|
PA3 = portA + 3
|
||||||
|
PA4 = portA + 4
|
||||||
|
PA5 = portA + 5
|
||||||
|
PA6 = portA + 6
|
||||||
|
PA7 = portA + 7
|
||||||
|
PA8 = portA + 8
|
||||||
|
PA9 = portA + 9
|
||||||
|
PA10 = portA + 10
|
||||||
|
PA11 = portA + 11
|
||||||
|
PA12 = portA + 12
|
||||||
|
PA13 = portA + 13
|
||||||
|
PA14 = portA + 14
|
||||||
|
PA15 = portA + 15
|
||||||
|
|
||||||
|
PB0 = portB + 0
|
||||||
|
PB1 = portB + 1
|
||||||
|
PB2 = portB + 2
|
||||||
|
PB3 = portB + 3
|
||||||
|
PB4 = portB + 4
|
||||||
|
PB5 = portB + 5
|
||||||
|
PB6 = portB + 6
|
||||||
|
PB7 = portB + 7
|
||||||
|
PB8 = portB + 8
|
||||||
|
PB9 = portB + 9
|
||||||
|
PB10 = portB + 10
|
||||||
|
PB11 = portB + 11
|
||||||
|
PB12 = portB + 12
|
||||||
|
PB13 = portB + 13
|
||||||
|
PB14 = portB + 14
|
||||||
|
PB15 = portB + 15
|
||||||
|
|
||||||
|
PC0 = portC + 0
|
||||||
|
PC1 = portC + 1
|
||||||
|
PC2 = portC + 2
|
||||||
|
PC3 = portC + 3
|
||||||
|
PC4 = portC + 4
|
||||||
|
PC5 = portC + 5
|
||||||
|
PC6 = portC + 6
|
||||||
|
PC7 = portC + 7
|
||||||
|
PC8 = portC + 8
|
||||||
|
PC9 = portC + 9
|
||||||
|
PC10 = portC + 10
|
||||||
|
PC11 = portC + 11
|
||||||
|
PC12 = portC + 12
|
||||||
|
PC13 = portC + 13
|
||||||
|
PC14 = portC + 14
|
||||||
|
PC15 = portC + 15
|
||||||
|
|
||||||
|
PD0 = portD + 0
|
||||||
|
PD1 = portD + 1
|
||||||
|
PD2 = portD + 2
|
||||||
|
PD3 = portD + 3
|
||||||
|
PD4 = portD + 4
|
||||||
|
PD5 = portD + 5
|
||||||
|
PD6 = portD + 6
|
||||||
|
PD7 = portD + 7
|
||||||
|
PD8 = portD + 8
|
||||||
|
PD9 = portD + 9
|
||||||
|
PD10 = portD + 10
|
||||||
|
PD11 = portD + 11
|
||||||
|
PD12 = portD + 12
|
||||||
|
PD13 = portD + 13
|
||||||
|
PD14 = portD + 14
|
||||||
|
PD15 = portD + 15
|
||||||
|
|
||||||
|
PE0 = portE + 0
|
||||||
|
PE1 = portE + 1
|
||||||
|
PE2 = portE + 2
|
||||||
|
PE3 = portE + 3
|
||||||
|
PE4 = portE + 4
|
||||||
|
PE5 = portE + 5
|
||||||
|
PE6 = portE + 6
|
||||||
|
PE7 = portE + 7
|
||||||
|
PE8 = portE + 8
|
||||||
|
PE9 = portE + 9
|
||||||
|
PE10 = portE + 10
|
||||||
|
PE11 = portE + 11
|
||||||
|
PE12 = portE + 12
|
||||||
|
PE13 = portE + 13
|
||||||
|
PE14 = portE + 14
|
||||||
|
PE15 = portE + 15
|
||||||
|
|
||||||
|
PH0 = portH + 0
|
||||||
|
PH1 = portH + 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p Pin) getPort() *stm32.GPIO_Type {
|
||||||
|
switch p / 16 {
|
||||||
|
case 0:
|
||||||
|
return stm32.GPIOA
|
||||||
|
case 1:
|
||||||
|
return stm32.GPIOB
|
||||||
|
case 2:
|
||||||
|
return stm32.GPIOC
|
||||||
|
case 3:
|
||||||
|
return stm32.GPIOD
|
||||||
|
case 4:
|
||||||
|
return stm32.GPIOE
|
||||||
|
case 5:
|
||||||
|
return stm32.GPIOF
|
||||||
|
case 6:
|
||||||
|
return stm32.GPIOG
|
||||||
|
case 7:
|
||||||
|
return stm32.GPIOH
|
||||||
|
case 8:
|
||||||
|
return stm32.GPIOI
|
||||||
|
default:
|
||||||
|
panic("machine: unknown port")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// enableClock enables the clock for this desired GPIO port.
|
||||||
|
func (p Pin) enableClock() {
|
||||||
|
switch p / 16 {
|
||||||
|
case 0:
|
||||||
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOAEN)
|
||||||
|
case 1:
|
||||||
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOBEN)
|
||||||
|
case 2:
|
||||||
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOCEN)
|
||||||
|
case 3:
|
||||||
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIODEN)
|
||||||
|
case 4:
|
||||||
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOEEN)
|
||||||
|
case 5:
|
||||||
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOFEN)
|
||||||
|
case 6:
|
||||||
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOGEN)
|
||||||
|
case 7:
|
||||||
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOHEN)
|
||||||
|
case 8:
|
||||||
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOIEN)
|
||||||
|
default:
|
||||||
|
panic("machine: unknown port")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable peripheral clock
|
||||||
|
func enableAltFuncClock(bus unsafe.Pointer) {
|
||||||
|
switch bus {
|
||||||
|
case unsafe.Pointer(stm32.DAC): // DAC interface clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_DACEN)
|
||||||
|
case unsafe.Pointer(stm32.PWR): // Power interface clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
|
||||||
|
case unsafe.Pointer(stm32.CAN2): // CAN 2 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_CAN2EN)
|
||||||
|
case unsafe.Pointer(stm32.CAN1): // CAN 1 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_CAN1EN)
|
||||||
|
case unsafe.Pointer(stm32.I2C3): // I2C3 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C3EN)
|
||||||
|
case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C2EN)
|
||||||
|
case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
|
||||||
|
case unsafe.Pointer(stm32.UART5): // UART5 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_UART5EN)
|
||||||
|
case unsafe.Pointer(stm32.UART4): // UART4 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_UART4EN)
|
||||||
|
case unsafe.Pointer(stm32.USART3): // USART3 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART3EN)
|
||||||
|
case unsafe.Pointer(stm32.USART2): // USART2 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
|
||||||
|
case unsafe.Pointer(stm32.SPI3): // SPI3 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI3EN)
|
||||||
|
case unsafe.Pointer(stm32.SPI2): // SPI2 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_SPI2EN)
|
||||||
|
case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_WWDGEN)
|
||||||
|
case unsafe.Pointer(stm32.TIM14): // TIM14 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM14EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM13): // TIM13 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM13EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM12): // TIM12 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM12EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM7): // TIM7 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM6): // TIM6 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM6EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM5): // TIM5 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM5EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM4): // TIM4 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM4EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM3): // TIM3 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM2): // TIM2 clock enable
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM2EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM11): // TIM11 clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM11EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM10): // TIM10 clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM10EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM9): // TIM9 clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM9EN)
|
||||||
|
case unsafe.Pointer(stm32.SYSCFG): // System configuration controller clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN)
|
||||||
|
case unsafe.Pointer(stm32.SPI1): // SPI1 clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
||||||
|
case unsafe.Pointer(stm32.SDIO): // SDIO clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SDIOEN)
|
||||||
|
case unsafe.Pointer(stm32.ADC3): // ADC3 clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADC3EN)
|
||||||
|
case unsafe.Pointer(stm32.ADC2): // ADC2 clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADC2EN)
|
||||||
|
case unsafe.Pointer(stm32.ADC1): // ADC1 clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_ADC1EN)
|
||||||
|
case unsafe.Pointer(stm32.USART6): // USART6 clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART6EN)
|
||||||
|
case unsafe.Pointer(stm32.USART1): // USART1 clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM8): // TIM8 clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM8EN)
|
||||||
|
case unsafe.Pointer(stm32.TIM1): // TIM1 clock enable
|
||||||
|
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM1EN)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// +build stm32f405
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// Peripheral abstraction layer for the stm32f405
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/stm32"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CPUFrequency() uint32 {
|
||||||
|
return 168000000
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- UART ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
type UART struct {
|
||||||
|
Buffer *RingBuffer
|
||||||
|
Bus *stm32.USART_Type
|
||||||
|
Interrupt interrupt.Interrupt
|
||||||
|
AltFuncSelector stm32.AltFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart UART) configurePins(config UARTConfig) {
|
||||||
|
// enable the alternate functions on the TX and RX pins
|
||||||
|
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.AltFuncSelector)
|
||||||
|
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.AltFuncSelector)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart UART) getBaudRateDivisor(baudRate uint32) uint32 {
|
||||||
|
var clock uint32
|
||||||
|
switch uart.Bus {
|
||||||
|
case stm32.USART1, stm32.USART6:
|
||||||
|
clock = CPUFrequency() / 2 // APB2 Frequency
|
||||||
|
case stm32.USART2, stm32.USART3, stm32.UART4, stm32.UART5:
|
||||||
|
clock = CPUFrequency() / 4 // APB1 Frequency
|
||||||
|
}
|
||||||
|
return clock / baudRate
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- SPI ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
type SPI struct {
|
||||||
|
Bus *stm32.SPI_Type
|
||||||
|
AltFuncSelector stm32.AltFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi SPI) configurePins(config SPIConfig) {}
|
||||||
|
func (spi SPI) getBaudRate(config SPIConfig) uint32 { return 0 }
|
||||||
|
|
||||||
|
// -- I2C ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
type I2C struct {
|
||||||
|
Bus *stm32.I2C_Type
|
||||||
|
AltFuncSelector stm32.AltFunc
|
||||||
|
}
|
||||||
@@ -1,84 +1,18 @@
|
|||||||
// +build stm32,stm32f407
|
// +build stm32f407
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
// Peripheral abstraction layer for the stm32f4(07)
|
// Peripheral abstraction layer for the stm32f407
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func CPUFrequency() uint32 {
|
func CPUFrequency() uint32 {
|
||||||
return 168000000
|
return 168000000
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Pin) getPort() *stm32.GPIO_Type {
|
|
||||||
switch p / 16 {
|
|
||||||
case 0:
|
|
||||||
return stm32.GPIOA
|
|
||||||
case 1:
|
|
||||||
return stm32.GPIOB
|
|
||||||
case 2:
|
|
||||||
return stm32.GPIOC
|
|
||||||
case 3:
|
|
||||||
return stm32.GPIOD
|
|
||||||
case 4:
|
|
||||||
return stm32.GPIOE
|
|
||||||
case 5:
|
|
||||||
return stm32.GPIOF
|
|
||||||
case 6:
|
|
||||||
return stm32.GPIOG
|
|
||||||
case 7:
|
|
||||||
return stm32.GPIOH
|
|
||||||
case 8:
|
|
||||||
return stm32.GPIOI
|
|
||||||
default:
|
|
||||||
panic("machine: unknown port")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// enableClock enables the clock for this desired GPIO port.
|
|
||||||
func (p Pin) enableClock() {
|
|
||||||
switch p / 16 {
|
|
||||||
case 0:
|
|
||||||
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOAEN)
|
|
||||||
case 1:
|
|
||||||
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOBEN)
|
|
||||||
case 2:
|
|
||||||
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOCEN)
|
|
||||||
case 3:
|
|
||||||
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIODEN)
|
|
||||||
case 4:
|
|
||||||
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOEEN)
|
|
||||||
case 5:
|
|
||||||
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOFEN)
|
|
||||||
case 6:
|
|
||||||
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOGEN)
|
|
||||||
case 7:
|
|
||||||
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOHEN)
|
|
||||||
case 8:
|
|
||||||
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOIEN)
|
|
||||||
default:
|
|
||||||
panic("machine: unknown port")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable peripheral clock
|
|
||||||
func enableAltFuncClock(bus unsafe.Pointer) {
|
|
||||||
switch bus {
|
|
||||||
case unsafe.Pointer(stm32.USART1):
|
|
||||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
|
||||||
case unsafe.Pointer(stm32.USART2):
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_USART2EN)
|
|
||||||
case unsafe.Pointer(stm32.I2C1):
|
|
||||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
|
|
||||||
case unsafe.Pointer(stm32.SPI1):
|
|
||||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------- UART related types and code
|
//---------- UART related types and code
|
||||||
|
|
||||||
// UART representation
|
// UART representation
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build avr nrf sam sifive stm32 k210 nxp
|
// +build avr esp nrf sam sifive stm32 k210 nxp
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -87,9 +87,12 @@ func exitCriticalSection() {
|
|||||||
|
|
||||||
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
|
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
|
||||||
func (usbcdc *USBCDC) Configure(config UARTConfig) {
|
func (usbcdc *USBCDC) Configure(config UARTConfig) {
|
||||||
// enable IRQ
|
// Enable IRQ. Make sure this is higher than the SWI2 interrupt handler so
|
||||||
|
// that it is possible to print to the console from a BLE interrupt. You
|
||||||
|
// shouldn't generally do that but it is useful for debugging and panic
|
||||||
|
// logging.
|
||||||
usbcdc.interrupt = interrupt.New(nrf.IRQ_USBD, USB.handleInterrupt)
|
usbcdc.interrupt = interrupt.New(nrf.IRQ_USBD, USB.handleInterrupt)
|
||||||
usbcdc.interrupt.SetPriority(0xD0)
|
usbcdc.interrupt.SetPriority(0x40) // interrupt priority 2 (lower number means more important)
|
||||||
usbcdc.interrupt.Enable()
|
usbcdc.interrupt.Enable()
|
||||||
|
|
||||||
// enable USB
|
// enable USB
|
||||||
|
|||||||
@@ -102,7 +102,3 @@ func procPin() {
|
|||||||
func procUnpin() {
|
func procUnpin() {
|
||||||
arm.EnableInterrupts(procPinnedMask)
|
arm.EnableInterrupts(procPinnedMask)
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForEvents() {
|
|
||||||
arm.Asm("wfe")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// +build xtensa
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
const GOARCH = "arm" // xtensa pretends to be arm
|
||||||
|
|
||||||
|
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||||
|
const TargetBits = 32
|
||||||
|
|
||||||
|
// Align on a word boundary.
|
||||||
|
func align(ptr uintptr) uintptr {
|
||||||
|
return (ptr + 3) &^ 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentStackPointer() uintptr
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const debugLoader = false
|
||||||
|
|
||||||
|
const (
|
||||||
|
rAARCH64_RELATIVE = 1027
|
||||||
|
dtNULL = 0 /* Terminating entry. */
|
||||||
|
dtRELA = 7 /* Address of ElfNN_Rela relocations. */
|
||||||
|
dtRELASZ = 8 /* Total size of ElfNN_Rela relocations. */
|
||||||
|
)
|
||||||
|
|
||||||
|
/* ELF64 relocations that need an addend field. */
|
||||||
|
type rela64 struct {
|
||||||
|
Off uint64 /* Location to be relocated. */
|
||||||
|
Info uint64 /* Relocation type and symbol index. */
|
||||||
|
Addend int64 /* Addend. */
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELF64 Dynamic structure. The ".dynamic" section contains an array of them.
|
||||||
|
type dyn64 struct {
|
||||||
|
Tag int64 /* Entry type. */
|
||||||
|
Val uint64 /* Integer/address value */
|
||||||
|
}
|
||||||
|
|
||||||
|
//export __dynamic_loader
|
||||||
|
func dynamicLoader(base uintptr, dyn *dyn64) {
|
||||||
|
var rela *rela64
|
||||||
|
relasz := uint64(0)
|
||||||
|
|
||||||
|
if debugLoader {
|
||||||
|
println("ASLR Base: ", base)
|
||||||
|
}
|
||||||
|
|
||||||
|
for dyn.Tag != dtNULL {
|
||||||
|
switch dyn.Tag {
|
||||||
|
case dtRELA:
|
||||||
|
rela = (*rela64)(unsafe.Pointer(base + uintptr(dyn.Val)))
|
||||||
|
case dtRELASZ:
|
||||||
|
relasz = uint64(dyn.Val) / uint64(unsafe.Sizeof(rela64{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr := uintptr(unsafe.Pointer(dyn))
|
||||||
|
ptr += unsafe.Sizeof(dyn64{})
|
||||||
|
dyn = (*dyn64)(unsafe.Pointer(ptr))
|
||||||
|
}
|
||||||
|
|
||||||
|
if rela == nil {
|
||||||
|
runtimePanic("bad reloc")
|
||||||
|
}
|
||||||
|
|
||||||
|
if debugLoader {
|
||||||
|
println("Sections to load: ", relasz)
|
||||||
|
}
|
||||||
|
|
||||||
|
for relasz > 0 && rela != nil {
|
||||||
|
switch rela.Info {
|
||||||
|
case rAARCH64_RELATIVE:
|
||||||
|
ptr := (*uint64)(unsafe.Pointer(base + uintptr(rela.Off)))
|
||||||
|
*ptr = uint64(base + uintptr(rela.Addend))
|
||||||
|
}
|
||||||
|
|
||||||
|
rptr := uintptr(unsafe.Pointer(rela))
|
||||||
|
rptr += unsafe.Sizeof(rela64{})
|
||||||
|
rela = (*rela64)(unsafe.Pointer(rptr))
|
||||||
|
relasz--
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// +build xtensa
|
||||||
|
|
||||||
|
package interrupt
|
||||||
|
|
||||||
|
import "device"
|
||||||
|
|
||||||
|
// State represents the previous global interrupt state.
|
||||||
|
type State uintptr
|
||||||
|
|
||||||
|
// Disable disables all interrupts and returns the previous interrupt state. It
|
||||||
|
// can be used in a critical section like this:
|
||||||
|
//
|
||||||
|
// state := interrupt.Disable()
|
||||||
|
// // critical section
|
||||||
|
// interrupt.Restore(state)
|
||||||
|
//
|
||||||
|
// Critical sections can be nested. Make sure to call Restore in the same order
|
||||||
|
// as you called Disable (this happens naturally with the pattern above).
|
||||||
|
func Disable() (state State) {
|
||||||
|
return State(device.AsmFull("rsil {}, 15", nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore restores interrupts to what they were before. Give the previous state
|
||||||
|
// returned by Disable as a parameter. If interrupts were disabled before
|
||||||
|
// calling Disable, this will not re-enable interrupts, allowing for nested
|
||||||
|
// cricital sections.
|
||||||
|
func Restore(state State) {
|
||||||
|
device.AsmFull("wsr {state}, PS", map[string]interface{}{
|
||||||
|
"state": state,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package pprof
|
||||||
|
|
||||||
|
// TinyGo does not implement pprof. However, a dummy shell is needed for the
|
||||||
|
// testing package (and testing/internal/pprof).
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrUnimplemented = errors.New("runtime/pprof: unimplemented")
|
||||||
|
|
||||||
|
type Profile struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartCPUProfile(w io.Writer) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func StopCPUProfile() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func Lookup(name string) *Profile {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Profile) WriteTo(w io.Writer, debug int) error {
|
||||||
|
return ErrUnimplemented
|
||||||
|
}
|
||||||
@@ -308,7 +308,7 @@ func timerSleep(ticks uint32) bool {
|
|||||||
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
||||||
|
|
||||||
wait:
|
wait:
|
||||||
arm.Asm("wfe")
|
waitForEvents()
|
||||||
if timerWakeup.Get() != 0 {
|
if timerWakeup.Get() != 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -345,3 +345,7 @@ func initADCClock() {
|
|||||||
sam.GCLK_CLKCTRL_CLKEN)
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
waitForSync()
|
waitForSync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func waitForEvents() {
|
||||||
|
arm.Asm("wfe")
|
||||||
|
}
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ func timerSleep(ticks uint32) bool {
|
|||||||
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
||||||
|
|
||||||
wait:
|
wait:
|
||||||
arm.Asm("wfe")
|
waitForEvents()
|
||||||
if timerWakeup.Get() != 0 {
|
if timerWakeup.Get() != 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -317,7 +317,7 @@ func initUSBClock() {
|
|||||||
|
|
||||||
// Put Generic Clock Generator 1 as source for USB
|
// Put Generic Clock Generator 1 as source for USB
|
||||||
//GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
|
//GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
|
||||||
sam.GCLK.PCHCTRL[10].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_USB].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,8 +327,12 @@ func initADCClock() {
|
|||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_ADC1_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_ADC1_)
|
||||||
|
|
||||||
// Put Generic Clock Generator 1 as source for ADC0 and ADC1.
|
// Put Generic Clock Generator 1 as source for ADC0 and ADC1.
|
||||||
sam.GCLK.PCHCTRL[40].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_ADC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
sam.GCLK.PCHCTRL[41].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_ADC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func waitForEvents() {
|
||||||
|
arm.Asm("wfe")
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,35 +9,35 @@ import (
|
|||||||
func initSERCOMClocks() {
|
func initSERCOMClocks() {
|
||||||
// Turn on clock to SERCOM0 for UART0
|
// Turn on clock to SERCOM0 for UART0
|
||||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
||||||
sam.GCLK.PCHCTRL[7].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// sets the "slow" clock shared by all SERCOM
|
// sets the "slow" clock shared by all SERCOM
|
||||||
sam.GCLK.PCHCTRL[3].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOMX_SLOW].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM1
|
// Turn on clock to SERCOM1
|
||||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
||||||
sam.GCLK.PCHCTRL[8].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM2
|
// Turn on clock to SERCOM2
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
||||||
sam.GCLK.PCHCTRL[23].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM3
|
// Turn on clock to SERCOM3
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
||||||
sam.GCLK.PCHCTRL[24].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM4
|
// Turn on clock to SERCOM4
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
||||||
sam.GCLK.PCHCTRL[34].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM5
|
// Turn on clock to SERCOM5
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
||||||
sam.GCLK.PCHCTRL[35].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,35 +9,35 @@ import (
|
|||||||
func initSERCOMClocks() {
|
func initSERCOMClocks() {
|
||||||
// Turn on clock to SERCOM0 for UART0
|
// Turn on clock to SERCOM0 for UART0
|
||||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
||||||
sam.GCLK.PCHCTRL[7].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// sets the "slow" clock shared by all SERCOM
|
// sets the "slow" clock shared by all SERCOM
|
||||||
sam.GCLK.PCHCTRL[3].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOMX_SLOW].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM1
|
// Turn on clock to SERCOM1
|
||||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
||||||
sam.GCLK.PCHCTRL[8].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM2
|
// Turn on clock to SERCOM2
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
||||||
sam.GCLK.PCHCTRL[23].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM3
|
// Turn on clock to SERCOM3
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
||||||
sam.GCLK.PCHCTRL[24].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM4
|
// Turn on clock to SERCOM4
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
||||||
sam.GCLK.PCHCTRL[34].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM5
|
// Turn on clock to SERCOM5
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
||||||
sam.GCLK.PCHCTRL[35].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,35 +9,35 @@ import (
|
|||||||
func initSERCOMClocks() {
|
func initSERCOMClocks() {
|
||||||
// Turn on clock to SERCOM0 for UART0
|
// Turn on clock to SERCOM0 for UART0
|
||||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
||||||
sam.GCLK.PCHCTRL[7].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// sets the "slow" clock shared by all SERCOM
|
// sets the "slow" clock shared by all SERCOM
|
||||||
sam.GCLK.PCHCTRL[3].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOMX_SLOW].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM1
|
// Turn on clock to SERCOM1
|
||||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
||||||
sam.GCLK.PCHCTRL[8].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM2
|
// Turn on clock to SERCOM2
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
||||||
sam.GCLK.PCHCTRL[23].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM3
|
// Turn on clock to SERCOM3
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
||||||
sam.GCLK.PCHCTRL[24].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM4
|
// Turn on clock to SERCOM4
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
||||||
sam.GCLK.PCHCTRL[34].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM5
|
// Turn on clock to SERCOM5
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
||||||
sam.GCLK.PCHCTRL[35].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,45 +9,45 @@ import (
|
|||||||
func initSERCOMClocks() {
|
func initSERCOMClocks() {
|
||||||
// Turn on clock to SERCOM0 for UART0
|
// Turn on clock to SERCOM0 for UART0
|
||||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
|
||||||
sam.GCLK.PCHCTRL[7].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM0_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// sets the "slow" clock shared by all SERCOM
|
// sets the "slow" clock shared by all SERCOM
|
||||||
sam.GCLK.PCHCTRL[3].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOMX_SLOW].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM1
|
// Turn on clock to SERCOM1
|
||||||
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
|
||||||
sam.GCLK.PCHCTRL[8].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM1_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM2
|
// Turn on clock to SERCOM2
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
|
||||||
sam.GCLK.PCHCTRL[23].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM2_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM3
|
// Turn on clock to SERCOM3
|
||||||
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
|
||||||
sam.GCLK.PCHCTRL[24].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM3_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM4
|
// Turn on clock to SERCOM4
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
|
||||||
sam.GCLK.PCHCTRL[34].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM4_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM5
|
// Turn on clock to SERCOM5
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
|
||||||
sam.GCLK.PCHCTRL[35].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM5_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM6
|
// Turn on clock to SERCOM6
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM6_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM6_)
|
||||||
sam.GCLK.PCHCTRL[36].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM6_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
|
|
||||||
// Turn on clock to SERCOM7
|
// Turn on clock to SERCOM7
|
||||||
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM7_)
|
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM7_)
|
||||||
sam.GCLK.PCHCTRL[37].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_SERCOM7_CORE].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,3 +50,7 @@ var stdoutWrite = (*volatile.Register8)(unsafe.Pointer(uintptr(0x4000c000)))
|
|||||||
func putchar(c byte) {
|
func putchar(c byte) {
|
||||||
stdoutWrite.Set(uint8(c))
|
stdoutWrite.Set(uint8(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func waitForEvents() {
|
||||||
|
arm.Asm("wfe")
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
// +build esp32
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device"
|
||||||
|
"device/esp"
|
||||||
|
"machine"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type timeUnit int64
|
||||||
|
|
||||||
|
var currentTime timeUnit
|
||||||
|
|
||||||
|
func putchar(c byte) {
|
||||||
|
machine.UART0.WriteByte(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func postinit() {}
|
||||||
|
|
||||||
|
// This is the function called on startup right after the stack pointer has been
|
||||||
|
// set.
|
||||||
|
//export main
|
||||||
|
func main() {
|
||||||
|
// Disable both watchdog timers that are enabled by default on startup.
|
||||||
|
// Note that these watchdogs can be protected, but the ROM bootloader
|
||||||
|
// doesn't seem to protect them.
|
||||||
|
esp.RTCCNTL.WDTCONFIG0.Set(0)
|
||||||
|
esp.TIMG0.WDTCONFIG0.Set(0)
|
||||||
|
|
||||||
|
// Switch SoC clock source to PLL (instead of the default which is XTAL).
|
||||||
|
// This switches the CPU (and APB) clock from 40MHz to 80MHz.
|
||||||
|
// Options:
|
||||||
|
// RTCCNTL_CLK_CONF_SOC_CLK_SEL: PLL (default XTAL)
|
||||||
|
// RTCCNTL_CLK_CONF_CK8M_DIV_SEL: 2 (default)
|
||||||
|
// RTCCNTL_CLK_CONF_DIG_CLK8M_D256_EN: Enable (default)
|
||||||
|
// RTCCNTL_CLK_CONF_CK8M_DIV: DIV256 (default)
|
||||||
|
// The only real change made here is modifying RTCCNTL_CLK_CONF_SOC_CLK_SEL,
|
||||||
|
// but setting a fixed value produces smaller code.
|
||||||
|
esp.RTCCNTL.CLK_CONF.Set((esp.RTCCNTL_CLK_CONF_SOC_CLK_SEL_PLL << esp.RTCCNTL_CLK_CONF_SOC_CLK_SEL_Pos) |
|
||||||
|
(2 << esp.RTCCNTL_CLK_CONF_CK8M_DIV_SEL_Pos) |
|
||||||
|
(esp.RTCCNTL_CLK_CONF_DIG_CLK8M_D256_EN_Enable << esp.RTCCNTL_CLK_CONF_DIG_CLK8M_D256_EN_Pos) |
|
||||||
|
(esp.RTCCNTL_CLK_CONF_CK8M_DIV_DIV256 << esp.RTCCNTL_CLK_CONF_CK8M_DIV_Pos))
|
||||||
|
|
||||||
|
// Switch CPU from 80MHz to 160MHz. This doesn't affect the APB clock,
|
||||||
|
// which is still running at 80MHz.
|
||||||
|
esp.DPORT.CPU_PER_CONF.Set(esp.DPORT_CPU_PER_CONF_CPUPERIOD_SEL_SEL_160)
|
||||||
|
|
||||||
|
// Clear .bss section. .data has already been loaded by the ROM bootloader.
|
||||||
|
// Do this after increasing the CPU clock to possibly make startup slightly
|
||||||
|
// faster.
|
||||||
|
preinit()
|
||||||
|
|
||||||
|
// Initialize UART.
|
||||||
|
machine.UART0.Configure(machine.UARTConfig{})
|
||||||
|
|
||||||
|
// Configure timer 0 in timer group 0, for timekeeping.
|
||||||
|
// EN: Enable the timer.
|
||||||
|
// INCREASE: Count up every tick (as opposed to counting down).
|
||||||
|
// DIVIDER: 16-bit prescaler, set to 2 for dividing the APB clock by two
|
||||||
|
// (40MHz).
|
||||||
|
esp.TIMG0.T0CONFIG.Set(esp.TIMG_T0CONFIG_T0_EN | esp.TIMG_T0CONFIG_T0_INCREASE | 2<<esp.TIMG_T0CONFIG_T0_DIVIDER_Pos)
|
||||||
|
|
||||||
|
// Set the timer counter value to 0.
|
||||||
|
esp.TIMG0.T0LOADLO.Set(0)
|
||||||
|
esp.TIMG0.T0LOADHI.Set(0)
|
||||||
|
esp.TIMG0.T0LOAD.Set(0) // value doesn't matter.
|
||||||
|
|
||||||
|
run()
|
||||||
|
|
||||||
|
// Fallback: if main ever returns, hang the CPU.
|
||||||
|
abort()
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:extern _sbss
|
||||||
|
var _sbss [0]byte
|
||||||
|
|
||||||
|
//go:extern _ebss
|
||||||
|
var _ebss [0]byte
|
||||||
|
|
||||||
|
func preinit() {
|
||||||
|
// Initialize .bss: zero-initialized global variables.
|
||||||
|
// The .data section has already been loaded by the ROM bootloader.
|
||||||
|
ptr := unsafe.Pointer(&_sbss)
|
||||||
|
for ptr != unsafe.Pointer(&_ebss) {
|
||||||
|
*(*uint32)(ptr) = 0
|
||||||
|
ptr = unsafe.Pointer(uintptr(ptr) + 4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ticks() timeUnit {
|
||||||
|
// First, update the LO and HI register pair by writing any value to the
|
||||||
|
// register. This allows reading the pair atomically.
|
||||||
|
esp.TIMG0.T0UPDATE.Set(0)
|
||||||
|
// Then read the two 32-bit parts of the timer.
|
||||||
|
return timeUnit(uint64(esp.TIMG0.T0LO.Get()) | uint64(esp.TIMG0.T0HI.Get())<<32)
|
||||||
|
}
|
||||||
|
|
||||||
|
const asyncScheduler = false
|
||||||
|
|
||||||
|
func nanosecondsToTicks(ns int64) timeUnit {
|
||||||
|
// Calculate the number of ticks from the number of nanoseconds. At a 80MHz
|
||||||
|
// APB clock, that's 25 nanoseconds per tick with a timer prescaler of 2:
|
||||||
|
// 25 = 1e9 / (80MHz / 2)
|
||||||
|
return timeUnit(ns / 25)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||||
|
// See nanosecondsToTicks.
|
||||||
|
return int64(ticks) * 25
|
||||||
|
}
|
||||||
|
|
||||||
|
// sleepTicks busy-waits until the given number of ticks have passed.
|
||||||
|
func sleepTicks(d timeUnit) {
|
||||||
|
sleepUntil := ticks() + d
|
||||||
|
for ticks() < sleepUntil {
|
||||||
|
// TODO: suspend the CPU to not burn power here unnecessarily.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func abort() {
|
||||||
|
for {
|
||||||
|
device.Asm("waiti 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
// +build esp8266
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device"
|
||||||
|
"device/esp"
|
||||||
|
"machine"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type timeUnit int64
|
||||||
|
|
||||||
|
var currentTime timeUnit = 0
|
||||||
|
|
||||||
|
func putchar(c byte) {
|
||||||
|
machine.UART0.WriteByte(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to the internal control bus (using I2C?).
|
||||||
|
// Signature found here:
|
||||||
|
// https://github.com/espressif/ESP8266_RTOS_SDK/blob/14171de0/components/esp8266/include/esp8266/rom_functions.h#L54
|
||||||
|
//export rom_i2c_writeReg
|
||||||
|
func rom_i2c_writeReg(block, host_id, reg_add, data uint8)
|
||||||
|
|
||||||
|
func postinit() {}
|
||||||
|
|
||||||
|
//export main
|
||||||
|
func main() {
|
||||||
|
// Clear .bss section. .data has already been loaded by the ROM bootloader.
|
||||||
|
preinit()
|
||||||
|
|
||||||
|
// Initialize PLL.
|
||||||
|
// I'm not quite sure what this magic incantation means, but it does set the
|
||||||
|
// esp8266 to the right clock speed. Without this, it is running too slow.
|
||||||
|
rom_i2c_writeReg(103, 4, 1, 136)
|
||||||
|
rom_i2c_writeReg(103, 4, 2, 145)
|
||||||
|
|
||||||
|
// Initialize UART.
|
||||||
|
machine.UART0.Configure(machine.UARTConfig{})
|
||||||
|
|
||||||
|
// Initialize timer. Bits:
|
||||||
|
// ENABLE: timer enable
|
||||||
|
// ROLLOVER: automatically reload when hitting 0
|
||||||
|
// PRESCALE: divide by 256
|
||||||
|
esp.TIMER.FRC1_CTRL.Set(
|
||||||
|
esp.TIMER_FRC1_CTRL_TIMER_ENABLE | esp.TIMER_FRC1_CTRL_ROLLOVER | esp.TIMER_FRC1_CTRL_PRESCALE_DIVIDER_DEVIDED_BY_256<<esp.TIMER_FRC1_CTRL_PRESCALE_DIVIDER_Pos)
|
||||||
|
esp.TIMER.FRC1_LOAD.Set(0x3fffff) // set all 22 bits to 1
|
||||||
|
esp.TIMER.FRC1_COUNT.Set(0x3fffff) // set all 22 bits to 1
|
||||||
|
|
||||||
|
run()
|
||||||
|
|
||||||
|
// Fallback: if main ever returns, hang the CPU.
|
||||||
|
abort()
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:extern _sbss
|
||||||
|
var _sbss [0]byte
|
||||||
|
|
||||||
|
//go:extern _ebss
|
||||||
|
var _ebss [0]byte
|
||||||
|
|
||||||
|
func preinit() {
|
||||||
|
// Initialize .bss: zero-initialized global variables.
|
||||||
|
ptr := unsafe.Pointer(&_sbss)
|
||||||
|
for ptr != unsafe.Pointer(&_ebss) {
|
||||||
|
*(*uint32)(ptr) = 0
|
||||||
|
ptr = unsafe.Pointer(uintptr(ptr) + 4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ticks() timeUnit {
|
||||||
|
// Get the counter value of the timer. It is 22 bits and starts with all
|
||||||
|
// ones (0x3fffff). To make it easier to work with, let it count upwards.
|
||||||
|
count := 0x3fffff - esp.TIMER.FRC1_COUNT.Get()
|
||||||
|
|
||||||
|
// Replace the lowest 22 bits of the current time with the counter.
|
||||||
|
newTime := (currentTime &^ 0x3fffff) | timeUnit(count)
|
||||||
|
|
||||||
|
// If there was an overflow, the new time will be lower than the current
|
||||||
|
// time, so will need to add (1<<22).
|
||||||
|
if newTime < currentTime {
|
||||||
|
newTime += 0x400000
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the timestamp for the next call to ticks().
|
||||||
|
currentTime = newTime
|
||||||
|
|
||||||
|
return currentTime
|
||||||
|
}
|
||||||
|
|
||||||
|
const asyncScheduler = false
|
||||||
|
|
||||||
|
const tickNanos = 3200 // time.Second / (80MHz / 256)
|
||||||
|
|
||||||
|
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||||
|
return int64(ticks) * tickNanos
|
||||||
|
}
|
||||||
|
|
||||||
|
func nanosecondsToTicks(ns int64) timeUnit {
|
||||||
|
return timeUnit(ns / tickNanos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sleepTicks busy-waits until the given number of ticks have passed.
|
||||||
|
func sleepTicks(d timeUnit) {
|
||||||
|
sleepUntil := ticks() + d
|
||||||
|
for ticks() < sleepUntil {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func abort() {
|
||||||
|
for {
|
||||||
|
device.Asm("waiti 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
type timeUnit int64
|
type timeUnit int64
|
||||||
|
|
||||||
const asyncScheduler = false
|
const asyncScheduler = false
|
||||||
@@ -40,21 +42,18 @@ func ticks() timeUnit {
|
|||||||
return timeUnit(ticksToNanoseconds(timeUnit(getArmSystemTick())))
|
return timeUnit(ticksToNanoseconds(timeUnit(getArmSystemTick())))
|
||||||
}
|
}
|
||||||
|
|
||||||
var stdoutBuffer = make([]byte, 0, 120)
|
var stdoutBuffer = make([]byte, 120)
|
||||||
|
var position = 0
|
||||||
|
|
||||||
func putchar(c byte) {
|
func putchar(c byte) {
|
||||||
if c == '\n' || len(stdoutBuffer)+1 >= 120 {
|
if c == '\n' || position > len(stdoutBuffer) {
|
||||||
NxOutputString(string(stdoutBuffer))
|
nxOutputString(&stdoutBuffer[0], uint64(position))
|
||||||
stdoutBuffer = stdoutBuffer[:0]
|
position = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
stdoutBuffer = append(stdoutBuffer, c)
|
stdoutBuffer[position] = c
|
||||||
}
|
position++
|
||||||
|
|
||||||
func usleep(usec uint) int {
|
|
||||||
sleepThread(uint64(usec) * 1000)
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func abort() {
|
func abort() {
|
||||||
@@ -63,6 +62,16 @@ func abort() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export write
|
||||||
|
func write(fd int32, buf *byte, count int) int {
|
||||||
|
// TODO: Proper handling write
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
putchar(*buf)
|
||||||
|
buf = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(buf)) + 1))
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
//export sleepThread
|
//export sleepThread
|
||||||
func sleepThread(nanos uint64)
|
func sleepThread(nanos uint64)
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,11 @@
|
|||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "unsafe"
|
|
||||||
|
|
||||||
const heapSize = 0x2000000 * 16 // Default by libnx
|
const heapSize = 0x2000000 * 16 // Default by libnx
|
||||||
|
|
||||||
//go:extern _stack_top
|
|
||||||
var stackTopSymbol [0]byte
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
heapStart = uintptr(0)
|
heapStart = uintptr(0)
|
||||||
heapEnd = uintptr(0)
|
heapEnd = uintptr(0)
|
||||||
stackTop = uintptr(unsafe.Pointer(&stackTopSymbol))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//export setHeapSize
|
//export setHeapSize
|
||||||
@@ -24,7 +18,7 @@ func preinit() {
|
|||||||
setHeapSize(&heapStart, heapSize)
|
setHeapSize(&heapStart, heapSize)
|
||||||
|
|
||||||
if heapStart == 0 {
|
if heapStart == 0 {
|
||||||
panic("failed to allocate heap")
|
runtimePanic("failed to allocate heap")
|
||||||
}
|
}
|
||||||
|
|
||||||
heapEnd = heapStart + heapSize
|
heapEnd = heapStart + heapSize
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/arm"
|
|
||||||
"device/nrf"
|
"device/nrf"
|
||||||
"machine"
|
"machine"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
@@ -114,6 +113,6 @@ func rtc_sleep(ticks uint32) {
|
|||||||
}
|
}
|
||||||
nrf.RTC1.CC[0].Set((nrf.RTC1.COUNTER.Get() + ticks) & 0x00ffffff)
|
nrf.RTC1.CC[0].Set((nrf.RTC1.COUNTER.Get() + ticks) & 0x00ffffff)
|
||||||
for rtc_wakeup.Get() == 0 {
|
for rtc_wakeup.Get() == 0 {
|
||||||
arm.Asm("wfi")
|
waitForEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// +build nrf,!softdevice
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "device/arm"
|
||||||
|
|
||||||
|
func waitForEvents() {
|
||||||
|
arm.Asm("wfe")
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// +build nrf,softdevice
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/arm"
|
||||||
|
"device/nrf"
|
||||||
|
)
|
||||||
|
|
||||||
|
//export sd_app_evt_wait
|
||||||
|
func sd_app_evt_wait()
|
||||||
|
|
||||||
|
func waitForEvents() {
|
||||||
|
// Call into the SoftDevice to sleep. This is necessary here because a
|
||||||
|
// normal wfe will not put the chip in low power mode (it still consumes
|
||||||
|
// 500µA-1mA). It is really needed to call sd_app_evt_wait for low power
|
||||||
|
// consumption.
|
||||||
|
|
||||||
|
// First check whether the SoftDevice is enabled. Unfortunately,
|
||||||
|
// sd_app_evt_wait cannot be called when the SoftDevice is not enabled.
|
||||||
|
var enabled uint8
|
||||||
|
arm.SVCall1(0x12, &enabled) // sd_softdevice_is_enabled
|
||||||
|
|
||||||
|
if enabled != 0 {
|
||||||
|
// Now pick the appropriate SVCall number. Hopefully they won't change
|
||||||
|
// in the future with a different SoftDevice version.
|
||||||
|
if nrf.DEVICE == "nrf51" {
|
||||||
|
// sd_app_evt_wait: SOC_SVC_BASE_NOT_AVAILABLE + 29
|
||||||
|
arm.SVCall0(0x2B + 29)
|
||||||
|
} else if nrf.DEVICE == "nrf52" || nrf.DEVICE == "nrf52840" {
|
||||||
|
// sd_app_evt_wait: SOC_SVC_BASE_NOT_AVAILABLE + 21
|
||||||
|
arm.SVCall0(0x2C + 21)
|
||||||
|
} else {
|
||||||
|
sd_app_evt_wait()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// SoftDevice is disabled so we can sleep normally.
|
||||||
|
arm.Asm("wfe")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -265,3 +265,7 @@ func abort() {
|
|||||||
machine.PollUART(&machine.UART2)
|
machine.PollUART(&machine.UART2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func waitForEvents() {
|
||||||
|
arm.Asm("wfe")
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
import "device/arm"
|
||||||
|
|
||||||
type timeUnit int64
|
type timeUnit int64
|
||||||
|
|
||||||
func postinit() {}
|
func postinit() {}
|
||||||
@@ -12,3 +14,7 @@ func main() {
|
|||||||
run()
|
run()
|
||||||
abort()
|
abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func waitForEvents() {
|
||||||
|
arm.Asm("wfe")
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,248 @@
|
|||||||
|
// +build stm32f405
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/arm"
|
||||||
|
"device/stm32"
|
||||||
|
"machine"
|
||||||
|
"runtime/interrupt"
|
||||||
|
"runtime/volatile"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initOSC() // configure oscillators
|
||||||
|
initCLK() // configure CPU, AHB, and APB bus clocks
|
||||||
|
initTIM() // configure timers
|
||||||
|
initCOM() // configure serial comm interfaces
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// +----------------------+
|
||||||
|
// | Clock Settings |
|
||||||
|
// +-------------+--------+
|
||||||
|
// | HSE | 12mhz |
|
||||||
|
// | SYSCLK | 168mhz |
|
||||||
|
// | HCLK | 168mhz |
|
||||||
|
// | APB1(PCLK1) | 42mhz |
|
||||||
|
// | APB2(PCLK2) | 84mhz |
|
||||||
|
// +-------------+--------+
|
||||||
|
HCLK_FREQ_HZ = 168000000
|
||||||
|
PCLK1_FREQ_HZ = HCLK_FREQ_HZ / 4
|
||||||
|
PCLK2_FREQ_HZ = HCLK_FREQ_HZ / 2
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PWR_SCALE1 = 1 << stm32.PWR_CSR_VOSRDY_Pos // max value of HCLK = 168 MHz
|
||||||
|
PWR_SCALE2 = 0 // max value of HCLK = 144 MHz
|
||||||
|
|
||||||
|
PLL_SRC_HSE = 1 << stm32.RCC_PLLCFGR_PLLSRC_Pos // use HSE for PLL and PLLI2S
|
||||||
|
PLL_SRC_HSI = 0 // use HSI for PLL and PLLI2S
|
||||||
|
|
||||||
|
PLL_DIV_M = 6 << stm32.RCC_PLLCFGR_PLLM0_Pos
|
||||||
|
PLL_MLT_N = 168 << stm32.RCC_PLLCFGR_PLLN0_Pos
|
||||||
|
PLL_DIV_P = ((2 >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP0_Pos
|
||||||
|
PLL_DIV_Q = 7 << stm32.RCC_PLLCFGR_PLLQ0_Pos
|
||||||
|
|
||||||
|
SYSCLK_SRC_PLL = 2 << stm32.RCC_CFGR_SW0_Pos
|
||||||
|
SYSCLK_STAT_PLL = 2 << stm32.RCC_CFGR_SWS0_Pos
|
||||||
|
|
||||||
|
RCC_DIV_PCLK1 = 5 << stm32.RCC_CFGR_PPRE1_Pos // HCLK / 4
|
||||||
|
RCC_DIV_PCLK2 = 4 << stm32.RCC_CFGR_PPRE2_Pos // HCLK / 2
|
||||||
|
RCC_DIV_HCLK = 0 << stm32.RCC_CFGR_HPRE_Pos // SYSCLK / 1
|
||||||
|
|
||||||
|
CLK_CCM_RAM = 1 << 20
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// +-----------------------------------+
|
||||||
|
// | Voltage range = 2.7V - 3.6V |
|
||||||
|
// +----------------+------------------+
|
||||||
|
// | Wait states | System Bus |
|
||||||
|
// | (WS, LATENCY) | HCLK (MHz) |
|
||||||
|
// +----------------+------------------+
|
||||||
|
// | 0 WS, 1 cycle | 0 < HCLK ≤ 30 |
|
||||||
|
// | 1 WS, 2 cycles | 30 < HCLK ≤ 60 |
|
||||||
|
// | 2 WS, 3 cycles | 60 < HCLK ≤ 90 |
|
||||||
|
// | 3 WS, 4 cycles | 90 < HCLK ≤ 120 |
|
||||||
|
// | 4 WS, 5 cycles | 120 < HCLK ≤ 150 |
|
||||||
|
// | 5 WS, 6 cycles | 150 < HCLK ≤ 168 |
|
||||||
|
// +----------------+------------------+
|
||||||
|
FLASH_LATENCY = 5 << stm32.FLASH_ACR_LATENCY_Pos // 5 WS (6 CPU cycles)
|
||||||
|
|
||||||
|
// instruction cache, data cache, and prefetch
|
||||||
|
FLASH_OPTIONS = stm32.FLASH_ACR_ICEN | stm32.FLASH_ACR_DCEN | stm32.FLASH_ACR_PRFTEN
|
||||||
|
)
|
||||||
|
|
||||||
|
func initOSC() {
|
||||||
|
// enable voltage regulator
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
|
||||||
|
stm32.PWR.CR.SetBits(PWR_SCALE1)
|
||||||
|
|
||||||
|
// enable HSE
|
||||||
|
stm32.RCC.CR.Set(stm32.RCC_CR_HSEON)
|
||||||
|
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_HSERDY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the main-PLL configuration parameters cannot be changed once PLL is
|
||||||
|
// enabled, it is recommended to configure PLL before enabling it (selection
|
||||||
|
// of the HSI or HSE oscillator as PLL clock source, and configuration of
|
||||||
|
// division factors M, N, P, and Q).
|
||||||
|
|
||||||
|
// disable PLL and wait for it to reset
|
||||||
|
stm32.RCC.CR.ClearBits(stm32.RCC_CR_PLLON)
|
||||||
|
for stm32.RCC.CR.HasBits(stm32.RCC_CR_PLLRDY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// set HSE as PLL source and configure clock divisors
|
||||||
|
stm32.RCC.PLLCFGR.Set(PLL_SRC_HSE | PLL_DIV_M | PLL_MLT_N | PLL_DIV_P | PLL_DIV_Q)
|
||||||
|
|
||||||
|
// enable PLL and wait for it to sync
|
||||||
|
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON)
|
||||||
|
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_PLLRDY) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initCLK() {
|
||||||
|
// After reset, the CPU clock frequency is 16 MHz and 0 wait state (WS) is
|
||||||
|
// configured in the FLASH_ACR register.
|
||||||
|
//
|
||||||
|
// It is highly recommended to use the following software sequences to tune
|
||||||
|
// the number of wait states needed to access the Flash memory with the CPU
|
||||||
|
// frequency.
|
||||||
|
//
|
||||||
|
// 1. Program the new number of wait states to the LATENCY bits in the
|
||||||
|
// FLASH_ACR register
|
||||||
|
// 2. Check that the new number of wait states is taken into account to access
|
||||||
|
// the Flash memory by reading the FLASH_ACR register
|
||||||
|
// 3. Modify the CPU clock source by writing the SW bits in the RCC_CFGR
|
||||||
|
// register
|
||||||
|
// 4. If needed, modify the CPU clock prescaler by writing the HPRE bits in
|
||||||
|
// RCC_CFGR
|
||||||
|
// 5. Check that the new CPU clock source or/and the new CPU clock prescaler
|
||||||
|
// value is/are taken into account by reading the clock source status (SWS
|
||||||
|
// bits) or/and the AHB prescaler value (HPRE bits), respectively, in the
|
||||||
|
// RCC_CFGR register.
|
||||||
|
|
||||||
|
// configure instruction/data caching, prefetch, and flash access wait states
|
||||||
|
stm32.FLASH.ACR.Set(FLASH_OPTIONS | FLASH_LATENCY)
|
||||||
|
for !stm32.FLASH.ACR.HasBits(FLASH_LATENCY) { // verify new wait states
|
||||||
|
}
|
||||||
|
|
||||||
|
// After a system reset, the HSI oscillator is selected as the system clock.
|
||||||
|
// When a clock source is used directly or through PLL as the system clock, it
|
||||||
|
// is not possible to stop it.
|
||||||
|
//
|
||||||
|
// A switch from one clock source to another occurs only if the target clock
|
||||||
|
// source is ready (clock stable after startup delay or PLL locked). If a
|
||||||
|
// clock source that is not yet ready is selected, the switch occurs when the
|
||||||
|
// clock source is ready. Status bits in the RCC clock control register
|
||||||
|
// (RCC_CR) indicate which clock(s) is (are) ready and which clock is
|
||||||
|
// currently used as the system clock.
|
||||||
|
|
||||||
|
// set CPU clock source to PLL
|
||||||
|
stm32.RCC.CFGR.SetBits(SYSCLK_SRC_PLL)
|
||||||
|
|
||||||
|
// update PCKL1/2 and HCLK divisors
|
||||||
|
stm32.RCC.CFGR.SetBits(RCC_DIV_PCLK1 | RCC_DIV_PCLK2 | RCC_DIV_HCLK)
|
||||||
|
|
||||||
|
// verify system clock source is ready
|
||||||
|
for !stm32.RCC.CFGR.HasBits(SYSCLK_STAT_PLL) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable the CCM RAM clock
|
||||||
|
stm32.RCC.AHB1ENR.SetBits(CLK_CCM_RAM)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initTIM() {
|
||||||
|
// enable sleep counter (TIM3)
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
|
||||||
|
|
||||||
|
tim3 := interrupt.New(stm32.IRQ_TIM3, handleTIM3)
|
||||||
|
tim3.SetPriority(0xC3)
|
||||||
|
tim3.Enable()
|
||||||
|
|
||||||
|
// enable tick counter (TIM7)
|
||||||
|
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM7EN)
|
||||||
|
|
||||||
|
stm32.TIM7.PSC.Set((PCLK1_FREQ_HZ*2)/10000 - 1) // 84mhz to 10khz(0.1ms)
|
||||||
|
stm32.TIM7.ARR.Set(10 - 1) // interrupt per 1ms
|
||||||
|
|
||||||
|
stm32.TIM7.DIER.SetBits(stm32.TIM_DIER_UIE) // enable interrupt
|
||||||
|
stm32.TIM7.CR1.SetBits(stm32.TIM_CR1_CEN) // enable timer
|
||||||
|
|
||||||
|
tim7 := interrupt.New(stm32.IRQ_TIM7, handleTIM7)
|
||||||
|
tim7.SetPriority(0xC1)
|
||||||
|
tim7.Enable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func initCOM() {
|
||||||
|
if machine.NUM_UART_INTERFACES > 0 {
|
||||||
|
machine.UART0.Configure(machine.UARTConfig{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// tick in milliseconds
|
||||||
|
tickCount timeUnit
|
||||||
|
timerWakeup volatile.Register8
|
||||||
|
)
|
||||||
|
|
||||||
|
const asyncScheduler = false
|
||||||
|
|
||||||
|
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||||
|
return int64(ticks) * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
func nanosecondsToTicks(ns int64) timeUnit {
|
||||||
|
return timeUnit(ns / 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sleepTicks should sleep for specific number of microseconds.
|
||||||
|
func sleepTicks(d timeUnit) {
|
||||||
|
timerSleep(uint32(d))
|
||||||
|
}
|
||||||
|
|
||||||
|
// number of ticks (microseconds) since start.
|
||||||
|
func ticks() timeUnit {
|
||||||
|
return tickCount * 1000 // milliseconds to microseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
// ticks are in microseconds
|
||||||
|
func timerSleep(ticks uint32) {
|
||||||
|
timerWakeup.Set(0)
|
||||||
|
|
||||||
|
stm32.TIM3.PSC.Set((PCLK1_FREQ_HZ*2)/10000 - 1) // 8399
|
||||||
|
arr := (ticks / 100) - 1 // microseconds to 0.1 ms
|
||||||
|
if arr == 0 {
|
||||||
|
arr = 1 // avoid blocking
|
||||||
|
}
|
||||||
|
stm32.TIM3.ARR.Set(arr)
|
||||||
|
|
||||||
|
stm32.TIM3.DIER.SetBits(stm32.TIM_DIER_UIE) // enable interrupt
|
||||||
|
stm32.TIM3.CR1.SetBits(stm32.TIM_CR1_CEN) // enable the timer
|
||||||
|
|
||||||
|
// wait for timer
|
||||||
|
for timerWakeup.Get() == 0 {
|
||||||
|
arm.Asm("wfi")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleTIM3(interrupt.Interrupt) {
|
||||||
|
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||||
|
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN) // disable the timer
|
||||||
|
stm32.TIM3.SR.ClearBits(stm32.TIM_SR_UIF) // clear the update flag
|
||||||
|
timerWakeup.Set(1) // flag timer ISR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleTIM7(interrupt.Interrupt) {
|
||||||
|
if stm32.TIM7.SR.HasBits(stm32.TIM_SR_UIF) {
|
||||||
|
stm32.TIM7.SR.ClearBits(stm32.TIM_SR_UIF) // clear the update flag
|
||||||
|
tickCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func putchar(c byte) {
|
||||||
|
machine.UART0.WriteByte(c)
|
||||||
|
}
|
||||||
@@ -41,11 +41,7 @@ tinygo_getSystemStackPointer:
|
|||||||
.cfi_endproc
|
.cfi_endproc
|
||||||
.size tinygo_getSystemStackPointer, .-tinygo_getSystemStackPointer
|
.size tinygo_getSystemStackPointer, .-tinygo_getSystemStackPointer
|
||||||
|
|
||||||
|
.section .text.tinygo_switchToScheduler
|
||||||
// switchToScheduler and switchToTask are also in the same section, to make sure
|
|
||||||
// relative branches work.
|
|
||||||
.section .text.tinygo_swapTask
|
|
||||||
|
|
||||||
.global tinygo_switchToScheduler
|
.global tinygo_switchToScheduler
|
||||||
.type tinygo_switchToScheduler, %function
|
.type tinygo_switchToScheduler, %function
|
||||||
tinygo_switchToScheduler:
|
tinygo_switchToScheduler:
|
||||||
@@ -62,6 +58,7 @@ tinygo_switchToScheduler:
|
|||||||
.cfi_endproc
|
.cfi_endproc
|
||||||
.size tinygo_switchToScheduler, .-tinygo_switchToScheduler
|
.size tinygo_switchToScheduler, .-tinygo_switchToScheduler
|
||||||
|
|
||||||
|
.section .text.tinygo_switchToTask
|
||||||
.global tinygo_switchToTask
|
.global tinygo_switchToTask
|
||||||
.type tinygo_switchToTask, %function
|
.type tinygo_switchToTask, %function
|
||||||
tinygo_switchToTask:
|
tinygo_switchToTask:
|
||||||
@@ -72,11 +69,11 @@ tinygo_switchToTask:
|
|||||||
// and then we can invoke swapTask.
|
// and then we can invoke swapTask.
|
||||||
msr PSP, r0
|
msr PSP, r0
|
||||||
|
|
||||||
// Continue executing in the swapTask function, which swaps the stack
|
b.n tinygo_swapTask
|
||||||
// pointer.
|
|
||||||
.cfi_endproc
|
.cfi_endproc
|
||||||
.size tinygo_switchToTask, .-tinygo_switchToTask
|
.size tinygo_switchToTask, .-tinygo_switchToTask
|
||||||
|
|
||||||
|
.section .text.tinygo_swapTask
|
||||||
.global tinygo_swapTask
|
.global tinygo_swapTask
|
||||||
.type tinygo_swapTask, %function
|
.type tinygo_swapTask, %function
|
||||||
tinygo_swapTask:
|
tinygo_swapTask:
|
||||||
|
|||||||
@@ -15,3 +15,8 @@ type B struct {
|
|||||||
common
|
common
|
||||||
N int
|
N int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InternalBenchmark struct {
|
||||||
|
Name string
|
||||||
|
F func(b *B)
|
||||||
|
}
|
||||||
|
|||||||
+18
-7
@@ -154,18 +154,16 @@ func (c *common) Skipped() bool {
|
|||||||
return c.skipped
|
return c.skipped
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestToCall is a reference to a test that should be called during a test suite run.
|
// InternalTest is a reference to a test that should be called during a test suite run.
|
||||||
type TestToCall struct {
|
type InternalTest struct {
|
||||||
// Name of the test to call.
|
|
||||||
Name string
|
Name string
|
||||||
// Function reference to the test.
|
F func(*T)
|
||||||
Func func(*T)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// M is a test suite.
|
// M is a test suite.
|
||||||
type M struct {
|
type M struct {
|
||||||
// tests is a list of the test names to execute
|
// tests is a list of the test names to execute
|
||||||
Tests []TestToCall
|
Tests []InternalTest
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the test suite.
|
// Run the test suite.
|
||||||
@@ -180,7 +178,7 @@ func (m *M) Run() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("=== RUN %s\n", test.Name)
|
fmt.Printf("=== RUN %s\n", test.Name)
|
||||||
test.Func(t)
|
test.F(t)
|
||||||
|
|
||||||
if t.failed {
|
if t.failed {
|
||||||
fmt.Printf("--- FAIL: %s\n", test.Name)
|
fmt.Printf("--- FAIL: %s\n", test.Name)
|
||||||
@@ -204,3 +202,16 @@ func (m *M) Run() int {
|
|||||||
func TestMain(m *M) {
|
func TestMain(m *M) {
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
|
||||||
|
return &M{
|
||||||
|
Tests: tests,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type InternalExample struct {
|
||||||
|
Name string
|
||||||
|
F func()
|
||||||
|
Output string
|
||||||
|
Unordered bool
|
||||||
|
}
|
||||||
|
|||||||
@@ -207,6 +207,16 @@ func (fi *frameInfo) exec(bytecode []byte) ([]frameInfoLine, error) {
|
|||||||
switch lowBits {
|
switch lowBits {
|
||||||
case 0: // DW_CFA_nop
|
case 0: // DW_CFA_nop
|
||||||
// no operation
|
// no operation
|
||||||
|
case 0x02: // DW_CFA_advance_loc1
|
||||||
|
// Very similar to DW_CFA_advance_loc but allows for a slightly
|
||||||
|
// larger range.
|
||||||
|
offset, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fi.loc += uint64(offset) * fi.cie.codeAlignmentFactor
|
||||||
|
entries = append(entries, fi.newLine())
|
||||||
|
// TODO: DW_CFA_advance_loc2 etc
|
||||||
case 0x07: // DW_CFA_undefined
|
case 0x07: // DW_CFA_undefined
|
||||||
// Marks a single register as undefined. This is used to stop
|
// Marks a single register as undefined. This is used to stop
|
||||||
// unwinding in tinygo_startTask using:
|
// unwinding in tinygo_startTask using:
|
||||||
|
|||||||
+24
-5
@@ -14,19 +14,38 @@ import (
|
|||||||
// set to true to print information useful for debugging
|
// set to true to print information useful for debugging
|
||||||
const debugPrint = false
|
const debugPrint = false
|
||||||
|
|
||||||
type sizeType uint8
|
// SizeType indicates whether a stack or frame size could be determined and if
|
||||||
|
// not, why.
|
||||||
|
type SizeType uint8
|
||||||
|
|
||||||
// Results after trying to determine the stack size of a function in the call
|
// Results after trying to determine the stack size of a function in the call
|
||||||
// graph. The goal is to find a maximum (bounded) stack size, but sometimes this
|
// graph. The goal is to find a maximum (bounded) stack size, but sometimes this
|
||||||
// is not possible for some reasons such as recursion or indirect calls.
|
// is not possible for some reasons such as recursion or indirect calls.
|
||||||
const (
|
const (
|
||||||
Undefined sizeType = iota // not yet calculated
|
Undefined SizeType = iota // not yet calculated
|
||||||
Unknown // child has unknown stack size
|
Unknown // child has unknown stack size
|
||||||
Bounded // stack size is fixed at compile time (no recursion etc)
|
Bounded // stack size is fixed at compile time (no recursion etc)
|
||||||
Recursive
|
Recursive
|
||||||
IndirectCall
|
IndirectCall
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (s SizeType) String() string {
|
||||||
|
switch s {
|
||||||
|
case Undefined:
|
||||||
|
return "undefined"
|
||||||
|
case Unknown:
|
||||||
|
return "unknown"
|
||||||
|
case Bounded:
|
||||||
|
return "bounded"
|
||||||
|
case Recursive:
|
||||||
|
return "recursive"
|
||||||
|
case IndirectCall:
|
||||||
|
return "indirect call"
|
||||||
|
default:
|
||||||
|
return "<?>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CallNode is a node in the call graph (that is, a function). Because this is
|
// CallNode is a node in the call graph (that is, a function). Because this is
|
||||||
// determined after linking, there may be multiple names for a single function
|
// determined after linking, there may be multiple names for a single function
|
||||||
// (due to aliases). It is also possible multiple functions have the same name
|
// (due to aliases). It is also possible multiple functions have the same name
|
||||||
@@ -37,9 +56,9 @@ type CallNode struct {
|
|||||||
Size uint64 // symbol size, in bytes
|
Size uint64 // symbol size, in bytes
|
||||||
Children []*CallNode // functions this function calls
|
Children []*CallNode // functions this function calls
|
||||||
FrameSize uint64 // frame size, if FrameSizeType is Bounded
|
FrameSize uint64 // frame size, if FrameSizeType is Bounded
|
||||||
FrameSizeType sizeType // can be Undefined or Bounded
|
FrameSizeType SizeType // can be Undefined or Bounded
|
||||||
stackSize uint64
|
stackSize uint64
|
||||||
stackSizeType sizeType
|
stackSizeType SizeType
|
||||||
missingFrameInfo *CallNode // the child function that is the cause for not being able to determine the stack size
|
missingFrameInfo *CallNode // the child function that is the cause for not being able to determine the stack size
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +255,7 @@ func findSymbol(symbolList []*CallNode, address uint64) *CallNode {
|
|||||||
// returns the maximum stack size, whether this size can be known at compile
|
// returns the maximum stack size, whether this size can be known at compile
|
||||||
// time and the call node responsible for failing to determine the maximum stack
|
// time and the call node responsible for failing to determine the maximum stack
|
||||||
// usage. The stack size is only valid if sizeType is Bounded.
|
// usage. The stack size is only valid if sizeType is Bounded.
|
||||||
func (node *CallNode) StackSize() (uint64, sizeType, *CallNode) {
|
func (node *CallNode) StackSize() (uint64, SizeType, *CallNode) {
|
||||||
if node.stackSizeType == Undefined {
|
if node.stackSizeType == Undefined {
|
||||||
node.determineStackSize(make(map[*CallNode]struct{}))
|
node.determineStackSize(make(map[*CallNode]struct{}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,5 @@
|
|||||||
"ldflags": [
|
"ldflags": [
|
||||||
"-Wl,--defsym=_bootloader_size=8192"
|
"-Wl,--defsym=_bootloader_size=8192"
|
||||||
],
|
],
|
||||||
"flash-command":"avrdude -c wiring -b 115200 -p atmega2560 -P {port} -U flash:w:{hex} -v -D"
|
"flash-command":"avrdude -c wiring -b 115200 -p atmega2560 -P {port} -U flash:w:{hex}:i -v -D"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,6 @@
|
|||||||
"-Wl,--defsym=_bootloader_size=512",
|
"-Wl,--defsym=_bootloader_size=512",
|
||||||
"-Wl,--defsym=_stack_size=512"
|
"-Wl,--defsym=_stack_size=512"
|
||||||
],
|
],
|
||||||
"flash-command": "avrdude -c arduino -p atmega328p -b 57600 -P {port} -U flash:w:{hex}:i"
|
"flash-command": "avrdude -c arduino -p atmega328p -b 57600 -P {port} -U flash:w:{hex}:i",
|
||||||
|
"emulator": ["simavr", "-m", "atmega328p", "-f", "16000000"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"inherits": ["atsamd21g18a"],
|
||||||
|
"build-tags": ["sam", "atsamd21g18a", "arduino_zero"],
|
||||||
|
"flash-command": "bossac -i -e -w -v -R -U --port={port} --offset=0x2000 {bin}",
|
||||||
|
"flash-1200-bps-reset": "true"
|
||||||
|
}
|
||||||
@@ -5,5 +5,6 @@
|
|||||||
"-Wl,--defsym=_bootloader_size=512",
|
"-Wl,--defsym=_bootloader_size=512",
|
||||||
"-Wl,--defsym=_stack_size=512"
|
"-Wl,--defsym=_stack_size=512"
|
||||||
],
|
],
|
||||||
"flash-command": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}:i"
|
"flash-command": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}:i",
|
||||||
|
"emulator": ["simavr", "-m", "atmega328p", "-f", "16000000"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ SECTIONS
|
|||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
} >FLASH_TEXT
|
} >FLASH_TEXT
|
||||||
|
|
||||||
|
.tinygo_stacksizes :
|
||||||
|
{
|
||||||
|
*(.tinygo_stacksizes)
|
||||||
|
} > FLASH_TEXT
|
||||||
|
|
||||||
/* Put the stack at the bottom of RAM, so that the application will
|
/* Put the stack at the bottom of RAM, so that the application will
|
||||||
* crash on stack overflow instead of silently corrupting memory.
|
* crash on stack overflow instead of silently corrupting memory.
|
||||||
* See: http://blog.japaric.io/stack-overflow-protection/ */
|
* See: http://blog.japaric.io/stack-overflow-protection/ */
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user