mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-10 14:03:39 +00:00
Compare commits
77 Commits
debug-flag
...
v0.20.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 1573826005 | |||
| ecd8c2d902 | |||
| a116fd0dc6 | |||
| a590d791bd | |||
| cb147b9475 | |||
| c830f878c6 | |||
| c7c874a487 | |||
| c528a168ee | |||
| 88b9c27dbf | |||
| 37ee4bea40 | |||
| ca4f251050 | |||
| 1e92e5f6c6 | |||
| 3eb9dca695 | |||
| 53794cf339 | |||
| da6c14481f | |||
| fb5516604d | |||
| e9f1ed701a | |||
| 6315db21f7 | |||
| 485a9284e7 | |||
| 602d3d7c78 | |||
| 409688e67a | |||
| d348db4a0d | |||
| 95ab7cb8d1 | |||
| 32de906f6d | |||
| eaab05fc43 | |||
| 4d1945b467 | |||
| fd9422d218 | |||
| bbbe7d43ce | |||
| b3f1dacbb9 | |||
| 5fa1e7163a | |||
| 4b1f92600f | |||
| ca39bc9f35 | |||
| f985f2c376 | |||
| f0936ffccb | |||
| 97d48e5c02 | |||
| a7c53cce06 | |||
| 2d224ae049 | |||
| 6c6fea5387 | |||
| 98bd947817 | |||
| 255f35671d | |||
| 8e88e560a1 | |||
| d45497691f | |||
| ad73986070 | |||
| 931f87f96a | |||
| 192a32f8d9 | |||
| 972f4254eb | |||
| 7d83e2ee5c | |||
| 0f2f73be53 | |||
| a2cc5715ba | |||
| 59d53182bb | |||
| 25c7bfd404 | |||
| 04f520040e | |||
| f57e9622fd | |||
| c25a7cc747 | |||
| 5e5ce98d42 | |||
| a3c4421f39 | |||
| 58565b42cc | |||
| ca7c849da3 | |||
| 6c1301688b | |||
| d05103668f | |||
| 4f7b23c2b7 | |||
| cfae2d4f9a | |||
| 55789fd2c2 | |||
| 478c592b13 | |||
| ab47cea055 | |||
| 98e70c9b19 | |||
| 7434e5a2c7 | |||
| 65c1978965 | |||
| e834d78871 | |||
| 03481789b0 | |||
| 73cf187552 | |||
| b40703e986 | |||
| 00ea0b1d57 | |||
| efa0410075 | |||
| 607d824211 | |||
| 8cc7c6d572 | |||
| cdba4fa8cc |
+7
-21
@@ -286,8 +286,8 @@ commands:
|
|||||||
- run:
|
- run:
|
||||||
name: "Install dependencies"
|
name: "Install dependencies"
|
||||||
command: |
|
command: |
|
||||||
curl https://dl.google.com/go/go1.16.darwin-amd64.tar.gz -o go1.16.darwin-amd64.tar.gz
|
curl https://dl.google.com/go/go1.17.darwin-amd64.tar.gz -o go1.17.darwin-amd64.tar.gz
|
||||||
sudo tar -C /usr/local -xzf go1.16.darwin-amd64.tar.gz
|
sudo tar -C /usr/local -xzf go1.17.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:
|
- install-xtensa-toolchain:
|
||||||
@@ -365,24 +365,12 @@ commands:
|
|||||||
- /go/pkg/mod
|
- /go/pkg/mod
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-llvm10-go113:
|
test-llvm10-go115:
|
||||||
docker:
|
|
||||||
- image: circleci/golang:1.13-buster
|
|
||||||
steps:
|
|
||||||
- test-linux:
|
|
||||||
llvm: "10"
|
|
||||||
test-llvm10-go114:
|
|
||||||
docker:
|
|
||||||
- image: circleci/golang:1.14-buster
|
|
||||||
steps:
|
|
||||||
- test-linux:
|
|
||||||
llvm: "10"
|
|
||||||
test-llvm11-go115:
|
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.15-buster
|
- image: circleci/golang:1.15-buster
|
||||||
steps:
|
steps:
|
||||||
- test-linux:
|
- test-linux:
|
||||||
llvm: "11"
|
llvm: "10"
|
||||||
test-llvm11-go116:
|
test-llvm11-go116:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.16-buster
|
- image: circleci/golang:1.16-buster
|
||||||
@@ -391,12 +379,12 @@ jobs:
|
|||||||
llvm: "11"
|
llvm: "11"
|
||||||
assert-test-linux:
|
assert-test-linux:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.14-stretch
|
- image: circleci/golang:1.17-stretch
|
||||||
steps:
|
steps:
|
||||||
- assert-test-linux
|
- assert-test-linux
|
||||||
build-linux:
|
build-linux:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.14-stretch
|
- image: circleci/golang:1.17-stretch
|
||||||
steps:
|
steps:
|
||||||
- build-linux
|
- build-linux
|
||||||
build-macos:
|
build-macos:
|
||||||
@@ -410,9 +398,7 @@ jobs:
|
|||||||
workflows:
|
workflows:
|
||||||
test-all:
|
test-all:
|
||||||
jobs:
|
jobs:
|
||||||
- test-llvm10-go113
|
- test-llvm10-go115
|
||||||
- test-llvm10-go114
|
|
||||||
- test-llvm11-go115
|
|
||||||
- test-llvm11-go116
|
- test-llvm11-go116
|
||||||
- build-linux
|
- build-linux
|
||||||
- build-macos
|
- build-macos
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
build/
|
||||||
|
llvm-*/
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
name: CI for tinygo-dev docker container
|
name: CI for tinygo-dev docker container
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ dev ]
|
branches: [ dev, fix-docker-llvm-build ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push_to_registry:
|
push_to_registry:
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ different guide:
|
|||||||
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
|
||||||
build tools to be built. Go is of course necessary to build TinyGo itself.
|
build tools to be built. Go is of course necessary to build TinyGo itself.
|
||||||
|
|
||||||
* Go (1.13+)
|
* Go (1.15+)
|
||||||
* Standard build tools (gcc/clang)
|
* Standard build tools (gcc/clang)
|
||||||
* git
|
* git
|
||||||
* CMake
|
* CMake
|
||||||
|
|||||||
@@ -1,3 +1,73 @@
|
|||||||
|
0.20.0
|
||||||
|
---
|
||||||
|
|
||||||
|
* **command line**
|
||||||
|
- add support for Go 1.17
|
||||||
|
- improve Go version detection
|
||||||
|
- add support for the Black Magic Probe (BMP)
|
||||||
|
- add a flag for creating cpu profiles
|
||||||
|
* **compiler**
|
||||||
|
- `builder:` list libraries at the end of the linker command
|
||||||
|
- `builder:` strip debug information at link time instead of at compile time
|
||||||
|
- `builder:` add missing error check for `ioutil.TempFile()`
|
||||||
|
- `builder:` simplify running of jobs
|
||||||
|
- `compiler:` move LLVM math builtin support into the compiler
|
||||||
|
- `compiler:` move math aliases from the runtime to the compiler
|
||||||
|
- `compiler:` add aliases for many hashing packages
|
||||||
|
- `compiler:` add `*ssa.MakeSlice` bounds tests
|
||||||
|
- `compiler:` fix max possible slice
|
||||||
|
- `compiler:` add support for new language features of Go 1.17
|
||||||
|
- `compiler:` fix equally named structs in different scopes
|
||||||
|
- `compiler:` avoid zero-sized alloca in channel operations
|
||||||
|
- `interp:` don't ignore array indices for untyped objects
|
||||||
|
- `interp:` keep reverted package initializers in order
|
||||||
|
- `interp:` fix bug in compiler-time/run-time package initializers
|
||||||
|
- `loader:` fix panic in CGo files with syntax errors
|
||||||
|
- `transform:` improve GC stack slot pass to work around a bug
|
||||||
|
* **standard library**
|
||||||
|
- `crypto/rand`: switch to `arc4random_buf`
|
||||||
|
- `math:` fix `math.Max` and `math.Min`
|
||||||
|
- `math/big`: fix undefined symbols error
|
||||||
|
- `net:` add MAC address implementation
|
||||||
|
- `os:` implement `os.Executable`
|
||||||
|
- `os:` add `SEEK_SET`, `SEEK_CUR`, and `SEEK_END`
|
||||||
|
- `reflect:` add StructField.IsExported method
|
||||||
|
- `runtime:` reset heapptr to heapStart after preinit()
|
||||||
|
- `runtime:` add `subsections_via_symbols` to assembly files on darwin
|
||||||
|
- `testing:` add subset implementation of Benchmark
|
||||||
|
- `testing:` test testing package using `tinygo test`
|
||||||
|
- `testing:` add support for the `-test.v` flag
|
||||||
|
* **targets**
|
||||||
|
- `386:` bump minimum requirement to the Pentium 4
|
||||||
|
- `arm:` switch to Thumb instruction set on ARM
|
||||||
|
- `atsamd:` fix copy-paste error for atsamd21/51 calibTrim block
|
||||||
|
- `baremetal`,`wasm`: support command line params and environment variables
|
||||||
|
- `cortexm:` fix stack overflow because of unaligned stacks
|
||||||
|
- `esp32c3:` add support for the ESP32-C3 from Espressif
|
||||||
|
- `nrf52840:` fix ram size
|
||||||
|
- `nxpmk66f18:` fix a suspicious bitwise operation
|
||||||
|
- `rp2040:` add SPI support
|
||||||
|
- `rp2040:` add I2C support
|
||||||
|
- `rp2040:` add PWM implementation
|
||||||
|
- `rp2040:` add openocd configuration
|
||||||
|
- `stm32:` add support for PortMask* functions for WS2812 support
|
||||||
|
- `unix:` fix time base for time.Now()
|
||||||
|
- `unix:` check for mmap error and act accordingly
|
||||||
|
- `wasm:` override dlmalloc heap implementation from wasi-libc
|
||||||
|
- `wasm:` align heap to 16 bytes
|
||||||
|
- `wasm:` add support for the crypto/rand package
|
||||||
|
* **boards**
|
||||||
|
- add `DefaultUART` to adafruit boards
|
||||||
|
- `arduino-mkrwifi1010:` add board definition for Arduino MKR WiFi 1010
|
||||||
|
- `arduino-mkrwifi1010:` fix pin definition of `NINA_RESETN`
|
||||||
|
- `feather-nrf52:` fix pin definition of uart
|
||||||
|
- `feather-rp2040:` add pin name definition
|
||||||
|
- `gameboy-advance:` fix ROM header
|
||||||
|
- `mdbt50qrx-uf2:` add Raytac MDBT50Q-RX Dongle with TinyUF2
|
||||||
|
- `nano-rp2040:` define `NINA_SPI` and fix wifinina pins
|
||||||
|
- `teensy40:` enable hardware UART reconfiguration, fix receive watermark interrupt
|
||||||
|
|
||||||
|
|
||||||
0.19.0
|
0.19.0
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
# TinyGo base stage installs the most recent Go 1.16.x, LLVM 11 and the TinyGo compiler itself.
|
# TinyGo base stage installs the most recent Go 1.17.x, LLVM 11 and the TinyGo compiler itself.
|
||||||
FROM golang:1.16 AS tinygo-base
|
FROM golang:1.17 AS tinygo-base
|
||||||
|
|
||||||
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
|
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
|
||||||
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main" >> /etc/apt/sources.list && \
|
echo "deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-11 main" >> /etc/apt/sources.list && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y llvm-11-dev libclang-11-dev lld-11 git
|
apt-get install -y llvm-11-dev libclang-11-dev lld-11 git
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ endif
|
|||||||
clean:
|
clean:
|
||||||
@rm -rf build
|
@rm -rf build
|
||||||
|
|
||||||
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/internal/reflectlite transform
|
FMT_PATHS = ./*.go builder cgo compiler interp loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall src/testing src/internal/reflectlite transform
|
||||||
fmt:
|
fmt:
|
||||||
@gofmt -l -w $(FMT_PATHS)
|
@gofmt -l -w $(FMT_PATHS)
|
||||||
fmt-check:
|
fmt-check:
|
||||||
@@ -124,6 +124,7 @@ build/gen-device-svd: ./tools/gen-device-svd/*.go
|
|||||||
|
|
||||||
gen-device-esp: build/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/
|
./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/
|
||||||
|
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Espressif -interrupts=software lib/cmsis-svd/data/Espressif/ src/device/esp/
|
||||||
GO111MODULE=off $(GO) fmt ./src/device/esp
|
GO111MODULE=off $(GO) fmt ./src/device/esp
|
||||||
|
|
||||||
gen-device-nrf: build/gen-device-svd
|
gen-device-nrf: build/gen-device-svd
|
||||||
@@ -190,6 +191,10 @@ TEST_PACKAGES = \
|
|||||||
container/list \
|
container/list \
|
||||||
container/ring \
|
container/ring \
|
||||||
crypto/des \
|
crypto/des \
|
||||||
|
crypto/md5 \
|
||||||
|
crypto/sha1 \
|
||||||
|
crypto/sha256 \
|
||||||
|
crypto/sha512 \
|
||||||
encoding \
|
encoding \
|
||||||
encoding/ascii85 \
|
encoding/ascii85 \
|
||||||
encoding/base32 \
|
encoding/base32 \
|
||||||
@@ -199,6 +204,7 @@ TEST_PACKAGES = \
|
|||||||
hash/crc64 \
|
hash/crc64 \
|
||||||
math \
|
math \
|
||||||
math/cmplx \
|
math/cmplx \
|
||||||
|
testing \
|
||||||
text/scanner \
|
text/scanner \
|
||||||
unicode/utf8 \
|
unicode/utf8 \
|
||||||
|
|
||||||
@@ -358,6 +364,8 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=arduino-nano33 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=arduino-nano33 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=arduino-mkrwifi1010 examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pico examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=pico examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=nano-33-ble examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=nano-33-ble examples/blinky1
|
||||||
@@ -425,6 +433,8 @@ ifneq ($(XTENSA), 0)
|
|||||||
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
|
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
|
||||||
@$(MD5SUM) test.bin
|
@$(MD5SUM) test.bin
|
||||||
endif
|
endif
|
||||||
|
$(TINYGO) build -size short -o test.bin -target=esp32c3 examples/serial
|
||||||
|
@$(MD5SUM) test.bin
|
||||||
$(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
|
||||||
$(TINYGO) build -size short -o test.hex -target=hifive1-qemu examples/serial
|
$(TINYGO) build -size short -o test.hex -target=hifive1-qemu examples/serial
|
||||||
@@ -444,6 +454,9 @@ endif
|
|||||||
@$(MD5SUM) test.nro
|
@$(MD5SUM) test.nro
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
ifneq ($(OS),Windows_NT)
|
||||||
|
$(TINYGO) build -o test.elf -gc=leaking -scheduler=none examples/serial
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
wasmtest:
|
wasmtest:
|
||||||
|
|||||||
@@ -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 68 microcontroller boards are currently supported:
|
The following 70 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)
|
||||||
@@ -69,6 +69,7 @@ The following 68 microcontroller boards are currently supported:
|
|||||||
* [Arduino Mega 1280](https://www.arduino.cc/en/Main/arduinoBoardMega/)
|
* [Arduino Mega 1280](https://www.arduino.cc/en/Main/arduinoBoardMega/)
|
||||||
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
|
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
|
||||||
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
|
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
|
||||||
|
* [Arduino MKR WiFi 1010](https://store.arduino.cc/usa/mkr-wifi-1010)
|
||||||
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
|
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
|
||||||
* [Arduino Nano 33 BLE](https://store.arduino.cc/nano-33-ble)
|
* [Arduino Nano 33 BLE](https://store.arduino.cc/nano-33-ble)
|
||||||
* [Arduino Nano 33 BLE Sense](https://store.arduino.cc/nano-33-ble-sense)
|
* [Arduino Nano 33 BLE Sense](https://store.arduino.cc/nano-33-ble-sense)
|
||||||
@@ -101,6 +102,7 @@ The following 68 microcontroller boards are currently supported:
|
|||||||
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
||||||
* [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html)
|
* [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html)
|
||||||
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
|
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
|
||||||
|
* [Raytac MDBT50Q-RX Dongle (with TinyUF2 bootloader)](https://www.adafruit.com/product/5199)
|
||||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||||
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
||||||
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- task: GoTool@0
|
- task: GoTool@0
|
||||||
inputs:
|
inputs:
|
||||||
version: '1.16'
|
version: '1.17'
|
||||||
- checkout: self
|
- checkout: self
|
||||||
fetchDepth: 1
|
fetchDepth: 1
|
||||||
- task: Cache@2
|
- task: Cache@2
|
||||||
|
|||||||
+61
-42
@@ -100,7 +100,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
AutomaticStackSize: config.AutomaticStackSize(),
|
AutomaticStackSize: config.AutomaticStackSize(),
|
||||||
DefaultStackSize: config.Target.DefaultStackSize,
|
DefaultStackSize: config.Target.DefaultStackSize,
|
||||||
NeedsStackObjects: config.NeedsStackObjects(),
|
NeedsStackObjects: config.NeedsStackObjects(),
|
||||||
Debug: config.Debug(),
|
Debug: true,
|
||||||
LLVMFeatures: config.LLVMFeatures(),
|
LLVMFeatures: config.LLVMFeatures(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,11 +124,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// The slice of jobs that orchestrates most of the build.
|
|
||||||
// This is somewhat like an in-memory Makefile with each job being a
|
|
||||||
// Makefile target.
|
|
||||||
var jobs []*compileJob
|
|
||||||
|
|
||||||
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
||||||
// program so it's pretty fast and doesn't need to be parallelized.
|
// program so it's pretty fast and doesn't need to be parallelized.
|
||||||
program := lprogram.LoadSSA()
|
program := lprogram.LoadSSA()
|
||||||
@@ -311,7 +306,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return os.Rename(f.Name(), bitcodePath)
|
return os.Rename(f.Name(), bitcodePath)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job)
|
|
||||||
packageJobs = append(packageJobs, job)
|
packageJobs = append(packageJobs, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,14 +396,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, programJob)
|
|
||||||
|
|
||||||
// Check whether we only need to create an object file.
|
// Check whether we only need to create an object file.
|
||||||
// If so, we don't need to link anything and will be finished quickly.
|
// If so, we don't need to link anything and will be finished quickly.
|
||||||
outext := filepath.Ext(outpath)
|
outext := filepath.Ext(outpath)
|
||||||
if outext == ".o" || outext == ".bc" || outext == ".ll" {
|
if outext == ".o" || outext == ".bc" || outext == ".ll" {
|
||||||
// Run jobs to produce the LLVM module.
|
// Run jobs to produce the LLVM module.
|
||||||
err := runJobs(jobs)
|
err := runJobs(programJob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -450,7 +443,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
|
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, outputObjectFileJob)
|
|
||||||
|
|
||||||
// Prepare link command.
|
// Prepare link command.
|
||||||
linkerDependencies := []*compileJob{outputObjectFileJob}
|
linkerDependencies := []*compileJob{outputObjectFileJob}
|
||||||
@@ -465,38 +457,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job.dependencies...)
|
|
||||||
jobs = append(jobs, job)
|
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add libc dependency if needed.
|
|
||||||
root := goenv.Get("TINYGOROOT")
|
|
||||||
switch config.Target.Libc {
|
|
||||||
case "picolibc":
|
|
||||||
job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// The library needs to be compiled (cache miss).
|
|
||||||
jobs = append(jobs, job.dependencies...)
|
|
||||||
jobs = append(jobs, job)
|
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
|
||||||
case "wasi-libc":
|
|
||||||
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
||||||
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
|
||||||
}
|
|
||||||
ldflags = append(ldflags, path)
|
|
||||||
case "":
|
|
||||||
// no library specified, so nothing to do
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add jobs to compile extra files. These files are in C or assembly and
|
// Add jobs to compile extra files. These files are in C or assembly and
|
||||||
// contain things like the interrupt vector table and low level operations
|
// contain things like the interrupt vector table and low level operations
|
||||||
// such as stack switching.
|
// such as stack switching.
|
||||||
|
root := goenv.Get("TINYGOROOT")
|
||||||
for _, path := range config.ExtraFiles() {
|
for _, path := range config.ExtraFiles() {
|
||||||
abspath := filepath.Join(root, path)
|
abspath := filepath.Join(root, path)
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
@@ -507,7 +474,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job)
|
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,7 +492,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
jobs = append(jobs, job)
|
|
||||||
linkerDependencies = append(linkerDependencies, job)
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -537,9 +502,63 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
ldflags = append(ldflags, lprogram.LDFlags...)
|
ldflags = append(ldflags, lprogram.LDFlags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add libc dependency if needed.
|
||||||
|
switch config.Target.Libc {
|
||||||
|
case "picolibc":
|
||||||
|
job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
|
case "wasi-libc":
|
||||||
|
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
||||||
|
}
|
||||||
|
job := dummyCompileJob(path)
|
||||||
|
linkerDependencies = append(linkerDependencies, job)
|
||||||
|
case "":
|
||||||
|
// no library specified, so nothing to do
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip debug information with -no-debug.
|
||||||
|
if !config.Debug() {
|
||||||
|
for _, tag := range config.BuildTags() {
|
||||||
|
if tag == "baremetal" {
|
||||||
|
// Don't use -no-debug on baremetal targets. It makes no sense:
|
||||||
|
// the debug information isn't flashed to the device anyway.
|
||||||
|
return fmt.Errorf("stripping debug information is unnecessary for baremetal targets")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if config.Target.Linker == "wasm-ld" {
|
||||||
|
// Don't just strip debug information, also compress relocations
|
||||||
|
// while we're at it. Relocations can only be compressed when debug
|
||||||
|
// information is stripped.
|
||||||
|
ldflags = append(ldflags, "--strip-debug", "--compress-relocations")
|
||||||
|
} else {
|
||||||
|
switch config.GOOS() {
|
||||||
|
case "linux":
|
||||||
|
// Either real linux or an embedded system (like AVR) that
|
||||||
|
// pretends to be Linux. It's a ELF linker wrapped by GCC in any
|
||||||
|
// case.
|
||||||
|
ldflags = append(ldflags, "-Wl,--strip-debug")
|
||||||
|
case "darwin":
|
||||||
|
// MacOS (darwin) doesn't have a linker flag to strip debug
|
||||||
|
// information. Apple expects you to use the strip command
|
||||||
|
// instead.
|
||||||
|
return errors.New("cannot remove debug information: MacOS doesn't suppor this linker flag")
|
||||||
|
default:
|
||||||
|
// Other OSes may have different flags.
|
||||||
|
return errors.New("cannot remove debug information: unknown OS: " + config.GOOS())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create a linker job, which links all object files together and does some
|
// Create a linker job, which links all object files together and does some
|
||||||
// extra stuff that can only be done after linking.
|
// extra stuff that can only be done after linking.
|
||||||
jobs = append(jobs, &compileJob{
|
linkJob := &compileJob{
|
||||||
description: "link",
|
description: "link",
|
||||||
dependencies: linkerDependencies,
|
dependencies: linkerDependencies,
|
||||||
run: func(job *compileJob) error {
|
run: func(job *compileJob) error {
|
||||||
@@ -612,12 +631,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
|
|
||||||
// Run all jobs to compile and link the program.
|
// Run all jobs to compile and link the program.
|
||||||
// Do this now (instead of after elf-to-hex and similar conversions) as it
|
// Do this now (instead of after elf-to-hex and similar conversions) as it
|
||||||
// is simpler and cannot be parallelized.
|
// is simpler and cannot be parallelized.
|
||||||
err = runJobs(jobs)
|
err = runJobs(linkJob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -642,7 +661,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "esp32", "esp8266":
|
case "esp32", "esp32c3", "esp8266":
|
||||||
// Special format for the ESP family of chips (parsed by the ROM
|
// Special format for the ESP family of chips (parsed by the ROM
|
||||||
// bootloader).
|
// bootloader).
|
||||||
tmppath = filepath.Join(dir, "main"+outext)
|
tmppath = filepath.Join(dir, "main"+outext)
|
||||||
|
|||||||
@@ -155,6 +155,10 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
|||||||
|
|
||||||
// Write dependencies file.
|
// Write dependencies file.
|
||||||
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
|
f, err := ioutil.TempFile(filepath.Dir(depfileCachePath), depfileName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
buf, err = json.MarshalIndent(dependencySlice, "", "\t")
|
buf, err = json.MarshalIndent(dependencySlice, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // shouldn't happen
|
panic(err) // shouldn't happen
|
||||||
|
|||||||
+2
-2
@@ -33,8 +33,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 < 13 || minor > 16 {
|
if major != 1 || minor < 15 || minor > 17 {
|
||||||
return nil, fmt.Errorf("requires go version 1.13 through 1.16, got go%d.%d", major, minor)
|
return nil, fmt.Errorf("requires go version 1.15 through 1.17, got go%d.%d", major, minor)
|
||||||
}
|
}
|
||||||
|
|
||||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||||
|
|||||||
+19
-6
@@ -78,11 +78,21 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
// An added benefit is that we don't need to check for errors all the time.
|
// An added benefit is that we don't need to check for errors all the time.
|
||||||
outf := &bytes.Buffer{}
|
outf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
// Chip IDs. Source:
|
||||||
|
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L22
|
||||||
|
chip_id := map[string]uint16{
|
||||||
|
"esp32": 0x0000,
|
||||||
|
"esp32c3": 0x0005,
|
||||||
|
}[format]
|
||||||
|
|
||||||
// Image header.
|
// Image header.
|
||||||
switch format {
|
switch format {
|
||||||
case "esp32":
|
case "esp32", "esp32c3":
|
||||||
// Header format:
|
// Header format:
|
||||||
// https://github.com/espressif/esp-idf/blob/8fbb63c2/components/bootloader_support/include/esp_image_format.h#L58
|
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
|
||||||
|
// Note: not adding a SHA256 hash as the binary is modified by
|
||||||
|
// esptool.py while flashing and therefore the hash won't be valid
|
||||||
|
// anymore.
|
||||||
binary.Write(outf, binary.LittleEndian, struct {
|
binary.Write(outf, binary.LittleEndian, struct {
|
||||||
magic uint8
|
magic uint8
|
||||||
segment_count uint8
|
segment_count uint8
|
||||||
@@ -91,15 +101,18 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
entry_addr uint32
|
entry_addr uint32
|
||||||
wp_pin uint8
|
wp_pin uint8
|
||||||
spi_pin_drv [3]uint8
|
spi_pin_drv [3]uint8
|
||||||
reserved [11]uint8
|
chip_id uint16
|
||||||
|
min_chip_rev uint8
|
||||||
|
reserved [8]uint8
|
||||||
hash_appended bool
|
hash_appended bool
|
||||||
}{
|
}{
|
||||||
magic: 0xE9,
|
magic: 0xE9,
|
||||||
segment_count: byte(len(segments)),
|
segment_count: byte(len(segments)),
|
||||||
spi_mode: 0, // irrelevant, replaced by esptool when flashing
|
spi_mode: 2, // ESP_IMAGE_SPI_MODE_DIO
|
||||||
spi_speed_size: 0, // spi_speed, spi_size: replaced by esptool when flashing
|
spi_speed_size: 0x1f, // ESP_IMAGE_SPI_SPEED_80M, ESP_IMAGE_FLASH_SIZE_2MB
|
||||||
entry_addr: uint32(inf.Entry),
|
entry_addr: uint32(inf.Entry),
|
||||||
wp_pin: 0xEE, // disable WP pin
|
wp_pin: 0xEE, // disable WP pin
|
||||||
|
chip_id: chip_id,
|
||||||
hash_appended: true, // add a SHA256 hash
|
hash_appended: true, // add a SHA256 hash
|
||||||
})
|
})
|
||||||
case "esp8266":
|
case "esp8266":
|
||||||
@@ -142,7 +155,7 @@ func makeESPFirmareImage(infile, outfile, format string) error {
|
|||||||
outf.Write(make([]byte, 15-outf.Len()%16))
|
outf.Write(make([]byte, 15-outf.Len()%16))
|
||||||
outf.WriteByte(checksum)
|
outf.WriteByte(checksum)
|
||||||
|
|
||||||
if format == "esp32" {
|
if format != "esp8266" {
|
||||||
// SHA256 hash (to protect against image corruption, not for security).
|
// SHA256 hash (to protect against image corruption, not for security).
|
||||||
hash := sha256.Sum256(outf.Bytes())
|
hash := sha256.Sum256(outf.Bytes())
|
||||||
outf.Write(hash[:])
|
outf.Write(hash[:])
|
||||||
|
|||||||
+23
-6
@@ -65,12 +65,29 @@ func (job *compileJob) readyToRun() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// runJobs runs all the jobs indicated in the jobs slice and returns the error
|
// runJobs runs the indicated job and all its dependencies. For every job, all
|
||||||
// of the first job that fails to run.
|
// the dependencies are run first. It returns the error of the first job that
|
||||||
// It runs all jobs in the order of the slice, as long as all dependencies have
|
// fails.
|
||||||
// already run. Therefore, if some jobs are preferred to run before others, they
|
// It runs all jobs in the order of the dependencies slice, depth-first.
|
||||||
// should be ordered as such in this slice.
|
// Therefore, if some jobs are preferred to run before others, they should be
|
||||||
func runJobs(jobs []*compileJob) error {
|
// ordered as such in the job dependencies.
|
||||||
|
func runJobs(job *compileJob) error {
|
||||||
|
// Create a slice of jobs to run, where all dependencies are run in order.
|
||||||
|
jobs := []*compileJob{}
|
||||||
|
addedJobs := map[*compileJob]struct{}{}
|
||||||
|
var addJobs func(*compileJob)
|
||||||
|
addJobs = func(job *compileJob) {
|
||||||
|
if _, ok := addedJobs[job]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, dep := range job.dependencies {
|
||||||
|
addJobs(dep)
|
||||||
|
}
|
||||||
|
jobs = append(jobs, job)
|
||||||
|
addedJobs[job] = struct{}{}
|
||||||
|
}
|
||||||
|
addJobs(job)
|
||||||
|
|
||||||
// Create channels to communicate with the workers.
|
// Create channels to communicate with the workers.
|
||||||
doneChan := make(chan *compileJob)
|
doneChan := make(chan *compileJob)
|
||||||
workerChan := make(chan *compileJob)
|
workerChan := make(chan *compileJob)
|
||||||
|
|||||||
+3
-4
@@ -46,15 +46,14 @@ func (l *Library) Load(target, tmpdir string) (path string, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
jobs := append([]*compileJob{job}, job.dependencies...)
|
err = runJobs(job)
|
||||||
err = runJobs(jobs)
|
|
||||||
return job.result, err
|
return job.result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// load returns a compile job to build this library file for the given target
|
// load returns a compile job to build this library file for the given target
|
||||||
// and CPU. It may return a dummy compileJob if the library build is already
|
// and CPU. It may return a dummy compileJob if the library build is already
|
||||||
// cached. The path is stored as job.result but is only valid if the job and
|
// cached. The path is stored as job.result but is only valid after the job has
|
||||||
// job.dependencies have been run.
|
// been run.
|
||||||
// The provided tmpdir will be used to store intermediary files and possibly the
|
// The provided tmpdir will be used to store intermediary files and possibly the
|
||||||
// output archive file, it is expected to be removed after use.
|
// output archive file, it is expected to be removed after use.
|
||||||
func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error) {
|
func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error) {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"go/types"
|
"go/types"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -25,14 +24,6 @@ var flagUpdate = flag.Bool("update", false, "Update images based on test output.
|
|||||||
// platforms and Go versions.
|
// platforms and Go versions.
|
||||||
func normalizeResult(result string) string {
|
func normalizeResult(result string) string {
|
||||||
actual := strings.ReplaceAll(result, "\r\n", "\n")
|
actual := strings.ReplaceAll(result, "\r\n", "\n")
|
||||||
|
|
||||||
// Make sure all functions are wrapped, even those that would otherwise be
|
|
||||||
// single-line functions. This is necessary because Go 1.14 changed the way
|
|
||||||
// such functions are wrapped and it's important to have consistent test
|
|
||||||
// results.
|
|
||||||
re := regexp.MustCompile(`func \((.+)\)( .*?) +{ (.+) }`)
|
|
||||||
actual = re.ReplaceAllString(actual, "func ($1)$2 {\n\t$3\n}")
|
|
||||||
|
|
||||||
return actual
|
return actual
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+5
-15
@@ -67,9 +67,7 @@ type C.union3_t = C.union_1
|
|||||||
type C.union_nested_t = C.union_3
|
type C.union_nested_t = C.union_3
|
||||||
type C.unionarray_t = struct{ arr [10]C.uchar }
|
type C.unionarray_t = struct{ arr [10]C.uchar }
|
||||||
|
|
||||||
func (s *C.struct_4) bitfield_a() C.uchar {
|
func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||||
return s.__bitfield_1 & 0x1f
|
|
||||||
}
|
|
||||||
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
|
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
|
||||||
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
|
||||||
}
|
}
|
||||||
@@ -105,15 +103,9 @@ type C.struct_type1 struct {
|
|||||||
}
|
}
|
||||||
type C.struct_type2 struct{ _type C.int }
|
type C.struct_type2 struct{ _type C.int }
|
||||||
|
|
||||||
func (union *C.union_1) unionfield_i() *C.int {
|
func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||||
return (*C.int)(unsafe.Pointer(&union.$union))
|
func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
|
||||||
}
|
func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
|
||||||
func (union *C.union_1) unionfield_d() *float64 {
|
|
||||||
return (*float64)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
func (union *C.union_1) unionfield_s() *C.short {
|
|
||||||
return (*C.short)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
|
|
||||||
type C.union_1 struct{ $union uint64 }
|
type C.union_1 struct{ $union uint64 }
|
||||||
|
|
||||||
@@ -138,9 +130,7 @@ func (union *C.union_3) unionfield_thing() *C.union3_t {
|
|||||||
|
|
||||||
type C.union_3 struct{ $union [2]uint64 }
|
type C.union_3 struct{ $union [2]uint64 }
|
||||||
|
|
||||||
func (union *C.union_union2d) unionfield_i() *C.int {
|
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||||
return (*C.int)(unsafe.Pointer(&union.$union))
|
|
||||||
}
|
|
||||||
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
func (union *C.union_union2d) unionfield_d() *[2]float64 {
|
||||||
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
return (*[2]float64)(unsafe.Pointer(&union.$union))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ func (c *Config) GOARCH() string {
|
|||||||
|
|
||||||
// BuildTags returns the complete list of build tags used during this build.
|
// BuildTags returns the complete list of build tags used during this build.
|
||||||
func (c *Config) BuildTags() []string {
|
func (c *Config) BuildTags() []string {
|
||||||
tags := append(c.Target.BuildTags, []string{"tinygo", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
||||||
for i := 1; i <= c.GoMinorVersion; i++ {
|
for i := 1; i <= c.GoMinorVersion; i++ {
|
||||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||||
}
|
}
|
||||||
@@ -209,9 +209,8 @@ func (c *Config) CFlags() []string {
|
|||||||
cflags = append(cflags, "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "picolibc", "newlib", "libc", "include"))
|
cflags = append(cflags, "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "picolibc", "newlib", "libc", "include"))
|
||||||
cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include"))
|
cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include"))
|
||||||
}
|
}
|
||||||
if c.Debug() {
|
// Always emit debug information. It is optionally stripped at link time.
|
||||||
cflags = append(cflags, "-g")
|
cflags = append(cflags, "-g")
|
||||||
}
|
|
||||||
return cflags
|
return cflags
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,8 +249,9 @@ func (c *Config) VerifyIR() bool {
|
|||||||
return c.Options.VerifyIR
|
return c.Options.VerifyIR
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug returns whether to add debug symbols to the IR, for debugging with GDB
|
// Debug returns whether debug (DWARF) information should be retained by the
|
||||||
// and similar.
|
// linker. By default, debug information is retained but it can be removed with
|
||||||
|
// the -no-debug flag.
|
||||||
func (c *Config) Debug() bool {
|
func (c *Config) Debug() bool {
|
||||||
return c.Options.Debug
|
return c.Options.Debug
|
||||||
}
|
}
|
||||||
@@ -297,6 +297,9 @@ func (c *Config) Programmer() (method, openocdInterface string) {
|
|||||||
case "openocd", "msd", "command":
|
case "openocd", "msd", "command":
|
||||||
// The -programmer flag only specifies the flash method.
|
// The -programmer flag only specifies the flash method.
|
||||||
return c.Options.Programmer, c.Target.OpenOCDInterface
|
return c.Options.Programmer, c.Target.OpenOCDInterface
|
||||||
|
case "bmp":
|
||||||
|
// The -programmer flag only specifies the flash method.
|
||||||
|
return c.Options.Programmer, ""
|
||||||
default:
|
default:
|
||||||
// The -programmer flag specifies something else, assume it specifies
|
// The -programmer flag specifies something else, assume it specifies
|
||||||
// the OpenOCD interface name.
|
// the OpenOCD interface name.
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ func LoadTarget(target string) (*TargetSpec, error) {
|
|||||||
"386": "i386",
|
"386": "i386",
|
||||||
"amd64": "x86_64",
|
"amd64": "x86_64",
|
||||||
"arm64": "aarch64",
|
"arm64": "aarch64",
|
||||||
|
"arm": "thumbv7",
|
||||||
}[goarch]
|
}[goarch]
|
||||||
if llvmarch == "" {
|
if llvmarch == "" {
|
||||||
llvmarch = goarch
|
llvmarch = goarch
|
||||||
@@ -253,6 +254,9 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
GDB: []string{"gdb"},
|
GDB: []string{"gdb"},
|
||||||
PortReset: "false",
|
PortReset: "false",
|
||||||
}
|
}
|
||||||
|
if goarch == "386" {
|
||||||
|
spec.CPU = "pentium4"
|
||||||
|
}
|
||||||
if goos == "darwin" {
|
if goos == "darwin" {
|
||||||
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
|
||||||
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
|
spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip")
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
// This file defines alias functions for functions that are normally defined in
|
||||||
|
// Go assembly.
|
||||||
|
//
|
||||||
|
// The Go toolchain defines many performance critical functions in assembly
|
||||||
|
// instead of plain Go. This is a problem for TinyGo as it currently (as of
|
||||||
|
// august 2021) is not able to compile these assembly files and even if it
|
||||||
|
// could, it would not be able to make use of them for many targets that are
|
||||||
|
// supported by TinyGo (baremetal RISC-V, AVR, etc). Therefore, many of these
|
||||||
|
// functions are aliased to their generic Go implementation.
|
||||||
|
// This results in slower than possible implementations, but at least they are
|
||||||
|
// usable.
|
||||||
|
|
||||||
|
import "tinygo.org/x/go-llvm"
|
||||||
|
|
||||||
|
var stdlibAliases = map[string]string{
|
||||||
|
// crypto packages
|
||||||
|
"crypto/md5.block": "crypto/md5.blockGeneric",
|
||||||
|
"crypto/sha1.block": "crypto/sha1.blockGeneric",
|
||||||
|
"crypto/sha1.blockAMD64": "crypto/sha1.blockGeneric",
|
||||||
|
"crypto/sha256.block": "crypto/sha256.blockGeneric",
|
||||||
|
"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",
|
||||||
|
|
||||||
|
// math package
|
||||||
|
"math.Asin": "math.asin",
|
||||||
|
"math.Asinh": "math.asinh",
|
||||||
|
"math.Acos": "math.acos",
|
||||||
|
"math.Acosh": "math.acosh",
|
||||||
|
"math.Atan": "math.atan",
|
||||||
|
"math.Atanh": "math.atanh",
|
||||||
|
"math.Atan2": "math.atan2",
|
||||||
|
"math.Cbrt": "math.cbrt",
|
||||||
|
"math.Ceil": "math.ceil",
|
||||||
|
"math.archCeil": "math.ceil",
|
||||||
|
"math.Cos": "math.cos",
|
||||||
|
"math.Cosh": "math.cosh",
|
||||||
|
"math.Erf": "math.erf",
|
||||||
|
"math.Erfc": "math.erfc",
|
||||||
|
"math.Exp": "math.exp",
|
||||||
|
"math.archExp": "math.exp",
|
||||||
|
"math.Expm1": "math.expm1",
|
||||||
|
"math.Exp2": "math.exp2",
|
||||||
|
"math.archExp2": "math.exp2",
|
||||||
|
"math.Floor": "math.floor",
|
||||||
|
"math.archFloor": "math.floor",
|
||||||
|
"math.Frexp": "math.frexp",
|
||||||
|
"math.Hypot": "math.hypot",
|
||||||
|
"math.archHypot": "math.hypot",
|
||||||
|
"math.Ldexp": "math.ldexp",
|
||||||
|
"math.Log": "math.log",
|
||||||
|
"math.archLog": "math.log",
|
||||||
|
"math.Log1p": "math.log1p",
|
||||||
|
"math.Log10": "math.log10",
|
||||||
|
"math.Log2": "math.log2",
|
||||||
|
"math.Max": "math.max",
|
||||||
|
"math.archMax": "math.max",
|
||||||
|
"math.Min": "math.min",
|
||||||
|
"math.archMin": "math.min",
|
||||||
|
"math.Mod": "math.mod",
|
||||||
|
"math.Modf": "math.modf",
|
||||||
|
"math.archModf": "math.modf",
|
||||||
|
"math.Pow": "math.pow",
|
||||||
|
"math.Remainder": "math.remainder",
|
||||||
|
"math.Sin": "math.sin",
|
||||||
|
"math.Sinh": "math.sinh",
|
||||||
|
"math.Sqrt": "math.sqrt",
|
||||||
|
"math.archSqrt": "math.sqrt",
|
||||||
|
"math.Tan": "math.tan",
|
||||||
|
"math.Tanh": "math.tanh",
|
||||||
|
"math.Trunc": "math.trunc",
|
||||||
|
"math.archTrunc": "math.trunc",
|
||||||
|
}
|
||||||
|
|
||||||
|
// createAlias implements the function (in the builder) as a call to the alias
|
||||||
|
// function.
|
||||||
|
func (b *builder) createAlias(alias llvm.Value) {
|
||||||
|
if b.Debug {
|
||||||
|
if b.fn.Syntax() != nil {
|
||||||
|
// Create debug info file if present.
|
||||||
|
b.difunc = b.attachDebugInfo(b.fn)
|
||||||
|
}
|
||||||
|
pos := b.program.Fset.Position(b.fn.Pos())
|
||||||
|
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
|
||||||
|
}
|
||||||
|
entryBlock := llvm.AddBasicBlock(b.llvmFn, "entry")
|
||||||
|
b.SetInsertPointAtEnd(entryBlock)
|
||||||
|
if b.llvmFn.Type() != alias.Type() {
|
||||||
|
b.addError(b.fn.Pos(), "alias function should have the same type as aliasee "+alias.Name())
|
||||||
|
b.CreateUnreachable()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result := b.CreateCall(alias, b.llvmFn.Params(), "")
|
||||||
|
if result.Type().TypeKind() == llvm.VoidTypeKind {
|
||||||
|
b.CreateRetVoid()
|
||||||
|
} else {
|
||||||
|
b.CreateRet(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -101,6 +101,55 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo
|
|||||||
b.createRuntimeAssert(outOfBounds, "slice", "slicePanic")
|
b.createRuntimeAssert(outOfBounds, "slice", "slicePanic")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createSliceToArrayPointerCheck adds a check for slice-to-array pointer
|
||||||
|
// conversions. This conversion was added in Go 1.17. For details, see:
|
||||||
|
// https://tip.golang.org/ref/spec#Conversions_from_slice_to_array_pointer
|
||||||
|
func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen int64) {
|
||||||
|
// From the spec:
|
||||||
|
// > If the length of the slice is less than the length of the array, a
|
||||||
|
// > run-time panic occurs.
|
||||||
|
arrayLenValue := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
|
||||||
|
isLess := b.CreateICmp(llvm.IntULT, sliceLen, arrayLenValue, "")
|
||||||
|
b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic")
|
||||||
|
}
|
||||||
|
|
||||||
|
// createUnsafeSliceCheck inserts a runtime check used for unsafe.Slice. This
|
||||||
|
// function must panic if the ptr/len parameters are invalid.
|
||||||
|
func (b *builder) createUnsafeSliceCheck(ptr, len llvm.Value, lenType *types.Basic) {
|
||||||
|
// From the documentation of unsafe.Slice:
|
||||||
|
// > At run time, if len is negative, or if ptr is nil and len is not
|
||||||
|
// > zero, a run-time panic occurs.
|
||||||
|
// However, in practice, it is also necessary to check that the length is
|
||||||
|
// not too big that a GEP wouldn't be possible without wrapping the pointer.
|
||||||
|
// These two checks (non-negative and not too big) can be merged into one
|
||||||
|
// using an unsiged greater than.
|
||||||
|
|
||||||
|
// Make sure the len value is at least as big as a uintptr.
|
||||||
|
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
||||||
|
if lenType.Info()&types.IsUnsigned != 0 {
|
||||||
|
len = b.CreateZExt(len, b.uintptrType, "")
|
||||||
|
} else {
|
||||||
|
len = b.CreateSExt(len, b.uintptrType, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the maximum slice size, and therefore the maximum value of the
|
||||||
|
// len parameter.
|
||||||
|
maxSize := b.maxSliceSize(ptr.Type().ElementType())
|
||||||
|
maxSizeValue := llvm.ConstInt(len.Type(), maxSize, false)
|
||||||
|
|
||||||
|
// Do the check. By using unsigned greater than for the length check, signed
|
||||||
|
// negative values are also checked (which are very large numbers when
|
||||||
|
// interpreted as signed values).
|
||||||
|
zero := llvm.ConstInt(len.Type(), 0, false)
|
||||||
|
lenOutOfBounds := b.CreateICmp(llvm.IntUGT, len, maxSizeValue, "")
|
||||||
|
ptrIsNil := b.CreateICmp(llvm.IntEQ, ptr, llvm.ConstNull(ptr.Type()), "")
|
||||||
|
lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "")
|
||||||
|
assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
|
||||||
|
assert = b.CreateOr(assert, lenOutOfBounds, "")
|
||||||
|
b.createRuntimeAssert(assert, "unsafe.Slice", "unsafeSlicePanic")
|
||||||
|
}
|
||||||
|
|
||||||
// createChanBoundsCheck creates a bounds check before creating a new channel to
|
// createChanBoundsCheck creates a bounds check before creating a new channel to
|
||||||
// check that the value is not too big for runtime.chanMake.
|
// check that the value is not too big for runtime.chanMake.
|
||||||
func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) {
|
func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value, bufSizeType *types.Basic, pos token.Pos) {
|
||||||
|
|||||||
+26
-7
@@ -32,8 +32,15 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
|||||||
|
|
||||||
// store value-to-send
|
// store value-to-send
|
||||||
valueType := b.getLLVMType(instr.X.Type())
|
valueType := b.getLLVMType(instr.X.Type())
|
||||||
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
|
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||||
|
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value
|
||||||
|
if isZeroSize {
|
||||||
|
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
|
||||||
|
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
|
||||||
|
} else {
|
||||||
|
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||||
b.CreateStore(chanValue, valueAlloca)
|
b.CreateStore(chanValue, valueAlloca)
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate blockedlist buffer.
|
// Allocate blockedlist buffer.
|
||||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||||
@@ -46,8 +53,10 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
|||||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||||
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
||||||
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||||
|
if !isZeroSize {
|
||||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// createChanRecv emits a pseudo chan receive operation. It is lowered to the
|
// createChanRecv emits a pseudo chan receive operation. It is lowered to the
|
||||||
// actual channel receive operation during goroutine lowering.
|
// actual channel receive operation during goroutine lowering.
|
||||||
@@ -56,7 +65,14 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
|||||||
ch := b.getValue(unop.X)
|
ch := b.getValue(unop.X)
|
||||||
|
|
||||||
// Allocate memory to receive into.
|
// Allocate memory to receive into.
|
||||||
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
|
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
|
||||||
|
var valueAlloca, valueAllocaCast, valueAllocaSize llvm.Value
|
||||||
|
if isZeroSize {
|
||||||
|
valueAlloca = llvm.ConstNull(llvm.PointerType(valueType, 0))
|
||||||
|
valueAllocaCast = llvm.ConstNull(b.i8ptrType)
|
||||||
|
} else {
|
||||||
|
valueAlloca, valueAllocaCast, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate blockedlist buffer.
|
// Allocate blockedlist buffer.
|
||||||
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||||
@@ -64,9 +80,14 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
|||||||
|
|
||||||
// Do the receive.
|
// Do the receive.
|
||||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
|
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
|
||||||
received := b.CreateLoad(valueAlloca, "chan.received")
|
var received llvm.Value
|
||||||
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
if isZeroSize {
|
||||||
|
received = llvm.ConstNull(valueType)
|
||||||
|
} else {
|
||||||
|
received = b.CreateLoad(valueAlloca, "chan.received")
|
||||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||||
|
}
|
||||||
|
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||||
|
|
||||||
if unop.CommaOk {
|
if unop.CommaOk {
|
||||||
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
|
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
|
||||||
@@ -116,7 +137,6 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
// determine the receive buffer size and alignment.
|
// determine the receive buffer size and alignment.
|
||||||
recvbufSize := uint64(0)
|
recvbufSize := uint64(0)
|
||||||
recvbufAlign := 0
|
recvbufAlign := 0
|
||||||
hasReceives := false
|
|
||||||
var selectStates []llvm.Value
|
var selectStates []llvm.Value
|
||||||
chanSelectStateType := b.getLLVMRuntimeType("chanSelectState")
|
chanSelectStateType := b.getLLVMRuntimeType("chanSelectState")
|
||||||
for _, state := range expr.States {
|
for _, state := range expr.States {
|
||||||
@@ -133,7 +153,6 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
if align := b.targetData.ABITypeAlignment(llvmType); align > recvbufAlign {
|
if align := b.targetData.ABITypeAlignment(llvmType); align > recvbufAlign {
|
||||||
recvbufAlign = align
|
recvbufAlign = align
|
||||||
}
|
}
|
||||||
hasReceives = true
|
|
||||||
case types.SendOnly:
|
case types.SendOnly:
|
||||||
// Store this value in an alloca and put a pointer to this alloca
|
// Store this value in an alloca and put a pointer to this alloca
|
||||||
// in the send state.
|
// in the send state.
|
||||||
@@ -150,7 +169,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
|
|||||||
|
|
||||||
// Create a receive buffer, where the received value will be stored.
|
// Create a receive buffer, where the received value will be stored.
|
||||||
recvbuf := llvm.Undef(b.i8ptrType)
|
recvbuf := llvm.Undef(b.i8ptrType)
|
||||||
if hasReceives {
|
if recvbufSize != 0 {
|
||||||
allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
|
allocaType := llvm.ArrayType(b.ctx.Int8Type(), int(recvbufSize))
|
||||||
recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
|
recvbufAlloca, _, _ := b.createTemporaryAlloca(allocaType, "select.recvbuf.alloca")
|
||||||
recvbufAlloca.SetAlignment(recvbufAlign)
|
recvbufAlloca.SetAlignment(recvbufAlign)
|
||||||
|
|||||||
+118
-13
@@ -23,7 +23,7 @@ import (
|
|||||||
// Version of the compiler pacakge. Must be incremented each time the compiler
|
// Version of the compiler pacakge. Must be incremented each time the compiler
|
||||||
// package changes in a way that affects the generated LLVM module.
|
// package changes in a way that affects the generated LLVM module.
|
||||||
// This version is independent of the TinyGo version number.
|
// This version is independent of the TinyGo version number.
|
||||||
const Version = 12 // last change: implement syscall.rawSyscallNoError
|
const Version = 19 // last change: fix channel ops with zero values
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
llvm.InitializeAllTargets()
|
llvm.InitializeAllTargets()
|
||||||
@@ -74,6 +74,7 @@ type compilerContext struct {
|
|||||||
cu llvm.Metadata
|
cu llvm.Metadata
|
||||||
difiles map[string]llvm.Metadata
|
difiles map[string]llvm.Metadata
|
||||||
ditypes map[types.Type]llvm.Metadata
|
ditypes map[types.Type]llvm.Metadata
|
||||||
|
llvmTypes map[types.Type]llvm.Type
|
||||||
machine llvm.TargetMachine
|
machine llvm.TargetMachine
|
||||||
targetData llvm.TargetData
|
targetData llvm.TargetData
|
||||||
intType llvm.Type
|
intType llvm.Type
|
||||||
@@ -94,6 +95,7 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C
|
|||||||
DumpSSA: dumpSSA,
|
DumpSSA: dumpSSA,
|
||||||
difiles: make(map[string]llvm.Metadata),
|
difiles: make(map[string]llvm.Metadata),
|
||||||
ditypes: make(map[types.Type]llvm.Metadata),
|
ditypes: make(map[types.Type]llvm.Metadata),
|
||||||
|
llvmTypes: make(map[types.Type]llvm.Type),
|
||||||
machine: machine,
|
machine: machine,
|
||||||
targetData: machine.CreateTargetData(),
|
targetData: machine.CreateTargetData(),
|
||||||
astComments: map[string]*ast.CommentGroup{},
|
astComments: map[string]*ast.CommentGroup{},
|
||||||
@@ -315,10 +317,23 @@ func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
|
|||||||
return c.getLLVMType(typ)
|
return c.getLLVMType(typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLLVMType creates and returns a LLVM type for a Go type. In the case of
|
// getLLVMType returns a LLVM type for a Go type. It doesn't recreate already
|
||||||
// named struct types (or Go types implemented as named LLVM structs such as
|
// created types. This is somewhat important for performance, but especially
|
||||||
// strings) it also creates it first if necessary.
|
// important for named struct types (which should only be created once).
|
||||||
func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
||||||
|
// Try to load the LLVM type from the cache.
|
||||||
|
if t, ok := c.llvmTypes[goType]; ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
// Not already created, so adding this type to the cache.
|
||||||
|
llvmType := c.makeLLVMType(goType)
|
||||||
|
c.llvmTypes[goType] = llvmType
|
||||||
|
return llvmType
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
|
||||||
|
// getLLVMType instead.
|
||||||
|
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
||||||
switch typ := goType.(type) {
|
switch typ := goType.(type) {
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
elemType := c.getLLVMType(typ.Elem())
|
elemType := c.getLLVMType(typ.Elem())
|
||||||
@@ -367,12 +382,10 @@ func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
|||||||
// LLVM. This is because it is otherwise impossible to create
|
// LLVM. This is because it is otherwise impossible to create
|
||||||
// self-referencing types such as linked lists.
|
// self-referencing types such as linked lists.
|
||||||
llvmName := typ.Obj().Pkg().Path() + "." + typ.Obj().Name()
|
llvmName := typ.Obj().Pkg().Path() + "." + typ.Obj().Name()
|
||||||
llvmType := c.mod.GetTypeByName(llvmName)
|
llvmType := c.ctx.StructCreateNamed(llvmName)
|
||||||
if llvmType.IsNil() {
|
c.llvmTypes[goType] = llvmType // avoid infinite recursion
|
||||||
llvmType = c.ctx.StructCreateNamed(llvmName)
|
|
||||||
underlying := c.getLLVMType(st)
|
underlying := c.getLLVMType(st)
|
||||||
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
llvmType.StructSetBody(underlying.StructElementTypes(), false)
|
||||||
}
|
|
||||||
return llvmType
|
return llvmType
|
||||||
}
|
}
|
||||||
return c.getLLVMType(typ.Underlying())
|
return c.getLLVMType(typ.Underlying())
|
||||||
@@ -768,6 +781,29 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add forwarding functions for functions that would otherwise be
|
||||||
|
// implemented in assembly.
|
||||||
|
for _, name := range members {
|
||||||
|
member := pkg.Members[name]
|
||||||
|
switch member := member.(type) {
|
||||||
|
case *ssa.Function:
|
||||||
|
if member.Blocks != nil {
|
||||||
|
continue // external function
|
||||||
|
}
|
||||||
|
info := c.getFunctionInfo(member)
|
||||||
|
if aliasName, ok := stdlibAliases[info.linkName]; ok {
|
||||||
|
alias := c.mod.NamedFunction(aliasName)
|
||||||
|
if alias.IsNil() {
|
||||||
|
// Shouldn't happen, but perhaps best to just ignore.
|
||||||
|
// The error will be a link error, if there is an error.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b := newBuilder(c, irbuilder, member)
|
||||||
|
b.createAlias(alias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// createFunction builds the LLVM IR implementation for this function. The
|
// createFunction builds the LLVM IR implementation for this function. The
|
||||||
@@ -1270,6 +1306,38 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
|||||||
case "ssa:wrapnilchk":
|
case "ssa:wrapnilchk":
|
||||||
// TODO: do an actual nil check?
|
// TODO: do an actual nil check?
|
||||||
return argValues[0], nil
|
return argValues[0], nil
|
||||||
|
|
||||||
|
// Builtins from the unsafe package.
|
||||||
|
case "Add": // unsafe.Add
|
||||||
|
// This is basically just a GEP operation.
|
||||||
|
// Note: the pointer is always of type *i8.
|
||||||
|
ptr := argValues[0]
|
||||||
|
len := argValues[1]
|
||||||
|
return b.CreateGEP(ptr, []llvm.Value{len}, ""), nil
|
||||||
|
case "Slice": // unsafe.Slice
|
||||||
|
// This creates a slice from a pointer and a length.
|
||||||
|
// Note that the exception mentioned in the documentation (if the
|
||||||
|
// pointer and length are nil, the slice is also nil) is trivially
|
||||||
|
// already the case.
|
||||||
|
ptr := argValues[0]
|
||||||
|
len := argValues[1]
|
||||||
|
slice := llvm.Undef(b.ctx.StructType([]llvm.Type{
|
||||||
|
ptr.Type(),
|
||||||
|
b.uintptrType,
|
||||||
|
b.uintptrType,
|
||||||
|
}, false))
|
||||||
|
b.createUnsafeSliceCheck(ptr, len, argTypes[1].Underlying().(*types.Basic))
|
||||||
|
if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
|
||||||
|
// Too small, zero-extend len.
|
||||||
|
len = b.CreateZExt(len, b.uintptrType, "")
|
||||||
|
} else if len.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() {
|
||||||
|
// Too big, truncate len.
|
||||||
|
len = b.CreateTrunc(len, b.uintptrType, "")
|
||||||
|
}
|
||||||
|
slice = b.CreateInsertValue(slice, ptr, 0, "")
|
||||||
|
slice = b.CreateInsertValue(slice, len, 1, "")
|
||||||
|
slice = b.CreateInsertValue(slice, len, 2, "")
|
||||||
|
return slice, nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
|
return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
|
||||||
}
|
}
|
||||||
@@ -1298,6 +1366,11 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
|||||||
return b.createMemoryCopyCall(fn, instr.Args)
|
return b.createMemoryCopyCall(fn, instr.Args)
|
||||||
case name == "runtime.memzero":
|
case name == "runtime.memzero":
|
||||||
return b.createMemoryZeroCall(instr.Args)
|
return b.createMemoryZeroCall(instr.Args)
|
||||||
|
case name == "math.Ceil" || name == "math.Floor" || name == "math.Sqrt" || name == "math.Trunc":
|
||||||
|
result, ok := b.createMathOp(instr)
|
||||||
|
if ok {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
|
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
|
||||||
return b.createInlineAsm(instr.Args)
|
return b.createInlineAsm(instr.Args)
|
||||||
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
|
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
|
||||||
@@ -1411,6 +1484,28 @@ func (b *builder) getValue(expr ssa.Value) llvm.Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// maxSliceSize determines the maximum size a slice of the given element type
|
||||||
|
// can be.
|
||||||
|
func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
|
||||||
|
// Calculate ^uintptr(0), which is the max value that fits in uintptr.
|
||||||
|
maxPointerValue := llvm.ConstNot(llvm.ConstInt(c.uintptrType, 0, false)).ZExtValue()
|
||||||
|
// Calculate (^uint(0))/2, which is the max value that fits in an int.
|
||||||
|
maxIntegerValue := llvm.ConstNot(llvm.ConstInt(c.intType, 0, false)).ZExtValue() / 2
|
||||||
|
|
||||||
|
// Determine the maximum allowed size for a slice. The biggest possible
|
||||||
|
// pointer (starting from 0) would be maxPointerValue*sizeof(elementType) so
|
||||||
|
// divide by the element type to get the real maximum size.
|
||||||
|
maxSize := maxPointerValue / c.targetData.TypeAllocSize(elementType)
|
||||||
|
|
||||||
|
// len(slice) is an int. Make sure the length remains small enough to fit in
|
||||||
|
// an int.
|
||||||
|
if maxSize > maxIntegerValue {
|
||||||
|
maxSize = maxIntegerValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxSize
|
||||||
|
}
|
||||||
|
|
||||||
// createExpr translates a Go SSA expression to LLVM IR. This can be zero, one,
|
// createExpr translates a Go SSA expression to LLVM IR. This can be zero, one,
|
||||||
// or multiple LLVM IR instructions and/or runtime calls.
|
// or multiple LLVM IR instructions and/or runtime calls.
|
||||||
func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
||||||
@@ -1624,10 +1719,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
elemSize := b.targetData.TypeAllocSize(llvmElemType)
|
elemSize := b.targetData.TypeAllocSize(llvmElemType)
|
||||||
elemSizeValue := llvm.ConstInt(b.uintptrType, elemSize, false)
|
elemSizeValue := llvm.ConstInt(b.uintptrType, elemSize, false)
|
||||||
|
|
||||||
// Calculate (^uintptr(0)) >> 1, which is the max value that fits in
|
maxSize := b.maxSliceSize(llvmElemType)
|
||||||
// uintptr if uintptr were signed.
|
if elemSize > maxSize {
|
||||||
maxSize := llvm.ConstLShr(llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)), llvm.ConstInt(b.uintptrType, 1, false))
|
|
||||||
if elemSize > maxSize.ZExtValue() {
|
|
||||||
// This seems to be checked by the typechecker already, but let's
|
// This seems to be checked by the typechecker already, but let's
|
||||||
// check it again just to be sure.
|
// check it again just to be sure.
|
||||||
return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("slice element type is too big (%v bytes)", elemSize))
|
return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("slice element type is too big (%v bytes)", elemSize))
|
||||||
@@ -1636,7 +1729,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
// Bounds checking.
|
// Bounds checking.
|
||||||
lenType := expr.Len.Type().Underlying().(*types.Basic)
|
lenType := expr.Len.Type().Underlying().(*types.Basic)
|
||||||
capType := expr.Cap.Type().Underlying().(*types.Basic)
|
capType := expr.Cap.Type().Underlying().(*types.Basic)
|
||||||
b.createSliceBoundsCheck(maxSize, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
|
maxSizeValue := llvm.ConstInt(b.uintptrType, maxSize, false)
|
||||||
|
b.createSliceBoundsCheck(maxSizeValue, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
|
||||||
|
|
||||||
// Allocate the backing array.
|
// Allocate the backing array.
|
||||||
sliceCapCast, err := b.createConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
|
sliceCapCast, err := b.createConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
|
||||||
@@ -1879,6 +1973,17 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
|
|||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(expr.Pos(), "unknown slice type: "+typ.String())
|
return llvm.Value{}, b.makeError(expr.Pos(), "unknown slice type: "+typ.String())
|
||||||
}
|
}
|
||||||
|
case *ssa.SliceToArrayPointer:
|
||||||
|
// Conversion from a slice to an array pointer, as the name clearly
|
||||||
|
// says. This requires a runtime check to make sure the slice is at
|
||||||
|
// least as big as the array.
|
||||||
|
slice := b.getValue(expr.X)
|
||||||
|
sliceLen := b.CreateExtractValue(slice, 1, "")
|
||||||
|
arrayLen := expr.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
|
||||||
|
b.createSliceToArrayPointerCheck(sliceLen, arrayLen)
|
||||||
|
ptr := b.CreateExtractValue(slice, 0, "")
|
||||||
|
ptr = b.CreateBitCast(ptr, b.getLLVMType(expr.Type()), "")
|
||||||
|
return ptr, nil
|
||||||
case *ssa.TypeAssert:
|
case *ssa.TypeAssert:
|
||||||
return b.createTypeAssert(expr), nil
|
return b.createTypeAssert(expr), nil
|
||||||
case *ssa.UnOp:
|
case *ssa.UnOp:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
"github.com/tinygo-org/tinygo/loader"
|
"github.com/tinygo-org/tinygo/loader"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
@@ -16,6 +17,11 @@ import (
|
|||||||
// Pass -update to go test to update the output of the test files.
|
// Pass -update to go test to update the output of the test files.
|
||||||
var flagUpdate = flag.Bool("update", false, "update tests based on test output")
|
var flagUpdate = flag.Bool("update", false, "update tests based on test output")
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
file string
|
||||||
|
target string
|
||||||
|
}
|
||||||
|
|
||||||
// Basic tests for the compiler. Build some Go files and compare the output with
|
// Basic tests for the compiler. Build some Go files and compare the output with
|
||||||
// the expected LLVM IR for regression testing.
|
// the expected LLVM IR for regression testing.
|
||||||
func TestCompiler(t *testing.T) {
|
func TestCompiler(t *testing.T) {
|
||||||
@@ -34,10 +40,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []testCase{
|
||||||
file string
|
|
||||||
target string
|
|
||||||
}{
|
|
||||||
{"basic.go", ""},
|
{"basic.go", ""},
|
||||||
{"pointer.go", ""},
|
{"pointer.go", ""},
|
||||||
{"slice.go", ""},
|
{"slice.go", ""},
|
||||||
@@ -48,6 +51,17 @@ func TestCompiler(t *testing.T) {
|
|||||||
{"pragma.go", ""},
|
{"pragma.go", ""},
|
||||||
{"goroutine.go", "wasm"},
|
{"goroutine.go", "wasm"},
|
||||||
{"goroutine.go", "cortex-m-qemu"},
|
{"goroutine.go", "cortex-m-qemu"},
|
||||||
|
{"channel.go", ""},
|
||||||
|
{"intrinsics.go", "cortex-m-qemu"},
|
||||||
|
{"intrinsics.go", "wasm"},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not read Go version:", err)
|
||||||
|
}
|
||||||
|
if minor >= 17 {
|
||||||
|
tests = append(tests, testCase{"go1.17.go", ""})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
|
|||||||
@@ -48,3 +48,57 @@ func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
|
|||||||
b.CreateCall(llvmFn, params, "")
|
b.CreateCall(llvmFn, params, "")
|
||||||
return llvm.Value{}, nil
|
return llvm.Value{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mathToLLVMMapping = map[string]string{
|
||||||
|
"math.Sqrt": "llvm.sqrt.f64",
|
||||||
|
"math.Floor": "llvm.floor.f64",
|
||||||
|
"math.Ceil": "llvm.ceil.f64",
|
||||||
|
"math.Trunc": "llvm.trunc.f64",
|
||||||
|
}
|
||||||
|
|
||||||
|
// createMathOp tries to lower the given call as a LLVM math intrinsic, if
|
||||||
|
// possible. It returns the call result if possible, and a boolean whether it
|
||||||
|
// succeeded. If it doesn't succeed, the architecture doesn't support the given
|
||||||
|
// intrinsic.
|
||||||
|
func (b *builder) createMathOp(call *ssa.CallCommon) (llvm.Value, bool) {
|
||||||
|
// Check whether this intrinsic is supported on the given GOARCH.
|
||||||
|
// If it is unsupported, this can have two reasons:
|
||||||
|
//
|
||||||
|
// 1. LLVM can expand the intrinsic inline (using float instructions), but
|
||||||
|
// the result doesn't pass the tests of the math package.
|
||||||
|
// 2. LLVM cannot expand the intrinsic inline, will therefore lower it as a
|
||||||
|
// libm function call, but the libm function call also fails the math
|
||||||
|
// package tests.
|
||||||
|
//
|
||||||
|
// Whatever the implementation, it must pass the tests in the math package
|
||||||
|
// so unfortunately only the below intrinsic+architecture combinations are
|
||||||
|
// supported.
|
||||||
|
name := call.StaticCallee().RelString(nil)
|
||||||
|
switch name {
|
||||||
|
case "math.Ceil", "math.Floor", "math.Trunc":
|
||||||
|
if b.GOARCH != "wasm" && b.GOARCH != "arm64" {
|
||||||
|
return llvm.Value{}, false
|
||||||
|
}
|
||||||
|
case "math.Sqrt":
|
||||||
|
if b.GOARCH != "wasm" && b.GOARCH != "amd64" && b.GOARCH != "386" {
|
||||||
|
return llvm.Value{}, false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return llvm.Value{}, false // only the above functions are supported.
|
||||||
|
}
|
||||||
|
|
||||||
|
llvmFn := b.mod.NamedFunction(mathToLLVMMapping[name])
|
||||||
|
if llvmFn.IsNil() {
|
||||||
|
// The intrinsic doesn't exist yet, so declare it.
|
||||||
|
// At the moment, all supported intrinsics have the form "double
|
||||||
|
// foo(double %x)" so we can hardcode the signature here.
|
||||||
|
llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false)
|
||||||
|
llvmFn = llvm.AddFunction(b.mod, mathToLLVMMapping[name], llvmType)
|
||||||
|
}
|
||||||
|
// Create a call to the intrinsic.
|
||||||
|
args := make([]llvm.Value, len(call.Args))
|
||||||
|
for i, arg := range call.Args {
|
||||||
|
args[i] = b.getValue(arg)
|
||||||
|
}
|
||||||
|
return b.CreateCall(llvmFn, args, ""), true
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+15
@@ -55,3 +55,18 @@ func complexMul(x, y complex64) complex64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: complexDiv (requires runtime call)
|
// TODO: complexDiv (requires runtime call)
|
||||||
|
|
||||||
|
// A type 'kv' also exists in function foo. Test that these two types don't
|
||||||
|
// conflict with each other.
|
||||||
|
type kv struct {
|
||||||
|
v float32
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo(a *kv) {
|
||||||
|
// Define a new 'kv' type.
|
||||||
|
type kv struct {
|
||||||
|
v byte
|
||||||
|
}
|
||||||
|
// Use this type.
|
||||||
|
func(b *kv) {}(nil)
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+14
@@ -3,6 +3,9 @@ source_filename = "basic.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "wasm32--wasi"
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
|
%main.kv = type { float }
|
||||||
|
%main.kv.0 = type { i8 }
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
@@ -98,3 +101,14 @@ entry:
|
|||||||
%7 = insertvalue { float, float } %6, float %5, 1
|
%7 = insertvalue { float, float } %6, float %5, 1
|
||||||
ret { float, float } %7
|
ret { float, float } %7
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define hidden void @main.foo(%main.kv* dereferenceable_or_null(4) %a, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @"main.foo$1"(%main.kv.0* null, i8* undef, i8* undef)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @"main.foo$1"(%main.kv.0* dereferenceable_or_null(1) %b, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func chanIntSend(ch chan int) {
|
||||||
|
ch <- 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func chanIntRecv(ch chan int) {
|
||||||
|
<-ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func chanZeroSend(ch chan struct{}) {
|
||||||
|
ch <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func chanZeroRecv(ch chan struct{}) {
|
||||||
|
<-ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectZeroRecv(ch1 chan int, ch2 chan struct{}) {
|
||||||
|
select {
|
||||||
|
case ch1 <- 1:
|
||||||
|
case <-ch2:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+114
@@ -0,0 +1,114 @@
|
|||||||
|
; ModuleID = 'channel.go'
|
||||||
|
source_filename = "channel.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
|
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
|
||||||
|
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
|
||||||
|
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
|
||||||
|
%"internal/task.state" = type { i8* }
|
||||||
|
%runtime.chanSelectState = type { %runtime.channel*, i8* }
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @main.chanIntSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
|
%chan.value = alloca i32, align 4
|
||||||
|
%chan.value.bitcast = bitcast i32* %chan.value to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||||
|
store i32 3, i32* %chan.value, align 4
|
||||||
|
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
call void @runtime.chanSend(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: argmemonly nounwind willreturn
|
||||||
|
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #0
|
||||||
|
|
||||||
|
declare void @runtime.chanSend(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*, i8*)
|
||||||
|
|
||||||
|
; Function Attrs: argmemonly nounwind willreturn
|
||||||
|
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #0
|
||||||
|
|
||||||
|
define hidden void @main.chanIntRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
|
%chan.value = alloca i32, align 4
|
||||||
|
%chan.value.bitcast = bitcast i32* %chan.value to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||||
|
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* nonnull %chan.value.bitcast, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %chan.value.bitcast)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i1 @runtime.chanRecv(%runtime.channel* dereferenceable_or_null(32), i8*, %runtime.channelBlockedList* dereferenceable_or_null(24), i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.chanZeroSend(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
|
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
call void @runtime.chanSend(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @main.chanZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%chan.blockedList = alloca %runtime.channelBlockedList, align 8
|
||||||
|
%chan.blockedList.bitcast = bitcast %runtime.channelBlockedList* %chan.blockedList to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
%0 = call i1 @runtime.chanRecv(%runtime.channel* %ch, i8* null, %runtime.channelBlockedList* nonnull %chan.blockedList, i8* undef, i8* null)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %chan.blockedList.bitcast)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden void @main.selectZeroRecv(%runtime.channel* dereferenceable_or_null(32) %ch1, %runtime.channel* dereferenceable_or_null(32) %ch2, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
|
||||||
|
%select.send.value = alloca i32, align 4
|
||||||
|
store i32 1, i32* %select.send.value, align 4
|
||||||
|
%select.states.alloca.bitcast = bitcast [2 x %runtime.chanSelectState]* %select.states.alloca to i8*
|
||||||
|
call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
|
||||||
|
%.repack = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 0
|
||||||
|
store %runtime.channel* %ch1, %runtime.channel** %.repack, align 8
|
||||||
|
%.repack1 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0, i32 1
|
||||||
|
%0 = bitcast i8** %.repack1 to i32**
|
||||||
|
store i32* %select.send.value, i32** %0, align 4
|
||||||
|
%.repack3 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 0
|
||||||
|
store %runtime.channel* %ch2, %runtime.channel** %.repack3, align 8
|
||||||
|
%.repack4 = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 1, i32 1
|
||||||
|
store i8* null, i8** %.repack4, align 4
|
||||||
|
%select.states = getelementptr inbounds [2 x %runtime.chanSelectState], [2 x %runtime.chanSelectState]* %select.states.alloca, i32 0, i32 0
|
||||||
|
%select.result = call { i32, i1 } @runtime.tryChanSelect(i8* undef, %runtime.chanSelectState* nonnull %select.states, i32 2, i32 2, i8* undef, i8* null)
|
||||||
|
call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %select.states.alloca.bitcast)
|
||||||
|
%1 = extractvalue { i32, i1 } %select.result, 0
|
||||||
|
%2 = icmp eq i32 %1, 0
|
||||||
|
br i1 %2, label %select.done, label %select.next
|
||||||
|
|
||||||
|
select.done: ; preds = %select.body, %select.next, %entry
|
||||||
|
ret void
|
||||||
|
|
||||||
|
select.next: ; preds = %entry
|
||||||
|
%3 = icmp eq i32 %1, 1
|
||||||
|
br i1 %3, label %select.body, label %select.done
|
||||||
|
|
||||||
|
select.body: ; preds = %select.next
|
||||||
|
br label %select.done
|
||||||
|
}
|
||||||
|
|
||||||
|
declare { i32, i1 } @runtime.tryChanSelect(i8*, %runtime.chanSelectState*, i32, i32, i8*, i8*)
|
||||||
|
|
||||||
|
attributes #0 = { argmemonly nounwind willreturn }
|
||||||
Vendored
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Test changes to the language introduced in Go 1.17.
|
||||||
|
// For details, see: https://tip.golang.org/doc/go1.17#language
|
||||||
|
// These tests should be merged into the regular slice tests once Go 1.17 is the
|
||||||
|
// minimun Go version for TinyGo.
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
func Add32(p unsafe.Pointer, len int) unsafe.Pointer {
|
||||||
|
return unsafe.Add(p, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Add64(p unsafe.Pointer, len int64) unsafe.Pointer {
|
||||||
|
return unsafe.Add(p, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceToArray(s []int) *[4]int {
|
||||||
|
return (*[4]int)(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceToArrayConst() *[4]int {
|
||||||
|
s := make([]int, 6)
|
||||||
|
return (*[4]int)(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceInt(ptr *int, len int) []int {
|
||||||
|
return unsafe.Slice(ptr, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceUint16(ptr *byte, len uint16) []byte {
|
||||||
|
return unsafe.Slice(ptr, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceUint64(ptr *int, len uint64) []int {
|
||||||
|
return unsafe.Slice(ptr, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceInt64(ptr *int, len int64) []int {
|
||||||
|
return unsafe.Slice(ptr, len)
|
||||||
|
}
|
||||||
Vendored
+136
@@ -0,0 +1,136 @@
|
|||||||
|
; ModuleID = 'go1.17.go'
|
||||||
|
source_filename = "go1.17.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden i8* @main.Add32(i8* %p, i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = getelementptr i8, i8* %p, i32 %len
|
||||||
|
ret i8* %0
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden i8* @main.Add64(i8* %p, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = trunc i64 %len to i32
|
||||||
|
%1 = getelementptr i8, i8* %p, i32 %0
|
||||||
|
ret i8* %1
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden [4 x i32]* @main.SliceToArray(i32* %s.data, i32 %s.len, i32 %s.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = icmp ult i32 %s.len, 4
|
||||||
|
br i1 %0, label %slicetoarray.throw, label %slicetoarray.next
|
||||||
|
|
||||||
|
slicetoarray.throw: ; preds = %entry
|
||||||
|
call void @runtime.sliceToArrayPointerPanic(i8* undef, i8* null)
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
slicetoarray.next: ; preds = %entry
|
||||||
|
%1 = bitcast i32* %s.data to [4 x i32]*
|
||||||
|
ret [4 x i32]* %1
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.sliceToArrayPointerPanic(i8*, i8*)
|
||||||
|
|
||||||
|
define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%makeslice = call i8* @runtime.alloc(i32 24, i8* undef, i8* null)
|
||||||
|
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
|
||||||
|
|
||||||
|
slicetoarray.throw: ; preds = %entry
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
slicetoarray.next: ; preds = %entry
|
||||||
|
%0 = bitcast i8* %makeslice to [4 x i32]*
|
||||||
|
ret [4 x i32]* %0
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden { i32*, i32, i32 } @main.SliceInt(i32* dereferenceable_or_null(4) %ptr, i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = icmp ugt i32 %len, 1073741823
|
||||||
|
%1 = icmp eq i32* %ptr, null
|
||||||
|
%2 = icmp ne i32 %len, 0
|
||||||
|
%3 = and i1 %1, %2
|
||||||
|
%4 = or i1 %3, %0
|
||||||
|
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
unsafe.Slice.next: ; preds = %entry
|
||||||
|
%5 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||||
|
%6 = insertvalue { i32*, i32, i32 } %5, i32 %len, 1
|
||||||
|
%7 = insertvalue { i32*, i32, i32 } %6, i32 %len, 2
|
||||||
|
ret { i32*, i32, i32 } %7
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.unsafeSlicePanic(i8*, i8*)
|
||||||
|
|
||||||
|
define hidden { i8*, i32, i32 } @main.SliceUint16(i8* dereferenceable_or_null(1) %ptr, i16 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = icmp eq i8* %ptr, null
|
||||||
|
%1 = icmp ne i16 %len, 0
|
||||||
|
%2 = and i1 %0, %1
|
||||||
|
br i1 %2, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
unsafe.Slice.next: ; preds = %entry
|
||||||
|
%3 = zext i16 %len to i32
|
||||||
|
%4 = insertvalue { i8*, i32, i32 } undef, i8* %ptr, 0
|
||||||
|
%5 = insertvalue { i8*, i32, i32 } %4, i32 %3, 1
|
||||||
|
%6 = insertvalue { i8*, i32, i32 } %5, i32 %3, 2
|
||||||
|
ret { i8*, i32, i32 } %6
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden { i32*, i32, i32 } @main.SliceUint64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = icmp ugt i64 %len, 1073741823
|
||||||
|
%1 = icmp eq i32* %ptr, null
|
||||||
|
%2 = icmp ne i64 %len, 0
|
||||||
|
%3 = and i1 %1, %2
|
||||||
|
%4 = or i1 %3, %0
|
||||||
|
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
unsafe.Slice.next: ; preds = %entry
|
||||||
|
%5 = trunc i64 %len to i32
|
||||||
|
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||||
|
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
||||||
|
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
||||||
|
ret { i32*, i32, i32 } %8
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden { i32*, i32, i32 } @main.SliceInt64(i32* dereferenceable_or_null(4) %ptr, i64 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = icmp ugt i64 %len, 1073741823
|
||||||
|
%1 = icmp eq i32* %ptr, null
|
||||||
|
%2 = icmp ne i64 %len, 0
|
||||||
|
%3 = and i1 %1, %2
|
||||||
|
%4 = or i1 %3, %0
|
||||||
|
br i1 %4, label %unsafe.Slice.throw, label %unsafe.Slice.next
|
||||||
|
|
||||||
|
unsafe.Slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.unsafeSlicePanic(i8* undef, i8* null)
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
unsafe.Slice.next: ; preds = %entry
|
||||||
|
%5 = trunc i64 %len to i32
|
||||||
|
%6 = insertvalue { i32*, i32, i32 } undef, i32* %ptr, 0
|
||||||
|
%7 = insertvalue { i32*, i32, i32 } %6, i32 %5, 1
|
||||||
|
%8 = insertvalue { i32*, i32, i32 } %7, i32 %5, 2
|
||||||
|
ret { i32*, i32, i32 } %8
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
; ModuleID = 'intrinsics.go'
|
||||||
|
source_filename = "intrinsics.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||||
|
target triple = "armv7m-none-eabi"
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden double @main.mySqrt(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = call double @math.Sqrt(double %x, i8* undef, i8* undef)
|
||||||
|
ret double %0
|
||||||
|
}
|
||||||
|
|
||||||
|
declare double @math.Sqrt(double, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden double @main.myTrunc(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = call double @math.Trunc(double %x, i8* undef, i8* undef)
|
||||||
|
ret double %0
|
||||||
|
}
|
||||||
|
|
||||||
|
declare double @math.Trunc(double, i8*, i8*)
|
||||||
Vendored
+31
@@ -0,0 +1,31 @@
|
|||||||
|
; ModuleID = 'intrinsics.go'
|
||||||
|
source_filename = "intrinsics.go"
|
||||||
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
|
target triple = "wasm32--wasi"
|
||||||
|
|
||||||
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden double @main.mySqrt(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = call double @llvm.sqrt.f64(double %x)
|
||||||
|
ret double %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind readnone speculatable willreturn
|
||||||
|
declare double @llvm.sqrt.f64(double) #0
|
||||||
|
|
||||||
|
define hidden double @main.myTrunc(double %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%0 = call double @llvm.trunc.f64(double %x)
|
||||||
|
ret double %0
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind readnone speculatable willreturn
|
||||||
|
declare double @llvm.trunc.f64(double) #0
|
||||||
|
|
||||||
|
attributes #0 = { nounwind readnone speculatable willreturn }
|
||||||
Vendored
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Test how intrinsics are lowered: either as regular calls to the math
|
||||||
|
// functions or as LLVM builtins (such as llvm.sqrt.f64).
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
func mySqrt(x float64) float64 {
|
||||||
|
return math.Sqrt(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func myTrunc(x float64) float64 {
|
||||||
|
return math.Trunc(x)
|
||||||
|
}
|
||||||
Vendored
+18
@@ -23,3 +23,21 @@ func sliceAppendSlice(ints, added []int) []int {
|
|||||||
func sliceCopy(dst, src []int) int {
|
func sliceCopy(dst, src []int) int {
|
||||||
return copy(dst, src)
|
return copy(dst, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test bounds checking in *ssa.MakeSlice instruction.
|
||||||
|
|
||||||
|
func makeByteSlice(len int) []byte {
|
||||||
|
return make([]byte, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeInt16Slice(len int) []int16 {
|
||||||
|
return make([]int16, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeArraySlice(len int) [][3]byte {
|
||||||
|
return make([][3]byte, len) // slice with element size of 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeInt32Slice(len int) []int32 {
|
||||||
|
return make([]int32, len)
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+76
@@ -86,3 +86,79 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
declare i32 @runtime.sliceCopy(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i32, i8*, i8*)
|
||||||
|
|
||||||
|
define hidden { i8*, i32, i32 } @main.makeByteSlice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%slice.maxcap = icmp slt i32 %len, 0
|
||||||
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
|
slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.slicePanic(i8* undef, i8* null)
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
slice.next: ; preds = %entry
|
||||||
|
%makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* undef, i8* null)
|
||||||
|
%0 = insertvalue { i8*, i32, i32 } undef, i8* %makeslice.buf, 0
|
||||||
|
%1 = insertvalue { i8*, i32, i32 } %0, i32 %len, 1
|
||||||
|
%2 = insertvalue { i8*, i32, i32 } %1, i32 %len, 2
|
||||||
|
ret { i8*, i32, i32 } %2
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @runtime.slicePanic(i8*, i8*)
|
||||||
|
|
||||||
|
define hidden { i16*, i32, i32 } @main.makeInt16Slice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%slice.maxcap = icmp slt i32 %len, 0
|
||||||
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
|
slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.slicePanic(i8* undef, i8* null)
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
slice.next: ; preds = %entry
|
||||||
|
%makeslice.cap = shl i32 %len, 1
|
||||||
|
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* undef, i8* null)
|
||||||
|
%makeslice.array = bitcast i8* %makeslice.buf to i16*
|
||||||
|
%0 = insertvalue { i16*, i32, i32 } undef, i16* %makeslice.array, 0
|
||||||
|
%1 = insertvalue { i16*, i32, i32 } %0, i32 %len, 1
|
||||||
|
%2 = insertvalue { i16*, i32, i32 } %1, i32 %len, 2
|
||||||
|
ret { i16*, i32, i32 } %2
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden { [3 x i8]*, i32, i32 } @main.makeArraySlice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%slice.maxcap = icmp ugt i32 %len, 1431655765
|
||||||
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
|
slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.slicePanic(i8* undef, i8* null)
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
slice.next: ; preds = %entry
|
||||||
|
%makeslice.cap = mul i32 %len, 3
|
||||||
|
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* undef, i8* null)
|
||||||
|
%makeslice.array = bitcast i8* %makeslice.buf to [3 x i8]*
|
||||||
|
%0 = insertvalue { [3 x i8]*, i32, i32 } undef, [3 x i8]* %makeslice.array, 0
|
||||||
|
%1 = insertvalue { [3 x i8]*, i32, i32 } %0, i32 %len, 1
|
||||||
|
%2 = insertvalue { [3 x i8]*, i32, i32 } %1, i32 %len, 2
|
||||||
|
ret { [3 x i8]*, i32, i32 } %2
|
||||||
|
}
|
||||||
|
|
||||||
|
define hidden { i32*, i32, i32 } @main.makeInt32Slice(i32 %len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
%slice.maxcap = icmp ugt i32 %len, 1073741823
|
||||||
|
br i1 %slice.maxcap, label %slice.throw, label %slice.next
|
||||||
|
|
||||||
|
slice.throw: ; preds = %entry
|
||||||
|
call void @runtime.slicePanic(i8* undef, i8* null)
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
slice.next: ; preds = %entry
|
||||||
|
%makeslice.cap = shl i32 %len, 2
|
||||||
|
%makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* undef, i8* null)
|
||||||
|
%makeslice.array = bitcast i8* %makeslice.buf to i32*
|
||||||
|
%0 = insertvalue { i32*, i32, i32 } undef, i32* %makeslice.array, 0
|
||||||
|
%1 = insertvalue { i32*, i32, i32 } %0, i32 %len, 1
|
||||||
|
%2 = insertvalue { i32*, i32, i32 } %1, i32 %len, 2
|
||||||
|
ret { i32*, i32, i32 } %2
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
module github.com/tinygo-org/tinygo
|
module github.com/tinygo-org/tinygo
|
||||||
|
|
||||||
go 1.13
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
|
||||||
@@ -10,7 +10,7 @@ require (
|
|||||||
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
|
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
|
||||||
github.com/mattn/go-colorable v0.1.8
|
github.com/mattn/go-colorable v0.1.8
|
||||||
go.bug.st/serial v1.1.2
|
go.bug.st/serial v1.1.2
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
|
||||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
|
tinygo.org/x/go-llvm v0.0.0-20210325115028-e7b85195e81c
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.bug.st/serial v1.0.0 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k=
|
go.bug.st/serial v1.0.0 h1:ogEPzrllCsnG00EqKRjeYvPRsO7NJW6DqykzkdD6E/k=
|
||||||
go.bug.st/serial v1.0.0/go.mod h1:rpXPISGjuNjPTRTcMlxi9lN6LoIPxd1ixVjBd8aSk/Q=
|
go.bug.st/serial v1.0.0/go.mod h1:rpXPISGjuNjPTRTcMlxi9lN6LoIPxd1ixVjBd8aSk/Q=
|
||||||
go.bug.st/serial v1.1.2 h1:6xDpbta8KJ+VLRTeM8ghhxXRMLE/Lr8h9iDKwydarAY=
|
go.bug.st/serial v1.1.2 h1:6xDpbta8KJ+VLRTeM8ghhxXRMLE/Lr8h9iDKwydarAY=
|
||||||
@@ -38,23 +40,32 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
|
|
||||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 h1:0sfSpGSa544Fwnbot3Oxq/U6SXqjty6Jy/3wRhVS7ig=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9 h1:nvvuMxmx1q0gfRki3T0hjG8EwAcVCs91oWAXvyt4zhI=
|
||||||
|
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
|
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
|
||||||
|
|||||||
+5
-5
@@ -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.20.0-dev"
|
const Version = "0.20.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.
|
||||||
@@ -48,7 +48,10 @@ func GetGorootVersion(goroot string) (major, minor int, err error) {
|
|||||||
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
|
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
|
||||||
// can have some variations (for beta releases, for example).
|
// can have some variations (for beta releases, for example).
|
||||||
func GorootVersionString(goroot string) (string, error) {
|
func GorootVersionString(goroot string) (string, error) {
|
||||||
if data, err := ioutil.ReadFile(filepath.Join(
|
if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
||||||
|
return string(data), nil
|
||||||
|
|
||||||
|
} else if data, err := ioutil.ReadFile(filepath.Join(
|
||||||
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
|
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
|
||||||
|
|
||||||
r := regexp.MustCompile("const TheVersion = `(.*)`")
|
r := regexp.MustCompile("const TheVersion = `(.*)`")
|
||||||
@@ -59,9 +62,6 @@ func GorootVersionString(goroot string) (string, error) {
|
|||||||
|
|
||||||
return string(matches[1]), nil
|
return string(matches[1]), nil
|
||||||
|
|
||||||
} else if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
|
|
||||||
return string(data), nil
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ func (r *runner) errorAt(inst instruction, err error) *Error {
|
|||||||
pos := getPosition(inst.llvmInst)
|
pos := getPosition(inst.llvmInst)
|
||||||
return &Error{
|
return &Error{
|
||||||
ImportPath: r.pkgName,
|
ImportPath: r.pkgName,
|
||||||
|
Inst: inst.llvmInst,
|
||||||
Pos: pos,
|
Pos: pos,
|
||||||
Err: err,
|
Err: err,
|
||||||
Traceback: []ErrorLine{{pos, inst.llvmInst}},
|
Traceback: []ErrorLine{{pos, inst.llvmInst}},
|
||||||
|
|||||||
+27
-2
@@ -15,7 +15,7 @@ import (
|
|||||||
// package is changed in a way that affects the output so that cached package
|
// package is changed in a way that affects the output so that cached package
|
||||||
// builds will be invalidated.
|
// builds will be invalidated.
|
||||||
// This version is independent of the TinyGo version number.
|
// This version is independent of the TinyGo version number.
|
||||||
const Version = 1
|
const Version = 2 // last change: fix GEP on untyped pointers
|
||||||
|
|
||||||
// Enable extra checks, which should be disabled by default.
|
// Enable extra checks, which should be disabled by default.
|
||||||
// This may help track down bugs by adding a few more sanity checks.
|
// This may help track down bugs by adding a few more sanity checks.
|
||||||
@@ -110,17 +110,26 @@ func Run(mod llvm.Module, debug bool) error {
|
|||||||
fmt.Fprintln(os.Stderr, "call:", fn.Name())
|
fmt.Fprintln(os.Stderr, "call:", fn.Name())
|
||||||
}
|
}
|
||||||
_, mem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
|
_, mem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
|
||||||
|
call.EraseFromParentAsInstruction()
|
||||||
if callErr != nil {
|
if callErr != nil {
|
||||||
if isRecoverableError(callErr.Err) {
|
if isRecoverableError(callErr.Err) {
|
||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error())
|
fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error())
|
||||||
}
|
}
|
||||||
|
// Remove instructions that were created as part of interpreting
|
||||||
|
// the package.
|
||||||
mem.revert()
|
mem.revert()
|
||||||
|
// Create a call to the package initializer (which was
|
||||||
|
// previously deleted).
|
||||||
|
i8undef := llvm.Undef(r.i8ptrType)
|
||||||
|
r.builder.CreateCall(fn, []llvm.Value{i8undef, i8undef}, "")
|
||||||
|
// Make sure that any globals touched by the package
|
||||||
|
// initializer, won't be accessed by later package initializers.
|
||||||
|
r.markExternalLoad(fn)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return callErr
|
return callErr
|
||||||
}
|
}
|
||||||
call.EraseFromParentAsInstruction()
|
|
||||||
for index, obj := range mem.objects {
|
for index, obj := range mem.objects {
|
||||||
r.objects[index] = obj
|
r.objects[index] = obj
|
||||||
}
|
}
|
||||||
@@ -270,3 +279,19 @@ func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
|||||||
r.functionCache[llvmFn] = fn
|
r.functionCache[llvmFn] = fn
|
||||||
return fn
|
return fn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// markExternalLoad marks the given llvmValue as being loaded externally. This
|
||||||
|
// is primarily used to mark package initializers that could not be run at
|
||||||
|
// compile time. As an example, a package initialize might store to a global
|
||||||
|
// variable. Another package initializer might read from the same global
|
||||||
|
// variable. By marking this function as being run at runtime, that load
|
||||||
|
// instruction will need to be run at runtime instead of at compile time.
|
||||||
|
func (r *runner) markExternalLoad(llvmValue llvm.Value) {
|
||||||
|
mem := memoryView{r: r}
|
||||||
|
mem.markExternalLoad(llvmValue)
|
||||||
|
for index, obj := range mem.objects {
|
||||||
|
if obj.marked > r.objects[index].marked {
|
||||||
|
r.objects[index].marked = obj.marked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ func TestInterp(t *testing.T) {
|
|||||||
"slice-copy",
|
"slice-copy",
|
||||||
"consteval",
|
"consteval",
|
||||||
"interface",
|
"interface",
|
||||||
|
"revert",
|
||||||
} {
|
} {
|
||||||
name := name // make tc local to this closure
|
name := name // make tc local to this closure
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -590,18 +590,14 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
// GetElementPtr does pointer arithmetic, changing the offset of the
|
// GetElementPtr does pointer arithmetic, changing the offset of the
|
||||||
// pointer into the underlying object.
|
// pointer into the underlying object.
|
||||||
var offset uint64
|
var offset uint64
|
||||||
var gepOperands []uint64
|
|
||||||
for i := 2; i < len(operands); i += 2 {
|
for i := 2; i < len(operands); i += 2 {
|
||||||
index := operands[i].Uint()
|
index := operands[i].Uint()
|
||||||
elementSize := operands[i+1].Uint()
|
elementSize := operands[i+1].Uint()
|
||||||
if int64(elementSize) < 0 {
|
if int64(elementSize) < 0 {
|
||||||
// This is a struct field.
|
// This is a struct field.
|
||||||
// The field number is encoded by flipping all the bits.
|
|
||||||
gepOperands = append(gepOperands, ^elementSize)
|
|
||||||
offset += index
|
offset += index
|
||||||
} else {
|
} else {
|
||||||
// This is a normal GEP, probably an array index.
|
// This is a normal GEP, probably an array index.
|
||||||
gepOperands = append(gepOperands, index)
|
|
||||||
offset += elementSize * index
|
offset += elementSize * index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -572,6 +572,17 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
|||||||
}
|
}
|
||||||
|
|
||||||
if llvmType.IsNil() {
|
if llvmType.IsNil() {
|
||||||
|
if v.offset() != 0 {
|
||||||
|
// If there is an offset, make sure to use a GEP to index into the
|
||||||
|
// pointer. Because there is no expected type, we use whatever is
|
||||||
|
// most convenient: an *i8 type. It is trivial to index byte-wise.
|
||||||
|
if llvmValue.Type() != mem.r.i8ptrType {
|
||||||
|
llvmValue = llvm.ConstBitCast(llvmValue, mem.r.i8ptrType)
|
||||||
|
}
|
||||||
|
llvmValue = llvm.ConstInBoundsGEP(llvmValue, []llvm.Value{
|
||||||
|
llvm.ConstInt(llvmValue.Type().Context().Int32Type(), uint64(v.offset()), false),
|
||||||
|
})
|
||||||
|
}
|
||||||
return llvmValue, nil
|
return llvmValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+32
@@ -0,0 +1,32 @@
|
|||||||
|
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
target triple = "x86_64--linux"
|
||||||
|
|
||||||
|
declare void @externalCall(i64)
|
||||||
|
|
||||||
|
@foo.knownAtRuntime = global i64 0
|
||||||
|
@bar.knownAtRuntime = global i64 0
|
||||||
|
|
||||||
|
define void @runtime.initAll() unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @foo.init(i8* undef, i8* undef)
|
||||||
|
call void @bar.init(i8* undef, i8* undef)
|
||||||
|
call void @main.init(i8* undef, i8* undef)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
store i64 5, i64* @foo.knownAtRuntime
|
||||||
|
unreachable ; this triggers a revert of @foo.init.
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @bar.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
%val = load i64, i64* @foo.knownAtRuntime
|
||||||
|
store i64 %val, i64* @bar.knownAtRuntime
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call void @externalCall(i64 3)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
target triple = "x86_64--linux"
|
||||||
|
|
||||||
|
@foo.knownAtRuntime = local_unnamed_addr global i64 0
|
||||||
|
@bar.knownAtRuntime = local_unnamed_addr global i64 0
|
||||||
|
|
||||||
|
declare void @externalCall(i64) local_unnamed_addr
|
||||||
|
|
||||||
|
define void @runtime.initAll() unnamed_addr {
|
||||||
|
entry:
|
||||||
|
call fastcc void @foo.init(i8* undef, i8* undef)
|
||||||
|
%val = load i64, i64* @foo.knownAtRuntime, align 8
|
||||||
|
store i64 %val, i64* @bar.knownAtRuntime, align 8
|
||||||
|
call void @externalCall(i64 3)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal fastcc void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||||
|
store i64 5, i64* @foo.knownAtRuntime, align 8
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
+1
-1
Submodule lib/cmsis-svd updated: 9c35b6d9df...df75ff974c
+1
-1
@@ -208,7 +208,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
|
|||||||
// with the TinyGo version. This is the case on some targets.
|
// with the TinyGo version. This is the case on some targets.
|
||||||
func needsSyscallPackage(buildTags []string) bool {
|
func needsSyscallPackage(buildTags []string) bool {
|
||||||
for _, tag := range buildTags {
|
for _, tag := range buildTags {
|
||||||
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" || tag == "wasi" {
|
if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" || tag == "tinygo.wasm" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -386,7 +386,11 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do CGo processing.
|
// Do CGo processing.
|
||||||
if len(p.CgoFiles) != 0 {
|
// This is done when there are any CgoFiles at all. In that case, len(files)
|
||||||
|
// should be non-zero. However, if len(GoFiles) == 0 and len(CgoFiles) == 1
|
||||||
|
// and there is a syntax error in a CGo file, len(files) may be 0. Don't try
|
||||||
|
// to call cgo.Process in that case as it will only cause issues.
|
||||||
|
if len(p.CgoFiles) != 0 && len(files) != 0 {
|
||||||
var initialCFlags []string
|
var initialCFlags []string
|
||||||
initialCFlags = append(initialCFlags, p.program.config.CFlags()...)
|
initialCFlags = append(initialCFlags, p.program.config.CFlags()...)
|
||||||
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"runtime/pprof"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@@ -158,7 +159,7 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
|
|||||||
|
|
||||||
// Test runs the tests in the given package. Returns whether the test passed and
|
// Test runs the tests in the given package. Returns whether the test passed and
|
||||||
// possibly an error if the test failed to run.
|
// possibly an error if the test failed to run.
|
||||||
func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, outpath string) (bool, error) {
|
func Test(pkgName string, options *compileopts.Options, testCompileOnly, testVerbose bool, outpath string) (bool, error) {
|
||||||
options.TestConfig.CompileTestBinary = true
|
options.TestConfig.CompileTestBinary = true
|
||||||
config, err := builder.NewConfig(options)
|
config, err := builder.NewConfig(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -184,7 +185,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
|
|||||||
// Run the test.
|
// Run the test.
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
var err error
|
var err error
|
||||||
passed, err = runPackageTest(config, result)
|
passed, err = runPackageTest(config, result, testVerbose)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -210,10 +211,14 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou
|
|||||||
// runPackageTest runs a test binary that was previously built. The return
|
// runPackageTest runs a test binary that was previously built. The return
|
||||||
// values are whether the test passed and any errors encountered while trying to
|
// values are whether the test passed and any errors encountered while trying to
|
||||||
// run the binary.
|
// run the binary.
|
||||||
func runPackageTest(config *compileopts.Config, result builder.BuildResult) (bool, error) {
|
func runPackageTest(config *compileopts.Config, result builder.BuildResult, testVerbose bool) (bool, error) {
|
||||||
if len(config.Target.Emulator) == 0 {
|
if len(config.Target.Emulator) == 0 {
|
||||||
// Run directly.
|
// Run directly.
|
||||||
cmd := executeCommand(config.Options, result.Binary)
|
var flags []string
|
||||||
|
if testVerbose {
|
||||||
|
flags = append(flags, "-test.v")
|
||||||
|
}
|
||||||
|
cmd := executeCommand(config.Options, result.Binary, flags...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
cmd.Dir = result.MainDir
|
cmd.Dir = result.MainDir
|
||||||
@@ -229,6 +234,7 @@ func runPackageTest(config *compileopts.Config, result builder.BuildResult) (boo
|
|||||||
return true, nil
|
return true, nil
|
||||||
} else {
|
} else {
|
||||||
// Run in an emulator.
|
// Run in an emulator.
|
||||||
|
// TODO: pass the -test.v flag if needed.
|
||||||
args := append(config.Target.Emulator[1:], result.Binary)
|
args := append(config.Target.Emulator[1:], result.Binary)
|
||||||
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
|
cmd := executeCommand(config.Options, config.Target.Emulator[0], args...)
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
@@ -288,6 +294,8 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
fileExt = filepath.Ext(config.Target.FlashFilename)
|
fileExt = filepath.Ext(config.Target.FlashFilename)
|
||||||
case "openocd":
|
case "openocd":
|
||||||
fileExt = ".hex"
|
fileExt = ".hex"
|
||||||
|
case "bmp":
|
||||||
|
fileExt = ".elf"
|
||||||
case "native":
|
case "native":
|
||||||
return errors.New("unknown flash method \"native\" - did you miss a -target flag?")
|
return errors.New("unknown flash method \"native\" - did you miss a -target flag?")
|
||||||
default:
|
default:
|
||||||
@@ -380,6 +388,25 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
|
|||||||
return &commandError{"failed to flash", result.Binary, err}
|
return &commandError{"failed to flash", result.Binary, err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
case "bmp":
|
||||||
|
gdb, err := config.Target.LookupGDB()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var bmpGDBPort string
|
||||||
|
bmpGDBPort, _, err = getBMPPorts()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
args := []string{"-ex", "target extended-remote " + bmpGDBPort, "-ex", "monitor swdp_scan", "-ex", "attach 1", "-ex", "load", filepath.ToSlash(result.Binary)}
|
||||||
|
cmd := executeCommand(config.Options, gdb, args...)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return &commandError{"failed to flash", result.Binary, err}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown flash method: %s", flashMethod)
|
return fmt.Errorf("unknown flash method: %s", flashMethod)
|
||||||
}
|
}
|
||||||
@@ -434,6 +461,13 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
|||||||
switch gdbInterface {
|
switch gdbInterface {
|
||||||
case "native":
|
case "native":
|
||||||
// Run GDB directly.
|
// Run GDB directly.
|
||||||
|
case "bmp":
|
||||||
|
var bmpGDBPort string
|
||||||
|
bmpGDBPort, _, err = getBMPPorts()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gdbCommands = append(gdbCommands, "target extended-remote "+bmpGDBPort, "monitor swdp_scan", "compare-sections", "attach 1", "load")
|
||||||
case "openocd":
|
case "openocd":
|
||||||
gdbCommands = append(gdbCommands, "target extended-remote :3333", "monitor halt", "load", "monitor reset halt")
|
gdbCommands = append(gdbCommands, "target extended-remote :3333", "monitor halt", "load", "monitor reset halt")
|
||||||
|
|
||||||
@@ -841,6 +875,35 @@ func getDefaultPort(portFlag string, usbInterfaces []string) (port string, err e
|
|||||||
return "", errors.New("port you specified '" + strings.Join(portCandidates, ",") + "' does not exist, available ports are " + strings.Join(ports, ", "))
|
return "", errors.New("port you specified '" + strings.Join(portCandidates, ",") + "' does not exist, available ports are " + strings.Join(ports, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getBMPPorts returns BlackMagicProbe's serial ports if any
|
||||||
|
func getBMPPorts() (gdbPort, uartPort string, err error) {
|
||||||
|
var portsList []*enumerator.PortDetails
|
||||||
|
portsList, err = enumerator.GetDetailedPortsList()
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
var ports []string
|
||||||
|
for _, p := range portsList {
|
||||||
|
if !p.IsUSB {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if p.VID != "" && p.PID != "" {
|
||||||
|
vid, vidErr := strconv.ParseUint(p.VID, 16, 16)
|
||||||
|
pid, pidErr := strconv.ParseUint(p.PID, 16, 16)
|
||||||
|
if vidErr == nil && pidErr == nil && vid == 0x1d50 && pid == 0x6018 {
|
||||||
|
ports = append(ports, p.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(ports) == 2 {
|
||||||
|
return ports[0], ports[1], nil
|
||||||
|
} else if len(ports) == 0 {
|
||||||
|
return "", "", errors.New("no BMP detected")
|
||||||
|
} else {
|
||||||
|
return "", "", fmt.Errorf("expected 2 BMP serial ports, found %d - did you perhaps connect more than one BMP?", len(ports))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
@@ -1019,7 +1082,7 @@ func main() {
|
|||||||
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
|
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
|
||||||
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
|
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
|
||||||
printCommands := flag.Bool("x", false, "Print commands")
|
printCommands := flag.Bool("x", false, "Print commands")
|
||||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
nodebug := flag.Bool("no-debug", false, "strip debug information")
|
||||||
ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)")
|
ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)")
|
||||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||||
port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)")
|
port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)")
|
||||||
@@ -1027,6 +1090,7 @@ func main() {
|
|||||||
ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags")
|
ldflags := flag.String("ldflags", "", "Go link tool compatible ldflags")
|
||||||
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic")
|
wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic")
|
||||||
llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable")
|
llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable")
|
||||||
|
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
|
||||||
|
|
||||||
var flagJSON, flagDeps, flagTest *bool
|
var flagJSON, flagDeps, flagTest *bool
|
||||||
if command == "help" || command == "list" {
|
if command == "help" || command == "list" {
|
||||||
@@ -1038,9 +1102,10 @@ func main() {
|
|||||||
if command == "help" || command == "build" || command == "build-library" || command == "test" {
|
if command == "help" || command == "build" || command == "build-library" || command == "test" {
|
||||||
flag.StringVar(&outpath, "o", "", "output filename")
|
flag.StringVar(&outpath, "o", "", "output filename")
|
||||||
}
|
}
|
||||||
var testCompileOnlyFlag *bool
|
var testCompileOnlyFlag, testVerboseFlag *bool
|
||||||
if command == "help" || command == "test" {
|
if command == "help" || command == "test" {
|
||||||
testCompileOnlyFlag = flag.Bool("c", false, "compile the test binary but do not run it")
|
testCompileOnlyFlag = flag.Bool("c", false, "compile the test binary but do not run it")
|
||||||
|
testVerboseFlag = flag.Bool("v", false, "verbose: print additional output")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Early command processing, before commands are interpreted by the Go flag
|
// Early command processing, before commands are interpreted by the Go flag
|
||||||
@@ -1110,6 +1175,20 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *cpuprofile != "" {
|
||||||
|
f, err := os.Create(*cpuprofile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "could not create CPU profile: ", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
if err := pprof.StartCPUProfile(f); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "could not start CPU profile: ", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer pprof.StopCPUProfile()
|
||||||
|
}
|
||||||
|
|
||||||
switch command {
|
switch command {
|
||||||
case "build":
|
case "build":
|
||||||
if outpath == "" {
|
if outpath == "" {
|
||||||
@@ -1201,7 +1280,7 @@ func main() {
|
|||||||
allTestsPassed := true
|
allTestsPassed := true
|
||||||
for _, pkgName := range pkgNames {
|
for _, pkgName := range pkgNames {
|
||||||
// TODO: parallelize building the test binaries
|
// TODO: parallelize building the test binaries
|
||||||
passed, err := Test(pkgName, options, *testCompileOnlyFlag, outpath)
|
passed, err := Test(pkgName, options, *testCompileOnlyFlag, *testVerboseFlag, outpath)
|
||||||
handleCompilerError(err)
|
handleCompilerError(err)
|
||||||
if !passed {
|
if !passed {
|
||||||
allTestsPassed = false
|
allTestsPassed = false
|
||||||
|
|||||||
+72
-49
@@ -51,9 +51,18 @@ func TestCompiler(t *testing.T) {
|
|||||||
"stdlib.go",
|
"stdlib.go",
|
||||||
"string.go",
|
"string.go",
|
||||||
"structs.go",
|
"structs.go",
|
||||||
|
"testing.go",
|
||||||
"zeroalloc.go",
|
"zeroalloc.go",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not read version from GOROOT:", err)
|
||||||
|
}
|
||||||
|
if minor >= 17 {
|
||||||
|
tests = append(tests, "go1.17.go")
|
||||||
|
}
|
||||||
|
|
||||||
if *testTarget != "" {
|
if *testTarget != "" {
|
||||||
// This makes it possible to run one specific test (instead of all),
|
// This makes it possible to run one specific test (instead of all),
|
||||||
// which is especially useful to quickly check whether some changes
|
// which is especially useful to quickly check whether some changes
|
||||||
@@ -94,21 +103,9 @@ func TestCompiler(t *testing.T) {
|
|||||||
t.Run("ARM64Linux", func(t *testing.T) {
|
t.Run("ARM64Linux", func(t *testing.T) {
|
||||||
runPlatTests("aarch64--linux-gnu", tests, t)
|
runPlatTests("aarch64--linux-gnu", tests, t)
|
||||||
})
|
})
|
||||||
goVersion, err := goenv.GorootVersionString(goenv.Get("GOROOT"))
|
|
||||||
if err != nil {
|
|
||||||
t.Error("could not get Go version:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
minorVersion := strings.Split(goVersion, ".")[1]
|
|
||||||
if minorVersion != "13" {
|
|
||||||
// WebAssembly tests fail on Go 1.13, so skip them there. Versions
|
|
||||||
// below that are also not supported but still seem to pass, so
|
|
||||||
// include them in the tests for now.
|
|
||||||
t.Run("WebAssembly", func(t *testing.T) {
|
t.Run("WebAssembly", func(t *testing.T) {
|
||||||
runPlatTests("wasm", tests, t)
|
runPlatTests("wasm", tests, t)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("WASI", func(t *testing.T) {
|
t.Run("WASI", func(t *testing.T) {
|
||||||
runPlatTests("wasi", tests, t)
|
runPlatTests("wasi", tests, t)
|
||||||
})
|
})
|
||||||
@@ -125,7 +122,7 @@ func TestCompiler(t *testing.T) {
|
|||||||
// Test with few optimizations enabled (no inlining, etc).
|
// Test with few optimizations enabled (no inlining, etc).
|
||||||
t.Run("opt=1", func(t *testing.T) {
|
t.Run("opt=1", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTestWithConfig("stdlib.go", "", t, &compileopts.Options{
|
runTestWithConfig("stdlib.go", "", t, compileopts.Options{
|
||||||
Opt: "1",
|
Opt: "1",
|
||||||
}, nil, nil)
|
}, nil, nil)
|
||||||
})
|
})
|
||||||
@@ -134,15 +131,14 @@ func TestCompiler(t *testing.T) {
|
|||||||
// TODO: fix this for stdlib.go, which currently fails.
|
// TODO: fix this for stdlib.go, which currently fails.
|
||||||
t.Run("opt=0", func(t *testing.T) {
|
t.Run("opt=0", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTestWithConfig("print.go", "", t, &compileopts.Options{
|
runTestWithConfig("print.go", "", t, compileopts.Options{
|
||||||
Opt: "0",
|
Opt: "0",
|
||||||
}, nil, nil)
|
}, nil, nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ldflags", func(t *testing.T) {
|
t.Run("ldflags", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTestWithConfig("ldflags.go", "", t, &compileopts.Options{
|
runTestWithConfig("ldflags.go", "", t, compileopts.Options{
|
||||||
Opt: "z",
|
|
||||||
GlobalValues: map[string]map[string]string{
|
GlobalValues: map[string]map[string]string{
|
||||||
"main": {
|
"main": {
|
||||||
"someGlobal": "foobar",
|
"someGlobal": "foobar",
|
||||||
@@ -163,14 +159,20 @@ func runPlatTests(target string, tests []string, t *testing.T) {
|
|||||||
runTest(name, target, t, nil, nil)
|
runTest(name, target, t, nil, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if target == "wasi" || target == "" {
|
t.Run("env.go", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
runTest("env.go", target, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
|
||||||
|
})
|
||||||
|
if target == "" || target == "wasi" {
|
||||||
t.Run("filesystem.go", func(t *testing.T) {
|
t.Run("filesystem.go", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTest("filesystem.go", target, t, nil, nil)
|
runTest("filesystem.go", target, t, nil, nil)
|
||||||
})
|
})
|
||||||
t.Run("env.go", func(t *testing.T) {
|
}
|
||||||
|
if target == "" || target == "wasi" || target == "wasm" {
|
||||||
|
t.Run("rand.go", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTest("env.go", target, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
|
runTest("rand.go", target, t, nil, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,20 +190,20 @@ func runBuild(src, out string, opts *compileopts.Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runTest(name, target string, t *testing.T, cmdArgs, environmentVars []string) {
|
func runTest(name, target string, t *testing.T, cmdArgs, environmentVars []string) {
|
||||||
options := &compileopts.Options{
|
options := compileopts.Options{
|
||||||
Target: target,
|
Target: target,
|
||||||
Opt: "z",
|
|
||||||
PrintIR: false,
|
|
||||||
DumpSSA: false,
|
|
||||||
VerifyIR: true,
|
|
||||||
Debug: true,
|
|
||||||
PrintSizes: "",
|
|
||||||
WasmAbi: "",
|
|
||||||
}
|
}
|
||||||
runTestWithConfig(name, target, t, options, cmdArgs, environmentVars)
|
runTestWithConfig(name, target, t, options, cmdArgs, environmentVars)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTestWithConfig(name, target string, t *testing.T, options *compileopts.Options, cmdArgs, environmentVars []string) {
|
func runTestWithConfig(name, target string, t *testing.T, options compileopts.Options, cmdArgs, environmentVars []string) {
|
||||||
|
// Set default config.
|
||||||
|
options.Debug = true
|
||||||
|
options.VerifyIR = true
|
||||||
|
if options.Opt == "" {
|
||||||
|
options.Opt = "z"
|
||||||
|
}
|
||||||
|
|
||||||
// Get the expected output for this test.
|
// Get the expected output for this test.
|
||||||
// Note: not using filepath.Join as it strips the path separator at the end
|
// Note: not using filepath.Join as it strips the path separator at the end
|
||||||
// of the path.
|
// of the path.
|
||||||
@@ -217,46 +219,61 @@ func runTestWithConfig(name, target string, t *testing.T, options *compileopts.O
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a temporary directory for test output files.
|
// Create a temporary directory for test output files.
|
||||||
tmpdir, err := ioutil.TempDir("", "tinygo-test")
|
tmpdir := t.TempDir()
|
||||||
|
|
||||||
|
// Determine whether we're on a system that supports environment variables
|
||||||
|
// and command line parameters (operating systems, WASI) or not (baremetal,
|
||||||
|
// WebAssembly in the browser). If we're on a system without an environment,
|
||||||
|
// we need to pass command line arguments and environment variables through
|
||||||
|
// global variables (built into the binary directly) instead of the
|
||||||
|
// conventional way.
|
||||||
|
spec, err := compileopts.LoadTarget(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("could not create temporary directory:", err)
|
t.Fatal("failed to load target spec:", err)
|
||||||
|
}
|
||||||
|
needsEnvInVars := spec.GOOS == "js"
|
||||||
|
for _, tag := range spec.BuildTags {
|
||||||
|
if tag == "baremetal" {
|
||||||
|
needsEnvInVars = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if needsEnvInVars {
|
||||||
|
runtimeGlobals := make(map[string]string)
|
||||||
|
if len(cmdArgs) != 0 {
|
||||||
|
runtimeGlobals["osArgs"] = strings.Join(cmdArgs, "\x00")
|
||||||
|
}
|
||||||
|
if len(environmentVars) != 0 {
|
||||||
|
runtimeGlobals["osEnv"] = strings.Join(environmentVars, "\x00")
|
||||||
|
}
|
||||||
|
if len(runtimeGlobals) != 0 {
|
||||||
|
// This sets the global variables like they would be set with
|
||||||
|
// `-ldflags="-X=runtime.osArgs=first\x00second`.
|
||||||
|
// The runtime package has two variables (osArgs and osEnv) that are
|
||||||
|
// both strings, from which the parameters and environment variables
|
||||||
|
// are read.
|
||||||
|
options.GlobalValues = map[string]map[string]string{
|
||||||
|
"runtime": runtimeGlobals,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
rerr := os.RemoveAll(tmpdir)
|
|
||||||
if rerr != nil {
|
|
||||||
t.Errorf("failed to remove temporary directory %q: %s", tmpdir, rerr.Error())
|
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
// Build the test binary.
|
// Build the test binary.
|
||||||
binary := filepath.Join(tmpdir, "test")
|
binary := filepath.Join(tmpdir, "test")
|
||||||
err = runBuild("./"+path, binary, options)
|
err = runBuild("./"+path, binary, &options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printCompilerError(t.Log, err)
|
printCompilerError(t.Log, err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the test.
|
// Create the test command, taking care of emulators etc.
|
||||||
runComplete := make(chan struct{})
|
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
ranTooLong := false
|
|
||||||
if target == "" {
|
|
||||||
cmd = exec.Command(binary)
|
|
||||||
cmd.Env = append(cmd.Env, environmentVars...)
|
|
||||||
cmd.Args = append(cmd.Args, cmdArgs...)
|
|
||||||
} else {
|
|
||||||
spec, err := compileopts.LoadTarget(target)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("failed to load target spec:", err)
|
|
||||||
}
|
|
||||||
if len(spec.Emulator) == 0 {
|
if len(spec.Emulator) == 0 {
|
||||||
cmd = exec.Command(binary)
|
cmd = exec.Command(binary)
|
||||||
} else {
|
} else {
|
||||||
args := append(spec.Emulator[1:], binary)
|
args := append(spec.Emulator[1:], binary)
|
||||||
cmd = exec.Command(spec.Emulator[0], args...)
|
cmd = exec.Command(spec.Emulator[0], args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
||||||
// Allow reading from the current directory.
|
// Allow reading from the current directory.
|
||||||
cmd.Args = append(cmd.Args, "--dir=.")
|
cmd.Args = append(cmd.Args, "--dir=.")
|
||||||
@@ -265,9 +282,15 @@ func runTestWithConfig(name, target string, t *testing.T, options *compileopts.O
|
|||||||
}
|
}
|
||||||
cmd.Args = append(cmd.Args, cmdArgs...)
|
cmd.Args = append(cmd.Args, cmdArgs...)
|
||||||
} else {
|
} else {
|
||||||
|
if !needsEnvInVars {
|
||||||
|
cmd.Args = append(cmd.Args, cmdArgs...) // works on qemu-aarch64 etc
|
||||||
cmd.Env = append(cmd.Env, environmentVars...)
|
cmd.Env = append(cmd.Env, environmentVars...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run the test.
|
||||||
|
runComplete := make(chan struct{})
|
||||||
|
ranTooLong := false
|
||||||
stdout := &bytes.Buffer{}
|
stdout := &bytes.Buffer{}
|
||||||
cmd.Stdout = stdout
|
cmd.Stdout = stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// +build darwin freebsd tinygo.wasm
|
||||||
|
|
||||||
|
// This implementation of crypto/rand uses the arc4random_buf function
|
||||||
|
// (available on both MacOS and WASI) to generate random numbers.
|
||||||
|
//
|
||||||
|
// Note: arc4random_buf (unlike what the name suggets) does not use the insecure
|
||||||
|
// RC4 cipher. Instead, it uses a high-quality cipher, varying by the libc
|
||||||
|
// implementation.
|
||||||
|
|
||||||
|
package rand
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Reader = &reader{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type reader struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *reader) Read(b []byte) (n int, err error) {
|
||||||
|
if len(b) != 0 {
|
||||||
|
libc_arc4random_buf(unsafe.Pointer(&b[0]), uint(len(b)))
|
||||||
|
}
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// void arc4random_buf(void *buf, size_t buflen);
|
||||||
|
//export arc4random_buf
|
||||||
|
func libc_arc4random_buf(buf unsafe.Pointer, buflen uint)
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
// +build darwin freebsd wasi
|
|
||||||
|
|
||||||
// This implementation of crypto/rand uses the getentropy system call (available
|
|
||||||
// on both MacOS and WASI) to generate random numbers.
|
|
||||||
|
|
||||||
package rand
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errReadFailed = errors.New("rand: could not read random bytes")
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
Reader = &reader{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type reader struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *reader) Read(b []byte) (n int, err error) {
|
|
||||||
if len(b) != 0 {
|
|
||||||
if len(b) > 256 {
|
|
||||||
b = b[:256]
|
|
||||||
}
|
|
||||||
result := libc_getentropy(unsafe.Pointer(&b[0]), len(b))
|
|
||||||
if result < 0 {
|
|
||||||
// Maybe we should return a syscall.Errno here?
|
|
||||||
return 0, errReadFailed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len(b), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// int getentropy(void *buf, size_t buflen);
|
|
||||||
//export getentropy
|
|
||||||
func libc_getentropy(buf unsafe.Pointer, buflen int) int
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// This is a very minimal bootloader for the ESP32-C3. It only initializes the
|
||||||
|
// flash and then continues with the generic RISC-V initialization code, which
|
||||||
|
// in turn will call runtime.main.
|
||||||
|
// It is written in assembly (and not in a higher level language) to make sure
|
||||||
|
// it is entirely loaded into IRAM and doesn't accidentally call functions
|
||||||
|
// stored in IROM.
|
||||||
|
//
|
||||||
|
// For reference, here is a nice introduction into RISC-V assembly:
|
||||||
|
// https://www.imperialviolet.org/2016/12/31/riscv.html
|
||||||
|
|
||||||
|
.section .init
|
||||||
|
.global call_start_cpu0
|
||||||
|
.type call_start_cpu0,@function
|
||||||
|
call_start_cpu0:
|
||||||
|
// At this point:
|
||||||
|
// - The ROM bootloader is finished and has jumped to here.
|
||||||
|
// - We're running from IRAM: both IRAM and DRAM segments have been loaded
|
||||||
|
// by the ROM bootloader.
|
||||||
|
// - We have a usable stack (but not the one we would like to use).
|
||||||
|
// - No flash mappings (MMU) are set up yet.
|
||||||
|
|
||||||
|
// Reset MMU, see bootloader_reset_mmu in the ESP-IDF.
|
||||||
|
call Cache_Suspend_ICache
|
||||||
|
mv s0, a0 // autoload value
|
||||||
|
call Cache_Invalidate_ICache_All
|
||||||
|
call Cache_MMU_Init
|
||||||
|
|
||||||
|
// Set up DROM from flash.
|
||||||
|
// Somehow, this also sets up IROM from flash. Not sure why, but it avoids
|
||||||
|
// the need for another such call.
|
||||||
|
// C equivalent:
|
||||||
|
// Cache_Dbus_MMU_Set(MMU_ACCESS_FLASH, 0x3C00_0000, 0, 64, 128, 0)
|
||||||
|
li a0, 0 // ext_ram: MMU_ACCESS_FLASH
|
||||||
|
li a1, 0x3C000000 // vaddr: address in the data bus
|
||||||
|
li a2, 0 // paddr: physical address in the flash chip
|
||||||
|
li a3, 64 // psize: always 64 (kilobytes)
|
||||||
|
li a4, 128 // num: pages to be set (8192K / 64K = 128)
|
||||||
|
li a5, 0 // fixed
|
||||||
|
call Cache_Dbus_MMU_Set
|
||||||
|
|
||||||
|
// Enable the flash cache.
|
||||||
|
mv a0, s0 // restore autoload value from Cache_Suspend_ICache call
|
||||||
|
call Cache_Resume_ICache
|
||||||
|
|
||||||
|
// Jump to generic RISC-V initialization, which initializes the stack
|
||||||
|
// pointer and globals register. It should not return.
|
||||||
|
// (It appears that the linker relaxes this jump and instead inserts the
|
||||||
|
// _start function right after here).
|
||||||
|
j _start
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
// Type definitions, fields, and constants associated with the MPU peripheral
|
// Type definitions, fields, and constants associated with the MPU peripheral
|
||||||
// of the NXP MIMXRT1062.
|
// of the NXP MIMXRT1062.
|
||||||
|
|
||||||
|
//go:build nxp && mimxrt1062
|
||||||
// +build nxp,mimxrt1062
|
// +build nxp,mimxrt1062
|
||||||
|
|
||||||
package nxp
|
package nxp
|
||||||
@@ -216,6 +217,12 @@ func enableIcache(enable bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
dcacheCcsidr volatile.Register32
|
||||||
|
dcacheSets volatile.Register32
|
||||||
|
dcacheWays volatile.Register32
|
||||||
|
)
|
||||||
|
|
||||||
func enableDcache(enable bool) {
|
func enableDcache(enable bool) {
|
||||||
if enable != SystemControl.CCR.HasBits(SCB_CCR_DC_Msk) {
|
if enable != SystemControl.CCR.HasBits(SCB_CCR_DC_Msk) {
|
||||||
if enable {
|
if enable {
|
||||||
@@ -244,11 +251,6 @@ func enableDcache(enable bool) {
|
|||||||
isb 0xF
|
isb 0xF
|
||||||
`, nil)
|
`, nil)
|
||||||
} else {
|
} else {
|
||||||
var (
|
|
||||||
ccsidr volatile.Register32
|
|
||||||
sets volatile.Register32
|
|
||||||
ways volatile.Register32
|
|
||||||
)
|
|
||||||
SystemControl.CSSELR.Set(0)
|
SystemControl.CSSELR.Set(0)
|
||||||
arm.AsmFull(`
|
arm.AsmFull(`
|
||||||
dsb 0xF
|
dsb 0xF
|
||||||
@@ -257,17 +259,17 @@ func enableDcache(enable bool) {
|
|||||||
arm.AsmFull(`
|
arm.AsmFull(`
|
||||||
dsb 0xF
|
dsb 0xF
|
||||||
`, nil)
|
`, nil)
|
||||||
ccsidr.Set(SystemControl.CCSIDR.Get())
|
dcacheCcsidr.Set(SystemControl.CCSIDR.Get())
|
||||||
sets.Set((ccsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos)
|
dcacheSets.Set((dcacheCcsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos)
|
||||||
for sets.Get() != 0 {
|
for dcacheSets.Get() != 0 {
|
||||||
ways.Set((ccsidr.Get() & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos)
|
dcacheWays.Set((dcacheCcsidr.Get() & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos)
|
||||||
for ways.Get() != 0 {
|
for dcacheWays.Get() != 0 {
|
||||||
SystemControl.DCCISW.Set(
|
SystemControl.DCCISW.Set(
|
||||||
((sets.Get() << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) |
|
((dcacheSets.Get() << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) |
|
||||||
((ways.Get() << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk))
|
((dcacheWays.Get() << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk))
|
||||||
ways.Set(ways.Get() - 1)
|
dcacheWays.Set(dcacheWays.Get() - 1)
|
||||||
}
|
}
|
||||||
sets.Set(sets.Get() - 1)
|
dcacheSets.Set(dcacheSets.Get() - 1)
|
||||||
}
|
}
|
||||||
arm.AsmFull(`
|
arm.AsmFull(`
|
||||||
dsb 0xF
|
dsb 0xF
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// +build pico
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "machine"
|
||||||
|
|
||||||
|
var (
|
||||||
|
pwm = machine.PWM4 // Pin 25 (LED on pico) corresponds to PWM4.
|
||||||
|
pinA = machine.LED
|
||||||
|
pinB = machine.GPIO24
|
||||||
|
)
|
||||||
@@ -16,6 +16,9 @@ type calleeSavedRegs struct {
|
|||||||
ebp uintptr
|
ebp uintptr
|
||||||
|
|
||||||
pc uintptr
|
pc uintptr
|
||||||
|
|
||||||
|
// Pad this struct so that tasks start on a 16-byte aligned stack.
|
||||||
|
_ [3]uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
// archInit runs architecture-specific setup for the goroutine startup.
|
// archInit runs architecture-specific setup for the goroutine startup.
|
||||||
|
|||||||
@@ -72,3 +72,8 @@ tinygo_swapTask:
|
|||||||
|
|
||||||
// Return into the new task, as if tinygo_swapTask was a regular call.
|
// Return into the new task, as if tinygo_swapTask was a regular call.
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
#ifdef __MACH__ // Darwin
|
||||||
|
// allow these symbols to stripped as dead code
|
||||||
|
.subsections_via_symbols
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
// +build arduino_mkrwifi1010
|
||||||
|
|
||||||
|
// This contains the pin mappings for the Arduino MKR WiFi 1010 board.
|
||||||
|
//
|
||||||
|
// For more information, see: https://store.arduino.cc/usa/mkr-wifi-1010
|
||||||
|
//
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/sam"
|
||||||
|
"runtime/interrupt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// used to reset into bootloader
|
||||||
|
const RESET_MAGIC_VALUE = 0x07738135
|
||||||
|
|
||||||
|
// GPIO Pins
|
||||||
|
const (
|
||||||
|
RX0 Pin = PB23 // UART1 RX
|
||||||
|
TX1 Pin = PB22 // UART1 TX
|
||||||
|
|
||||||
|
D0 Pin = PA22 // PWM available
|
||||||
|
D1 Pin = PA23 // PWM available
|
||||||
|
D2 Pin = PA10 // PWM available
|
||||||
|
D3 Pin = PA11 // PWM available
|
||||||
|
D4 Pin = PB10 // PWM available
|
||||||
|
D5 Pin = PB11 // PWM available
|
||||||
|
|
||||||
|
D6 Pin = PA20 // PWM available
|
||||||
|
D7 Pin = PA21 // PWM available
|
||||||
|
D8 Pin = PA16 // PWM available
|
||||||
|
D9 Pin = PA17
|
||||||
|
D10 Pin = PA19 // PWM available
|
||||||
|
D11 Pin = PA08 // SDA
|
||||||
|
D12 Pin = PA09 // PWM available, SCL
|
||||||
|
D13 Pin = PB23 // RX
|
||||||
|
D14 Pin = PB22 // TX
|
||||||
|
)
|
||||||
|
|
||||||
|
// Analog pins
|
||||||
|
const (
|
||||||
|
A0 Pin = PA02 // ADC0/AIN[0]
|
||||||
|
A1 Pin = PB02 // AIN[10]
|
||||||
|
A2 Pin = PB03 // AIN[11]
|
||||||
|
A3 Pin = PA04 // AIN[04]
|
||||||
|
A4 Pin = PA05 // AIN[05]
|
||||||
|
A5 Pin = PA06 // AIN[06]
|
||||||
|
A6 Pin = PA07 // AIN[07]
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LED = D6
|
||||||
|
)
|
||||||
|
|
||||||
|
// USBCDC pins
|
||||||
|
const (
|
||||||
|
USBCDC_DM_PIN Pin = PA24
|
||||||
|
USBCDC_DP_PIN Pin = PA25
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART1 pins
|
||||||
|
const (
|
||||||
|
UART_TX_PIN Pin = PB22
|
||||||
|
UART_RX_PIN Pin = PB23
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C pins
|
||||||
|
const (
|
||||||
|
SDA_PIN Pin = D11 // SDA
|
||||||
|
SCL_PIN Pin = D12 // SCL
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI pins
|
||||||
|
const (
|
||||||
|
SPI0_SCK_PIN Pin = D9 // SCK: S1
|
||||||
|
SPI0_SDO_PIN Pin = D8 // SDO: S1
|
||||||
|
SPI0_SDI_PIN Pin = D10 // SDI: S1
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2S pins
|
||||||
|
const (
|
||||||
|
I2S_SCK_PIN Pin = PA10
|
||||||
|
I2S_SD_PIN Pin = PA07
|
||||||
|
I2S_WS_PIN = NoPin // TODO: figure out what this is on Arduino MKR WiFi 1010.
|
||||||
|
)
|
||||||
|
|
||||||
|
// NINA-W102 Pins
|
||||||
|
const (
|
||||||
|
NINA_SDO Pin = PA12
|
||||||
|
NINA_SDI Pin = PA13
|
||||||
|
NINA_CS Pin = PA14
|
||||||
|
NINA_SCK Pin = PA15
|
||||||
|
NINA_GPIO0 Pin = PA27
|
||||||
|
NINA_RESETN Pin = PB08
|
||||||
|
NINA_ACK Pin = PA28
|
||||||
|
NINA_TX Pin = PA22
|
||||||
|
NINA_RX Pin = PA23
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART on the Arduino MKR WiFi 1010.
|
||||||
|
var (
|
||||||
|
UART1 = &_UART1
|
||||||
|
_UART1 = UART{
|
||||||
|
Buffer: NewRingBuffer(),
|
||||||
|
Bus: sam.SERCOM5_USART,
|
||||||
|
SERCOM: 5,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5, _UART1.handleInterrupt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// I2C on the Arduino MKR WiFi 1010.
|
||||||
|
var (
|
||||||
|
I2C0 = &I2C{
|
||||||
|
Bus: sam.SERCOM2_I2CM,
|
||||||
|
SERCOM: 2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI on the Arduino MKR WiFi 1010.
|
||||||
|
var (
|
||||||
|
SPI0 = SPI{
|
||||||
|
Bus: sam.SERCOM1_SPI,
|
||||||
|
SERCOM: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
SPI1 = SPI{
|
||||||
|
Bus: sam.SERCOM4_SPI,
|
||||||
|
SERCOM: 4,
|
||||||
|
}
|
||||||
|
NINA_SPI = SPI1
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2S on the Arduino MKR WiFi 1010.
|
||||||
|
var (
|
||||||
|
I2S0 = I2S{Bus: sam.I2S}
|
||||||
|
)
|
||||||
|
|
||||||
|
// USB CDC identifiers
|
||||||
|
const (
|
||||||
|
usb_STRING_PRODUCT = "Arduino MKR WiFi 1010"
|
||||||
|
usb_STRING_MANUFACTURER = "Arduino"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
usb_VID uint16 = 0x2341
|
||||||
|
usb_PID uint16 = 0x8054
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
@@ -83,3 +83,7 @@ var (
|
|||||||
usb_VID uint16 = 0x239A
|
usb_VID uint16 = 0x239A
|
||||||
usb_PID uint16 = 0x8045
|
usb_PID uint16 = 0x8045
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART0
|
||||||
|
)
|
||||||
|
|||||||
@@ -47,3 +47,7 @@ var (
|
|||||||
var (
|
var (
|
||||||
I2S0 = I2S{Bus: sam.I2S}
|
I2S0 = I2S{Bus: sam.I2S}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|||||||
@@ -127,3 +127,7 @@ var (
|
|||||||
usb_VID uint16 = 0x239A
|
usb_VID uint16 = 0x239A
|
||||||
usb_PID uint16 = 0x8072
|
usb_PID uint16 = 0x8072
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART0
|
||||||
|
)
|
||||||
|
|||||||
@@ -114,3 +114,7 @@ var (
|
|||||||
usb_VID uint16 = 0x239A
|
usb_VID uint16 = 0x239A
|
||||||
usb_PID uint16 = 0x801B
|
usb_PID uint16 = 0x801B
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|||||||
@@ -154,3 +154,7 @@ var (
|
|||||||
Bus: sam.CAN1,
|
Bus: sam.CAN1,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|||||||
@@ -43,3 +43,7 @@ var (
|
|||||||
SERCOM: 1,
|
SERCOM: 1,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ const (
|
|||||||
|
|
||||||
// UART0 pins (logical UART1)
|
// UART0 pins (logical UART1)
|
||||||
const (
|
const (
|
||||||
UART_RX_PIN = D0
|
UART_RX_PIN = D1
|
||||||
UART_TX_PIN = D1
|
UART_TX_PIN = D0
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
@@ -99,3 +99,7 @@ var (
|
|||||||
usb_VID uint16 = 0x239A
|
usb_VID uint16 = 0x239A
|
||||||
usb_PID uint16 = 0x8088
|
usb_PID uint16 = 0x8088
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART0
|
||||||
|
)
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ const (
|
|||||||
|
|
||||||
// UART0 pins (logical UART1)
|
// UART0 pins (logical UART1)
|
||||||
const (
|
const (
|
||||||
UART_RX_PIN = D0
|
UART_RX_PIN = D1
|
||||||
UART_TX_PIN = D1
|
UART_TX_PIN = D0
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
@@ -99,3 +99,7 @@ var (
|
|||||||
usb_VID uint16 = 0x239A
|
usb_VID uint16 = 0x239A
|
||||||
usb_PID uint16 = 0x802A
|
usb_PID uint16 = 0x802A
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART0
|
||||||
|
)
|
||||||
|
|||||||
@@ -8,3 +8,51 @@ const (
|
|||||||
// Onboard crystal oscillator frequency, in MHz.
|
// Onboard crystal oscillator frequency, in MHz.
|
||||||
xoscFreq = 12 // MHz
|
xoscFreq = 12 // MHz
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GPIO Pins
|
||||||
|
const (
|
||||||
|
D4 = GPIO6
|
||||||
|
D5 = GPIO7
|
||||||
|
D6 = GPIO8
|
||||||
|
D9 = GPIO9
|
||||||
|
D10 = GPIO10
|
||||||
|
D11 = GPIO11
|
||||||
|
D12 = GPIO12
|
||||||
|
D13 = GPIO13
|
||||||
|
D24 = GPIO24
|
||||||
|
D25 = GPIO25
|
||||||
|
)
|
||||||
|
|
||||||
|
// Analog pins
|
||||||
|
const (
|
||||||
|
A0 = GPIO26
|
||||||
|
A1 = GPIO27
|
||||||
|
A2 = GPIO28
|
||||||
|
A3 = GPIO29
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C Pins.
|
||||||
|
const (
|
||||||
|
I2C0_SDA_PIN = GPIO24
|
||||||
|
I2C0_SCL_PIN = GPIO25
|
||||||
|
|
||||||
|
I2C1_SDA_PIN = GPIO2
|
||||||
|
I2C1_SCL_PIN = GPIO3
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI default pins
|
||||||
|
const (
|
||||||
|
// Default Serial Clock Bus 0 for SPI communications
|
||||||
|
SPI0_SCK_PIN = GPIO18
|
||||||
|
// Default Serial Out Bus 0 for SPI communications
|
||||||
|
SPI0_SDO_PIN = GPIO19 // Tx
|
||||||
|
// Default Serial In Bus 0 for SPI communications
|
||||||
|
SPI0_SDI_PIN = GPIO20 // Rx
|
||||||
|
|
||||||
|
// Default Serial Clock Bus 1 for SPI communications
|
||||||
|
SPI1_SCK_PIN = GPIO10
|
||||||
|
// Default Serial Out Bus 1 for SPI communications
|
||||||
|
SPI1_SDO_PIN = GPIO11 // Tx
|
||||||
|
// Default Serial In Bus 1 for SPI communications
|
||||||
|
SPI1_SDI_PIN = GPIO12 // Rx
|
||||||
|
)
|
||||||
|
|||||||
@@ -65,3 +65,7 @@ var (
|
|||||||
SERCOM: 2,
|
SERCOM: 2,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|||||||
@@ -135,3 +135,7 @@ var (
|
|||||||
usb_VID uint16 = 0x239A
|
usb_VID uint16 = 0x239A
|
||||||
usb_PID uint16 = 0x800F
|
usb_PID uint16 = 0x800F
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|||||||
@@ -43,3 +43,7 @@ var (
|
|||||||
SERCOM: 1,
|
SERCOM: 1,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|||||||
@@ -93,3 +93,7 @@ var (
|
|||||||
usb_VID uint16 = 0x239A
|
usb_VID uint16 = 0x239A
|
||||||
usb_PID uint16 = 0x8051
|
usb_PID uint16 = 0x8051
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART0
|
||||||
|
)
|
||||||
|
|||||||
@@ -50,3 +50,7 @@ var (
|
|||||||
SERCOM: 0,
|
SERCOM: 0,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// +build mdbt50qrx
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
const HasLowFrequencyCrystal = false
|
||||||
|
|
||||||
|
// GPIO Pins
|
||||||
|
const (
|
||||||
|
D0 = P1_13 // LED1
|
||||||
|
D1 = P1_11 // LED2 (not populated by default)
|
||||||
|
D2 = P0_15 // Button
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LED = D0
|
||||||
|
)
|
||||||
|
|
||||||
|
// MDBT50Q-RX dongle does not have pins broken out for the peripherals below,
|
||||||
|
// however the machine_nrf*.go implementations of I2C/SPI/etc expect the pin
|
||||||
|
// constants to be defined, so we are defining them all as 0
|
||||||
|
const (
|
||||||
|
UART_TX_PIN = 0
|
||||||
|
UART_RX_PIN = 0
|
||||||
|
SDA_PIN = 0
|
||||||
|
SCL_PIN = 0
|
||||||
|
SPI0_SCK_PIN = 0
|
||||||
|
SPI0_SDO_PIN = 0
|
||||||
|
SPI0_SDI_PIN = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// USB CDC identifiers
|
||||||
|
const (
|
||||||
|
usb_STRING_PRODUCT = "Raytac MDBT50Q - RX"
|
||||||
|
usb_STRING_MANUFACTURER = "Raytac Corporation"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
usb_VID uint16 = 0x239A
|
||||||
|
usb_PID uint16 = 0x810B
|
||||||
|
)
|
||||||
@@ -52,3 +52,7 @@ var (
|
|||||||
SERCOM: 1,
|
SERCOM: 1,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|||||||
@@ -49,15 +49,27 @@ const (
|
|||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
SDA_PIN Pin = GPIO12
|
I2C0_SDA_PIN Pin = GPIO12
|
||||||
SCL_PIN Pin = GPIO13
|
I2C0_SCL_PIN Pin = GPIO13
|
||||||
|
|
||||||
|
I2C1_SDA_PIN Pin = GPIO18
|
||||||
|
I2C1_SCL_PIN Pin = GPIO19
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins. SPI1 not available on Nano RP2040 Connect.
|
||||||
const (
|
const (
|
||||||
SPI0_SCK_PIN Pin = GPIO6
|
SPI0_SCK_PIN Pin = GPIO6
|
||||||
SPI0_SDO_PIN Pin = GPIO7
|
SPI0_SDO_PIN Pin = GPIO7
|
||||||
SPI0_SDI_PIN Pin = GPIO4
|
SPI0_SDI_PIN Pin = GPIO4
|
||||||
|
|
||||||
|
// GPIO22 does not have SPI functionality so we set it to avoid interfering with NINA.
|
||||||
|
SPI1_SCK_PIN Pin = GPIO22
|
||||||
|
SPI1_SDO_PIN Pin = GPIO22
|
||||||
|
SPI1_SDI_PIN Pin = GPIO22
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
NINA_SPI = SPI1
|
||||||
)
|
)
|
||||||
|
|
||||||
// NINA-W102 Pins
|
// NINA-W102 Pins
|
||||||
@@ -68,7 +80,7 @@ const (
|
|||||||
|
|
||||||
NINA_CS Pin = GPIO9
|
NINA_CS Pin = GPIO9
|
||||||
NINA_ACK Pin = GPIO10
|
NINA_ACK Pin = GPIO10
|
||||||
NINA_GPIO0 Pin = GPIO0
|
NINA_GPIO0 Pin = GPIO2
|
||||||
NINA_RESETN Pin = GPIO3
|
NINA_RESETN Pin = GPIO3
|
||||||
|
|
||||||
NINA_TX Pin = GPIO9
|
NINA_TX Pin = GPIO9
|
||||||
|
|||||||
@@ -37,3 +37,29 @@ const (
|
|||||||
// Onboard crystal oscillator frequency, in MHz.
|
// Onboard crystal oscillator frequency, in MHz.
|
||||||
xoscFreq = 12 // MHz
|
xoscFreq = 12 // MHz
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// I2C Default pins on Raspberry Pico.
|
||||||
|
const (
|
||||||
|
I2C0_SDA_PIN = GP4
|
||||||
|
I2C0_SCL_PIN = GP5
|
||||||
|
|
||||||
|
I2C1_SDA_PIN = GP2
|
||||||
|
I2C1_SCL_PIN = GP3
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI default pins
|
||||||
|
const (
|
||||||
|
// Default Serial Clock Bus 0 for SPI communications
|
||||||
|
SPI0_SCK_PIN = GPIO18
|
||||||
|
// Default Serial Out Bus 0 for SPI communications
|
||||||
|
SPI0_SDO_PIN = GPIO19 // Tx
|
||||||
|
// Default Serial In Bus 0 for SPI communications
|
||||||
|
SPI0_SDI_PIN = GPIO16 // Rx
|
||||||
|
|
||||||
|
// Default Serial Clock Bus 1 for SPI communications
|
||||||
|
SPI1_SCK_PIN = GPIO10
|
||||||
|
// Default Serial Out Bus 1 for SPI communications
|
||||||
|
SPI1_SDO_PIN = GPIO11 // Tx
|
||||||
|
// Default Serial In Bus 1 for SPI communications
|
||||||
|
SPI1_SDI_PIN = GPIO12 // Rx
|
||||||
|
)
|
||||||
|
|||||||
@@ -51,3 +51,7 @@ var (
|
|||||||
SERCOM: 4,
|
SERCOM: 4,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|||||||
@@ -36,3 +36,7 @@ var (
|
|||||||
}
|
}
|
||||||
NINA_SPI = SPI0
|
NINA_SPI = SPI0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|||||||
@@ -119,3 +119,7 @@ var (
|
|||||||
usb_VID uint16 = 0x239A
|
usb_VID uint16 = 0x239A
|
||||||
usb_PID uint16 = 0x80CB
|
usb_PID uint16 = 0x80CB
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|||||||
@@ -88,15 +88,20 @@ const (
|
|||||||
I2C_SCL_PIN = I2C1_SCL_PIN // D19/A5
|
I2C_SCL_PIN = I2C1_SCL_PIN // D19/A5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Default peripherals
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// register any interrupt handlers for this board's peripherals
|
// register any interrupt handlers for this board's peripherals
|
||||||
UART1.Interrupt = interrupt.New(nxp.IRQ_LPUART6, _UART1.handleInterrupt)
|
_UART1.Interrupt = interrupt.New(nxp.IRQ_LPUART6, _UART1.handleInterrupt)
|
||||||
UART2.Interrupt = interrupt.New(nxp.IRQ_LPUART4, _UART2.handleInterrupt)
|
_UART2.Interrupt = interrupt.New(nxp.IRQ_LPUART4, _UART2.handleInterrupt)
|
||||||
UART3.Interrupt = interrupt.New(nxp.IRQ_LPUART2, _UART3.handleInterrupt)
|
_UART3.Interrupt = interrupt.New(nxp.IRQ_LPUART2, _UART3.handleInterrupt)
|
||||||
UART4.Interrupt = interrupt.New(nxp.IRQ_LPUART3, _UART4.handleInterrupt)
|
_UART4.Interrupt = interrupt.New(nxp.IRQ_LPUART3, _UART4.handleInterrupt)
|
||||||
UART5.Interrupt = interrupt.New(nxp.IRQ_LPUART8, _UART5.handleInterrupt)
|
_UART5.Interrupt = interrupt.New(nxp.IRQ_LPUART8, _UART5.handleInterrupt)
|
||||||
UART6.Interrupt = interrupt.New(nxp.IRQ_LPUART1, _UART6.handleInterrupt)
|
_UART6.Interrupt = interrupt.New(nxp.IRQ_LPUART1, _UART6.handleInterrupt)
|
||||||
UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
|
_UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// #=====================================================#
|
// #=====================================================#
|
||||||
@@ -136,7 +141,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultUART = UART1
|
|
||||||
UART1 = &_UART1
|
UART1 = &_UART1
|
||||||
_UART1 = UART{
|
_UART1 = UART{
|
||||||
Bus: nxp.LPUART6,
|
Bus: nxp.LPUART6,
|
||||||
|
|||||||
@@ -105,3 +105,7 @@ var (
|
|||||||
usb_VID uint16 = 0x239A
|
usb_VID uint16 = 0x239A
|
||||||
usb_PID uint16 = 0x801E
|
usb_PID uint16 = 0x801E
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART1
|
||||||
|
)
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build atmega nrf sam stm32 fe310 k210
|
// +build atmega nrf sam stm32 fe310 k210 rp2040
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -1958,7 +1958,7 @@ func handlePadCalibration() {
|
|||||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
||||||
|
|
||||||
if calibTrim == 0x7 {
|
if calibTrim == 0x7 {
|
||||||
calibTransN = 3
|
calibTrim = 3
|
||||||
}
|
}
|
||||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2167,7 +2167,7 @@ func handlePadCalibration() {
|
|||||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
||||||
|
|
||||||
if calibTrim == 0x7 {
|
if calibTrim == 0x7 {
|
||||||
calibTransN = 3
|
calibTrim = 3
|
||||||
}
|
}
|
||||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
// +build esp32c3
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/esp"
|
||||||
|
"runtime/volatile"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PinOutput PinMode = iota
|
||||||
|
PinInput
|
||||||
|
PinInputPullup
|
||||||
|
PinInputPulldown
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configure this pin with the given configuration.
|
||||||
|
func (p Pin) Configure(config PinConfig) {
|
||||||
|
if p == NoPin {
|
||||||
|
// This simplifies pin configuration in peripherals such as SPI.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var muxConfig uint32
|
||||||
|
|
||||||
|
// Configure this pin as a GPIO pin.
|
||||||
|
const function = 1 // function 1 is GPIO for every pin
|
||||||
|
muxConfig |= function << esp.IO_MUX_GPIO_MCU_SEL_Pos
|
||||||
|
|
||||||
|
// Make this pin an input pin (always).
|
||||||
|
muxConfig |= esp.IO_MUX_GPIO_FUN_IE
|
||||||
|
|
||||||
|
// Set drive strength: 0 is lowest, 3 is highest.
|
||||||
|
muxConfig |= 2 << esp.IO_MUX_GPIO_FUN_DRV_Pos
|
||||||
|
|
||||||
|
// Select pull mode.
|
||||||
|
if config.Mode == PinInputPullup {
|
||||||
|
muxConfig |= esp.IO_MUX_GPIO_FUN_WPU
|
||||||
|
} else if config.Mode == PinInputPulldown {
|
||||||
|
muxConfig |= esp.IO_MUX_GPIO_FUN_WPD
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the pad with the given IO mux configuration.
|
||||||
|
p.mux().Set(muxConfig)
|
||||||
|
|
||||||
|
// Set the output signal to the simple GPIO output.
|
||||||
|
p.outFunc().Set(0x80)
|
||||||
|
|
||||||
|
switch config.Mode {
|
||||||
|
case PinOutput:
|
||||||
|
// Set the 'output enable' bit.
|
||||||
|
esp.GPIO.ENABLE_W1TS.Set(1 << p)
|
||||||
|
case PinInput, PinInputPullup, PinInputPulldown:
|
||||||
|
// Clear the 'output enable' bit.
|
||||||
|
esp.GPIO.ENABLE_W1TC.Set(1 << p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// mux returns the I/O mux configuration register corresponding to the given
|
||||||
|
// GPIO pin.
|
||||||
|
func (p Pin) mux() *volatile.Register32 {
|
||||||
|
return (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.IO_MUX.GPIO0)) + uintptr(p)*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) {
|
||||||
|
return &esp.GPIO.OUT_W1TS, 1 << p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Pin) portMaskClear() (*volatile.Register32, uint32) {
|
||||||
|
return &esp.GPIO.OUT_W1TC, 1 << p
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultUART = UART0
|
||||||
|
|
||||||
|
var (
|
||||||
|
UART0 = &_UART0
|
||||||
|
_UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
|
||||||
|
UART1 = &_UART1
|
||||||
|
_UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()}
|
||||||
|
)
|
||||||
|
|
||||||
|
type UART struct {
|
||||||
|
Bus *esp.UART_Type
|
||||||
|
Buffer *RingBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart *UART) WriteByte(b byte) error {
|
||||||
|
for (uart.Bus.STATUS.Get()&esp.UART_STATUS_TXFIFO_CNT_Msk)>>esp.UART_STATUS_TXFIFO_CNT_Pos >= 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.FIFO.Set(uint32(b))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -33,7 +33,6 @@ type UART struct {
|
|||||||
|
|
||||||
// auxiliary state data used internally
|
// auxiliary state data used internally
|
||||||
configured bool
|
configured bool
|
||||||
msbFirst bool
|
|
||||||
transmitting volatile.Register32
|
transmitting volatile.Register32
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,13 +55,14 @@ func (uart *UART) Configure(config UARTConfig) {
|
|||||||
if config.BaudRate == 0 {
|
if config.BaudRate == 0 {
|
||||||
config.BaudRate = defaultUartFreq
|
config.BaudRate = defaultUartFreq
|
||||||
}
|
}
|
||||||
uart.baud = config.BaudRate
|
|
||||||
|
|
||||||
// use default UART pins if not specified
|
// use default UART pins if not specified
|
||||||
if config.RX == 0 && config.TX == 0 {
|
if config.RX == 0 && config.TX == 0 {
|
||||||
config.RX = UART_RX_PIN
|
config.RX = UART_RX_PIN
|
||||||
config.TX = UART_TX_PIN
|
config.TX = UART_TX_PIN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uart.baud = config.BaudRate
|
||||||
uart.rx = config.RX
|
uart.rx = config.RX
|
||||||
uart.tx = config.TX
|
uart.tx = config.TX
|
||||||
|
|
||||||
@@ -77,9 +77,38 @@ func (uart *UART) Configure(config UARTConfig) {
|
|||||||
// reset all internal logic and registers
|
// reset all internal logic and registers
|
||||||
uart.resetTransmitting()
|
uart.resetTransmitting()
|
||||||
|
|
||||||
|
// disable until we have finished configuring registers
|
||||||
|
uart.Bus.CTRL.Set(0)
|
||||||
|
|
||||||
// determine the baud rate and over-sample divisors
|
// determine the baud rate and over-sample divisors
|
||||||
sbr, osr := uart.getBaudRateDivisor(uart.baud)
|
sbr, osr := uart.getBaudRateDivisor(uart.baud)
|
||||||
|
|
||||||
|
// set the baud rate, over-sample configuration, stop bits
|
||||||
|
baudBits := (((osr - 1) << nxp.LPUART_BAUD_OSR_Pos) & nxp.LPUART_BAUD_OSR_Msk) |
|
||||||
|
((sbr << nxp.LPUART_BAUD_SBR_Pos) & nxp.LPUART_BAUD_SBR_Msk)
|
||||||
|
if osr <= 8 {
|
||||||
|
// if OSR less than or equal to 8, we must enable sampling on both edges
|
||||||
|
baudBits |= nxp.LPUART_BAUD_BOTHEDGE
|
||||||
|
}
|
||||||
|
uart.Bus.BAUD.Set(baudBits)
|
||||||
|
uart.Bus.PINCFG.Set(0) // disable triggers
|
||||||
|
|
||||||
|
// configure watermarks, flush and enable TX/RX FIFOs
|
||||||
|
rxSize, txSize := uart.getFIFOSize()
|
||||||
|
rxWater := rxSize >> 1
|
||||||
|
if rxWater > uint32(nxp.LPUART_FIFO_RXFIFOSIZE_Msk>>nxp.LPUART_FIFO_RXFIFOSIZE_Pos) {
|
||||||
|
rxWater = uint32(nxp.LPUART_FIFO_RXFIFOSIZE_Msk >> nxp.LPUART_FIFO_RXFIFOSIZE_Pos)
|
||||||
|
}
|
||||||
|
txWater := txSize >> 1
|
||||||
|
if txWater > uint32(nxp.LPUART_FIFO_TXFIFOSIZE_Msk>>nxp.LPUART_FIFO_TXFIFOSIZE_Pos) {
|
||||||
|
txWater = uint32(nxp.LPUART_FIFO_TXFIFOSIZE_Msk >> nxp.LPUART_FIFO_TXFIFOSIZE_Pos)
|
||||||
|
}
|
||||||
|
uart.Bus.WATER.Set(
|
||||||
|
((rxWater << nxp.LPUART_WATER_RXWATER_Pos) & nxp.LPUART_WATER_RXWATER_Msk) |
|
||||||
|
((txWater << nxp.LPUART_WATER_TXWATER_Pos) & nxp.LPUART_WATER_TXWATER_Msk))
|
||||||
|
uart.Bus.FIFO.SetBits(nxp.LPUART_FIFO_RXFE | nxp.LPUART_FIFO_TXFE |
|
||||||
|
nxp.LPUART_FIFO_RXFLUSH | nxp.LPUART_FIFO_TXFLUSH)
|
||||||
|
|
||||||
// for now we assume some configuration. in particular:
|
// for now we assume some configuration. in particular:
|
||||||
// Data bits -> 8-bit
|
// Data bits -> 8-bit
|
||||||
// Parity bit -> None (parity bit generation disabled)
|
// Parity bit -> None (parity bit generation disabled)
|
||||||
@@ -90,71 +119,16 @@ func (uart *UART) Configure(config UARTConfig) {
|
|||||||
// RX RTS enabled -> false
|
// RX RTS enabled -> false
|
||||||
// TX CTS enabled -> false
|
// TX CTS enabled -> false
|
||||||
|
|
||||||
// set the baud rate, over-sample configuration, stop bits
|
// enable transmitter, receiver functions
|
||||||
baudBits := (((osr - 1) << nxp.LPUART_BAUD_OSR_Pos) & nxp.LPUART_BAUD_OSR_Msk) |
|
uart.Bus.CTRL.Set(nxp.LPUART_CTRL_TE | nxp.LPUART_CTRL_RE |
|
||||||
((sbr << nxp.LPUART_BAUD_SBR_Pos) & nxp.LPUART_BAUD_SBR_Msk) |
|
// enable receiver, idle line interrupts
|
||||||
((nxp.LPUART_BAUD_SBNS_SBNS_0 << nxp.LPUART_BAUD_SBNS_Pos) & nxp.LPUART_BAUD_SBNS_Msk)
|
nxp.LPUART_CTRL_RIE | nxp.LPUART_CTRL_ILIE)
|
||||||
if osr <= 8 {
|
|
||||||
// if OSR less than or equal to 8, we must enable sampling on both edges
|
|
||||||
baudBits |= nxp.LPUART_BAUD_BOTHEDGE
|
|
||||||
}
|
|
||||||
uart.Bus.BAUD.Set(baudBits)
|
|
||||||
|
|
||||||
uart.Bus.PINCFG.Set(0) // disable triggers
|
|
||||||
|
|
||||||
// use 8 data bits, disable parity, use 1 idle char, and idle count starts
|
|
||||||
// after start bit
|
|
||||||
ctrlBits := uint32(((nxp.LPUART_CTRL_M_M_0 << nxp.LPUART_CTRL_M_Pos) & nxp.LPUART_CTRL_M_Msk) |
|
|
||||||
((nxp.LPUART_CTRL_PE_PE_0 << nxp.LPUART_CTRL_PE_Pos) & nxp.LPUART_CTRL_PE_Msk) |
|
|
||||||
((nxp.LPUART_CTRL_ILT_ILT_0 << nxp.LPUART_CTRL_ILT_Pos) & nxp.LPUART_CTRL_ILT_Msk) |
|
|
||||||
((nxp.LPUART_CTRL_IDLECFG_IDLECFG_0 << nxp.LPUART_CTRL_IDLECFG_Pos) & nxp.LPUART_CTRL_IDLECFG_Msk))
|
|
||||||
uart.Bus.CTRL.Set(ctrlBits)
|
|
||||||
|
|
||||||
rxSize, txSize := uart.getFIFOSize()
|
|
||||||
|
|
||||||
rxWater := rxSize >> 1
|
|
||||||
if rxWater > uint32(nxp.LPUART_FIFO_RXFIFOSIZE_Msk>>nxp.LPUART_FIFO_RXFIFOSIZE_Pos) {
|
|
||||||
rxWater = uint32(nxp.LPUART_FIFO_RXFIFOSIZE_Msk >> nxp.LPUART_FIFO_RXFIFOSIZE_Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
txWater := txSize >> 1
|
|
||||||
if txWater > uint32(nxp.LPUART_FIFO_TXFIFOSIZE_Msk>>nxp.LPUART_FIFO_TXFIFOSIZE_Pos) {
|
|
||||||
txWater = uint32(nxp.LPUART_FIFO_TXFIFOSIZE_Msk >> nxp.LPUART_FIFO_TXFIFOSIZE_Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
uart.Bus.WATER.Set(
|
|
||||||
((rxWater << nxp.LPUART_WATER_RXWATER_Pos) & nxp.LPUART_WATER_RXWATER_Msk) |
|
|
||||||
((txWater << nxp.LPUART_WATER_TXWATER_Pos) & nxp.LPUART_WATER_TXWATER_Msk))
|
|
||||||
|
|
||||||
// enable TX/RX FIFOs
|
|
||||||
uart.Bus.FIFO.SetBits(nxp.LPUART_FIFO_RXFE | nxp.LPUART_FIFO_TXFE)
|
|
||||||
|
|
||||||
// flush TX/RX FIFOs
|
|
||||||
uart.Bus.FIFO.SetBits(nxp.LPUART_FIFO_RXFLUSH | nxp.LPUART_FIFO_TXFLUSH)
|
|
||||||
|
|
||||||
uart.Bus.MODIR.SetBits( // set the CTS configuration/TX CTS source
|
|
||||||
((nxp.LPUART_MODIR_TXCTSC_TXCTSC_0 << nxp.LPUART_MODIR_TXCTSC_Pos) & nxp.LPUART_MODIR_TXCTSC_Msk) |
|
|
||||||
((nxp.LPUART_MODIR_TXCTSSRC_TXCTSSRC_0 << nxp.LPUART_MODIR_TXCTSSRC_Pos) & nxp.LPUART_MODIR_TXCTSSRC_Msk))
|
|
||||||
|
|
||||||
// clear all status flags
|
// clear all status flags
|
||||||
stat := uint32(nxp.LPUART_STAT_RXEDGIF_Msk | nxp.LPUART_STAT_IDLE_Msk | nxp.LPUART_STAT_OR_Msk |
|
uart.Bus.STAT.Set(uart.Bus.STAT.Get())
|
||||||
nxp.LPUART_STAT_NF_Msk | nxp.LPUART_STAT_FE_Msk | nxp.LPUART_STAT_PF_Msk |
|
|
||||||
nxp.LPUART_STAT_LBKDIF_Msk | nxp.LPUART_STAT_MA1F_Msk | nxp.LPUART_STAT_MA2F_Msk)
|
|
||||||
|
|
||||||
// set data bits order
|
// enable RX interrupt
|
||||||
if uart.msbFirst {
|
uart.Interrupt.SetPriority(0xC0)
|
||||||
stat |= nxp.LPUART_STAT_MSBF
|
|
||||||
} else {
|
|
||||||
stat &^= nxp.LPUART_STAT_MSBF
|
|
||||||
}
|
|
||||||
|
|
||||||
uart.Bus.STAT.SetBits(stat)
|
|
||||||
|
|
||||||
// enable RX/TX functions
|
|
||||||
uart.Bus.CTRL.SetBits(nxp.LPUART_CTRL_TE | nxp.LPUART_CTRL_RE)
|
|
||||||
|
|
||||||
// enable RX IRQ
|
|
||||||
uart.Interrupt.SetPriority(0xc0)
|
|
||||||
uart.Interrupt.Enable()
|
uart.Interrupt.Enable()
|
||||||
|
|
||||||
uart.configured = true
|
uart.configured = true
|
||||||
@@ -211,13 +185,13 @@ func (uart *UART) WriteByte(c byte) error {
|
|||||||
// corresponding baud rate divisor (1..8191) that best partition a given baud
|
// corresponding baud rate divisor (1..8191) that best partition a given baud
|
||||||
// rate into equal intervals.
|
// rate into equal intervals.
|
||||||
//
|
//
|
||||||
// This is an integral (i.e. non-floating point) port of the logic at the
|
// This is an integral (non-floating point) translation of the logic at the
|
||||||
// beginning of:
|
// beginning of:
|
||||||
// void HardwareSerial::begin(uint32_t baud, uint16_t format)
|
// void HardwareSerial::begin(uint32_t baud, uint16_t format)
|
||||||
// (from Teensyduino: `cores/teensy4/HardwareSerial.cpp`)
|
// (from Teensyduino: `cores/teensy4/HardwareSerial.cpp`)
|
||||||
//
|
//
|
||||||
// We don't want to risk using floating point here in the machine package in
|
// We don't want to use floating point here in case it gets called from an ISR
|
||||||
// case it gets called before the FPU or interrupts are ready (e.g., init()).
|
// or very early during system init.
|
||||||
func (uart *UART) getBaudRateDivisor(baudRate uint32) (sbr uint32, osr uint32) {
|
func (uart *UART) getBaudRateDivisor(baudRate uint32) (sbr uint32, osr uint32) {
|
||||||
const clock = 24000000 // UART is muxed to 24 MHz OSC
|
const clock = 24000000 // UART is muxed to 24 MHz OSC
|
||||||
err := uint32(0xFFFFFFFF)
|
err := uint32(0xFFFFFFFF)
|
||||||
|
|||||||
@@ -47,14 +47,27 @@ type pinFunc uint8
|
|||||||
// GPIO function selectors
|
// GPIO function selectors
|
||||||
const (
|
const (
|
||||||
fnJTAG pinFunc = 0
|
fnJTAG pinFunc = 0
|
||||||
fnSPI pinFunc = 1
|
fnSPI pinFunc = 1 // Connect one of the internal PL022 SPI peripherals to GPIO
|
||||||
fnUART pinFunc = 2
|
fnUART pinFunc = 2
|
||||||
fnI2C pinFunc = 3
|
fnI2C pinFunc = 3
|
||||||
|
// Connect a PWM slice to GPIO. There are eight PWM slices,
|
||||||
|
// each with two outputchannels (A/B). The B pin can also be used as an input,
|
||||||
|
// for frequency and duty cyclemeasurement
|
||||||
fnPWM pinFunc = 4
|
fnPWM pinFunc = 4
|
||||||
|
// Software control of GPIO, from the single-cycle IO (SIO) block.
|
||||||
|
// The SIO function (F5)must be selected for the processors to drive a GPIO,
|
||||||
|
// but the input is always connected,so software can check the state of GPIOs at any time.
|
||||||
fnSIO pinFunc = 5
|
fnSIO pinFunc = 5
|
||||||
fnPIO0 pinFunc = 6
|
// Connect one of the programmable IO blocks (PIO) to GPIO. PIO can implement a widevariety of interfaces,
|
||||||
fnPIO1 pinFunc = 7
|
// and has its own internal pin mapping hardware, allowing flexibleplacement of digital interfaces on bank 0 GPIOs.
|
||||||
|
// The PIO function (F6, F7) must beselected for PIO to drive a GPIO, but the input is always connected,
|
||||||
|
// so the PIOs canalways see the state of all pins.
|
||||||
|
fnPIO0, fnPIO1 pinFunc = 6, 7
|
||||||
|
// General purpose clock inputs/outputs. Can be routed to a number of internal clock domains onRP2040,
|
||||||
|
// e.g. Input: to provide a 1 Hz clock for the RTC, or can be connected to an internalfrequency counter.
|
||||||
|
// e.g. Output: optional integer divide
|
||||||
fnGPCK pinFunc = 8
|
fnGPCK pinFunc = 8
|
||||||
|
// USB power control signals to/from the internal USB controller
|
||||||
fnUSB pinFunc = 9
|
fnUSB pinFunc = 9
|
||||||
fnNULL pinFunc = 0x1f
|
fnNULL pinFunc = 0x1f
|
||||||
|
|
||||||
@@ -68,6 +81,9 @@ const (
|
|||||||
PinInputPullup
|
PinInputPullup
|
||||||
PinAnalog
|
PinAnalog
|
||||||
PinUART
|
PinUART
|
||||||
|
PinPWM
|
||||||
|
PinI2C
|
||||||
|
PinSPI
|
||||||
)
|
)
|
||||||
|
|
||||||
// set drives the pin high
|
// set drives the pin high
|
||||||
@@ -90,7 +106,7 @@ func (p Pin) xor() {
|
|||||||
|
|
||||||
// get returns the pin value
|
// get returns the pin value
|
||||||
func (p Pin) get() bool {
|
func (p Pin) get() bool {
|
||||||
return rp.SIO.GPIO_IN.HasBits(uint32(1) << p)
|
return rp.SIO.GPIO_IN.HasBits(1 << p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Pin) ioCtrl() *volatile.Register32 {
|
func (p Pin) ioCtrl() *volatile.Register32 {
|
||||||
@@ -116,6 +132,17 @@ func (p Pin) pulloff() {
|
|||||||
p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE)
|
p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setSlew sets pad slew rate control.
|
||||||
|
// true sets to fast. false sets to slow.
|
||||||
|
func (p Pin) setSlew(sr bool) {
|
||||||
|
p.padCtrl().ReplaceBits(boolToBit(sr)<<rp.PADS_BANK0_GPIO0_SLEWFAST_Pos, rp.PADS_BANK0_GPIO0_SLEWFAST_Msk, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setSchmitt enables or disables Schmitt trigger.
|
||||||
|
func (p Pin) setSchmitt(trigger bool) {
|
||||||
|
p.padCtrl().ReplaceBits(boolToBit(trigger)<<rp.PADS_BANK0_GPIO0_SCHMITT_Pos, rp.PADS_BANK0_GPIO0_SCHMITT_Msk, 0)
|
||||||
|
}
|
||||||
|
|
||||||
// setFunc will set pin function to fn.
|
// setFunc will set pin function to fn.
|
||||||
func (p Pin) setFunc(fn pinFunc) {
|
func (p Pin) setFunc(fn pinFunc) {
|
||||||
// Set input enable, Clear output disable
|
// Set input enable, Clear output disable
|
||||||
@@ -155,6 +182,16 @@ func (p Pin) Configure(config PinConfig) {
|
|||||||
p.pulloff()
|
p.pulloff()
|
||||||
case PinUART:
|
case PinUART:
|
||||||
p.setFunc(fnUART)
|
p.setFunc(fnUART)
|
||||||
|
case PinPWM:
|
||||||
|
p.setFunc(fnPWM)
|
||||||
|
case PinI2C:
|
||||||
|
// IO config according to 4.3.1.3 of rp2040 datasheet.
|
||||||
|
p.setFunc(fnI2C)
|
||||||
|
p.pullup()
|
||||||
|
p.setSchmitt(true)
|
||||||
|
p.setSlew(false)
|
||||||
|
case PinSPI:
|
||||||
|
p.setFunc(fnSPI)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,461 @@
|
|||||||
|
// +build rp2040
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/rp"
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C on the RP2040.
|
||||||
|
var (
|
||||||
|
I2C0 = &_I2C0
|
||||||
|
_I2C0 = I2C{
|
||||||
|
Bus: rp.I2C0,
|
||||||
|
}
|
||||||
|
I2C1 = &_I2C1
|
||||||
|
_I2C1 = I2C{
|
||||||
|
Bus: rp.I2C1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Features: Taken from datasheet.
|
||||||
|
// Default master mode, with slave mode available (not simulataneously).
|
||||||
|
// Default slave address of RP2040: 0x055
|
||||||
|
// Supports 10-bit addressing in Master mode
|
||||||
|
// 16-element transmit buffer
|
||||||
|
// 16-element receive buffer
|
||||||
|
// Can be driven from DMA
|
||||||
|
// Can generate interrupts
|
||||||
|
// Fast mode plus max transfer speed (1000kb/s)
|
||||||
|
|
||||||
|
// GPIO config
|
||||||
|
// Each controller must connect its clock SCL and data SDA to one pair of GPIOs.
|
||||||
|
// The I2C standard requires that drivers drivea signal low, or when not driven the signal will be pulled high.
|
||||||
|
// This applies to SCL and SDA. The GPIO pads should beconfigured for:
|
||||||
|
// Pull-up enabled
|
||||||
|
// Slew rate limited
|
||||||
|
// Schmitt trigger enabled
|
||||||
|
// Note: There should also be external pull-ups on the board as the internal pad pull-ups may not be strong enough to pull upexternal circuits.
|
||||||
|
|
||||||
|
// I2CConfig is used to store config info for I2C.
|
||||||
|
type I2CConfig struct {
|
||||||
|
Frequency uint32
|
||||||
|
// SDA/SCL Serial Data and clock pins. Refer to datasheet to see
|
||||||
|
// which pins match the desired bus.
|
||||||
|
SDA, SCL Pin
|
||||||
|
}
|
||||||
|
|
||||||
|
type I2C struct {
|
||||||
|
Bus *rp.I2C0_Type
|
||||||
|
restartOnNext bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidI2CBaudrate = errors.New("invalid i2c baudrate")
|
||||||
|
ErrInvalidTgtAddr = errors.New("invalid target i2c address not in 0..0x80 or is reserved")
|
||||||
|
ErrI2CGeneric = errors.New("i2c error")
|
||||||
|
ErrRP2040I2CDisable = errors.New("i2c rp2040 peripheral timeout in disable")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tx performs a write and then a read transfer placing the result in
|
||||||
|
// in r.
|
||||||
|
//
|
||||||
|
// Passing a nil value for w or r skips the transfer corresponding to write
|
||||||
|
// or read, respectively.
|
||||||
|
//
|
||||||
|
// i2c.Tx(addr, nil, r)
|
||||||
|
// Performs only a read transfer.
|
||||||
|
//
|
||||||
|
// i2c.Tx(addr, w, nil)
|
||||||
|
// Performs only a write transfer.
|
||||||
|
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||||
|
// timeout in microseconds.
|
||||||
|
const timeout = 40 * 1000 // 40ms is a reasonable time for a real-time system.
|
||||||
|
if len(w) > 0 {
|
||||||
|
if err := i2c.tx(uint8(addr), w, false, timeout); nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(r) > 0 {
|
||||||
|
if err := i2c.rx(uint8(addr), r, false, timeout); nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure initializes i2c peripheral and configures I2C config's pins passed.
|
||||||
|
// Here's a list of valid SDA and SCL GPIO pins on bus I2C0 of the rp2040:
|
||||||
|
// SDA: 0, 4, 8, 12, 16, 20
|
||||||
|
// SCL: 1, 5, 9, 13, 17, 21
|
||||||
|
// Same as above for I2C1 bus:
|
||||||
|
// SDA: 2, 6, 10, 14, 18, 26
|
||||||
|
// SCL: 3, 7, 11, 15, 19, 27
|
||||||
|
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||||
|
if config.SCL == 0 {
|
||||||
|
// If config pins are zero valued or clock pin is invalid then we set default values.
|
||||||
|
switch i2c.Bus {
|
||||||
|
case rp.I2C0:
|
||||||
|
config.SCL = I2C0_SCL_PIN
|
||||||
|
config.SDA = I2C0_SDA_PIN
|
||||||
|
case rp.I2C1:
|
||||||
|
config.SCL = I2C1_SCL_PIN
|
||||||
|
config.SDA = I2C1_SDA_PIN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.SDA.Configure(PinConfig{PinI2C})
|
||||||
|
config.SCL.Configure(PinConfig{PinI2C})
|
||||||
|
return i2c.init(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBaudrate sets the I2C frequency. It has the side effect of also
|
||||||
|
// enabling the I2C hardware if disabled beforehand.
|
||||||
|
//go:inline
|
||||||
|
func (i2c *I2C) SetBaudrate(br uint32) error {
|
||||||
|
const freqin uint32 = 125 * MHz
|
||||||
|
// Find smallest prescale value which puts o
|
||||||
|
|
||||||
|
// TODO there are some subtleties to I2C timing which we are completely ignoring here
|
||||||
|
period := (freqin + br/2) / br
|
||||||
|
lcnt := period * 3 / 5 // oof this one hurts
|
||||||
|
hcnt := period - lcnt
|
||||||
|
// Check for out-of-range divisors:
|
||||||
|
if hcnt > rp.I2C0_IC_FS_SCL_HCNT_IC_FS_SCL_HCNT_Msk || hcnt < 8 || lcnt > rp.I2C0_IC_FS_SCL_LCNT_IC_FS_SCL_LCNT_Msk || lcnt < 8 {
|
||||||
|
return ErrInvalidI2CBaudrate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per I2C-bus specification a device in standard or fast mode must
|
||||||
|
// internally provide a hold time of at least 300ns for the SDA signal to
|
||||||
|
// bridge the undefined region of the falling edge of SCL. A smaller hold
|
||||||
|
// time of 120ns is used for fast mode plus.
|
||||||
|
|
||||||
|
// sda_tx_hold_count = freq_in [cycles/s] * 300ns * (1s / 1e9ns)
|
||||||
|
// Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint.
|
||||||
|
// Add 1 to avoid division truncation.
|
||||||
|
sdaTxHoldCnt := ((freqin * 3) / 10000000) + 1
|
||||||
|
if br >= 1_000_000 {
|
||||||
|
// sda_tx_hold_count = freq_in [cycles/s] * 120ns * (1s / 1e9ns)
|
||||||
|
// Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint.
|
||||||
|
// Add 1 to avoid division truncation.
|
||||||
|
sdaTxHoldCnt = ((freqin * 3) / 25000000) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if sdaTxHoldCnt > lcnt-2 {
|
||||||
|
return ErrInvalidI2CBaudrate
|
||||||
|
}
|
||||||
|
err := i2c.disable()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Always use "fast" mode (<= 400 kHz, works fine for standard mode too)
|
||||||
|
|
||||||
|
i2c.Bus.IC_CON.ReplaceBits(rp.I2C0_IC_CON_SPEED_FAST<<rp.I2C0_IC_CON_SPEED_Pos, rp.I2C0_IC_CON_SPEED_Msk, 0)
|
||||||
|
i2c.Bus.IC_FS_SCL_HCNT.Set(hcnt)
|
||||||
|
i2c.Bus.IC_FS_SCL_LCNT.Set(lcnt)
|
||||||
|
|
||||||
|
i2c.Bus.IC_FS_SPKLEN.Set(u32max(1, lcnt/16))
|
||||||
|
|
||||||
|
i2c.Bus.IC_SDA_HOLD.ReplaceBits(sdaTxHoldCnt<<rp.I2C0_IC_SDA_HOLD_IC_SDA_TX_HOLD_Pos, rp.I2C0_IC_SDA_HOLD_IC_SDA_TX_HOLD_Msk, 0)
|
||||||
|
i2c.enable()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func (i2c *I2C) enable() {
|
||||||
|
i2c.Bus.IC_ENABLE.ReplaceBits(rp.I2C0_IC_ENABLE_ENABLE<<rp.I2C0_IC_ENABLE_ENABLE_Pos, rp.I2C0_IC_ENABLE_ENABLE_Msk, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implemented as per 4.3.10.3. Disabling DW_apb_i2c section.
|
||||||
|
//go:inline
|
||||||
|
func (i2c *I2C) disable() error {
|
||||||
|
const MAX_T_POLL_COUNT = 64 // 64 us timeout corresponds to around 1000kb/s i2c transfer rate.
|
||||||
|
deadline := ticks() + MAX_T_POLL_COUNT
|
||||||
|
i2c.Bus.IC_ENABLE.Set(0)
|
||||||
|
for i2c.Bus.IC_ENABLE_STATUS.Get()&1 != 0 {
|
||||||
|
if ticks() > deadline {
|
||||||
|
return ErrRP2040I2CDisable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func (i2c *I2C) init(config I2CConfig) error {
|
||||||
|
i2c.reset()
|
||||||
|
if err := i2c.disable(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i2c.restartOnNext = false
|
||||||
|
// Configure as a fast-mode master with RepStart support, 7-bit addresses
|
||||||
|
i2c.Bus.IC_CON.Set((rp.I2C0_IC_CON_SPEED_FAST << rp.I2C0_IC_CON_SPEED_Pos) |
|
||||||
|
rp.I2C0_IC_CON_MASTER_MODE | rp.I2C0_IC_CON_IC_SLAVE_DISABLE |
|
||||||
|
rp.I2C0_IC_CON_IC_RESTART_EN | rp.I2C0_IC_CON_TX_EMPTY_CTRL) // sets TX_EMPTY_CTRL to enable TX_EMPTY interrupt status
|
||||||
|
|
||||||
|
// Set FIFO watermarks to 1 to make things simpler. This is encoded by a register value of 0.
|
||||||
|
i2c.Bus.IC_TX_TL.Set(0)
|
||||||
|
i2c.Bus.IC_RX_TL.Set(0)
|
||||||
|
|
||||||
|
// Always enable the DREQ signalling -- harmless if DMA isn't listening
|
||||||
|
i2c.Bus.IC_DMA_CR.Set(rp.I2C0_IC_DMA_CR_TDMAE | rp.I2C0_IC_DMA_CR_RDMAE)
|
||||||
|
return i2c.SetBaudrate(config.Frequency)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset sets I2C register RESET bits in the reset peripheral and then clears them.
|
||||||
|
//go:inline
|
||||||
|
func (i2c *I2C) reset() {
|
||||||
|
resetVal := i2c.deinit()
|
||||||
|
rp.RESETS.RESET.ClearBits(resetVal)
|
||||||
|
// Wait until reset is done.
|
||||||
|
for !rp.RESETS.RESET_DONE.HasBits(resetVal) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deinit sets reset bit for I2C. Must call reset to reenable I2C after deinit.
|
||||||
|
//go:inline
|
||||||
|
func (i2c *I2C) deinit() (resetVal uint32) {
|
||||||
|
switch {
|
||||||
|
case i2c.Bus == rp.I2C0:
|
||||||
|
resetVal = rp.RESETS_RESET_I2C0
|
||||||
|
case i2c.Bus == rp.I2C1:
|
||||||
|
resetVal = rp.RESETS_RESET_I2C1
|
||||||
|
}
|
||||||
|
// Perform I2C reset.
|
||||||
|
rp.RESETS.RESET.SetBits(resetVal)
|
||||||
|
|
||||||
|
return resetVal
|
||||||
|
}
|
||||||
|
|
||||||
|
// tx is a primitive i2c blocking write to bus routine. timeout is time to wait
|
||||||
|
// in microseconds since calling this function for write to finish.
|
||||||
|
func (i2c *I2C) tx(addr uint8, tx []byte, nostop bool, timeout uint64) (err error) {
|
||||||
|
deadline := ticks() + timeout
|
||||||
|
if addr >= 0x80 || isReservedI2CAddr(addr) {
|
||||||
|
return ErrInvalidTgtAddr
|
||||||
|
}
|
||||||
|
tlen := len(tx)
|
||||||
|
// Quick return if possible.
|
||||||
|
if tlen == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = i2c.disable()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i2c.Bus.IC_TAR.Set(uint32(addr))
|
||||||
|
i2c.enable()
|
||||||
|
// If no timeout was passed timeoutCheck is false.
|
||||||
|
abort := false
|
||||||
|
var abortReason uint32
|
||||||
|
byteCtr := 0
|
||||||
|
for ; byteCtr < tlen; byteCtr++ {
|
||||||
|
first := byteCtr == 0
|
||||||
|
last := byteCtr == tlen-1
|
||||||
|
i2c.Bus.IC_DATA_CMD.Set(
|
||||||
|
(boolToBit(first && i2c.restartOnNext) << rp.I2C0_IC_DATA_CMD_RESTART_Pos) |
|
||||||
|
(boolToBit(last && !nostop) << rp.I2C0_IC_DATA_CMD_STOP_Pos) |
|
||||||
|
uint32(tx[byteCtr]))
|
||||||
|
|
||||||
|
// Wait until the transmission of the address/data from the internal
|
||||||
|
// shift register has completed. For this to function correctly, the
|
||||||
|
// TX_EMPTY_CTRL flag in IC_CON must be set. The TX_EMPTY_CTRL flag
|
||||||
|
// was set in i2c_init.
|
||||||
|
|
||||||
|
// IC_RAW_INTR_STAT_TX_EMPTY: This bit is set to 1 when the transmit buffer is at or below
|
||||||
|
// the threshold value set in the IC_TX_TL register and the
|
||||||
|
// transmission of the address/data from the internal shift
|
||||||
|
// register for the most recently popped command is
|
||||||
|
// completed. It is automatically cleared by hardware when
|
||||||
|
// the buffer level goes above the threshold. When
|
||||||
|
// IC_ENABLE[0] is set to 0, the TX FIFO is flushed and held
|
||||||
|
// in reset. There the TX FIFO looks like it has no data within
|
||||||
|
// it, so this bit is set to 1, provided there is activity in the
|
||||||
|
// master or slave state machines. When there is no longer
|
||||||
|
// any activity, then with ic_en=0, this bit is set to 0.
|
||||||
|
for !i2c.interrupted(rp.I2C0_IC_RAW_INTR_STAT_TX_EMPTY) {
|
||||||
|
if ticks() > deadline {
|
||||||
|
i2c.restartOnNext = nostop
|
||||||
|
println(1)
|
||||||
|
return errI2CWriteTimeout // If there was a timeout, don't attempt to do anything else.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abortReason = i2c.getAbortReason()
|
||||||
|
if abortReason != 0 {
|
||||||
|
i2c.clearAbortReason()
|
||||||
|
abort = true
|
||||||
|
}
|
||||||
|
if abort || (last && !nostop) {
|
||||||
|
// If the transaction was aborted or if it completed
|
||||||
|
// successfully wait until the STOP condition has occured.
|
||||||
|
|
||||||
|
// TODO Could there be an abort while waiting for the STOP
|
||||||
|
// condition here? If so, additional code would be needed here
|
||||||
|
// to take care of the abort.
|
||||||
|
for !i2c.interrupted(rp.I2C0_IC_RAW_INTR_STAT_STOP_DET) {
|
||||||
|
if ticks() > deadline {
|
||||||
|
println(2)
|
||||||
|
i2c.restartOnNext = nostop
|
||||||
|
return errI2CWriteTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i2c.Bus.IC_CLR_STOP_DET.Get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// From Pico SDK: A lot of things could have just happened due to the ingenious and
|
||||||
|
// creative design of I2C. Try to figure things out.
|
||||||
|
if abort {
|
||||||
|
switch {
|
||||||
|
case abortReason == 0 || abortReason&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK != 0:
|
||||||
|
// No reported errors - seems to happen if there is nothing connected to the bus.
|
||||||
|
// Address byte not acknowledged
|
||||||
|
err = ErrI2CGeneric
|
||||||
|
case abortReason&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK != 0:
|
||||||
|
// Address acknowledged, some data not acknowledged
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
// panic("unknown i2c abortReason:" + strconv.Itoa(abortReason)
|
||||||
|
err = makeI2CBuffError(byteCtr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nostop means we are now at the end of a *message* but not the end of a *transfer*
|
||||||
|
i2c.restartOnNext = nostop
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// rx is a primitive i2c blocking read routine. timeout is time to wait
|
||||||
|
// in microseconds since calling this function for read to finish.
|
||||||
|
func (i2c *I2C) rx(addr uint8, rx []byte, nostop bool, timeout uint64) (err error) {
|
||||||
|
deadline := ticks() + timeout
|
||||||
|
if addr >= 0x80 || isReservedI2CAddr(addr) {
|
||||||
|
return ErrInvalidTgtAddr
|
||||||
|
}
|
||||||
|
rlen := len(rx)
|
||||||
|
// Quick return if possible.
|
||||||
|
if rlen == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err = i2c.disable()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i2c.Bus.IC_TAR.Set(uint32(addr))
|
||||||
|
i2c.enable()
|
||||||
|
// If no timeout was passed timeoutCheck is false.
|
||||||
|
abort := false
|
||||||
|
var abortReason uint32
|
||||||
|
byteCtr := 0
|
||||||
|
for ; byteCtr < rlen; byteCtr++ {
|
||||||
|
first := byteCtr == 0
|
||||||
|
last := byteCtr == rlen-1
|
||||||
|
for i2c.writeAvailable() == 0 {
|
||||||
|
}
|
||||||
|
i2c.Bus.IC_DATA_CMD.Set(
|
||||||
|
boolToBit(first && i2c.restartOnNext)<<rp.I2C0_IC_DATA_CMD_RESTART_Pos |
|
||||||
|
boolToBit(last && !nostop)<<rp.I2C0_IC_DATA_CMD_STOP_Pos |
|
||||||
|
rp.I2C0_IC_DATA_CMD_CMD) // -> 1 for read
|
||||||
|
|
||||||
|
for !abort && i2c.readAvailable() == 0 {
|
||||||
|
abortReason = i2c.getAbortReason()
|
||||||
|
i2c.clearAbortReason()
|
||||||
|
if abortReason != 0 {
|
||||||
|
abort = true
|
||||||
|
}
|
||||||
|
if ticks() > deadline {
|
||||||
|
i2c.restartOnNext = nostop
|
||||||
|
return errI2CReadTimeout // If there was a timeout, don't attempt to do anything else.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if abort {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
rx[byteCtr] = uint8(i2c.Bus.IC_DATA_CMD.Get())
|
||||||
|
}
|
||||||
|
|
||||||
|
if abort {
|
||||||
|
switch {
|
||||||
|
case abortReason == 0 || abortReason&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK != 0:
|
||||||
|
// No reported errors - seems to happen if there is nothing connected to the bus.
|
||||||
|
// Address byte not acknowledged
|
||||||
|
err = ErrI2CGeneric
|
||||||
|
default:
|
||||||
|
// undefined abort sequence
|
||||||
|
err = makeI2CBuffError(byteCtr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c.restartOnNext = nostop
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeAvailable determines non-blocking write space available
|
||||||
|
//go:inline
|
||||||
|
func (i2c *I2C) writeAvailable() uint32 {
|
||||||
|
return rp.I2C0_IC_COMP_PARAM_1_TX_BUFFER_DEPTH_Pos - i2c.Bus.IC_TXFLR.Get()
|
||||||
|
}
|
||||||
|
|
||||||
|
// readAvailable determines number of bytes received
|
||||||
|
//go:inline
|
||||||
|
func (i2c *I2C) readAvailable() uint32 {
|
||||||
|
return i2c.Bus.IC_RXFLR.Get()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equivalent to IC_CLR_TX_ABRT.Get() (side effect clears ABORT_REASON)
|
||||||
|
//go:inline
|
||||||
|
func (i2c *I2C) clearAbortReason() {
|
||||||
|
// Note clearing the abort flag also clears the reason, and
|
||||||
|
// this instance of flag is clear-on-read! Note also the
|
||||||
|
// IC_CLR_TX_ABRT register always reads as 0.
|
||||||
|
i2c.Bus.IC_CLR_TX_ABRT.Get()
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func (i2c *I2C) getAbortReason() uint32 {
|
||||||
|
return i2c.Bus.IC_TX_ABRT_SOURCE.Get()
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if RAW_INTR_STAT bits in mask are all set. performs:
|
||||||
|
// RAW_INTR_STAT & mask == mask
|
||||||
|
//go:inline
|
||||||
|
func (i2c *I2C) interrupted(mask uint32) bool {
|
||||||
|
reg := i2c.Bus.IC_RAW_INTR_STAT.Get()
|
||||||
|
return reg&mask == mask
|
||||||
|
}
|
||||||
|
|
||||||
|
type i2cBuffError int
|
||||||
|
|
||||||
|
func (b i2cBuffError) Error() string {
|
||||||
|
return "i2c err after addr ack at data " + strconv.Itoa(int(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func makeI2CBuffError(idx int) error {
|
||||||
|
return i2cBuffError(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func boolToBit(a bool) uint32 {
|
||||||
|
if a {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func u32max(a, b uint32) uint32 {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func isReservedI2CAddr(addr uint8) bool {
|
||||||
|
return (addr&0x78) == 0 || (addr&0x78) == 0x78
|
||||||
|
}
|
||||||
@@ -0,0 +1,384 @@
|
|||||||
|
// +build rp2040
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/rp"
|
||||||
|
"errors"
|
||||||
|
"runtime/volatile"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrPeriodTooBig = errors.New("period outside valid range 1..4e9ns")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxPWMPins = 29
|
||||||
|
)
|
||||||
|
|
||||||
|
// pwmGroup is one PWM peripheral, which consists of a counter and two output
|
||||||
|
// channels. You can set the frequency using SetPeriod,
|
||||||
|
// but only for all the channels in this PWM peripheral at once.
|
||||||
|
//
|
||||||
|
// div: integer value to reduce counting rate by. Must be greater than or equal to 1.
|
||||||
|
//
|
||||||
|
// cc: counter compare level. Contains 2 channel levels. The 16 LSBs are Channel A's level (Duty Cycle)
|
||||||
|
// and the 16 MSBs are Channel B's level.
|
||||||
|
//
|
||||||
|
// top: Wrap. Highest number counter will reach before wrapping over. usually 0xffff.
|
||||||
|
//
|
||||||
|
// csr: Clock mode. PWM_CH0_CSR_DIVMODE_xxx registers have 4 possible modes, of which Free-running is used.
|
||||||
|
// csr contains output polarity bit at PWM_CH0_CSR_x_INV where x is the channel.
|
||||||
|
// csr contains phase correction bit at PWM_CH0_CSR_PH_CORRECT_Msk.
|
||||||
|
// csr contains PWM enable bit at PWM_CH0_CSR_EN. If not enabled PWM will not be active.
|
||||||
|
//
|
||||||
|
// ctr: PWM counter value.
|
||||||
|
type pwmGroup struct {
|
||||||
|
CSR volatile.Register32
|
||||||
|
DIV volatile.Register32
|
||||||
|
CTR volatile.Register32
|
||||||
|
CC volatile.Register32
|
||||||
|
TOP volatile.Register32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equivalent of
|
||||||
|
// var pwmSlice []pwmGroup = (*[8]pwmGroup)(unsafe.Pointer(rp.PWM))[:]
|
||||||
|
// return &pwmSlice[index]
|
||||||
|
// 0x14 is the size of a pwmGroup.
|
||||||
|
func getPWMGroup(index uintptr) *pwmGroup {
|
||||||
|
return (*pwmGroup)(unsafe.Pointer(uintptr(unsafe.Pointer(rp.PWM)) + 0x14*index))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PWM peripherals available on RP2040. Each peripheral has 2 pins available for
|
||||||
|
// a total of 16 available PWM outputs. Some pins may not be available on some boards.
|
||||||
|
var (
|
||||||
|
PWM0 = getPWMGroup(0)
|
||||||
|
PWM1 = getPWMGroup(1)
|
||||||
|
PWM2 = getPWMGroup(2)
|
||||||
|
PWM3 = getPWMGroup(3)
|
||||||
|
PWM4 = getPWMGroup(4)
|
||||||
|
PWM5 = getPWMGroup(5)
|
||||||
|
PWM6 = getPWMGroup(6)
|
||||||
|
PWM7 = getPWMGroup(7)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configure enables and configures this PWM.
|
||||||
|
func (pwm *pwmGroup) Configure(config PWMConfig) error {
|
||||||
|
return pwm.init(config, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel returns a PWM channel for the given pin. If pin does
|
||||||
|
// not belong to PWM peripheral ErrInvalidOutputPin error is returned.
|
||||||
|
// It also configures pin as PWM output.
|
||||||
|
func (pwm *pwmGroup) Channel(pin Pin) (channel uint8, err error) {
|
||||||
|
if pin > maxPWMPins || pwmGPIOToSlice(pin) != pwm.peripheral() {
|
||||||
|
return 3, ErrInvalidOutputPin
|
||||||
|
}
|
||||||
|
pin.Configure(PinConfig{PinPWM})
|
||||||
|
return pwmGPIOToChannel(pin), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peripheral returns the RP2040 PWM peripheral which ranges from 0 to 7. Each
|
||||||
|
// PWM peripheral has 2 channels, A and B which correspond to 0 and 1 in the program.
|
||||||
|
// This number corresponds to the package's PWM0 throughout PWM7 handles
|
||||||
|
func PWMPeripheral(pin Pin) (sliceNum uint8, err error) {
|
||||||
|
if pin > maxPWMPins {
|
||||||
|
return 0, ErrInvalidOutputPin
|
||||||
|
}
|
||||||
|
return pwmGPIOToSlice(pin), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the number of the pwm peripheral (0-7)
|
||||||
|
func (pwm *pwmGroup) peripheral() uint8 {
|
||||||
|
return uint8((uintptr(unsafe.Pointer(pwm)) - uintptr(unsafe.Pointer(rp.PWM))) / 0x14)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPeriod updates the period of this PWM peripheral.
|
||||||
|
// To set a particular frequency, use the following formula:
|
||||||
|
//
|
||||||
|
// period = 1e9 / frequency
|
||||||
|
//
|
||||||
|
// If you use a period of 0, a period that works well for LEDs will be picked.
|
||||||
|
//
|
||||||
|
// SetPeriod will not change the prescaler, but also won't change the current
|
||||||
|
// value in any of the channels. This means that you may need to update the
|
||||||
|
// value for the particular channel.
|
||||||
|
//
|
||||||
|
// Note that you cannot pick any arbitrary period after the PWM peripheral has
|
||||||
|
// been configured. If you want to switch between frequencies, pick the lowest
|
||||||
|
// frequency (longest period) once when calling Configure and adjust the
|
||||||
|
// frequency here as needed.
|
||||||
|
func (p *pwmGroup) SetPeriod(period uint64) error {
|
||||||
|
if period > 0xffff_ffff {
|
||||||
|
return ErrPeriodTooBig
|
||||||
|
}
|
||||||
|
if period == 0 {
|
||||||
|
period = 1e5
|
||||||
|
}
|
||||||
|
p.setPeriod(period)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top returns the current counter top, for use in duty cycle calculation.
|
||||||
|
//
|
||||||
|
// The value returned here is hardware dependent. In general, it's best to treat
|
||||||
|
// it as an opaque value that can be divided by some number and passed to Set
|
||||||
|
// (see Set documentation for more information).
|
||||||
|
func (p *pwmGroup) Top() uint32 {
|
||||||
|
return p.getWrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Counter returns the current counter value of the timer in this PWM
|
||||||
|
// peripheral. It may be useful for debugging.
|
||||||
|
func (p *pwmGroup) Counter() uint32 {
|
||||||
|
return (p.CTR.Get() & rp.PWM_CH0_CTR_CH0_CTR_Msk) >> rp.PWM_CH0_CTR_CH0_CTR_Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// Period returns the used PWM period in nanoseconds. It might deviate slightly
|
||||||
|
// from the configured period due to rounding.
|
||||||
|
func (p *pwmGroup) Period() uint64 {
|
||||||
|
periodPerCycle := getPeriod()
|
||||||
|
top := p.getWrap()
|
||||||
|
phc := p.getPhaseCorrect()
|
||||||
|
Int, frac := p.getClockDiv()
|
||||||
|
return uint64((Int + frac/16) * (top + 1) * (phc + 1) * periodPerCycle) // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInverting sets whether to invert the output of this channel.
|
||||||
|
// Without inverting, a 25% duty cycle would mean the output is high for 25% of
|
||||||
|
// the time and low for the rest. Inverting flips the output as if a NOT gate
|
||||||
|
// was placed at the output, meaning that the output would be 25% low and 75%
|
||||||
|
// high with a duty cycle of 25%.
|
||||||
|
func (p *pwmGroup) SetInverting(channel uint8, inverting bool) {
|
||||||
|
channel &= 1
|
||||||
|
p.setInverting(channel, inverting)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set updates the channel value. This is used to control the channel duty
|
||||||
|
// cycle, in other words the fraction of time the channel output is high (or low
|
||||||
|
// when inverted). For example, to set it to a 25% duty cycle, use:
|
||||||
|
//
|
||||||
|
// pwm.Set(channel, pwm.Top() / 4)
|
||||||
|
//
|
||||||
|
// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
|
||||||
|
// pwm.Top()) will set the output to high, assuming the output isn't inverted.
|
||||||
|
func (p *pwmGroup) Set(channel uint8, value uint32) {
|
||||||
|
val := uint16(value)
|
||||||
|
channel &= 1
|
||||||
|
p.setChanLevel(channel, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current level (last set by Set). Default value on initialization is 0.
|
||||||
|
func (p *pwmGroup) Get(channel uint8) (value uint32) {
|
||||||
|
channel &= 1
|
||||||
|
return uint32(p.getChanLevel(channel))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTop sets TOP control register. Max value is 16bit (0xffff).
|
||||||
|
func (p *pwmGroup) SetTop(top uint32) {
|
||||||
|
p.setWrap(uint16(top))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable enables or disables PWM peripheral channels.
|
||||||
|
func (p *pwmGroup) Enable(enable bool) {
|
||||||
|
p.enable(enable)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnabled returns true if peripheral is enabled.
|
||||||
|
func (p *pwmGroup) IsEnabled() (enabled bool) {
|
||||||
|
return (p.CSR.Get()&rp.PWM_CH0_CSR_EN_Msk)>>rp.PWM_CH0_CSR_EN_Pos != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hardware Pulse Width Modulation (PWM) API
|
||||||
|
//
|
||||||
|
// The RP2040 PWM block has 8 identical slices. Each slice can drive two PWM output signals, or
|
||||||
|
// measure the frequency or duty cycle of an input signal. This gives a total of up to 16 controllable
|
||||||
|
// PWM outputs. All 30 GPIOs can be driven by the PWM block
|
||||||
|
//
|
||||||
|
// The PWM hardware functions by continuously comparing the input value to a free-running counter. This produces a
|
||||||
|
// toggling output where the amount of time spent at the high output level is proportional to the input value. The fraction of
|
||||||
|
// time spent at the high signal level is known as the duty cycle of the signal.
|
||||||
|
//
|
||||||
|
// The default behaviour of a PWM slice is to count upward until the wrap value (\ref pwm_config_set_wrap) is reached, and then
|
||||||
|
// immediately wrap to 0. PWM slices also offer a phase-correct mode, where the counter starts to count downward after
|
||||||
|
// reaching TOP, until it reaches 0 again.
|
||||||
|
type pwms struct {
|
||||||
|
slice pwmGroup
|
||||||
|
hw *rp.PWM_Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle to all pwm peripheral registers.
|
||||||
|
var _PWM = pwms{
|
||||||
|
hw: rp.PWM,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise a PWM with settings from a configuration object.
|
||||||
|
// If start is true then PWM starts on initialization.
|
||||||
|
func (pwm *pwmGroup) init(config PWMConfig, start bool) error {
|
||||||
|
// Not enable Phase correction
|
||||||
|
pwm.setPhaseCorrect(false)
|
||||||
|
|
||||||
|
// Clock mode set by default to Free running
|
||||||
|
pwm.setDivMode(rp.PWM_CH0_CSR_DIVMODE_DIV)
|
||||||
|
|
||||||
|
// Set Output polarity (false/false)
|
||||||
|
pwm.setInverting(0, false)
|
||||||
|
pwm.setInverting(1, false)
|
||||||
|
|
||||||
|
// Set wrap. The highest value the counter will reach before returning to zero, also known as TOP.
|
||||||
|
pwm.setWrap(0xffff)
|
||||||
|
// period is set after TOP (Wrap).
|
||||||
|
err := pwm.SetPeriod(config.Period)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// period already set beforea
|
||||||
|
// Reset counter and compare (pwm level set to zero)
|
||||||
|
pwm.CTR.ReplaceBits(0, rp.PWM_CH0_CTR_CH0_CTR_Msk, 0) // PWM_CH0_CTR_RESET
|
||||||
|
pwm.CC.Set(0) // PWM_CH0_CC_RESET
|
||||||
|
|
||||||
|
pwm.enable(start)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pwm *pwmGroup) setPhaseCorrect(correct bool) {
|
||||||
|
pwm.CSR.ReplaceBits(boolToBit(correct)<<rp.PWM_CH0_CSR_PH_CORRECT_Pos, rp.PWM_CH0_CSR_PH_CORRECT_Msk, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes any of the following:
|
||||||
|
// rp.PWM_CH0_CSR_DIVMODE_DIV, rp.PWM_CH0_CSR_DIVMODE_FALL,
|
||||||
|
// rp.PWM_CH0_CSR_DIVMODE_LEVEL, rp.PWM_CH0_CSR_DIVMODE_RISE
|
||||||
|
func (pwm *pwmGroup) setDivMode(mode uint32) {
|
||||||
|
pwm.CSR.ReplaceBits(mode<<rp.PWM_CH0_CSR_DIVMODE_Pos, rp.PWM_CH0_CSR_DIVMODE_Msk, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPeriod sets the pwm peripheral period (frequency). Calculates DIV_INT and sets it from following equation:
|
||||||
|
// cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
|
||||||
|
// where cycles is amount of clock cycles per PWM period.
|
||||||
|
func (pwm *pwmGroup) setPeriod(period uint64) {
|
||||||
|
targetPeriod := uint32(period)
|
||||||
|
periodPerCycle := getPeriod()
|
||||||
|
top := pwm.getWrap()
|
||||||
|
phc := pwm.getPhaseCorrect()
|
||||||
|
_, frac := pwm.getClockDiv()
|
||||||
|
// clearing above expression:
|
||||||
|
// DIV_INT = cycles / ( (TOP+1) * (CSRPHCorrect+1) ) - DIV_FRAC/16
|
||||||
|
// where cycles must be converted to time:
|
||||||
|
// target_period = cycles * period_per_cycle ==> cycles = target_period/period_per_cycle
|
||||||
|
Int := targetPeriod/((1+phc)*periodPerCycle*(1+top)) - frac/16
|
||||||
|
if Int > 0xff {
|
||||||
|
Int = 0xff
|
||||||
|
}
|
||||||
|
pwm.setClockDiv(uint8(Int), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int is integer value to reduce counting rate by. Must be greater than or equal to 1. DIV_INT is bits 4:11 (8 bits).
|
||||||
|
// frac's (DIV_FRAC) default value on reset is 0. Max value for frac is 15 (4 bits). This is known as a fixed-point
|
||||||
|
// fractional number.
|
||||||
|
//
|
||||||
|
// cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
|
||||||
|
func (pwm *pwmGroup) setClockDiv(Int, frac uint8) {
|
||||||
|
pwm.DIV.ReplaceBits((uint32(frac)<<rp.PWM_CH0_DIV_FRAC_Pos)|
|
||||||
|
u32max(uint32(Int), 1)<<rp.PWM_CH0_DIV_INT_Pos, rp.PWM_CH0_DIV_FRAC_Msk|rp.PWM_CH0_DIV_INT_Msk, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the highest value the counter will reach before returning to 0. Also
|
||||||
|
// known as TOP.
|
||||||
|
//
|
||||||
|
// The counter wrap value is double-buffered in hardware. This means that,
|
||||||
|
// when the PWM is running, a write to the counter wrap value does not take
|
||||||
|
// effect until after the next time the PWM slice wraps (or, in phase-correct
|
||||||
|
// mode, the next time the slice reaches 0). If the PWM is not running, the
|
||||||
|
// write is latched in immediately.
|
||||||
|
func (pwm *pwmGroup) setWrap(wrap uint16) {
|
||||||
|
pwm.TOP.ReplaceBits(uint32(wrap)<<rp.PWM_CH0_TOP_CH0_TOP_Pos, rp.PWM_CH0_TOP_CH0_TOP_Msk, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// enables/disables the PWM peripheral with rp.PWM_CH0_CSR_EN bit.
|
||||||
|
func (pwm *pwmGroup) enable(enable bool) {
|
||||||
|
pwm.CSR.ReplaceBits(boolToBit(enable)<<rp.PWM_CH0_CSR_EN_Pos, rp.PWM_CH0_CSR_EN_Msk, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pwm *pwmGroup) setInverting(channel uint8, invert bool) {
|
||||||
|
var pos uint8
|
||||||
|
var msk uint32
|
||||||
|
switch channel {
|
||||||
|
case 0:
|
||||||
|
pos = rp.PWM_CH0_CSR_A_INV_Pos
|
||||||
|
msk = rp.PWM_CH0_CSR_A_INV_Msk
|
||||||
|
case 1:
|
||||||
|
pos = rp.PWM_CH0_CSR_B_INV_Pos
|
||||||
|
msk = rp.PWM_CH0_CSR_B_INV_Msk
|
||||||
|
}
|
||||||
|
pwm.CSR.ReplaceBits(boolToBit(invert)<<pos, msk, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the current PWM counter compare value for one channel
|
||||||
|
//
|
||||||
|
// The counter compare register is double-buffered in hardware. This means
|
||||||
|
// that, when the PWM is running, a write to the counter compare values does
|
||||||
|
// not take effect until the next time the PWM slice wraps (or, in
|
||||||
|
// phase-correct mode, the next time the slice reaches 0). If the PWM is not
|
||||||
|
// running, the write is latched in immediately.
|
||||||
|
// Channel is 0 for A, 1 for B.
|
||||||
|
func (pwm *pwmGroup) setChanLevel(channel uint8, level uint16) {
|
||||||
|
var pos uint8
|
||||||
|
var mask uint32
|
||||||
|
switch channel {
|
||||||
|
case 0:
|
||||||
|
pos = rp.PWM_CH0_CC_A_Pos
|
||||||
|
mask = rp.PWM_CH0_CC_A_Msk
|
||||||
|
case 1:
|
||||||
|
pos = rp.PWM_CH0_CC_B_Pos
|
||||||
|
mask = rp.PWM_CH0_CC_B_Msk
|
||||||
|
}
|
||||||
|
pwm.CC.ReplaceBits(uint32(level)<<pos, mask, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pwm *pwmGroup) getChanLevel(channel uint8) (level uint16) {
|
||||||
|
var pos uint8
|
||||||
|
var mask uint32
|
||||||
|
switch channel {
|
||||||
|
case 0:
|
||||||
|
pos = rp.PWM_CH0_CC_A_Pos
|
||||||
|
mask = rp.PWM_CH0_CC_A_Msk
|
||||||
|
case 1:
|
||||||
|
pos = rp.PWM_CH0_CC_B_Pos
|
||||||
|
mask = rp.PWM_CH0_CC_B_Msk
|
||||||
|
}
|
||||||
|
|
||||||
|
level = uint16((pwm.CC.Get() & mask) >> pos)
|
||||||
|
return level
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pwm *pwmGroup) getWrap() (top uint32) {
|
||||||
|
return (pwm.TOP.Get() & rp.PWM_CH0_TOP_CH0_TOP_Msk) >> rp.PWM_CH0_TOP_CH0_TOP_Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pwm *pwmGroup) getPhaseCorrect() (phCorrect uint32) {
|
||||||
|
return (pwm.CSR.Get() & rp.PWM_CH0_CSR_PH_CORRECT_Msk) >> rp.PWM_CH0_CSR_PH_CORRECT_Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pwm *pwmGroup) getClockDiv() (Int, frac uint32) {
|
||||||
|
div := pwm.DIV.Get()
|
||||||
|
return (div & rp.PWM_CH0_DIV_INT_Msk) >> rp.PWM_CH0_DIV_INT_Pos, (div & rp.PWM_CH0_DIV_FRAC_Msk) >> rp.PWM_CH0_DIV_FRAC_Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// pwmGPIOToSlice Determine the PWM channel that is attached to the specified GPIO.
|
||||||
|
// gpio must be less than 30. Returns the PWM slice number that controls the specified GPIO.
|
||||||
|
func pwmGPIOToSlice(gpio Pin) (slicenum uint8) {
|
||||||
|
return (uint8(gpio) >> 1) & 7
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the PWM channel that is attached to the specified GPIO.
|
||||||
|
// Each slice 0 to 7 has two channels, A and B.
|
||||||
|
func pwmGPIOToChannel(gpio Pin) (channel uint8) {
|
||||||
|
return uint8(gpio) & 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the period of a clock cycle for the raspberry pi pico in nanoseconds.
|
||||||
|
func getPeriod() uint32 {
|
||||||
|
const periodIn uint32 = 1e9 / (125 * MHz)
|
||||||
|
return periodIn
|
||||||
|
}
|
||||||
@@ -0,0 +1,361 @@
|
|||||||
|
// +build rp2040
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/rp"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI on the RP2040
|
||||||
|
var (
|
||||||
|
SPI0 = &_SPI0
|
||||||
|
_SPI0 = SPI{
|
||||||
|
Bus: rp.SPI0,
|
||||||
|
}
|
||||||
|
SPI1 = &_SPI1
|
||||||
|
_SPI1 = SPI{
|
||||||
|
Bus: rp.SPI1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPIConfig is used to store config info for SPI.
|
||||||
|
type SPIConfig struct {
|
||||||
|
Frequency uint32
|
||||||
|
// LSB not supported on rp2040.
|
||||||
|
LSBFirst bool
|
||||||
|
// Mode's two most LSB are CPOL and CPHA. i.e. Mode==2 (0b10) is CPOL=1, CPHA=0
|
||||||
|
Mode uint8
|
||||||
|
// Number of data bits per transfer. Valid values 4..16. Default and recommended is 8.
|
||||||
|
DataBits uint8
|
||||||
|
// Serial clock pin
|
||||||
|
SCK Pin
|
||||||
|
// TX or Serial Data Out (MOSI if rp2040 is master)
|
||||||
|
SDO Pin
|
||||||
|
// RX or Serial Data In (MISO if rp2040 is master)
|
||||||
|
SDI Pin
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrLSBNotSupported = errors.New("SPI LSB unsupported on PL022")
|
||||||
|
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
|
||||||
|
ErrSPITimeout = errors.New("SPI timeout")
|
||||||
|
ErrSPIBaud = errors.New("SPI baud too low or above 66.5Mhz")
|
||||||
|
)
|
||||||
|
|
||||||
|
type SPI struct {
|
||||||
|
Bus *rp.SPI0_Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// time to wait on a transaction before dropping. Unit in Microseconds for compatibility with ticks().
|
||||||
|
const _SPITimeout = 10 * 1000 // 10 ms
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// The Tx method knows about this, and offers a few different ways of calling it.
|
||||||
|
//
|
||||||
|
// This form sends the bytes in tx buffer, putting the resulting bytes read into the rx buffer.
|
||||||
|
// Note that the tx and rx buffers must be the same size:
|
||||||
|
//
|
||||||
|
// spi.Tx(tx, rx)
|
||||||
|
//
|
||||||
|
// This form sends the tx buffer, ignoring the result. Useful for sending "commands" that return zeros
|
||||||
|
// until all the bytes in the command packet have been received:
|
||||||
|
//
|
||||||
|
// spi.Tx(tx, nil)
|
||||||
|
//
|
||||||
|
// This form sends zeros, putting the result into the rx buffer. Good for reading a "result packet":
|
||||||
|
//
|
||||||
|
// spi.Tx(nil, rx)
|
||||||
|
//
|
||||||
|
// Remark: This implementation (RP2040) allows reading into buffer with a custom repeated
|
||||||
|
// value on tx.
|
||||||
|
//
|
||||||
|
// spi.Tx([]byte{0xff}, rx) // may cause unwanted heap allocations.
|
||||||
|
//
|
||||||
|
// This form sends 0xff and puts the result into rx buffer. Useful for reading from SD cards
|
||||||
|
// which require 0xff input on SI.
|
||||||
|
func (spi SPI) Tx(w, r []byte) (err error) {
|
||||||
|
switch {
|
||||||
|
case w == nil:
|
||||||
|
// read only, so write zero and read a result.
|
||||||
|
err = spi.rx(r, 0)
|
||||||
|
case r == nil:
|
||||||
|
// write only
|
||||||
|
err = spi.tx(w)
|
||||||
|
case len(w) == 1 && len(r) > 1:
|
||||||
|
// Read with custom repeated value.
|
||||||
|
err = spi.rx(r, w[0])
|
||||||
|
default:
|
||||||
|
// write/read
|
||||||
|
err = spi.txrx(w, r)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a single byte and read a single byte from TX/RX FIFO.
|
||||||
|
func (spi SPI) Transfer(w byte) (byte, error) {
|
||||||
|
var deadline = ticks() + _SPITimeout
|
||||||
|
for !spi.isWritable() {
|
||||||
|
if ticks() > deadline {
|
||||||
|
return 0, ErrSPITimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spi.Bus.SSPDR.Set(uint32(w))
|
||||||
|
|
||||||
|
for !spi.isReadable() {
|
||||||
|
if ticks() > deadline {
|
||||||
|
return 0, ErrSPITimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uint8(spi.Bus.SSPDR.Get()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi SPI) SetBaudRate(br uint32) error {
|
||||||
|
const freqin uint32 = 125 * MHz
|
||||||
|
const maxBaud uint32 = 66.5 * MHz // max output frequency is 66.5MHz on rp2040. see Note page 527.
|
||||||
|
// Find smallest prescale value which puts output frequency in range of
|
||||||
|
// post-divide. Prescale is an even number from 2 to 254 inclusive.
|
||||||
|
var prescale, postdiv uint32
|
||||||
|
for prescale = 2; prescale < 255; prescale += 2 {
|
||||||
|
if freqin < (prescale+2)*256*br {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if prescale > 254 || br > maxBaud {
|
||||||
|
return ErrSPIBaud
|
||||||
|
}
|
||||||
|
// Find largest post-divide which makes output <= baudrate. Post-divide is
|
||||||
|
// an integer in the range 1 to 256 inclusive.
|
||||||
|
for postdiv = 256; postdiv > 1; postdiv-- {
|
||||||
|
if freqin/(prescale*(postdiv-1)) > br {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spi.Bus.SSPCPSR.Set(prescale)
|
||||||
|
spi.Bus.SSPCR0.ReplaceBits((postdiv-1)<<rp.SPI0_SSPCR0_SCR_Pos, rp.SPI0_SSPCR0_SCR_Msk, 0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi SPI) GetBaudRate() uint32 {
|
||||||
|
const freqin uint32 = 125 * MHz
|
||||||
|
prescale := spi.Bus.SSPCPSR.Get()
|
||||||
|
postdiv := ((spi.Bus.SSPCR0.Get() & rp.SPI0_SSPCR0_SCR_Msk) >> rp.SPI0_SSPCR0_SCR_Pos) + 1
|
||||||
|
return freqin / (prescale * postdiv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure is intended to setup/initialize the SPI interface.
|
||||||
|
// Default baudrate of 115200 is used if Frequency == 0. Default
|
||||||
|
// word length (data bits) is 8.
|
||||||
|
// Below is a list of GPIO pins corresponding to SPI0 bus on the rp2040:
|
||||||
|
// SI : 0, 4, 17 a.k.a RX and MISO (if rp2040 is master)
|
||||||
|
// SO : 3, 7, 19 a.k.a TX and MOSI (if rp2040 is master)
|
||||||
|
// SCK: 2, 6, 18
|
||||||
|
// SPI1 bus GPIO pins:
|
||||||
|
// SI : 8, 12
|
||||||
|
// SO : 11, 15
|
||||||
|
// SCK: 10, 14
|
||||||
|
// No pin configuration is needed of SCK, SDO and SDI needed after calling Configure.
|
||||||
|
func (spi SPI) Configure(config SPIConfig) error {
|
||||||
|
const defaultBaud uint32 = 115200
|
||||||
|
if config.SCK == 0 {
|
||||||
|
// set default pins if config zero valued or invalid clock pin supplied.
|
||||||
|
switch spi.Bus {
|
||||||
|
case rp.SPI0:
|
||||||
|
config.SCK = SPI0_SCK_PIN
|
||||||
|
config.SDO = SPI0_SDO_PIN
|
||||||
|
config.SDI = SPI0_SDI_PIN
|
||||||
|
case rp.SPI1:
|
||||||
|
config.SCK = SPI1_SCK_PIN
|
||||||
|
config.SDO = SPI1_SDO_PIN
|
||||||
|
config.SDI = SPI1_SDI_PIN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if config.DataBits < 4 || config.DataBits > 16 {
|
||||||
|
config.DataBits = 8
|
||||||
|
}
|
||||||
|
if config.Frequency == 0 {
|
||||||
|
config.Frequency = defaultBaud
|
||||||
|
}
|
||||||
|
// SPI pin configuration
|
||||||
|
config.SCK.setFunc(fnSPI)
|
||||||
|
config.SDO.setFunc(fnSPI)
|
||||||
|
config.SDI.setFunc(fnSPI)
|
||||||
|
|
||||||
|
return spi.initSPI(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi SPI) initSPI(config SPIConfig) (err error) {
|
||||||
|
spi.reset()
|
||||||
|
// LSB-first not supported on PL022:
|
||||||
|
if config.LSBFirst {
|
||||||
|
return ErrLSBNotSupported
|
||||||
|
}
|
||||||
|
err = spi.SetBaudRate(config.Frequency)
|
||||||
|
// Set SPI Format (CPHA and CPOL) and frame format (default is Motorola)
|
||||||
|
spi.setFormat(config.DataBits, config.Mode, rp.XIP_SSI_CTRLR0_SPI_FRF_STD)
|
||||||
|
|
||||||
|
// Always enable DREQ signals -- harmless if DMA is not listening
|
||||||
|
spi.Bus.SSPDMACR.SetBits(rp.SPI0_SSPDMACR_TXDMAE | rp.SPI0_SSPDMACR_RXDMAE)
|
||||||
|
// Finally enable the SPI
|
||||||
|
spi.Bus.SSPCR1.SetBits(rp.SPI0_SSPCR1_SSE)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func (spi SPI) setFormat(databits, mode uint8, frameFormat uint32) {
|
||||||
|
cpha := uint32(mode) & 1
|
||||||
|
cpol := uint32(mode>>1) & 1
|
||||||
|
spi.Bus.SSPCR0.ReplaceBits(
|
||||||
|
(cpha<<rp.SPI0_SSPCR0_SPH_Pos)|
|
||||||
|
(cpol<<rp.SPI0_SSPCR0_SPO_Pos)|
|
||||||
|
(uint32(databits-1)<<rp.SPI0_SSPCR0_DSS_Pos)| // Set databits (SPI word length). Valid inputs are 4-16.
|
||||||
|
(frameFormat&0b11)<<rp.SPI0_SSPCR0_FRF_Pos, // Frame format bits 4:5
|
||||||
|
rp.SPI0_SSPCR0_SPH_Msk|rp.SPI0_SSPCR0_SPO_Msk|rp.SPI0_SSPCR0_DSS_Msk|rp.SPI0_SSPCR0_FRF_Msk, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset resets SPI and waits until reset is done.
|
||||||
|
//go:inline
|
||||||
|
func (spi SPI) reset() {
|
||||||
|
resetVal := spi.deinit()
|
||||||
|
rp.RESETS.RESET.ClearBits(resetVal)
|
||||||
|
// Wait until reset is done.
|
||||||
|
for !rp.RESETS.RESET_DONE.HasBits(resetVal) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func (spi SPI) deinit() (resetVal uint32) {
|
||||||
|
switch spi.Bus {
|
||||||
|
case rp.SPI0:
|
||||||
|
resetVal = rp.RESETS_RESET_SPI0
|
||||||
|
case rp.SPI1:
|
||||||
|
resetVal = rp.RESETS_RESET_SPI1
|
||||||
|
}
|
||||||
|
// Perform SPI reset.
|
||||||
|
rp.RESETS.RESET.SetBits(resetVal)
|
||||||
|
return resetVal
|
||||||
|
}
|
||||||
|
|
||||||
|
// isWritable returns false if no space is available to write. True if a write is possible
|
||||||
|
//go:inline
|
||||||
|
func (spi SPI) isWritable() bool {
|
||||||
|
return spi.Bus.SSPSR.HasBits(rp.SPI0_SSPSR_TNF)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isReadable returns true if a read is possible i.e. data is present
|
||||||
|
//go:inline
|
||||||
|
func (spi SPI) isReadable() bool {
|
||||||
|
return spi.Bus.SSPSR.HasBits(rp.SPI0_SSPSR_RNE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintRegs prints SPI's peripheral common registries current values
|
||||||
|
func (spi SPI) PrintRegs() {
|
||||||
|
cr0 := spi.Bus.SSPCR0.Get()
|
||||||
|
cr1 := spi.Bus.SSPCR1.Get()
|
||||||
|
dmacr := spi.Bus.SSPDMACR.Get()
|
||||||
|
cpsr := spi.Bus.SSPCPSR.Get()
|
||||||
|
dr := spi.Bus.SSPDR.Get()
|
||||||
|
ris := spi.Bus.SSPRIS.Get()
|
||||||
|
println("CR0:", cr0)
|
||||||
|
println("CR1:", cr1)
|
||||||
|
println("DMACR:", dmacr)
|
||||||
|
println("CPSR:", cpsr)
|
||||||
|
println("DR:", dr)
|
||||||
|
println("RIS:", ris)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:inline
|
||||||
|
func (spi SPI) isBusy() bool {
|
||||||
|
return spi.Bus.SSPSR.HasBits(rp.SPI0_SSPSR_BSY)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tx writes buffer to SPI ignoring Rx.
|
||||||
|
func (spi SPI) tx(tx []byte) error {
|
||||||
|
var deadline = ticks() + _SPITimeout
|
||||||
|
// Write to TX FIFO whilst ignoring RX, then clean up afterward. When RX
|
||||||
|
// is full, PL022 inhibits RX pushes, and sets a sticky flag on
|
||||||
|
// push-on-full, but continues shifting. Safe if SSPIMSC_RORIM is not set.
|
||||||
|
for i := range tx {
|
||||||
|
for !spi.isWritable() {
|
||||||
|
if ticks() > deadline {
|
||||||
|
return ErrSPITimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spi.Bus.SSPDR.Set(uint32(tx[i]))
|
||||||
|
}
|
||||||
|
// Drain RX FIFO, then wait for shifting to finish (which may be *after*
|
||||||
|
// TX FIFO drains), then drain RX FIFO again
|
||||||
|
for spi.isReadable() {
|
||||||
|
spi.Bus.SSPDR.Get()
|
||||||
|
}
|
||||||
|
for spi.isBusy() {
|
||||||
|
if ticks() > deadline {
|
||||||
|
return ErrSPITimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for spi.isReadable() {
|
||||||
|
spi.Bus.SSPDR.Get()
|
||||||
|
}
|
||||||
|
// Don't leave overrun flag set
|
||||||
|
spi.Bus.SSPICR.Set(rp.SPI0_SSPICR_RORIC)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// rx reads buffer to SPI ignoring x.
|
||||||
|
// txrepeat is output repeatedly on SO as data is read in from SI.
|
||||||
|
// Generally this can be 0, but some devices require a specific value here,
|
||||||
|
// e.g. SD cards expect 0xff
|
||||||
|
func (spi SPI) rx(rx []byte, txrepeat byte) error {
|
||||||
|
var deadline = ticks() + _SPITimeout
|
||||||
|
plen := len(rx)
|
||||||
|
const fifoDepth = 8 // see txrx
|
||||||
|
var rxleft, txleft = plen, plen
|
||||||
|
for txleft != 0 || rxleft != 0 {
|
||||||
|
if txleft != 0 && spi.isWritable() && rxleft < txleft+fifoDepth {
|
||||||
|
spi.Bus.SSPDR.Set(uint32(txrepeat))
|
||||||
|
txleft--
|
||||||
|
}
|
||||||
|
if rxleft != 0 && spi.isReadable() {
|
||||||
|
rx[plen-rxleft] = uint8(spi.Bus.SSPDR.Get())
|
||||||
|
rxleft--
|
||||||
|
continue // if reading succesfully in rx there is no need to check deadline.
|
||||||
|
}
|
||||||
|
if ticks() > deadline {
|
||||||
|
return ErrSPITimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write len bytes from src to SPI. Simultaneously read len bytes from SPI to dst.
|
||||||
|
// Note this function is guaranteed to exit in a known amount of time (bits sent * time per bit)
|
||||||
|
func (spi SPI) txrx(tx, rx []byte) error {
|
||||||
|
var deadline = ticks() + _SPITimeout
|
||||||
|
plen := len(tx)
|
||||||
|
if plen != len(rx) {
|
||||||
|
return ErrTxInvalidSliceSize
|
||||||
|
}
|
||||||
|
// Never have more transfers in flight than will fit into the RX FIFO,
|
||||||
|
// else FIFO will overflow if this code is heavily interrupted.
|
||||||
|
const fifoDepth = 8
|
||||||
|
var rxleft, txleft = plen, plen
|
||||||
|
for (txleft != 0 || rxleft != 0) && ticks() <= deadline {
|
||||||
|
if txleft != 0 && spi.isWritable() && rxleft < txleft+fifoDepth {
|
||||||
|
spi.Bus.SSPDR.Set(uint32(tx[plen-txleft]))
|
||||||
|
txleft--
|
||||||
|
}
|
||||||
|
if rxleft != 0 && spi.isReadable() {
|
||||||
|
rx[plen-rxleft] = uint8(spi.Bus.SSPDR.Get())
|
||||||
|
rxleft--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if txleft != 0 || rxleft != 0 {
|
||||||
|
// Transaction ended early due to timeout
|
||||||
|
return ErrSPITimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -58,3 +58,19 @@ func (p Pin) Get() bool {
|
|||||||
val := port.IDR.Get() & (1 << pin)
|
val := port.IDR.Get() & (1 << pin)
|
||||||
return (val > 0)
|
return (val > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PortMaskSet returns the register and mask to enable a given GPIO pin. This
|
||||||
|
// can be used to implement bit-banged drivers.
|
||||||
|
func (p Pin) PortMaskSet() (*uint32, uint32) {
|
||||||
|
port := p.getPort()
|
||||||
|
pin := uint8(p) % 16
|
||||||
|
return &port.BSRR.Reg, 1 << pin
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortMaskClear returns the register and mask to disable a given port. This can
|
||||||
|
// be used to implement bit-banged drivers.
|
||||||
|
func (p Pin) PortMaskClear() (*uint32, uint32) {
|
||||||
|
port := p.getPort()
|
||||||
|
pin := uint8(p) % 16
|
||||||
|
return &port.BSRR.Reg, 1 << (pin + 16)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package os
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
func Executable() (string, error) {
|
||||||
|
return "", errors.New("Executable not implemented")
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// The following is copied from Go 1.17 official implementation.
|
||||||
|
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package os
|
||||||
|
|
||||||
|
func Executable() (string, error) {
|
||||||
|
path, err := Readlink("/proc/self/exe")
|
||||||
|
|
||||||
|
// When the executable has been deleted then Readlink returns a
|
||||||
|
// path appended with " (deleted)".
|
||||||
|
return stringsTrimSuffix(path, " (deleted)"), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringsTrimSuffix is the same as strings.TrimSuffix.
|
||||||
|
func stringsTrimSuffix(s, suffix string) string {
|
||||||
|
if len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix {
|
||||||
|
return s[:len(s)-len(suffix)]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
@@ -10,6 +10,15 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Seek whence values.
|
||||||
|
//
|
||||||
|
// Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.
|
||||||
|
const (
|
||||||
|
SEEK_SET int = io.SeekStart
|
||||||
|
SEEK_CUR int = io.SeekCurrent
|
||||||
|
SEEK_END int = io.SeekEnd
|
||||||
|
)
|
||||||
|
|
||||||
// Mkdir creates a directory. If the operation fails, it will return an error of
|
// Mkdir creates a directory. If the operation fails, it will return an error of
|
||||||
// type *PathError.
|
// type *PathError.
|
||||||
func Mkdir(path string, perm FileMode) error {
|
func Mkdir(path string, perm FileMode) error {
|
||||||
@@ -24,6 +33,11 @@ func Mkdir(path string, perm FileMode) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MkdirTemp is a stub, it will always return an error.
|
||||||
|
func MkdirTemp(dir, pattern string) (string, error) {
|
||||||
|
return "", &PathError{"mkdirtemp", dir, ErrNotImplemented}
|
||||||
|
}
|
||||||
|
|
||||||
// Remove removes a file or (empty) directory. If the operation fails, it will
|
// Remove removes a file or (empty) directory. If the operation fails, it will
|
||||||
// return an error of type *PathError.
|
// return an error of type *PathError.
|
||||||
func Remove(path string) error {
|
func Remove(path string) error {
|
||||||
|
|||||||
@@ -706,6 +706,11 @@ type StructField struct {
|
|||||||
Offset uintptr
|
Offset uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsExported reports whether the field is exported.
|
||||||
|
func (f StructField) IsExported() bool {
|
||||||
|
return f.PkgPath == ""
|
||||||
|
}
|
||||||
|
|
||||||
// rawStructField is the same as StructField but with the Type member replaced
|
// rawStructField is the same as StructField but with the Type member replaced
|
||||||
// with rawType. For internal use only. Avoiding this conversion to the Type
|
// with rawType. For internal use only. Avoiding this conversion to the Type
|
||||||
// interface improves code size in many cases.
|
// interface improves code size in many cases.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const TargetBits = 32
|
|||||||
|
|
||||||
// Align on word boundary.
|
// Align on word boundary.
|
||||||
func align(ptr uintptr) uintptr {
|
func align(ptr uintptr) uintptr {
|
||||||
return (ptr + 3) &^ 3
|
return (ptr + 15) &^ 15
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCurrentStackPointer() uintptr {
|
func getCurrentStackPointer() uintptr {
|
||||||
|
|||||||
@@ -32,9 +32,11 @@ var (
|
|||||||
|
|
||||||
const wasmPageSize = 64 * 1024
|
const wasmPageSize = 64 * 1024
|
||||||
|
|
||||||
// Align on word boundary.
|
|
||||||
func align(ptr uintptr) uintptr {
|
func align(ptr uintptr) uintptr {
|
||||||
return (ptr + 3) &^ 3
|
// Align to 16, which is the alignment of max_align_t:
|
||||||
|
// https://godbolt.org/z/dYqTsWrGq
|
||||||
|
const heapAlign = 16
|
||||||
|
return (ptr + heapAlign - 1) &^ (heapAlign - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCurrentStackPointer() uintptr
|
func getCurrentStackPointer() uintptr
|
||||||
@@ -55,3 +57,51 @@ func growHeap() bool {
|
|||||||
// Heap has grown successfully.
|
// Heap has grown successfully.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The below functions override the default allocator of wasi-libc.
|
||||||
|
// Most functions are defined but unimplemented to make sure that if there is
|
||||||
|
// any code using them, they will get an error instead of (incorrectly) using
|
||||||
|
// the wasi-libc dlmalloc heap implementation instead. If they are needed by any
|
||||||
|
// program, they can certainly be implemented.
|
||||||
|
|
||||||
|
//export malloc
|
||||||
|
func libc_malloc(size uintptr) unsafe.Pointer {
|
||||||
|
return alloc(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export free
|
||||||
|
func libc_free(ptr unsafe.Pointer) {
|
||||||
|
free(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export calloc
|
||||||
|
func libc_calloc(nmemb, size uintptr) unsafe.Pointer {
|
||||||
|
// Note: we could be even more correct here and check that nmemb * size
|
||||||
|
// doesn't overflow. However the current implementation should normally work
|
||||||
|
// fine.
|
||||||
|
return alloc(nmemb * size)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export realloc
|
||||||
|
func libc_realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
|
||||||
|
runtimePanic("unimplemented: realloc")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//export posix_memalign
|
||||||
|
func libc_posix_memalign(memptr *unsafe.Pointer, alignment, size uintptr) int {
|
||||||
|
runtimePanic("unimplemented: posix_memalign")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
//export aligned_alloc
|
||||||
|
func libc_aligned_alloc(alignment, bytes uintptr) unsafe.Pointer {
|
||||||
|
runtimePanic("unimplemented: aligned_alloc")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//export malloc_usable_size
|
||||||
|
func libc_malloc_usable_size(ptr unsafe.Pointer) uintptr {
|
||||||
|
runtimePanic("unimplemented: malloc_usable_size")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,3 +52,24 @@ func syscall_Exit(code int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const baremetal = true
|
const baremetal = true
|
||||||
|
|
||||||
|
// timeOffset is how long the monotonic clock started after the Unix epoch. It
|
||||||
|
// should be a positive integer under normal operation or zero when it has not
|
||||||
|
// been set.
|
||||||
|
var timeOffset int64
|
||||||
|
|
||||||
|
//go:linkname now time.now
|
||||||
|
func now() (sec int64, nsec int32, mono int64) {
|
||||||
|
mono = nanotime()
|
||||||
|
sec = (mono + timeOffset) / (1000 * 1000 * 1000)
|
||||||
|
nsec = int32((mono + timeOffset) - sec*(1000*1000*1000))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdjustTimeOffset adds the given offset to the built-in time offset. A
|
||||||
|
// positive value adds to the time (skipping some time), a negative value moves
|
||||||
|
// the clock into the past.
|
||||||
|
func AdjustTimeOffset(offset int64) {
|
||||||
|
// TODO: do this atomically?
|
||||||
|
timeOffset += offset
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,10 +13,11 @@ tinygo_scanCurrentStack:
|
|||||||
pushl %ebp
|
pushl %ebp
|
||||||
|
|
||||||
// Scan the stack.
|
// Scan the stack.
|
||||||
|
subl $8, %esp // adjust the stack before the call to maintain 16-byte alignment
|
||||||
pushl %esp
|
pushl %esp
|
||||||
calll tinygo_scanstack
|
calll tinygo_scanstack
|
||||||
|
|
||||||
// Restore the stack pointer. Registers do not need to be restored as they
|
// Restore the stack pointer. Registers do not need to be restored as they
|
||||||
// were only pushed to be discoverable by the GC.
|
// were only pushed to be discoverable by the GC.
|
||||||
addl $20, %esp
|
addl $28, %esp
|
||||||
retl
|
retl
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user