Compare commits

..

2 Commits

Author SHA1 Message Date
sago35 3591e194f7 machine: changed to block if no read data exists on UART and USB 2022-03-29 08:53:52 +09:00
sago35 cc3009b5c1 os, runtime: enable os.Stdin for baremetal target 2022-03-29 08:51:23 +09:00
257 changed files with 3611 additions and 15109 deletions
+14 -10
View File
@@ -22,16 +22,15 @@ commands:
steps:
- restore_cache:
keys:
- llvm-source-14-v2
- llvm-source-13-v1
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
key: llvm-source-14-v2
key: llvm-source-13-v1
paths:
- llvm-project/clang/lib/Headers
- llvm-project/clang/include
- llvm-project/compiler-rt
- llvm-project/lld/include
- llvm-project/llvm/include
hack-ninja-jobs:
@@ -86,10 +85,10 @@ commands:
- run: go install -tags=llvm<<parameters.llvm>> .
- restore_cache:
keys:
- wasi-libc-sysroot-systemclang-v5
- wasi-libc-sysroot-systemclang-v4
- run: make wasi-libc
- save_cache:
key: wasi-libc-sysroot-systemclang-v5
key: wasi-libc-sysroot-systemclang-v4
paths:
- lib/wasi-libc/sysroot
- run: make gen-device -j4
@@ -102,16 +101,21 @@ commands:
- run: make fmt-check
jobs:
test-llvm13-go116:
test-llvm11-go115:
docker:
- image: circleci/golang:1.15-buster
steps:
- test-linux:
llvm: "11"
test-llvm12-go116:
docker:
- image: circleci/golang:1.16-buster
steps:
- test-linux:
llvm: "13"
llvm: "12"
workflows:
test-all:
jobs:
# This tests our lowest supported versions of Go and LLVM, to make sure at
# least the smoke tests still pass.
- test-llvm13-go116
- test-llvm11-go115
- test-llvm12-go116
+5 -6
View File
@@ -10,12 +10,12 @@ on:
jobs:
build-macos:
name: build-macos
runs-on: macos-11
runs-on: macos-10.15
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18.1'
go-version: '1.17'
- name: Install Dependencies
shell: bash
run: |
@@ -35,11 +35,10 @@ jobs:
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-14-macos-v1
key: llvm-source-13-macos-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Download LLVM source
@@ -49,7 +48,7 @@ jobs:
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-14-macos-v1
key: llvm-build-13-macos-v3
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -67,7 +66,7 @@ jobs:
uses: actions/cache@v2
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-v3
key: wasi-libc-sysroot-v2
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
+12 -218
View File
@@ -26,7 +26,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18.1'
go-version: '1.17'
- name: Cache Go
uses: actions/cache@v2
with:
@@ -38,11 +38,10 @@ jobs:
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-14-linux-v2
key: llvm-source-13-linux-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Download LLVM source
@@ -52,7 +51,7 @@ jobs:
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-14-linux-v1
key: llvm-build-13-linux-v2
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -77,13 +76,14 @@ jobs:
uses: actions/cache@v2
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-linux-asserts-v4
key: wasi-libc-sysroot-linux-asserts-v3
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- name: Install fpm
run: |
sudo apt-get install ruby ruby-dev
sudo gem install --no-document fpm
- name: Build TinyGo release
run: |
@@ -93,7 +93,7 @@ jobs:
- name: Publish release artifact
uses: actions/upload-artifact@v2
with:
name: linux-amd64-double-zipped
name: release-double-zipped
path: |
/tmp/tinygo.linux-amd64.tar.gz
/tmp/tinygo_amd64.deb
@@ -107,7 +107,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18.1'
go-version: '1.17'
- name: Install wasmtime
run: |
curl https://wasmtime.dev/install.sh -sSf | bash
@@ -115,7 +115,7 @@ jobs:
- name: Download release artifact
uses: actions/download-artifact@v2
with:
name: linux-amd64-double-zipped
name: release-double-zipped
- name: Extract release tarball
run: |
mkdir -p ~/lib
@@ -159,7 +159,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18.1'
go-version: '1.17'
- name: Install Node.js
uses: actions/setup-node@v2
with:
@@ -179,11 +179,10 @@ jobs:
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-14-linux-asserts-v2
key: llvm-source-13-linux-asserts-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Download LLVM source
@@ -193,7 +192,7 @@ jobs:
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-14-linux-asserts-v1
key: llvm-build-13-linux-asserts-v2
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -218,7 +217,7 @@ jobs:
uses: actions/cache@v2
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-linux-asserts-v4
key: wasi-libc-sysroot-linux-asserts-v3
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
@@ -241,208 +240,3 @@ jobs:
- run: make smoketest
- run: make wasmtest
- run: make tinygo-baremetal
build-linux-arm:
# Build ARM Linux binaries, ready for release.
# This intentionally uses an older Linux image, so that we compile against
# an older glibc version and therefore are compatible with a wide range of
# Linux distributions.
# It is set to "needs: build-linux" because it modifies the release created
# in that process to avoid doing lots of duplicate work and to avoid
# complications around precompiled libraries such as compiler-rt shipped as
# part of the release tarball.
runs-on: ubuntu-18.04
needs: build-linux
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install apt dependencies
run: |
sudo apt-get install --no-install-recommends \
qemu-user \
g++-arm-linux-gnueabihf \
libc6-dev-armhf-cross
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18.1'
- name: Cache Go
uses: actions/cache@v2
with:
key: go-cache-linux-arm-v2-${{ hashFiles('go.mod') }}
path: |
~/.cache/go-build
~/go/pkg/mod
- name: Cache LLVM source
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-14-linux-v2
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Cache LLVM build
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-14-linux-arm-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
run: |
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# Install build dependencies.
sudo apt-get install --no-install-recommends ninja-build
# build!
make llvm-build CROSS=arm-linux-gnueabihf
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Cache Binaryen
uses: actions/cache@v2
id: cache-binaryen
with:
key: binaryen-linux-arm-v1
path: build/wasm-opt
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: |
sudo apt-get install --no-install-recommends ninja-build
git submodule update --init lib/binaryen
make CROSS=arm-linux-gnueabihf binaryen
- name: Install fpm
run: |
sudo gem install --no-document fpm
- name: Build TinyGo binary
run: |
make CROSS=arm-linux-gnueabihf
- name: Download amd64 release
uses: actions/download-artifact@v2
with:
name: linux-amd64-double-zipped
- name: Extract amd64 release
run: |
mkdir -p build/release
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
- name: Modify release
run: |
cp -p build/tinygo build/release/tinygo/bin
cp -p build/wasm-opt build/release/tinygo/bin
- name: Create arm release
run: |
make release deb RELEASEONLY=1 DEB_ARCH=armhf
cp -p build/release.tar.gz /tmp/tinygo.linux-arm.tar.gz
cp -p build/release.deb /tmp/tinygo_armhf.deb
- name: Publish release artifact
uses: actions/upload-artifact@v2
with:
name: linux-arm-double-zipped
path: |
/tmp/tinygo.linux-arm.tar.gz
/tmp/tinygo_armhf.deb
build-linux-arm64:
# Build ARM64 Linux binaries, ready for release.
# It is set to "needs: build-linux" because it modifies the release created
# in that process to avoid doing lots of duplicate work and to avoid
# complications around precompiled libraries such as compiler-rt shipped as
# part of the release tarball.
runs-on: ubuntu-18.04
needs: build-linux
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install apt dependencies
run: |
sudo apt-get install --no-install-recommends \
qemu-user \
g++-aarch64-linux-gnu \
libc6-dev-arm64-cross \
ninja-build
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18.1'
- name: Cache Go
uses: actions/cache@v2
with:
key: go-cache-linux-arm64-v2-${{ hashFiles('go.mod') }}
path: |
~/.cache/go-build
~/go/pkg/mod
- name: Cache LLVM source
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-14-linux-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Download LLVM source
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
run: make llvm-source
- name: Cache LLVM build
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-14-linux-arm64-v1
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
run: |
# fetch LLVM source
rm -rf llvm-project
make llvm-source
# build!
make llvm-build CROSS=aarch64-linux-gnu
# Remove unnecessary object files (to reduce cache size).
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
- name: Cache Binaryen
uses: actions/cache@v2
id: cache-binaryen
with:
key: binaryen-linux-arm64-v1
path: build/wasm-opt
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: |
git submodule update --init lib/binaryen
make CROSS=aarch64-linux-gnu binaryen
- name: Install fpm
run: |
sudo gem install --no-document fpm
- name: Build TinyGo binary
run: |
make CROSS=aarch64-linux-gnu
- name: Download amd64 release
uses: actions/download-artifact@v2
with:
name: linux-amd64-double-zipped
- name: Extract amd64 release
run: |
mkdir -p build/release
tar -xf tinygo.linux-amd64.tar.gz -C build/release tinygo
- name: Modify release
run: |
cp -p build/tinygo build/release/tinygo/bin
cp -p build/wasm-opt build/release/tinygo/bin
- name: Create arm64 release
run: |
make release deb RELEASEONLY=1 DEB_ARCH=arm64
cp -p build/release.tar.gz /tmp/tinygo.linux-arm64.tar.gz
cp -p build/release.deb /tmp/tinygo_arm64.deb
- name: Publish release artifact
uses: actions/upload-artifact@v2
with:
name: linux-arm64-double-zipped
path: |
/tmp/tinygo.linux-arm64.tar.gz
/tmp/tinygo_arm64.deb
+5 -13
View File
@@ -9,15 +9,13 @@ on:
jobs:
build-windows:
runs-on: windows-2022
runs-on: windows-2019
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18.1'
go-version: '1.17'
- uses: brechtm/setup-scoop@v2
with:
scoop_update: 'false'
- name: Install Dependencies
shell: bash
run: |
@@ -37,11 +35,10 @@ jobs:
uses: actions/cache@v2
id: cache-llvm-source
with:
key: llvm-source-14-windows-v2
key: llvm-source-13-windows-v1
path: |
llvm-project/clang/lib/Headers
llvm-project/clang/include
llvm-project/compiler-rt
llvm-project/lld/include
llvm-project/llvm/include
- name: Download LLVM source
@@ -51,7 +48,7 @@ jobs:
uses: actions/cache@v2
id: cache-llvm-build
with:
key: llvm-build-14-windows-v1
key: llvm-build-13-windows-v2
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -68,14 +65,11 @@ jobs:
uses: actions/cache@v2
id: cache-wasi-libc
with:
key: wasi-libc-sysroot-v3
key: wasi-libc-sysroot-v2
path: lib/wasi-libc/sysroot
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- name: Install wasmtime
run: |
scoop install wasmtime
- name: Test TinyGo
shell: bash
run: make test GOTESTFLAGS="-v -short"
@@ -101,5 +95,3 @@ jobs:
run: make smoketest TINYGO=$(PWD)/build/tinygo AVR=0 XTENSA=0
- name: Test stdlib packages
run: make tinygo-test
- name: Test stdlib packages on wasi
run: make tinygo-test-wasi-fast
+4
View File
@@ -10,6 +10,10 @@
[submodule "lib/cmsis-svd"]
path = lib/cmsis-svd
url = https://github.com/tinygo-org/cmsis-svd
[submodule "lib/compiler-rt"]
path = lib/compiler-rt
url = https://github.com/llvm-mirror/compiler-rt.git
branch = release_80
[submodule "lib/wasi-libc"]
path = lib/wasi-libc
url = https://github.com/CraneStation/wasi-libc
+1 -1
View File
@@ -18,7 +18,7 @@ tarball. If you want to help with development of TinyGo itself, you should follo
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.
* Go (1.16+)
* Go (1.15+)
* Standard build tools (gcc/clang)
* git
* CMake
-98
View File
@@ -1,101 +1,3 @@
0.23.0
---
* **command line**
- add `-work` flag
- add Go 1.18 support
- add LLVM 14 support
- `run`: add support for command-line parameters
- `build`: calculate default output path if `-o` is not specified
- `build`: add JSON output
- `test`: support multiple test binaries with `-c`
- `test`: support flags like `-v` on all targets (including emulated firmware)
* **compiler**
- add support for ThinLTO
- use compiler-rt from LLVM
- `builder`: prefer GNU build ID over Go build ID for caching
- `builder`: add support for cross compiling to Darwin
- `builder`: support machine outlining pass in stacksize calculation
- `builder`: disable asynchronous unwind tables
- `compileopts`: fix emulator configuration on non-amd64 Linux architectures
- `compiler`: move allocations > 256 bytes to the heap
- `compiler`: fix incorrect `unsafe.Alignof` on some 32-bit architectures
- `compiler`: accept alias for slice `cap` builtin
- `compiler`: allow slices of empty structs
- `compiler`: fix difference in aliases in interface methods
- `compiler`: make `RawSyscall` an alias for `Syscall`
- `compiler`: remove support for memory references in `AsmFull`
- `loader`: only add Clang header path for CGo
- `transform`: fix poison value in heap-to-stack transform
* **standard library**
- `internal/fuzz`: add this package as a shim
- `os`: implement readdir for darwin and linux
- `os`: add `DirFS`, which is used by many programs to access readdir.
- `os`: isWine: be compatible with older versions of wine, too
- `os`: implement `RemoveAll`
- `os`: Use a `uintptr` for `NewFile`
- `os`: add stubs for `exec.ExitError` and `ProcessState.ExitCode`
- `os`: export correct values for `DevNull` for each OS
- `os`: improve support for `Signal` by fixing various bugs
- `os`: implement `File.Fd` method
- `os`: implement `UserHomeDir`
- `os`: add `exec.ProcessState` stub
- `os`: implement `Pipe` for darwin
- `os`: define stub `ErrDeadlineExceeded`
- `reflect`: add stubs for more missing methods
- `reflect`: rename `reflect.Ptr` to `reflect.Pointer`
- `reflect`: add `Value.FieldByIndexErr` stub
- `runtime`: fix various small GC bugs
- `runtime`: use memzero for leaking collector instead of manually zeroing objects
- `runtime`: implement `memhash`
- `runtime`: implement `fastrand`
- `runtime`: add stub for `debug.ReadBuildInfo`
- `runtime`: add stub for `NumCPU`
- `runtime`: don't inline `runtime.alloc` with `-gc=leaking`
- `runtime`: add `Version`
- `runtime`: add stubs for `NumCgoCall` and `NumGoroutine`
- `runtime`: stub {Lock,Unlock}OSThread on Windows
- `runtime`: be able to deal with a very small heap
- `syscall`: make `Environ` return a copy of the environment
- `syscall`: implement getpagesize and munmap
- `syscall`: `wasi`: define `MAP_SHARED` and `PROT_READ`
- `syscall`: stub mmap(), munmap(), MAP_SHARED, PROT_READ, SIGBUS, etc. on nonhosted targets
- `syscall`: darwin: more complete list of signals
- `syscall`: `wasi`: more complete list of signals
- `syscall`: stub `WaitStatus`
- `syscall/js`: allow copyBytesTo(Go|JS) to use `Uint8ClampedArray`
- `testing`: implement `TempDir`
- `testing`: nudge type TB closer to upstream; should be a no-op change.
- `testing`: on baremetal platforms, use simpler test matcher
* **targets**
- `atsamd`: fix usbcdc initialization when `-serial=uart`
- `atsamd51`: allow higher frequency when using SPI
- `esp`: support CGo
- `esp32c3`: add support for input pin
- `esp32c3`: add support for GPIO interrupts
- `esp32c3`: add support to receive UART data
- `rp2040`: fix PWM bug at high frequency
- `rp2040`: fix some minor I2C bugs
- `rp2040`: fix incorrect inline assembly
- `rp2040`: fix spurious i2c STOP during write+read transaction
- `rp2040`: improve ADC support
- `wasi`: remove `--export-dynamic` linker flag
- `wasm`: remove heap allocator from wasi-libc
* **boards**
- `circuitplay-bluefruit`: move pin mappings so board can be compiled for WASM use in Playground
- `esp32-c3-12f`: add the ESP32-C3-12f Kit
- `m5stamp-c3`: add pin setting of UART
- `macropad-rp2040`: add the Adafruit MacroPad RP2040 board
- `nano-33-ble`: typo in LPS22HB peripheral definition and documentation (#2579)
- `teensy41`: add the Teensy 4.1 board
- `teensy40`: add ADC support
- `teensy40`: add SPI support
- `thingplus-rp2040`: add the SparkFun Thing Plus RP2040 board
- `wioterminal`: add DefaultUART
- `wioterminal`: verify written data when flashing through OpenOCD
- `xiao-ble`: add XIAO BLE nRF52840 support
0.22.0
---
+1 -1
View File
@@ -1,5 +1,5 @@
# tinygo-llvm stage obtains the llvm source for TinyGo
FROM golang:1.18 AS tinygo-llvm
FROM golang:1.17 AS tinygo-llvm
RUN apt-get update && \
apt-get install -y apt-utils make cmake clang-11 binutils-avr gcc-avr avr-libc ninja-build
+28 -71
View File
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
# Try to autodetect LLVM build tools.
# Versions are listed here in descending priority order.
LLVM_VERSIONS = 14 13 12 11
LLVM_VERSIONS = 13 12 11
errifempty = $(if $(1),$(1),$(error $(2)))
detect = $(shell which $(call errifempty,$(firstword $(foreach p,$(2),$(shell command -v $(p) 2> /dev/null && echo $(p)))),failed to locate $(1) at any of: $(2)))
toolSearchPathsVersion = $(1)-$(2)
@@ -50,38 +50,6 @@ else
LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF'
endif
# Cross compiling support.
ifneq ($(CROSS),)
CC = $(CROSS)-gcc
CXX = $(CROSS)-g++
LLVM_OPTION += \
-DCMAKE_C_COMPILER=$(CC) \
-DCMAKE_CXX_COMPILER=$(CXX) \
-DLLVM_DEFAULT_TARGET_TRIPLE=$(CROSS) \
-DCROSS_TOOLCHAIN_FLAGS_NATIVE="-UCMAKE_C_COMPILER;-UCMAKE_CXX_COMPILER"
ifeq ($(CROSS), arm-linux-gnueabihf)
# Assume we're building on a Debian-like distro, with QEMU installed.
LLVM_CONFIG_PREFIX = qemu-arm -L /usr/arm-linux-gnueabihf/
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
LLVM_OPTION += \
-DCMAKE_SYSTEM_NAME=Linux \
-DLLVM_TARGET_ARCH=ARM
GOENVFLAGS = GOARCH=arm CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
else ifeq ($(CROSS), aarch64-linux-gnu)
# Assume we're building on a Debian-like distro, with QEMU installed.
LLVM_CONFIG_PREFIX = qemu-aarch64 -L /usr/aarch64-linux-gnu/
# The CMAKE_SYSTEM_NAME flag triggers cross compilation mode.
LLVM_OPTION += \
-DCMAKE_SYSTEM_NAME=Linux \
-DLLVM_TARGET_ARCH=AArch64
GOENVFLAGS = GOARCH=arm64 CC=$(CC) CXX=$(CXX) CGO_ENABLED=1
BINARYEN_OPTION += -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)
else
$(error Unknown cross compilation target: $(CROSS))
endif
endif
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest
@@ -123,11 +91,11 @@ CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGe
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
# Libraries that should be linked in for the statically linked LLD.
LLD_LIB_NAMES = lldCOFF lldCommon lldELF lldMachO lldMinGW lldWasm
LLD_LIB_NAMES = lldCOFF lldCommon lldCore lldDriver lldELF lldMachO2 lldMinGW lldReaderWriter lldWasm lldYAML
LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP)
# Other libraries that are needed to link TinyGo.
EXTRA_LIB_NAMES = LLVMInterpreter LLVMMCA LLVMX86TargetMCA
EXTRA_LIB_NAMES = LLVMInterpreter
# All libraries to be built and linked with the tinygo binary (lib/lib*.a).
LIB_NAMES = clang $(CLANG_LIB_NAMES) $(LLD_LIB_NAMES) $(EXTRA_LIB_NAMES)
@@ -142,9 +110,9 @@ NINJA_BUILD_TARGETS = clang llvm-config llvm-ar llvm-nm $(addprefix lib/lib,$(ad
# For static linking.
ifneq ("$(wildcard $(LLVM_BUILDDIR)/bin/llvm-config*)","")
CGO_CPPFLAGS+=$(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
CGO_CPPFLAGS+=$(shell $(LLVM_BUILDDIR)/bin/llvm-config --cppflags) -I$(abspath $(LLVM_BUILDDIR))/tools/clang/include -I$(abspath $(CLANG_SRC))/include -I$(abspath $(LLD_SRC))/include
CGO_CXXFLAGS=-std=c++14
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_CONFIG_PREFIX) $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
CGO_LDFLAGS+=-L$(abspath $(LLVM_BUILDDIR)/lib) -lclang $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUILDDIR)/bin/llvm-config --ldflags --libs --system-libs $(LLVM_COMPONENTS)) -lstdc++ $(CGO_LDFLAGS_EXTRA)
endif
clean:
@@ -207,12 +175,12 @@ gen-device-rp: build/gen-device-svd
# Get LLVM sources.
$(LLVM_PROJECTDIR)/llvm:
git clone -b xtensa_release_14.0.0-patched --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
git clone -b xtensa_release_13.0.0 --depth=1 https://github.com/tinygo-org/llvm-project $(LLVM_PROJECTDIR)
llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd)
$(LLVM_BUILDDIR)/build.ninja:
$(LLVM_BUILDDIR)/build.ninja: llvm-source
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
# Build LLVM.
@@ -240,18 +208,21 @@ lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
# Build the Go compiler.
tinygo:
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" .
test: wasi-libc
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
# Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi
TEST_PACKAGES_SLOW = \
compress/bzip2 \
compress/flate \
crypto/dsa \
index/suffixarray \
# Standard library packages that pass tests quickly on darwin, linux, wasi, and windows
TEST_PACKAGES_FAST = \
compress/lzw \
compress/zlib \
container/heap \
container/list \
@@ -265,7 +236,6 @@ TEST_PACKAGES_FAST = \
crypto/sha256 \
crypto/sha512 \
debug/macho \
embed/internal/embedtest \
encoding \
encoding/ascii85 \
encoding/base32 \
@@ -299,25 +269,17 @@ TEST_PACKAGES_FAST = \
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
# io/fs requires os.ReadDir, which is not yet supported on windows or wasi
# testing/fstest requires os.ReadDir, which is not yet supported on windows or wasi
# compress/flate fails windows go 1.18, https://github.com/tinygo-org/tinygo/issues/2762
# compress/lzw fails windows go 1.18 wasi, https://github.com/tinygo-org/tinygo/issues/2762
# Additional standard library packages that pass tests on individual platforms
TEST_PACKAGES_LINUX := \
archive/zip \
compress/flate \
compress/lzw \
debug/dwarf \
debug/plan9obj \
io/fs \
io/ioutil \
testing/fstest
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
TEST_PACKAGES_WINDOWS := \
compress/lzw
# Report platforms on which each standard library package is known to pass tests
jointmp := $(shell echo /tmp/join.$$$$)
report-stdlib-tests-pass:
@@ -336,7 +298,7 @@ ifeq ($(shell uname),Linux)
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
endif
ifeq ($(OS),Windows_NT)
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST)
endif
# Test known-working standard library packages.
@@ -396,6 +358,8 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo2
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
@@ -426,8 +390,6 @@ ifneq ($(WASM), 0)
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_express examples/blinky1
@$(MD5SUM) test.wasm
$(TINYGO) build -size short -o test.wasm -tags=circuitplay_bluefruit examples/blinky1
@$(MD5SUM) test.wasm
endif
# test all targets/boards
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
@@ -644,8 +606,7 @@ endif
@$(MD5SUM) test.hex
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
GOOS=darwin GOARCH=amd64 $(TINYGO) build -o test ./testdata/cgo
ifneq ($(OS),Windows_NT)
# TODO: this does not yet work on Windows. Somehow, unused functions are
# not garbage collected.
@@ -660,6 +621,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
@mkdir -p build/release/tinygo/bin
@mkdir -p build/release/tinygo/lib/clang/include
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
@mkdir -p build/release/tinygo/lib/macos-minimal-sdk
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
@@ -681,6 +643,9 @@ endif
@cp -p $(abspath $(CLANG_SRC))/lib/Headers/*.h build/release/tinygo/lib/clang/include
@cp -rp lib/CMSIS/CMSIS/Include build/release/tinygo/lib/CMSIS/CMSIS
@cp -rp lib/CMSIS/README.md build/release/tinygo/lib/CMSIS
@cp -rp lib/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt/lib
@cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt
@cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt
@cp -rp lib/macos-minimal-sdk/* build/release/tinygo/lib/macos-minimal-sdk
@cp -rp lib/musl/arch/aarch64 build/release/tinygo/lib/musl/arch
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
@@ -717,29 +682,21 @@ endif
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
@cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
@cp -rp src build/release/tinygo/src
@cp -rp targets build/release/tinygo/targets
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc
./build/release/tinygo/bin/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc
./build/release/tinygo/bin/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/compiler-rt compiler-rt
./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/compiler-rt compiler-rt
./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/compiler-rt compiler-rt
./build/tinygo build-library -target=cortex-m0 -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0/picolibc picolibc
./build/tinygo build-library -target=cortex-m0plus -o build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus/picolibc picolibc
./build/tinygo build-library -target=cortex-m4 -o build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4/picolibc picolibc
release:
release: build/release
tar -czf build/release.tar.gz -C build/release tinygo
DEB_ARCH ?= native
deb:
deb: build/release
@mkdir -p build/release-deb/usr/local/bin
@mkdir -p build/release-deb/usr/local/lib
cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo
ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo
fpm -f -s dir -t deb -n tinygo -a $(DEB_ARCH) -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
ifneq ($(RELEASEONLY), 1)
release: build/release
deb: build/release
endif
fpm -f -s dir -t deb -n tinygo -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
+1 -4
View File
@@ -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.
The following 85 microcontroller boards are currently supported:
The following 82 microcontroller boards are currently supported:
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
@@ -85,7 +85,6 @@ The following 85 microcontroller boards are currently supported:
* [Dragino LoRaWAN GPS Tracker LGT-92](http://www.dragino.com/products/lora-lorawan-end-node/item/142-lgt-92.html)
* [ESP32 - Core board](https://www.espressif.com/en/products/socs/esp32)
* [ESP32 - mini32](https://www.espressif.com/en/products/socs/esp32)
* [ESP32-C3-12f](https://www.espressif.com/en/products/socs/esp32-c3)
* [ESP8266 - d1mini](https://www.espressif.com/en/products/socs/esp8266)
* [ESP8266 - NodeMCU](https://www.espressif.com/en/products/socs/esp8266)
* [Game Boy Advance](https://en.wikipedia.org/wiki/Game_Boy_Advance)
@@ -113,12 +112,10 @@ The following 85 microcontroller boards are currently supported:
* [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 Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
* [Seeed XIAO BLE](https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html)
* [Seeed LoRa-E5 Development Kit](https://www.seeedstudio.com/LoRa-E5-Dev-Kit-p-4868.html)
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
* [SiFIve HiFive1 Rev B](https://www.sifive.com/boards/hifive1)
* [Sparkfun Thing Plus RP2040](https://www.sparkfun.com/products/17745)
* [ST Micro "Nucleo" F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
* [ST Micro "Nucleo" F722ZE](https://www.st.com/en/evaluation-tools/nucleo-f722ze.html)
* [ST Micro "Nucleo" L031K6](https://www.st.com/ja/evaluation-tools/nucleo-l031k6.html)
+57 -250
View File
@@ -4,7 +4,6 @@
package builder
import (
"crypto/sha256"
"crypto/sha512"
"debug/elf"
"encoding/binary"
@@ -38,14 +37,8 @@ import (
// BuildResult is the output of a build. This includes the binary itself and
// some other metadata that is obtained while building the binary.
type BuildResult struct {
// The executable directly from the linker, usually including debug
// information. Used for GDB for example.
Executable string
// A path to the output binary. It will be removed after Build returns, so
// if it should be kept it must be copied or moved away.
// It is often the same as Executable, but differs if the output format is
// .hex for example (instead of the usual ELF).
Binary string
// The directory of the main package. This is useful for testing as the test
@@ -81,7 +74,6 @@ type packageAction struct {
Config *compiler.Config
CFlags []string
FileHashes map[string]string // hash of every file that's part of the package
EmbeddedFiles map[string]string // hash of all the //go:embed files in the package
Imports map[string]string // map from imported package to action ID hash
OptLevel int // LLVM optimization level (0-3)
SizeLevel int // LLVM optimization for size level (0-2)
@@ -190,7 +182,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil {
return err
}
defer machine.Dispose()
// Load entire program AST into memory.
lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
@@ -211,7 +202,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Add jobs to compile each package.
// Packages that have a cache hit will not be compiled again.
var packageJobs []*compileJob
packageActionIDJobs := make(map[string]*compileJob)
packageBitcodePaths := make(map[string]string)
packageActionIDs := make(map[string]string)
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
version := goenv.Version
@@ -227,7 +219,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
config.Options.GlobalValues["runtime"]["buildVersion"] = version
}
var embedFileObjects []*compileJob
for _, pkg := range lprogram.Sorted() {
pkg := pkg // necessary to avoid a race condition
@@ -237,114 +228,52 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
}
sort.Strings(undefinedGlobals)
// Make compile jobs to load files to be embedded in the output binary.
var actionIDDependencies []*compileJob
allFiles := map[string][]*loader.EmbedFile{}
for _, files := range pkg.EmbedGlobals {
for _, file := range files {
allFiles[file.Name] = append(allFiles[file.Name], file)
}
// Create a cache key: a hash from the action ID below that contains all
// the parameters for the build.
actionID := packageAction{
ImportPath: pkg.ImportPath,
CompilerBuildID: string(compilerBuildID),
TinyGoVersion: goenv.Version,
LLVMVersion: llvm.Version,
Config: compilerConfig,
CFlags: pkg.CFlags,
FileHashes: make(map[string]string, len(pkg.FileHashes)),
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
OptLevel: optLevel,
SizeLevel: sizeLevel,
UndefinedGlobals: undefinedGlobals,
}
for name, files := range allFiles {
name := name
files := files
job := &compileJob{
description: "make object file for " + name,
run: func(job *compileJob) error {
// Read the file contents in memory.
path := filepath.Join(pkg.Dir, name)
data, err := os.ReadFile(path)
if err != nil {
return err
}
// Hash the file.
sum := sha256.Sum256(data)
hexSum := hex.EncodeToString(sum[:16])
for _, file := range files {
file.Size = uint64(len(data))
file.Hash = hexSum
if file.NeedsData {
file.Data = data
}
}
job.result, err = createEmbedObjectFile(string(data), hexSum, name, pkg.OriginalDir(), dir, compilerConfig)
return err
},
}
actionIDDependencies = append(actionIDDependencies, job)
embedFileObjects = append(embedFileObjects, job)
for filePath, hash := range pkg.FileHashes {
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
}
// Action ID jobs need to know the action ID of all the jobs the package
// imports.
var importedPackages []*compileJob
for _, imported := range pkg.Pkg.Imports() {
job, ok := packageActionIDJobs[imported.Path()]
hash, ok := packageActionIDs[imported.Path()]
if !ok {
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
}
importedPackages = append(importedPackages, job)
actionIDDependencies = append(actionIDDependencies, job)
actionID.Imports[imported.Path()] = hash
}
// Create a job that will calculate the action ID for a package compile
// job. The action ID is the cache key that is used for caching this
// package.
packageActionIDJob := &compileJob{
description: "calculate cache key for package " + pkg.ImportPath,
dependencies: actionIDDependencies,
run: func(job *compileJob) error {
// Create a cache key: a hash from the action ID below that contains all
// the parameters for the build.
actionID := packageAction{
ImportPath: pkg.ImportPath,
CompilerBuildID: string(compilerBuildID),
TinyGoVersion: goenv.Version,
LLVMVersion: llvm.Version,
Config: compilerConfig,
CFlags: pkg.CFlags,
FileHashes: make(map[string]string, len(pkg.FileHashes)),
EmbeddedFiles: make(map[string]string, len(allFiles)),
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
OptLevel: optLevel,
SizeLevel: sizeLevel,
UndefinedGlobals: undefinedGlobals,
}
for filePath, hash := range pkg.FileHashes {
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
}
for name, files := range allFiles {
actionID.EmbeddedFiles[name] = files[0].Hash
}
for i, imported := range pkg.Pkg.Imports() {
actionID.Imports[imported.Path()] = importedPackages[i].result
}
buf, err := json.Marshal(actionID)
if err != nil {
return err // shouldn't happen
}
hash := sha512.Sum512_224(buf)
job.result = hex.EncodeToString(hash[:])
return nil
},
buf, err := json.Marshal(actionID)
if err != nil {
panic(err) // shouldn't happen
}
packageActionIDJobs[pkg.ImportPath] = packageActionIDJob
hash := sha512.Sum512_224(buf)
packageActionIDs[pkg.ImportPath] = hex.EncodeToString(hash[:])
// Now create the job to actually build the package. It will exit early
// if the package is already compiled.
// Determine the path of the bitcode file (which is a serialized version
// of a LLVM module).
bitcodePath := filepath.Join(cacheDir, "pkg-"+hex.EncodeToString(hash[:])+".bc")
packageBitcodePaths[pkg.ImportPath] = bitcodePath
// The package has not yet been compiled, so create a job to do so.
job := &compileJob{
description: "compile package " + pkg.ImportPath,
dependencies: []*compileJob{packageActionIDJob},
run: func(job *compileJob) error {
job.result = filepath.Join(cacheDir, "pkg-"+packageActionIDJob.result+".bc")
description: "compile package " + pkg.ImportPath,
run: func(*compileJob) error {
// Acquire a lock (if supported).
unlock := lock(job.result + ".lock")
unlock := lock(bitcodePath + ".lock")
defer unlock()
if _, err := os.Stat(job.result); err == nil {
if _, err := os.Stat(bitcodePath); err == nil {
// Already cached, don't recreate this package.
return nil
}
@@ -352,8 +281,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Compile AST to IR. The compiler.CompilePackage function will
// build the SSA as needed.
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
defer mod.Context().Dispose()
defer mod.Dispose()
if errs != nil {
return newMultiError(errs)
}
@@ -465,7 +392,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Write to a temporary path that is renamed to the destination
// file to avoid race conditions with other TinyGo invocatiosn
// that might also be compiling this package at the same time.
f, err := ioutil.TempFile(filepath.Dir(job.result), filepath.Base(job.result))
f, err := ioutil.TempFile(filepath.Dir(bitcodePath), filepath.Base(bitcodePath))
if err != nil {
return err
}
@@ -485,13 +412,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil {
// WriteBitcodeToFile doesn't produce a useful error on its
// own, so create a somewhat useful error message here.
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, job.result)
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, bitcodePath)
}
err = f.Close()
if err != nil {
return err
}
return os.Rename(f.Name(), job.result)
return os.Rename(f.Name(), bitcodePath)
},
}
packageJobs = append(packageJobs, job)
@@ -499,13 +426,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Add job that links and optimizes all packages together.
var mod llvm.Module
defer func() {
if !mod.IsNil() {
ctx := mod.Context()
mod.Dispose()
ctx.Dispose()
}
}()
var stackSizeLoads []string
programJob := &compileJob{
description: "link+optimize packages (LTO)",
@@ -515,8 +435,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// anything, it only links the bitcode files together.
ctx := llvm.NewContext()
mod = ctx.NewModule("main")
for _, pkgJob := range packageJobs {
pkgMod, err := ctx.ParseBitcodeFile(pkgJob.result)
for _, pkg := range lprogram.Sorted() {
pkgMod, err := ctx.ParseBitcodeFile(packageBitcodePaths[pkg.ImportPath])
if err != nil {
return fmt.Errorf("failed to load bitcode file: %w", err)
}
@@ -608,7 +528,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil {
return err
}
defer llvmBuf.Dispose()
return ioutil.WriteFile(outpath, llvmBuf.Bytes(), 0666)
case ".bc":
var buf llvm.MemoryBuffer
@@ -717,9 +636,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Add libc dependencies, if they exist.
linkerDependencies = append(linkerDependencies, libcDependencies...)
// Add embedded files.
linkerDependencies = append(linkerDependencies, embedFileObjects...)
// Strip debug information with -no-debug.
if !config.Debug() {
for _, tag := range config.BuildTags() {
@@ -772,24 +688,11 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
config.Options.PrintCommands(config.Target.Linker, ldflags...)
}
if config.UseThinLTO() {
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
if config.GOOS() == "windows" {
// Options for the MinGW wrapper for the lld COFF linker.
ldflags = append(ldflags,
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
} else if config.GOOS() == "darwin" {
// Options for the ld64-compatible lld linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel),
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
} else {
// Options for the ELF linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
)
}
ldflags = append(ldflags,
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
"-plugin-opt=mcpu="+config.CPU(),
"-plugin-opt=O"+strconv.Itoa(optLevel),
"-plugin-opt=thinlto")
if config.CodeModel() != "default" {
ldflags = append(ldflags,
"-mllvm", "-code-model="+config.CodeModel())
@@ -932,7 +835,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil {
return err
}
case "esp32", "esp32-img", "esp32c3", "esp8266":
case "esp32", "esp32c3", "esp8266":
// Special format for the ESP family of chips (parsed by the ROM
// bootloader).
tmppath = filepath.Join(dir, "main"+outext)
@@ -964,7 +867,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
}
return action(BuildResult{
Executable: executable,
Binary: tmppath,
MainDir: lprogram.MainPkg().Dir,
ModuleRoot: moduleroot,
@@ -972,112 +874,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
})
}
// createEmbedObjectFile creates a new object file with the given contents, for
// the embed package.
func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, compilerConfig *compiler.Config) (string, error) {
// TODO: this works for small files, but can be a problem for larger files.
// For larger files, it seems more appropriate to generate the object file
// manually without going through LLVM.
// On the other hand, generating DWARF like we do here can be difficult
// without assistance from LLVM.
// Create new LLVM module just for this file.
ctx := llvm.NewContext()
defer ctx.Dispose()
mod := ctx.NewModule("data")
defer mod.Dispose()
// Create data global.
value := ctx.ConstString(data, false)
globalName := "embed/file_" + hexSum
global := llvm.AddGlobal(mod, value.Type(), globalName)
global.SetInitializer(value)
global.SetLinkage(llvm.LinkOnceODRLinkage)
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
global.SetAlignment(1)
if compilerConfig.GOOS != "darwin" {
// MachO doesn't support COMDATs, while COFF requires it (to avoid
// "duplicate symbol" errors). ELF works either way.
// Therefore, only use a COMDAT on non-MachO systems (aka non-MacOS).
global.SetComdat(mod.Comdat(globalName))
}
// Add DWARF debug information to this global, so that it is
// correctly counted when compiling with the -size= flag.
dibuilder := llvm.NewDIBuilder(mod)
dibuilder.CreateCompileUnit(llvm.DICompileUnit{
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
File: sourceFile,
Dir: sourceDir,
Producer: "TinyGo",
Optimized: false,
})
ditype := dibuilder.CreateArrayType(llvm.DIArrayType{
SizeInBits: uint64(len(data)) * 8,
AlignInBits: 8,
ElementType: dibuilder.CreateBasicType(llvm.DIBasicType{
Name: "byte",
SizeInBits: 8,
Encoding: llvm.DW_ATE_unsigned_char,
}),
Subscripts: []llvm.DISubrange{
{
Lo: 0,
Count: int64(len(data)),
},
},
})
difile := dibuilder.CreateFile(sourceFile, sourceDir)
diglobalexpr := dibuilder.CreateGlobalVariableExpression(difile, llvm.DIGlobalVariableExpression{
Name: globalName,
File: difile,
Line: 1,
Type: ditype,
Expr: dibuilder.CreateExpression(nil),
AlignInBits: 8,
})
global.AddMetadata(0, diglobalexpr)
mod.AddNamedMetadataOperand("llvm.module.flags",
ctx.MDNode([]llvm.Metadata{
llvm.ConstInt(ctx.Int32Type(), 2, false).ConstantAsMetadata(), // Warning on mismatch
ctx.MDString("Debug Info Version"),
llvm.ConstInt(ctx.Int32Type(), 3, false).ConstantAsMetadata(),
}),
)
mod.AddNamedMetadataOperand("llvm.module.flags",
ctx.MDNode([]llvm.Metadata{
llvm.ConstInt(ctx.Int32Type(), 7, false).ConstantAsMetadata(), // Max on mismatch
ctx.MDString("Dwarf Version"),
llvm.ConstInt(ctx.Int32Type(), 4, false).ConstantAsMetadata(),
}),
)
dibuilder.Finalize()
dibuilder.Destroy()
// Write this LLVM module out as an object file.
machine, err := compiler.NewTargetMachine(compilerConfig)
if err != nil {
return "", err
}
defer machine.Dispose()
outfile, err := os.CreateTemp(tmpdir, "embed-"+hexSum+"-*.o")
if err != nil {
return "", err
}
defer outfile.Close()
buf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil {
return "", err
}
defer buf.Dispose()
_, err = outfile.Write(buf.Bytes())
if err != nil {
return "", err
}
return outfile.Name(), outfile.Close()
}
// optimizeProgram runs a series of optimizations and transformations that are
// needed to convert a program to its final form. Some transformations are not
// optional and must be run as the compiler expects them to run.
@@ -1132,6 +928,17 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
return errors.New("verification failure after LLVM optimization passes")
}
// LLVM 11 by default tries to emit tail calls (even with the target feature
// disabled) unless it is explicitly disabled with a function attribute.
// This is a problem, as it tries to emit them and prints an error when it
// can't with this feature disabled.
// Because as of september 2020 tail calls are not yet widely supported,
// they need to be disabled until they are widely supported (at which point
// the +tail-call target feautre can be set).
if strings.HasPrefix(config.Triple(), "wasm") {
transform.DisableTailCalls(mod)
}
return nil
}
+1 -1
View File
@@ -26,7 +26,7 @@ func TestClangAttributes(t *testing.T) {
"cortex-m0",
"cortex-m0plus",
"cortex-m3",
"cortex-m33",
//"cortex-m33", // TODO: broken in LLVM 11, fixed in https://reviews.llvm.org/D90305
"cortex-m4",
"cortex-m7",
"esp32c3",
+3 -22
View File
@@ -1,11 +1,7 @@
package builder
import (
"os"
"path/filepath"
"strings"
"github.com/tinygo-org/tinygo/goenv"
)
// These are the GENERIC_SOURCES according to CMakeList.txt.
@@ -40,6 +36,7 @@ var genericBuiltins = []string{
"divdf3.c",
"divdi3.c",
"divmoddi4.c",
"divmodsi4.c",
"divsc3.c",
"divsf3.c",
"divsi3.c",
@@ -75,7 +72,6 @@ var genericBuiltins = []string{
"floatunsisf.c",
"floatuntidf.c",
"floatuntisf.c",
"fp_mode.c",
//"int_util.c",
"lshrdi3.c",
"lshrti3.c",
@@ -126,6 +122,7 @@ var genericBuiltins = []string{
"ucmpti2.c",
"udivdi3.c",
"udivmoddi4.c",
"udivmodsi4.c",
"udivmodti4.c",
"udivsi3.c",
"udivti3.c",
@@ -152,14 +149,6 @@ var aeabiBuiltins = []string{
"arm/aeabi_memset.S",
"arm/aeabi_uidivmod.S",
"arm/aeabi_uldivmod.S",
// These two are not technically EABI builtins but are used by them and only
// seem to be used on ARM. LLVM seems to use __divsi3 and __modsi3 on most
// other architectures.
// Most importantly, they have a different calling convention on AVR so
// should not be used on AVR.
"divmodsi4.c",
"udivmodsi4.c",
}
// CompilerRT is a library with symbols required by programs compiled with LLVM.
@@ -172,15 +161,7 @@ var CompilerRT = Library{
cflags: func(target, headerPath string) []string {
return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"}
},
sourceDir: func() string {
llvmDir := filepath.Join(goenv.Get("TINYGOROOT"), "llvm-project/compiler-rt/lib/builtins")
if _, err := os.Stat(llvmDir); err == nil {
// Release build.
return llvmDir
}
// Development build.
return filepath.Join(goenv.Get("TINYGOROOT"), "lib/compiler-rt-builtins")
},
sourceDir: "lib/compiler-rt/lib/builtins",
librarySources: func(target string) []string {
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
+4 -3
View File
@@ -38,7 +38,6 @@
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCTargetOptions.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
@@ -52,6 +51,7 @@
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
@@ -120,6 +120,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
.Case("none", llvm::DebugCompressionType::None)
.Case("zlib", llvm::DebugCompressionType::Z)
.Case("zlib-gnu", llvm::DebugCompressionType::GNU)
.Default(llvm::DebugCompressionType::None);
}
@@ -381,7 +382,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
/*DWARFMustBeAtTheEnd*/ true));
Str.get()->initSections(Opts.NoExecStack, *STI);
Str.get()->InitSections(Opts.NoExecStack);
}
// When -fembed-bitcode is passed to clang_as, a 1-byte marker
@@ -441,7 +442,7 @@ bool ExecuteAssembler(AssemblerInvocation &Opts,
return Failed;
}
static void LLVMErrorHandler(void *UserData, const char *Message,
static void LLVMErrorHandler(void *UserData, const std::string &Message,
bool GenCrashDiag) {
DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
+1 -1
View File
@@ -74,7 +74,7 @@ func LookupCommand(name string) (string, error) {
}
return cmdName, nil
}
return "", errors.New("none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
return "", errors.New("%#v: none of these commands were found in your $PATH: " + strings.Join(commands[name], " "))
}
func execCommand(name string, args ...string) error {
+2 -2
View File
@@ -33,8 +33,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if err != nil {
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
}
if major != 1 || minor < 16 || minor > 18 {
return nil, fmt.Errorf("requires go version 1.16 through 1.18, got go%d.%d", major, minor)
if major != 1 || minor < 15 || minor > 17 {
return nil, fmt.Errorf("requires go version 1.15 through 1.17, got go%d.%d", major, minor)
}
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
+1 -1
View File
@@ -39,7 +39,7 @@ func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJ
// Link object file to dynamic library.
platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx")
flags = []string{
"-flavor", "darwin",
"-flavor", "darwinnew",
"-demangle",
"-dynamic",
"-dylib",
+3 -30
View File
@@ -15,7 +15,6 @@ import (
"fmt"
"io/ioutil"
"sort"
"strings"
)
type espImageSegment struct {
@@ -79,31 +78,15 @@ func makeESPFirmareImage(infile, outfile, format string) error {
// An added benefit is that we don't need to check for errors all the time.
outf := &bytes.Buffer{}
// Separate esp32 and esp32-img. The -img suffix indicates we should make an
// image, not just a binary to be flashed at 0x1000 for example.
chip := format
makeImage := false
if strings.HasSuffix(format, "-img") {
makeImage = true
chip = format[:len(format)-len("-img")]
}
if makeImage {
// The bootloader starts at 0x1000, or 4096.
// TinyGo doesn't use a separate bootloader and runs the entire
// application in the bootloader location.
outf.Write(make([]byte, 4096))
}
// 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,
}[chip]
}[format]
// Image header.
switch chip {
switch format {
case "esp32", "esp32c3":
// Header format:
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
@@ -172,22 +155,12 @@ func makeESPFirmareImage(infile, outfile, format string) error {
outf.Write(make([]byte, 15-outf.Len()%16))
outf.WriteByte(checksum)
if chip != "esp8266" {
if format != "esp8266" {
// SHA256 hash (to protect against image corruption, not for security).
hash := sha256.Sum256(outf.Bytes())
outf.Write(hash[:])
}
// QEMU (or more precisely, qemu-system-xtensa from Espressif) expects the
// image to be a certain size.
if makeImage {
// Use a default image size of 4MB.
grow := 4096*1024 - outf.Len()
if grow > 0 {
outf.Write(make([]byte, grow))
}
}
// Write the image to the output file.
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)
}
+5 -22
View File
@@ -24,8 +24,8 @@ type Library struct {
// cflags returns the C flags specific to this library
cflags func(target, headerPath string) []string
// The source directory.
sourceDir func() string
// The source directory, relative to TINYGOROOT.
sourceDir string
// The source files, relative to sourceDir.
librarySources func(target string) []string
@@ -148,21 +148,12 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
// However, ARM has not done this.
if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") {
args = append(args, "-march="+cpu)
} else if strings.HasPrefix(target, "avr") {
args = append(args, "-mmcu="+cpu)
} else {
args = append(args, "-mcpu="+cpu)
}
}
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
}
if strings.HasPrefix(target, "avr") {
// AVR defaults to C float and double both being 32-bit. This deviates
// from what most code (and certainly compiler-rt) expects. So we need
// to force the compiler to use 64-bit floating point numbers for
// double.
args = append(args, "-mdouble=64")
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables")
}
if strings.HasPrefix(target, "riscv32-") {
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
@@ -170,12 +161,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
if strings.HasPrefix(target, "riscv64-") {
args = append(args, "-march=rv64gc", "-mabi=lp64")
}
if strings.HasPrefix(target, "xtensa") {
// Hack to work around an issue in the Xtensa port:
// https://github.com/espressif/llvm-project/issues/52
// Hopefully this will be fixed soon (LLVM 14).
args = append(args, "-D__ELF__")
}
var once sync.Once
@@ -210,8 +195,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
},
}
sourceDir := l.sourceDir()
// Create jobs to compile all sources. These jobs are depended upon by the
// archive job above, so must be run first.
for _, path := range l.librarySources(target) {
@@ -220,7 +203,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
for strings.HasPrefix(cleanpath, "../") {
cleanpath = cleanpath[3:]
}
srcpath := filepath.Join(sourceDir, path)
srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, path)
objpath := filepath.Join(dir, cleanpath+".o")
os.MkdirAll(filepath.Dir(objpath), 0o777)
objs = append(objs, objpath)
@@ -244,7 +227,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
// (It could be done in parallel with creating the ar file, but it probably
// won't make much of a difference in speed).
if l.crt1Source != "" {
srcpath := filepath.Join(sourceDir, l.crt1Source)
srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, l.crt1Source)
job.dependencies = append(job.dependencies, &compileJob{
description: "compile " + srcpath,
run: func(*compileJob) error {
+4 -4
View File
@@ -8,22 +8,22 @@ extern "C" {
bool tinygo_link_elf(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc);
return lld::elf::link(args, llvm::outs(), llvm::errs(), false, false);
return lld::elf::link(args, false, llvm::outs(), llvm::errs());
}
bool tinygo_link_macho(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc);
return lld::macho::link(args, llvm::outs(), llvm::errs(), false, false);
return lld::macho::link(args, false, llvm::outs(), llvm::errs());
}
bool tinygo_link_mingw(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc);
return lld::mingw::link(args, llvm::outs(), llvm::errs(), false, false);
return lld::mingw::link(args, false, llvm::outs(), llvm::errs());
}
bool tinygo_link_wasm(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc);
return lld::wasm::link(args, llvm::outs(), llvm::errs(), false, false);
return lld::wasm::link(args, false, llvm::outs(), llvm::errs());
}
} // external "C"
-1
View File
@@ -26,7 +26,6 @@ var MinGW = Library{
_, err = io.Copy(outf, inf)
return err
},
sourceDir: func() string { return "" }, // unused
cflags: func(target, headerPath string) []string {
// No flags necessary because there are no files to compile.
return nil
+1 -1
View File
@@ -104,7 +104,7 @@ var Musl = Library{
"-fno-stack-protector",
}
},
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl/src") },
sourceDir: "lib/musl/src",
librarySources: func(target string) []string {
arch := compileopts.MuslArchitecture(target)
globs := []string{
+1 -1
View File
@@ -33,7 +33,7 @@ var Picolibc = Library{
"-I" + headerPath,
}
},
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc") },
sourceDir: "lib/picolibc/newlib/libc",
librarySources: func(target string) []string {
return picolibcSources
},
+1 -54
View File
@@ -4,7 +4,6 @@ import (
"bytes"
"debug/dwarf"
"debug/elf"
"debug/macho"
"debug/pe"
"encoding/binary"
"fmt"
@@ -117,7 +116,7 @@ var (
// alloc: heap allocations during init interpretation
// pack: data created when storing a constant in an interface for example
// string: buffer behind strings
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|embedfsfiles|embedfsslice|embedslice|pack|string)(\.[0-9]+)?$`)
packageSymbolRegexp = regexp.MustCompile(`\$(alloc|pack|string)(\.[0-9]+)?$`)
// Reflect sidetables. Created by the reflect lowering pass.
// See src/reflect/sidetables.go.
@@ -369,58 +368,6 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
})
}
}
} else if file, err := macho.NewFile(f); err == nil {
// TODO: read DWARF information. On MacOS, DWARF debug information isn't
// stored in the executable but stays in the object files. The
// executable does however contain the object file paths that contain
// debug information.
// Read segments, for use while reading through sections.
segments := map[string]*macho.Segment{}
for _, load := range file.Loads {
switch load := load.(type) {
case *macho.Segment:
segments[load.Name] = load
}
}
// Read MachO sections.
for _, section := range file.Sections {
sectionType := section.Flags & 0xff
sectionFlags := section.Flags >> 8
segment := segments[section.Seg]
// For the constants used here, see:
// https://github.com/llvm/llvm-project/blob/release/14.x/llvm/include/llvm/BinaryFormat/MachO.h
if sectionFlags&0x800000 != 0 { // S_ATTR_PURE_INSTRUCTIONS
// Section containing only instructions.
sections = append(sections, memorySection{
Address: section.Addr,
Size: uint64(section.Size),
Type: memoryCode,
})
} else if sectionType == 1 { // S_ZEROFILL
// Section filled with zeroes on demand.
sections = append(sections, memorySection{
Address: section.Addr,
Size: uint64(section.Size),
Type: memoryBSS,
})
} else if segment.Maxprot&0b011 == 0b001 { // --r (read-only data)
// Protection doesn't allow writes, so mark this section read-only.
sections = append(sections, memorySection{
Address: section.Addr,
Size: uint64(section.Size),
Type: memoryROData,
})
} else {
// The rest is assumed to be regular data.
sections = append(sections, memorySection{
Address: section.Addr,
Size: uint64(section.Size),
Type: memoryData,
})
}
}
} else if file, err := pe.NewFile(f); err == nil {
// Read DWARF information. The error is intentionally ignored.
data, _ := file.DWARF()
+1 -1
View File
@@ -54,7 +54,7 @@ func RunTool(tool string, args ...string) error {
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
case "ld.lld":
switch linker {
case "darwin":
case "darwinnew":
ok = C.tinygo_link_macho(C.int(len(args)), (**C.char)(buf))
case "elf":
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
+533 -367
View File
File diff suppressed because it is too large Load Diff
+199 -383
View File
@@ -72,11 +72,7 @@ var diagnosticSeverity = [...]string{
C.CXDiagnostic_Fatal: "fatal",
}
// Alias so that cgo.go (which doesn't import Clang related stuff and is in
// theory decoupled from Clang) can also use this type.
type clangCursor = C.GoCXCursor
func (f *cgoFile) readNames(fragment string, cflags []string, filename string, callback func(map[string]clangCursor)) {
func (p *cgoPackage) parseFragment(fragment string, cflags []string, filename string) {
index := C.clang_createIndex(0, 0)
defer C.clang_disposeIndex(index)
@@ -123,8 +119,8 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
location := C.clang_getDiagnosticLocation(diagnostic)
pos := f.getClangLocationPosition(location, unit)
f.addError(pos, severity+": "+spelling)
pos := p.getClangLocationPosition(location, unit)
p.addError(pos, severity+": "+spelling)
}
for i := 0; i < numDiagnostics; i++ {
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
@@ -139,7 +135,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
}
// Extract information required by CGo.
ref := storedRefs.Put(f)
ref := storedRefs.Put(p)
defer storedRefs.Remove(ref)
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
@@ -159,64 +155,35 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
// Hash the contents if it isn't hashed yet.
if _, ok := f.visitedFiles[path]; !ok {
if _, ok := p.visitedFiles[path]; !ok {
// already stored
sum := sha512.Sum512_224(data)
f.visitedFiles[path] = sum[:]
p.visitedFiles[path] = sum[:]
}
}
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
defer storedRefs.Remove(inclusionCallbackRef)
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
// Do all the C AST operations inside a callback. This makes sure that
// libclang related memory is only freed after it is not necessary anymore.
callback(f.names)
}
// Convert the AST node under the given Clang cursor to a Go AST node and return
// it.
func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaboratedTypeInfo) {
//export tinygo_clang_globals_visitor
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
kind := C.tinygo_clang_getCursorKind(c)
pos := f.getCursorPosition(c)
pos := p.getCursorPosition(c)
switch kind {
case C.CXCursor_FunctionDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
if _, required := p.missingSymbols[name]; !required {
return C.CXChildVisit_Continue
}
cursorType := C.tinygo_clang_getCursorType(c)
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
obj := &ast.Object{
Kind: ast.Fun,
Name: "C." + name,
}
args := make([]*ast.Field, numArgs)
decl := &ast.FuncDecl{
Doc: &ast.CommentGroup{
List: []*ast.Comment{
{
Slash: pos - 1,
Text: "//export " + name,
},
},
},
Name: &ast.Ident{
NamePos: pos,
Name: "C." + name,
Obj: obj,
},
Type: &ast.FuncType{
Func: pos,
Params: &ast.FieldList{
Opening: pos,
List: args,
Closing: pos,
},
},
}
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
decl.Doc.List = append(decl.Doc.List, &ast.Comment{
Slash: pos - 1,
Text: "//go:variadic",
})
fn := &functionInfo{
pos: pos,
variadic: C.clang_isFunctionTypeVariadic(cursorType) != 0,
}
p.functions[name] = fn
for i := 0; i < numArgs; i++ {
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
@@ -224,108 +191,50 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaborat
if argName == "" {
argName = "$" + strconv.Itoa(i)
}
args[i] = &ast.Field{
Names: []*ast.Ident{
{
NamePos: pos,
Name: argName,
Obj: &ast.Object{
Kind: ast.Var,
Name: argName,
Decl: decl,
},
},
},
Type: f.makeDecayingASTType(argType, pos),
}
fn.args = append(fn.args, paramInfo{
name: argName,
typeExpr: p.makeDecayingASTType(argType, pos),
})
}
resultType := C.tinygo_clang_getCursorResultType(c)
if resultType.kind != C.CXType_Void {
decl.Type.Results = &ast.FieldList{
fn.results = &ast.FieldList{
List: []*ast.Field{
{
Type: f.makeASTType(resultType, pos),
Type: p.makeASTType(resultType, pos),
},
},
}
}
obj.Decl = decl
return decl, nil
case C.CXCursor_StructDecl, C.CXCursor_UnionDecl:
typ := f.makeASTRecordType(c, pos)
typeName := "C." + name
typeExpr := typ.typeExpr
if typ.unionSize != 0 {
// Convert to a single-field struct type.
typeExpr = f.makeUnionField(typ)
case C.CXCursor_StructDecl:
typ := C.tinygo_clang_getCursorType(c)
name := getString(C.tinygo_clang_getCursorSpelling(c))
if _, required := p.missingSymbols["struct_"+name]; !required {
return C.CXChildVisit_Continue
}
obj := &ast.Object{
Kind: ast.Typ,
Name: typeName,
}
typeSpec := &ast.TypeSpec{
Name: &ast.Ident{
NamePos: typ.pos,
Name: typeName,
Obj: obj,
},
Type: typeExpr,
}
obj.Decl = typeSpec
return typeSpec, typ
p.makeASTType(typ, pos)
case C.CXCursor_TypedefDecl:
typeName := "C." + name
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
obj := &ast.Object{
Kind: ast.Typ,
Name: typeName,
typedefType := C.tinygo_clang_getCursorType(c)
name := getString(C.clang_getTypedefName(typedefType))
if _, required := p.missingSymbols[name]; !required {
return C.CXChildVisit_Continue
}
typeSpec := &ast.TypeSpec{
Name: &ast.Ident{
NamePos: pos,
Name: typeName,
Obj: obj,
},
Type: f.makeASTType(underlyingType, pos),
}
if underlyingType.kind != C.CXType_Enum {
typeSpec.Assign = pos
}
obj.Decl = typeSpec
return typeSpec, nil
p.makeASTType(typedefType, pos)
case C.CXCursor_VarDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
if _, required := p.missingSymbols[name]; !required {
return C.CXChildVisit_Continue
}
cursorType := C.tinygo_clang_getCursorType(c)
typeExpr := f.makeASTType(cursorType, pos)
gen := &ast.GenDecl{
TokPos: pos,
Tok: token.VAR,
Lparen: token.NoPos,
Rparen: token.NoPos,
Doc: &ast.CommentGroup{
List: []*ast.Comment{
{
Slash: pos - 1,
Text: "//go:extern " + name,
},
},
},
p.globals[name] = globalInfo{
typeExpr: p.makeASTType(cursorType, pos),
pos: pos,
}
obj := &ast.Object{
Kind: ast.Var,
Name: "C." + name,
}
valueSpec := &ast.ValueSpec{
Names: []*ast.Ident{{
NamePos: pos,
Name: "C." + name,
Obj: obj,
}},
Type: typeExpr,
}
obj.Decl = valueSpec
gen.Specs = append(gen.Specs, valueSpec)
return gen, nil
case C.CXCursor_MacroDefinition:
name := getString(C.tinygo_clang_getCursorSpelling(c))
if _, required := p.missingSymbols[name]; !required {
return C.CXChildVisit_Continue
}
sourceRange := C.tinygo_clang_getCursorExtent(c)
start := C.clang_getRangeStart(sourceRange)
end := C.clang_getRangeEnd(sourceRange)
@@ -333,17 +242,17 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaborat
var startOffset, endOffset C.unsigned
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
if file == nil {
f.addError(pos, "internal error: could not find file where macro is defined")
return nil, nil
p.addError(pos, "internal error: could not find file where macro is defined")
break
}
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
if file != endFile {
f.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
return nil, nil
p.addError(pos, "internal error: expected start and end location of a macro to be in the same file")
break
}
if startOffset > endOffset {
f.addError(pos, "internal error: start offset of macro is after end offset")
return nil, nil
p.addError(pos, "internal error: start offset of macro is after end offset")
break
}
// read file contents and extract the relevant byte range
@@ -351,94 +260,31 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, *elaborat
var size C.size_t
sourcePtr := C.clang_getFileContents(tu, file, &size)
if endOffset >= C.uint(size) {
f.addError(pos, "internal error: end offset of macro lies after end of file")
return nil, nil
p.addError(pos, "internal error: end offset of macro lies after end of file")
break
}
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
if !strings.HasPrefix(source, name) {
f.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
return nil, nil
p.addError(pos, fmt.Sprintf("internal error: expected macro value to start with %#v, got %#v", name, source))
break
}
value := source[len(name):]
// Try to convert this #define into a Go constant expression.
expr, scannerError := parseConst(pos+token.Pos(len(name)), f.fset, value)
expr, scannerError := parseConst(pos+token.Pos(len(name)), p.fset, value)
if scannerError != nil {
f.errors = append(f.errors, *scannerError)
return nil, nil
p.errors = append(p.errors, *scannerError)
}
gen := &ast.GenDecl{
TokPos: token.NoPos,
Tok: token.CONST,
Lparen: token.NoPos,
Rparen: token.NoPos,
if expr != nil {
// Parsing was successful.
p.constants[name] = constantInfo{expr, pos}
}
obj := &ast.Object{
Kind: ast.Con,
Name: "C." + name,
}
valueSpec := &ast.ValueSpec{
Names: []*ast.Ident{{
NamePos: pos,
Name: "C." + name,
Obj: obj,
}},
Values: []ast.Expr{expr},
}
obj.Decl = valueSpec
gen.Specs = append(gen.Specs, valueSpec)
return gen, nil
case C.CXCursor_EnumDecl:
obj := &ast.Object{
Kind: ast.Typ,
Name: "C." + name,
}
underlying := C.tinygo_clang_getEnumDeclIntegerType(c)
// TODO: gc's CGo implementation uses types such as `uint32` for enums
// instead of types such as C.int, which are used here.
typeSpec := &ast.TypeSpec{
Name: &ast.Ident{
NamePos: pos,
Name: "C." + name,
Obj: obj,
},
Assign: pos,
Type: f.makeASTType(underlying, pos),
}
obj.Decl = typeSpec
return typeSpec, nil
case C.CXCursor_EnumConstantDecl:
value := C.tinygo_clang_getEnumConstantDeclValue(c)
expr := &ast.BasicLit{
ValuePos: pos,
Kind: token.INT,
Value: strconv.FormatInt(int64(value), 10),
}
gen := &ast.GenDecl{
TokPos: token.NoPos,
Tok: token.CONST,
Lparen: token.NoPos,
Rparen: token.NoPos,
}
obj := &ast.Object{
Kind: ast.Con,
Name: "C." + name,
}
valueSpec := &ast.ValueSpec{
Names: []*ast.Ident{{
NamePos: pos,
Name: "C." + name,
Obj: obj,
}},
Values: []ast.Expr{expr},
}
obj.Decl = valueSpec
gen.Specs = append(gen.Specs, valueSpec)
return gen, nil
default:
f.addError(pos, fmt.Sprintf("internal error: unknown cursor type: %d", kind))
return nil, nil
// Visit all enums, because the fields may be used even when the enum
// type itself is not.
typ := C.tinygo_clang_getCursorType(c)
p.makeASTType(typ, pos)
}
return C.CXChildVisit_Continue
}
func getString(clangString C.CXString) (s string) {
@@ -448,49 +294,6 @@ func getString(clangString C.CXString) (s string) {
return
}
//export tinygo_clang_globals_visitor
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
f := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoFile)
switch C.tinygo_clang_getCursorKind(c) {
case C.CXCursor_FunctionDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
f.names[name] = c
case C.CXCursor_StructDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
if name != "" {
f.names["struct_"+name] = c
}
case C.CXCursor_UnionDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
if name != "" {
f.names["union_"+name] = c
}
case C.CXCursor_TypedefDecl:
typedefType := C.tinygo_clang_getCursorType(c)
name := getString(C.clang_getTypedefName(typedefType))
f.names[name] = c
case C.CXCursor_VarDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
f.names[name] = c
case C.CXCursor_MacroDefinition:
name := getString(C.tinygo_clang_getCursorSpelling(c))
f.names[name] = c
case C.CXCursor_EnumDecl:
name := getString(C.tinygo_clang_getCursorSpelling(c))
if name != "" {
// Named enum, which can be referenced from Go using C.enum_foo.
f.names["enum_"+name] = c
}
// The enum fields are in global scope, so recurse to visit them.
return C.CXChildVisit_Recurse
case C.CXCursor_EnumConstantDecl:
// We arrive here because of the "Recurse" above.
name := getString(C.tinygo_clang_getCursorSpelling(c))
f.names[name] = c
}
return C.CXChildVisit_Continue
}
// getCursorPosition returns a usable token.Pos from a libclang cursor.
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
return p.getClangLocationPosition(C.tinygo_clang_getCursorLocation(cursor), C.tinygo_clang_Cursor_getTranslationUnit(cursor))
@@ -588,7 +391,7 @@ func (p *cgoPackage) addErrorAt(position token.Position, msg string) {
// makeDecayingASTType does the same as makeASTType but takes care of decaying
// types (arrays in function parameters, etc). It is otherwise identical to
// makeASTType.
func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
func (p *cgoPackage) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
// Strip typedefs, if any.
underlyingType := typ
if underlyingType.kind == C.CXType_Typedef {
@@ -614,15 +417,15 @@ func (f *cgoFile) makeDecayingASTType(typ C.CXType, pos token.Pos) ast.Expr {
pointeeType := C.clang_getElementType(underlyingType)
return &ast.StarExpr{
Star: pos,
X: f.makeASTType(pointeeType, pos),
X: p.makeASTType(pointeeType, pos),
}
}
return f.makeASTType(typ, pos)
return p.makeASTType(typ, pos)
}
// makeASTType return the ast.Expr for the given libclang type. In other words,
// it converts a libclang type to a type in the Go AST.
func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
var typeName string
switch typ.kind {
case C.CXType_Char_S, C.CXType_Char_U:
@@ -683,7 +486,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
}
return &ast.StarExpr{
Star: pos,
X: f.makeASTType(pointeeType, pos),
X: p.makeASTType(pointeeType, pos),
}
case C.CXType_ConstantArray:
return &ast.ArrayType{
@@ -693,7 +496,7 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
Kind: token.INT,
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
},
Elt: f.makeASTType(C.clang_getElementType(typ), pos),
Elt: p.makeASTType(C.clang_getElementType(typ), pos),
}
case C.CXType_FunctionProto:
// Be compatible with gc, which uses the *[0]byte type for function
@@ -714,21 +517,71 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
}
case C.CXType_Typedef:
name := getString(C.clang_getTypedefName(typ))
c := C.tinygo_clang_getTypeDeclaration(typ)
if _, ok := p.typedefs[name]; !ok {
p.typedefs[name] = nil // don't recurse
c := C.tinygo_clang_getTypeDeclaration(typ)
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
expr := p.makeASTType(underlyingType, pos)
if strings.HasPrefix(name, "_Cgo_") {
expr := expr.(*ast.Ident)
typeSize := C.clang_Type_getSizeOf(underlyingType)
switch expr.Name {
case "C.char":
if typeSize != 1 {
// This happens for some very special purpose architectures
// (DSPs etc.) that are not currently targeted.
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
}
switch underlyingType.kind {
case C.CXType_Char_S:
expr.Name = "int8"
case C.CXType_Char_U:
expr.Name = "uint8"
}
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
switch typeSize {
case 1:
expr.Name = "int8"
case 2:
expr.Name = "int16"
case 4:
expr.Name = "int32"
case 8:
expr.Name = "int64"
}
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
switch typeSize {
case 1:
expr.Name = "uint8"
case 2:
expr.Name = "uint16"
case 4:
expr.Name = "uint32"
case 8:
expr.Name = "uint64"
}
}
}
p.typedefs[name] = &typedefInfo{
typeExpr: expr,
pos: pos,
}
}
return &ast.Ident{
NamePos: pos,
Name: f.getASTDeclName(name, c, false),
Name: "C." + name,
}
case C.CXType_Elaborated:
underlying := C.clang_Type_getNamedType(typ)
switch underlying.kind {
case C.CXType_Record:
return f.makeASTType(underlying, pos)
return p.makeASTType(underlying, pos)
case C.CXType_Enum:
return f.makeASTType(underlying, pos)
return p.makeASTType(underlying, pos)
default:
typeKindSpelling := getString(C.clang_getTypeKindSpelling(underlying.kind))
f.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
p.addError(pos, fmt.Sprintf("unknown elaborated type (libclang type kind %s)", typeKindSpelling))
typeName = "<unknown>"
}
case C.CXType_Record:
@@ -746,46 +599,63 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
}
if name == "" {
// Anonymous record, probably inside a typedef.
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
var file C.CXFile
var line C.unsigned
var column C.unsigned
C.clang_getFileLocation(clangLocation, &file, &line, &column, nil)
location := token.Position{
Filename: getString(C.clang_getFileName(file)),
Line: int(line),
Column: int(column),
typeInfo := p.makeASTRecordType(cursor, pos)
if typeInfo.bitfields != nil || typeInfo.unionSize != 0 {
// This record is a union or is a struct with bitfields, so we
// have to declare it as a named type (for getters/setters to
// work).
p.anonStructNum++
cgoName := cgoRecordPrefix + strconv.Itoa(p.anonStructNum)
p.elaboratedTypes[cgoName] = typeInfo
return &ast.Ident{
NamePos: pos,
Name: "C." + cgoName,
}
}
if location.Filename == "" || location.Line == 0 {
// Not sure when this would happen, but protect from it anyway.
f.addError(pos, "could not find file/line information")
}
name = f.getUnnamedDeclName("_Ctype_"+cgoRecordPrefix+"__", location)
return typeInfo.typeExpr
} else {
name = cgoRecordPrefix + name
}
return &ast.Ident{
NamePos: pos,
Name: f.getASTDeclName(name, cursor, false),
cgoName := cgoRecordPrefix + name
if _, ok := p.elaboratedTypes[cgoName]; !ok {
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
p.elaboratedTypes[cgoName] = p.makeASTRecordType(cursor, pos)
}
return &ast.Ident{
NamePos: pos,
Name: "C." + cgoName,
}
}
case C.CXType_Enum:
cursor := C.tinygo_clang_getTypeDeclaration(typ)
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
underlying := C.tinygo_clang_getEnumDeclIntegerType(cursor)
if name == "" {
name = f.getUnnamedDeclName("_Ctype_enum___", cursor)
// anonymous enum
ref := storedRefs.Put(p)
defer storedRefs.Remove(ref)
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
return p.makeASTType(underlying, pos)
} else {
name = "enum_" + name
}
return &ast.Ident{
NamePos: pos,
Name: f.getASTDeclName(name, cursor, false),
// named enum
if _, ok := p.enums[name]; !ok {
ref := storedRefs.Put(p)
defer storedRefs.Remove(ref)
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_enum_visitor), C.CXClientData(ref))
p.enums[name] = enumInfo{
typeExpr: p.makeASTType(underlying, pos),
pos: pos,
}
}
return &ast.Ident{
NamePos: pos,
Name: "C.enum_" + name,
}
}
}
if typeName == "" {
// Report this as an error.
typeSpelling := getString(C.clang_getTypeSpelling(typ))
typeKindSpelling := getString(C.clang_getTypeKindSpelling(typ.kind))
f.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
p.addError(pos, fmt.Sprintf("unknown C type: %v (libclang type kind %s)", typeSpelling, typeKindSpelling))
typeName = "C.<unknown>"
}
return &ast.Ident{
@@ -794,80 +664,9 @@ func (f *cgoFile) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
}
}
// getIntegerType returns an AST node that defines types such as C.int.
func (p *cgoPackage) getIntegerType(name string, cursor clangCursor) *ast.TypeSpec {
pos := p.getCursorPosition(cursor)
// Find a Go type that matches the size and signedness of the given C type.
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(cursor)
var goName string
typeSize := C.clang_Type_getSizeOf(underlyingType)
switch name {
case "C.char":
if typeSize != 1 {
// This happens for some very special purpose architectures
// (DSPs etc.) that are not currently targeted.
// https://www.embecosm.com/2017/04/18/non-8-bit-char-support-in-clang-and-llvm/
p.addError(pos, fmt.Sprintf("unknown char width: %d", typeSize))
}
switch underlyingType.kind {
case C.CXType_Char_S:
goName = "int8"
case C.CXType_Char_U:
goName = "uint8"
}
case "C.schar", "C.short", "C.int", "C.long", "C.longlong":
switch typeSize {
case 1:
goName = "int8"
case 2:
goName = "int16"
case 4:
goName = "int32"
case 8:
goName = "int64"
}
case "C.uchar", "C.ushort", "C.uint", "C.ulong", "C.ulonglong":
switch typeSize {
case 1:
goName = "uint8"
case 2:
goName = "uint16"
case 4:
goName = "uint32"
case 8:
goName = "uint64"
}
}
if goName == "" { // should not happen
p.addError(pos, "internal error: did not find Go type for C type "+name)
goName = "int"
}
// Construct an *ast.TypeSpec for this type.
obj := &ast.Object{
Kind: ast.Typ,
Name: name,
}
spec := &ast.TypeSpec{
Name: &ast.Ident{
NamePos: pos,
Name: name,
Obj: obj,
},
Type: &ast.Ident{
NamePos: pos,
Name: goName,
},
}
obj.Decl = spec
return spec
}
// makeASTRecordType parses a C record (struct or union) and translates it into
// a Go struct type.
func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
fieldList := &ast.FieldList{
Opening: pos,
Closing: pos,
@@ -877,11 +676,11 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
bitfieldNum := 0
ref := storedRefs.Put(struct {
fieldList *ast.FieldList
file *cgoFile
pkg *cgoPackage
inBitfield *bool
bitfieldNum *int
bitfieldList *[]bitfieldInfo
}{fieldList, f, &inBitfield, &bitfieldNum, &bitfieldList})
}{fieldList, p, &inBitfield, &bitfieldNum, &bitfieldList})
defer storedRefs.Remove(ref)
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
renameFieldKeywords(fieldList)
@@ -910,13 +709,13 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
}
if bitfieldList != nil {
// This is valid C... but please don't do this.
f.addError(pos, "bitfield in a union is not supported")
p.addError(pos, "bitfield in a union is not supported")
}
typ := C.tinygo_clang_getCursorType(cursor)
alignInBytes := int64(C.clang_Type_getAlignOf(typ))
sizeInBytes := int64(C.clang_Type_getSizeOf(typ))
if sizeInBytes == 0 {
f.addError(pos, "zero-length union is not supported")
p.addError(pos, "zero-length union is not supported")
}
typeInfo.unionSize = sizeInBytes
typeInfo.unionAlign = alignInBytes
@@ -924,7 +723,7 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
default:
cursorKind := C.tinygo_clang_getCursorKind(cursor)
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
f.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
p.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
return &elaboratedTypeInfo{
typeExpr: &ast.StructType{
Struct: pos,
@@ -938,17 +737,17 @@ func (f *cgoFile) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elabora
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
fieldList *ast.FieldList
file *cgoFile
pkg *cgoPackage
inBitfield *bool
bitfieldNum *int
bitfieldList *[]bitfieldInfo
})
fieldList := passed.fieldList
f := passed.file
p := passed.pkg
inBitfield := passed.inBitfield
bitfieldNum := passed.bitfieldNum
bitfieldList := passed.bitfieldList
pos := f.getCursorPosition(c)
pos := p.getCursorPosition(c)
switch cursorKind := C.tinygo_clang_getCursorKind(c); cursorKind {
case C.CXCursor_FieldDecl:
// Expected. This is a regular field.
@@ -957,7 +756,7 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
return C.CXChildVisit_Continue
default:
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
f.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
p.addError(pos, fmt.Sprintf("expected FieldDecl in struct or union, not %s", cursorKindSpelling))
return C.CXChildVisit_Continue
}
name := getString(C.tinygo_clang_getCursorSpelling(c))
@@ -968,14 +767,14 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
}
typ := C.tinygo_clang_getCursorType(c)
field := &ast.Field{
Type: f.makeASTType(typ, f.getCursorPosition(c)),
Type: p.makeASTType(typ, p.getCursorPosition(c)),
}
offsetof := int64(C.clang_Type_getOffsetOf(C.tinygo_clang_getCursorType(parent), C.CString(name)))
alignOf := int64(C.clang_Type_getAlignOf(typ) * 8)
bitfieldOffset := offsetof % alignOf
if bitfieldOffset != 0 {
if C.tinygo_clang_Cursor_isBitField(c) != 1 {
f.addError(pos, "expected a bitfield")
p.addError(pos, "expected a bitfield")
return C.CXChildVisit_Continue
}
if !*inBitfield {
@@ -1022,6 +821,23 @@ func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientD
return C.CXChildVisit_Continue
}
//export tinygo_clang_enum_visitor
func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
name := getString(C.tinygo_clang_getCursorSpelling(c))
pos := p.getCursorPosition(c)
value := C.tinygo_clang_getEnumConstantDeclValue(c)
p.constants[name] = constantInfo{
expr: &ast.BasicLit{
ValuePos: pos,
Kind: token.INT,
Value: strconv.FormatInt(int64(value), 10),
},
pos: pos,
}
return C.CXChildVisit_Continue
}
//export tinygo_clang_inclusion_visitor
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
+16
View File
@@ -0,0 +1,16 @@
//go:build !byollvm && llvm11
// +build !byollvm,llvm11
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@11/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@11/include
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@11/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
*/
import "C"
+16
View File
@@ -0,0 +1,16 @@
//go:build !byollvm && llvm12
// +build !byollvm,llvm12
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-12/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@12/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@12/include
#cgo freebsd CFLAGS: -I/usr/local/llvm12/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-12/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@12/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@12/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm12/lib -lclang
*/
import "C"
+2 -2
View File
@@ -1,5 +1,5 @@
//go:build !byollvm && !llvm14
// +build !byollvm,!llvm14
//go:build !byollvm && !llvm11 && !llvm12
// +build !byollvm,!llvm11,!llvm12
package cgo
-16
View File
@@ -1,16 +0,0 @@
//go:build !byollvm && llvm14
// +build !byollvm,llvm14
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-14/include
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@14/include
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@14/include
#cgo freebsd CFLAGS: -I/usr/local/llvm14/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-14/lib -lclang
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@14/lib -lclang -lffi
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@14/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm14/lib -lclang
*/
import "C"
+20 -13
View File
@@ -24,16 +24,23 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
type (
C.char uint8
C.schar int8
C.uchar uint8
C.short int16
C.ushort uint16
C.int int32
C.uint uint32
C.long int32
C.ulong uint32
C.longlong int64
C.ulonglong uint64
)
type C.int16_t = int16
type C.int32_t = int32
type C.int64_t = int64
type C.int8_t = int8
type C.uint16_t = uint16
type C.uint32_t = uint32
type C.uint64_t = uint64
type C.uint8_t = uint8
type C.uintptr_t = uintptr
type C.char uint8
type C.int int32
type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
+22 -15
View File
@@ -24,19 +24,26 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
type (
C.char uint8
C.schar int8
C.uchar uint8
C.short int16
C.ushort uint16
C.int int32
C.uint uint32
C.long int32
C.ulong uint32
C.longlong int64
C.ulonglong uint64
)
const C.foo = 3
const C.bar = C.foo
const C.foo = 3
type C.int16_t = int16
type C.int32_t = int32
type C.int64_t = int64
type C.int8_t = int8
type C.uint16_t = uint16
type C.uint32_t = uint32
type C.uint64_t = uint64
type C.uint8_t = uint8
type C.uintptr_t = uintptr
type C.char uint8
type C.int int32
type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
+1 -1
View File
@@ -27,7 +27,7 @@ import "C"
//line errors.go:100
var (
// constant too large
_ C.char = 2 << 10
_ C.uint8_t = 2 << 10
// z member does not exist
_ C.point_t = C.point_t{z: 3}
+24 -18
View File
@@ -6,7 +6,7 @@
// testdata/errors.go:19:26: unexpected token ), expected end of expression
// Type checking errors after CGo processing:
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as C.char value in variable declaration (overflows)
// testdata/errors.go:102: cannot use 2 << 10 (untyped int constant 2048) as uint8 value in variable declaration (overflows)
// testdata/errors.go:105: unknown field z in struct literal
// testdata/errors.go:108: undeclared name: C.SOME_CONST_1
// testdata/errors.go:110: cannot use C.SOME_CONST_3 (untyped int constant 1234) as byte value in variable declaration (overflows)
@@ -38,23 +38,29 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
type (
C.char uint8
C.schar int8
C.uchar uint8
C.short int16
C.ushort uint16
C.int int32
C.uint uint32
C.long int32
C.ulong uint32
C.longlong int64
C.ulonglong uint64
)
type C._Ctype_struct___0 struct {
const C.SOME_CONST_3 = 1234
type C.int16_t = int16
type C.int32_t = int32
type C.int64_t = int64
type C.int8_t = int8
type C.uint16_t = uint16
type C.uint32_t = uint32
type C.uint64_t = uint64
type C.uint8_t = uint8
type C.uintptr_t = uintptr
type C.char uint8
type C.int int32
type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
type C.point_t = struct {
x C.int
y C.int
}
type C.point_t = C._Ctype_struct___0
const C.SOME_CONST_3 = 1234
+21 -14
View File
@@ -29,19 +29,26 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
type (
C.char uint8
C.schar int8
C.uchar uint8
C.short int16
C.ushort uint16
C.int int32
C.uint uint32
C.long int32
C.ulong uint32
C.longlong int64
C.ulonglong uint64
)
const C.BAR = 3
const C.FOO_H = 1
type C.int16_t = int16
type C.int32_t = int32
type C.int64_t = int64
type C.int8_t = int8
type C.uint16_t = uint16
type C.uint32_t = uint32
type C.uint64_t = uint64
type C.uint8_t = uint8
type C.uintptr_t = uintptr
type C.char uint8
type C.int int32
type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
+23 -18
View File
@@ -24,36 +24,41 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
type (
C.char uint8
C.schar int8
C.uchar uint8
C.short int16
C.ushort uint16
C.int int32
C.uint uint32
C.long int32
C.ulong uint32
C.longlong int64
C.ulonglong uint64
)
//export foo
func C.foo(a C.int, b C.int) C.int
var C.foo$funcaddr unsafe.Pointer
//export variadic0
//go:variadic
func C.variadic0()
var C.variadic0$funcaddr unsafe.Pointer
//export variadic2
//go:variadic
func C.variadic2(x C.int, y C.int)
var C.foo$funcaddr unsafe.Pointer
var C.variadic0$funcaddr unsafe.Pointer
var C.variadic2$funcaddr unsafe.Pointer
//go:extern someValue
var C.someValue C.int
type C.int16_t = int16
type C.int32_t = int32
type C.int64_t = int64
type C.int8_t = int8
type C.uint16_t = uint16
type C.uint32_t = uint32
type C.uint64_t = uint64
type C.uint8_t = uint8
type C.uintptr_t = uintptr
type C.char uint8
type C.int int32
type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
+106 -100
View File
@@ -24,126 +24,132 @@ func C.GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
}
type (
C.char uint8
C.schar int8
C.uchar uint8
C.short int16
C.ushort uint16
C.int int32
C.uint uint32
C.long int32
C.ulong uint32
C.longlong int64
C.ulonglong uint64
)
const C.option2A = 20
const C.optionA = 0
const C.optionB = 1
const C.optionC = -5
const C.optionD = -4
const C.optionE = 10
const C.optionF = 11
const C.optionG = 12
const C.unused1 = 5
type C.int16_t = int16
type C.int32_t = int32
type C.int64_t = int64
type C.int8_t = int8
type C.uint16_t = uint16
type C.uint32_t = uint32
type C.uint64_t = uint64
type C.uint8_t = uint8
type C.uintptr_t = uintptr
type C.char uint8
type C.int int32
type C.long int32
type C.longlong int64
type C.schar int8
type C.short int16
type C.uchar uint8
type C.uint uint32
type C.ulong uint32
type C.ulonglong uint64
type C.ushort uint16
type C.bitfield_t = C.struct_4
type C.myIntArray = [10]C.int
type C.myint = C.int
type C._Ctype_struct___0 struct {
type C.option2_t = C.uint
type C.option_t = C.enum_option
type C.point2d_t = struct {
x C.int
y C.int
}
type C.point2d_t = C._Ctype_struct___0
type C.struct_point3d struct {
x C.int
y C.int
z C.int
}
type C.point3d_t = C.struct_point3d
type C.struct_type1 struct {
_type C.int
__type C.int
___type C.int
}
type C.struct_type2 struct{ _type C.int }
type C._Ctype_union___1 struct{ i C.int }
type C.union1_t = C._Ctype_union___1
type C._Ctype_union___2 struct{ $union uint64 }
func (union *C._Ctype_union___2) unionfield_i() *C.int {
return (*C.int)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___2) unionfield_d() *float64 {
return (*float64)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___2) unionfield_s() *C.short {
return (*C.short)(unsafe.Pointer(&union.$union))
}
type C.union3_t = C._Ctype_union___2
type C.union_union2d struct{ $union [2]uint64 }
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
func (union *C.union_union2d) unionfield_d() *[2]float64 {
return (*[2]float64)(unsafe.Pointer(&union.$union))
}
type C.union2d_t = C.union_union2d
type C._Ctype_union___3 struct{ arr [10]C.uchar }
type C.unionarray_t = C._Ctype_union___3
type C._Ctype_union___5 struct{ $union [3]uint32 }
func (union *C._Ctype_union___5) unionfield_area() *C.point2d_t {
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___5) unionfield_solid() *C.point3d_t {
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
}
type C._Ctype_struct___4 struct {
type C.struct_nested_t = struct {
begin C.point2d_t
end C.point2d_t
tag C.int
coord C._Ctype_union___5
coord C.union_2
}
type C.struct_nested_t = C._Ctype_struct___4
type C._Ctype_union___6 struct{ $union [2]uint64 }
func (union *C._Ctype_union___6) unionfield_point() *C.point3d_t {
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___6) unionfield_array() *C.unionarray_t {
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
}
func (union *C._Ctype_union___6) unionfield_thing() *C.union3_t {
return (*C.union3_t)(unsafe.Pointer(&union.$union))
}
type C.union_nested_t = C._Ctype_union___6
type C.enum_option = C.int
type C.option_t = C.enum_option
type C._Ctype_enum___7 = C.uint
type C.option2_t = C._Ctype_enum___7
type C._Ctype_struct___8 struct {
type C.types_t = struct {
f float32
d float64
ptr *C.int
}
type C.types_t = C._Ctype_struct___8
type C.myIntArray = [10]C.int
type C._Ctype_struct___9 struct {
type C.union1_t = struct{ i C.int }
type C.union2d_t = C.union_union2d
type C.union3_t = C.union_1
type C.union_nested_t = C.union_3
type C.unionarray_t = struct{ arr [10]C.uchar }
func (s *C.struct_4) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
func (s *C.struct_4) set_bitfield_a(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
}
func (s *C.struct_4) bitfield_b() C.uchar {
return s.__bitfield_1 >> 5 & 0x1
}
func (s *C.struct_4) set_bitfield_b(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
}
func (s *C.struct_4) bitfield_c() C.uchar {
return s.__bitfield_1 >> 6
}
func (s *C.struct_4) set_bitfield_c(value C.uchar,
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
type C.struct_4 struct {
start C.uchar
__bitfield_1 C.uchar
d C.uchar
e C.uchar
}
type C.struct_point3d struct {
x C.int
y C.int
z C.int
}
type C.struct_type1 struct {
_type C.int
__type C.int
___type C.int
}
type C.struct_type2 struct{ _type C.int }
func (s *C._Ctype_struct___9) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
func (s *C._Ctype_struct___9) set_bitfield_a(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0
}
func (s *C._Ctype_struct___9) bitfield_b() C.uchar {
return s.__bitfield_1 >> 5 & 0x1
}
func (s *C._Ctype_struct___9) set_bitfield_b(value C.uchar) {
s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5
}
func (s *C._Ctype_struct___9) bitfield_c() C.uchar {
return s.__bitfield_1 >> 6
}
func (s *C._Ctype_struct___9) set_bitfield_c(value C.uchar,
func (union *C.union_1) unionfield_i() *C.int { 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)) }
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
type C.union_1 struct{ $union uint64 }
type C.bitfield_t = C._Ctype_struct___9
func (union *C.union_2) unionfield_area() *C.point2d_t {
return (*C.point2d_t)(unsafe.Pointer(&union.$union))
}
func (union *C.union_2) unionfield_solid() *C.point3d_t {
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
}
type C.union_2 struct{ $union [3]uint32 }
func (union *C.union_3) unionfield_point() *C.point3d_t {
return (*C.point3d_t)(unsafe.Pointer(&union.$union))
}
func (union *C.union_3) unionfield_array() *C.unionarray_t {
return (*C.unionarray_t)(unsafe.Pointer(&union.$union))
}
func (union *C.union_3) unionfield_thing() *C.union3_t {
return (*C.union3_t)(unsafe.Pointer(&union.$union))
}
type C.union_3 struct{ $union [2]uint64 }
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
func (union *C.union_union2d) unionfield_d() *[2]float64 {
return (*[2]float64)(unsafe.Pointer(&union.$union))
}
type C.union_union2d struct{ $union [2]uint64 }
type C.enum_option C.int
type C.enum_unused C.uint
+14 -44
View File
@@ -10,7 +10,6 @@ import (
"regexp"
"strings"
"github.com/google/shlex"
"github.com/tinygo-org/tinygo/goenv"
)
@@ -192,6 +191,15 @@ func (c *Config) UseThinLTO() bool {
// through a plugin, but it's too much hassle to set up.
return false
}
if len(parts) >= 2 && strings.HasPrefix(parts[2], "macos") {
// We use an external linker here at the moment.
return false
}
if len(parts) >= 2 && parts[2] == "windows" {
// Linker error (undefined runtime.trackedGlobalsBitmap) when linking
// for Windows. Disable it for now until that's figured out and fixed.
return false
}
// Other architectures support ThinLTO.
return true
}
@@ -279,7 +287,6 @@ func (c *Config) CFlags() []string {
path, _ := c.LibcPath("picolibc")
cflags = append(cflags,
"--sysroot="+path,
"-isystem", filepath.Join(path, "include"), // necessary for Xtensa
"-isystem", filepath.Join(picolibcDir, "include"),
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
)
@@ -386,13 +393,6 @@ func (c *Config) BinaryFormat(ext string) string {
return c.Target.BinaryFormat
}
return "bin"
case ".img":
// Image file. Only defined for the ESP32 at the moment, where it is a
// full (runnable) image that can be used in the Espressif QEMU fork.
if c.Target.BinaryFormat != "" {
return c.Target.BinaryFormat + "-img"
}
return "bin"
case ".hex":
// Similar to bin, but includes the start address and is thus usually a
// better format.
@@ -493,43 +493,13 @@ func (c *Config) WasmAbi() string {
return c.Target.WasmAbi
}
// EmulatorName is a shorthand to get the command for this emulator, something
// like qemu-system-arm or simavr.
func (c *Config) EmulatorName() string {
parts := strings.SplitN(c.Target.Emulator, " ", 2)
if len(parts) > 1 {
return parts[0]
}
return ""
}
// EmulatorFormat returns the binary format for the emulator and the associated
// file extension. An empty string means to pass directly whatever the linker
// produces directly without conversion (usually ELF format).
func (c *Config) EmulatorFormat() (format, fileExt string) {
switch {
case strings.Contains(c.Target.Emulator, "{img}"):
return "img", ".img"
default:
return "", ""
}
}
// Emulator returns a ready-to-run command to run the given binary in an
// emulator. Give it the format (returned by EmulatorFormat()) and the path to
// the compiled binary.
func (c *Config) Emulator(format, binary string) ([]string, error) {
parts, err := shlex.Split(c.Target.Emulator)
if err != nil {
return nil, fmt.Errorf("could not parse emulator command: %w", err)
}
// Emulator returns the emulator target config
func (c *Config) Emulator() []string {
var emulator []string
for _, s := range parts {
s = strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT"))
s = strings.ReplaceAll(s, "{"+format+"}", binary)
emulator = append(emulator, s)
for _, s := range c.Target.Emulator {
emulator = append(emulator, strings.ReplaceAll(s, "{root}", goenv.Get("TINYGOROOT")))
}
return emulator, nil
return emulator
}
type TestConfig struct {
+30 -13
View File
@@ -12,9 +12,11 @@ import (
"path/filepath"
"reflect"
"runtime"
"strconv"
"strings"
"github.com/tinygo-org/tinygo/goenv"
"tinygo.org/x/go-llvm"
)
// Target specification for a given target. Used for bare metal targets.
@@ -42,8 +44,8 @@ type TargetSpec struct {
LDFlags []string `json:"ldflags"`
LinkerScript string `json:"linkerscript"`
ExtraFiles []string `json:"extra-files"`
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
Emulator string `json:"emulator"`
RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum
Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
FlashCommand string `json:"flash-command"`
GDB []string `json:"gdb"`
PortReset string `json:"flash-1200-bps-reset"`
@@ -57,7 +59,6 @@ type TargetSpec struct {
OpenOCDTarget string `json:"openocd-target"`
OpenOCDTransport string `json:"openocd-transport"`
OpenOCDCommands []string `json:"openocd-commands"`
OpenOCDVerify *bool `json:"openocd-verify"` // enable verify when flashing with openocd
JLinkDevice string `json:"jlink-device"`
CodeModel string `json:"code-model"`
RelocationModel string `json:"relocation-model"`
@@ -88,8 +89,19 @@ func (spec *TargetSpec) overrideProperties(child *TargetSpec) {
if !src.IsNil() {
dst.Set(src)
}
case reflect.Slice: // for slices, append the field
dst.Set(reflect.AppendSlice(dst, src))
case reflect.Slice: // for slices...
if src.Len() > 0 { // ... if not empty ...
switch tag := field.Tag.Get("override"); tag {
case "copy":
// copy the field of child to spec
dst.Set(src)
case "append", "":
// or append the field of child to spec
dst.Set(reflect.AppendSlice(dst, src))
default:
panic("override mode must be 'copy' or 'append' (default). I don't know how to '" + tag + "'.")
}
}
default:
panic("unknown field type : " + kind.String())
}
@@ -246,7 +258,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
case "arm":
spec.CPU = "generic"
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
spec.CFlags = append(spec.CFlags, "-fno-unwind-tables")
switch strings.Split(triple, "-")[0] {
case "armv5":
spec.Features = "+armv5t,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp"
@@ -265,7 +277,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
arch := strings.Split(triple, "-")[0]
platformVersion := strings.TrimPrefix(strings.Split(triple, "-")[2], "macosx")
spec.LDFlags = append(spec.LDFlags,
"-flavor", "darwin",
"-flavor", "darwinnew",
"-dead_strip",
"-arch", arch,
"-platform_version", "macos", platformVersion, platformVersion,
@@ -290,8 +302,13 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
"--image-base", "0x400000",
"--gc-sections",
"--no-insert-timestamp",
"--no-dynamicbase",
)
llvmMajor, _ := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
if llvmMajor >= 12 {
// This flag was added in LLVM 12. At the same time, LLVM 12
// switched the default from --dynamicbase to --no-dynamicbase.
spec.LDFlags = append(spec.LDFlags, "--no-dynamicbase")
}
} else {
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
}
@@ -313,20 +330,20 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
case "386":
// amd64 can _usually_ run 32-bit programs, so skip the emulator in that case.
if runtime.GOARCH != "amd64" {
spec.Emulator = "qemu-i386 {}"
spec.Emulator = []string{"qemu-i386"}
}
case "amd64":
spec.Emulator = "qemu-x86_64 {}"
spec.Emulator = []string{"qemu-x86_64"}
case "arm":
spec.Emulator = "qemu-arm {}"
spec.Emulator = []string{"qemu-arm"}
case "arm64":
spec.Emulator = "qemu-aarch64 {}"
spec.Emulator = []string{"qemu-aarch64"}
}
}
}
if goos != runtime.GOOS {
if goos == "windows" {
spec.Emulator = "wine {}"
spec.Emulator = []string{"wine"}
}
}
return &spec, nil
+5
View File
@@ -29,6 +29,7 @@ func TestOverrideProperties(t *testing.T) {
CPU: "baseCpu",
CFlags: []string{"-base-foo", "-base-bar"},
BuildTags: []string{"bt1", "bt2"},
Emulator: []string{"be1", "be2"},
DefaultStackSize: 42,
AutoStackSize: &baseAutoStackSize,
}
@@ -37,6 +38,7 @@ func TestOverrideProperties(t *testing.T) {
GOOS: "",
CPU: "chlidCpu",
CFlags: []string{"-child-foo", "-child-bar"},
Emulator: []string{"ce1", "ce2"},
AutoStackSize: &childAutoStackSize,
DefaultStackSize: 64,
}
@@ -55,6 +57,9 @@ func TestOverrideProperties(t *testing.T) {
if !reflect.DeepEqual(base.BuildTags, []string{"bt1", "bt2"}) {
t.Errorf("Overriding failed : got %v", base.BuildTags)
}
if !reflect.DeepEqual(base.Emulator, []string{"ce1", "ce2"}) {
t.Errorf("Overriding failed : got %v", base.Emulator)
}
if *base.AutoStackSize != false {
t.Errorf("Overriding failed : got %v", base.AutoStackSize)
}
+24
View File
@@ -52,6 +52,30 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
ptr := b.getValue(call.Args[0])
old := b.getValue(call.Args[1])
newVal := b.getValue(call.Args[2])
if strings.HasSuffix(name, "64") {
if strings.HasPrefix(b.Triple, "thumb") {
// Work around a bug in LLVM, at least LLVM 11:
// https://reviews.llvm.org/D95891
// Check for thumbv6m, thumbv7, thumbv7em, and perhaps others.
// See also: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
compareAndSwap := b.mod.NamedFunction("__sync_val_compare_and_swap_8")
if compareAndSwap.IsNil() {
// Declare the function if it isn't already declared.
i64Type := b.ctx.Int64Type()
fnType := llvm.FunctionType(i64Type, []llvm.Type{llvm.PointerType(i64Type, 0), i64Type, i64Type}, false)
compareAndSwap = llvm.AddFunction(b.mod, "__sync_val_compare_and_swap_8", fnType)
}
actualOldValue := b.CreateCall(compareAndSwap, []llvm.Value{ptr, old, newVal}, "")
// The __sync_val_compare_and_swap_8 function returns the old
// value. However, we shouldn't return the old value, we should
// return whether the compare/exchange was successful. This is
// easily done by comparing the returned (actual) old value with
// the expected old value passed to
// __sync_val_compare_and_swap_8.
swapped := b.CreateICmp(llvm.IntEQ, old, actualOldValue, "")
return swapped, true
}
}
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
swapped := b.CreateExtractValue(tuple, 1, "")
return swapped, true
+41 -202
View File
@@ -9,7 +9,6 @@ import (
"go/token"
"go/types"
"math/bits"
"path"
"path/filepath"
"sort"
"strconv"
@@ -77,7 +76,6 @@ type compilerContext struct {
program *ssa.Program
diagnostics []error
astComments map[string]*ast.CommentGroup
embedGlobals map[string][]*loader.EmbedFile
pkg *types.Package
packageDir string // directory for this package
runtimePkg *types.Package
@@ -252,7 +250,6 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
c := newCompilerContext(moduleName, machine, config, dumpSSA)
c.packageDir = pkg.OriginalDir()
c.embedGlobals = pkg.EmbedGlobals
c.pkg = pkg.Pkg
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
c.program = ssaPkg.Prog
@@ -304,7 +301,6 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
}),
)
c.dibuilder.Finalize()
c.dibuilder.Destroy()
}
return c.mod, c.diagnostics
@@ -818,9 +814,7 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
// Global variable.
info := c.getGlobalInfo(member)
global := c.getGlobal(member)
if files, ok := c.embedGlobals[member.Name()]; ok {
c.createEmbedGlobal(member, global, files)
} else if !info.extern {
if !info.extern {
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
global.SetVisibility(llvm.HiddenVisibility)
if info.section != "" {
@@ -854,150 +848,6 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
}
}
// createEmbedGlobal creates an initializer for a //go:embed global variable.
func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Value, files []*loader.EmbedFile) {
switch typ := member.Type().(*types.Pointer).Elem().Underlying().(type) {
case *types.Basic:
// String type.
if typ.Kind() != types.String {
// This is checked at the AST level, so should be unreachable.
panic("expected a string type")
}
if len(files) != 1 {
c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
return
}
strObj := c.getEmbedFileString(files[0])
global.SetInitializer(strObj)
global.SetVisibility(llvm.HiddenVisibility)
case *types.Slice:
if typ.Elem().Underlying().(*types.Basic).Kind() != types.Byte {
// This is checked at the AST level, so should be unreachable.
panic("expected a byte slice")
}
if len(files) != 1 {
c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
return
}
file := files[0]
bufferValue := c.ctx.ConstString(string(file.Data), false)
bufferGlobal := llvm.AddGlobal(c.mod, bufferValue.Type(), c.pkg.Path()+"$embedslice")
bufferGlobal.SetInitializer(bufferValue)
bufferGlobal.SetLinkage(llvm.InternalLinkage)
bufferGlobal.SetAlignment(1)
slicePtr := llvm.ConstInBoundsGEP(bufferGlobal, []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstInt(c.uintptrType, 0, false),
})
sliceLen := llvm.ConstInt(c.uintptrType, file.Size, false)
sliceObj := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
global.SetInitializer(sliceObj)
global.SetVisibility(llvm.HiddenVisibility)
case *types.Struct:
// Assume this is an embed.FS struct:
// https://cs.opensource.google/go/go/+/refs/tags/go1.18.2:src/embed/embed.go;l=148
// It looks like this:
// type FS struct {
// files *file
// }
// Make a slice of the files, as they will appear in the binary. They
// are sorted in a special way to allow for binary searches, see
// src/embed/embed.go for details.
dirset := map[string]struct{}{}
var allFiles []*loader.EmbedFile
for _, file := range files {
allFiles = append(allFiles, file)
dirname := file.Name
for {
dirname, _ = path.Split(path.Clean(dirname))
if dirname == "" {
break
}
if _, ok := dirset[dirname]; ok {
break
}
dirset[dirname] = struct{}{}
allFiles = append(allFiles, &loader.EmbedFile{
Name: dirname,
})
}
}
sort.Slice(allFiles, func(i, j int) bool {
dir1, name1 := path.Split(path.Clean(allFiles[i].Name))
dir2, name2 := path.Split(path.Clean(allFiles[j].Name))
if dir1 != dir2 {
return dir1 < dir2
}
return name1 < name2
})
// Make the backing array for the []files slice. This is a LLVM global.
embedFileStructType := c.getLLVMType(typ.Field(0).Type().(*types.Pointer).Elem().(*types.Slice).Elem())
var fileStructs []llvm.Value
for _, file := range allFiles {
fileStruct := llvm.ConstNull(embedFileStructType)
name := c.createConst(ssa.NewConst(constant.MakeString(file.Name), types.Typ[types.String]))
fileStruct = llvm.ConstInsertValue(fileStruct, name, []uint32{0}) // "name" field
if file.Hash != "" {
data := c.getEmbedFileString(file)
fileStruct = llvm.ConstInsertValue(fileStruct, data, []uint32{1}) // "data" field
}
fileStructs = append(fileStructs, fileStruct)
}
sliceDataInitializer := llvm.ConstArray(embedFileStructType, fileStructs)
sliceDataGlobal := llvm.AddGlobal(c.mod, sliceDataInitializer.Type(), c.pkg.Path()+"$embedfsfiles")
sliceDataGlobal.SetInitializer(sliceDataInitializer)
sliceDataGlobal.SetLinkage(llvm.InternalLinkage)
sliceDataGlobal.SetGlobalConstant(true)
sliceDataGlobal.SetUnnamedAddr(true)
sliceDataGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceDataInitializer.Type()))
// Create the slice object itself.
// Because embed.FS refers to it as *[]embed.file instead of a plain
// []embed.file, we have to store this as a global.
slicePtr := llvm.ConstInBoundsGEP(sliceDataGlobal, []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstInt(c.uintptrType, 0, false),
})
sliceLen := llvm.ConstInt(c.uintptrType, uint64(len(fileStructs)), false)
sliceInitializer := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
sliceGlobal := llvm.AddGlobal(c.mod, sliceInitializer.Type(), c.pkg.Path()+"$embedfsslice")
sliceGlobal.SetInitializer(sliceInitializer)
sliceGlobal.SetLinkage(llvm.InternalLinkage)
sliceGlobal.SetGlobalConstant(true)
sliceGlobal.SetUnnamedAddr(true)
sliceGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceInitializer.Type()))
// Define the embed.FS struct. It has only one field: the files (as a
// *[]embed.file).
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
globalInitializer = llvm.ConstInsertValue(globalInitializer, sliceGlobal, []uint32{0})
global.SetInitializer(globalInitializer)
global.SetVisibility(llvm.HiddenVisibility)
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
}
}
// getEmbedFileString returns the (constant) string object with the contents of
// the given file. This is a llvm.Value of a regular Go string.
func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value {
dataGlobalName := "embed/file_" + file.Hash
dataGlobal := c.mod.NamedGlobal(dataGlobalName)
if dataGlobal.IsNil() {
dataGlobalType := llvm.ArrayType(c.ctx.Int8Type(), int(file.Size))
dataGlobal = llvm.AddGlobal(c.mod, dataGlobalType, dataGlobalName)
}
strPtr := llvm.ConstInBoundsGEP(dataGlobal, []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstInt(c.uintptrType, 0, false),
})
strLen := llvm.ConstInt(c.uintptrType, file.Size, false)
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
}
// createFunction builds the LLVM IR implementation for this function. The
// function must not yet be defined, otherwise this function will create a
// diagnostic.
@@ -1107,10 +957,10 @@ func (b *builder) createFunction() {
} else {
fieldOffsets := b.expandFormalParamOffsets(llvmType)
for i, field := range fields {
expr := b.dibuilder.CreateExpression([]uint64{
0x1000, // DW_OP_LLVM_fragment
fieldOffsets[i] * 8, // offset in bits
b.targetData.TypeAllocSize(field.Type()) * 8, // size in bits
expr := b.dibuilder.CreateExpression([]int64{
0x1000, // DW_OP_LLVM_fragment
int64(fieldOffsets[i]) * 8, // offset in bits
int64(b.targetData.TypeAllocSize(field.Type())) * 8, // size in bits
})
b.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock)
}
@@ -1590,7 +1440,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
return b.emitSV64Call(instr.Args)
case strings.HasPrefix(name, "(device/riscv.CSR)."):
return b.emitCSROperation(instr)
case strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall"):
case strings.HasPrefix(name, "syscall.Syscall"):
return b.createSyscall(instr)
case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
return b.createRawSyscallNoError(instr)
@@ -1619,14 +1469,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
case *ssa.Function:
// Regular function call. No context is necessary.
context = llvm.Undef(b.i8ptrType)
if info.variadic && len(fn.Params) == 0 {
// This matches Clang, see: https://godbolt.org/z/Gqv49xKMq
// Eventually we might be able to eliminate this special case
// entirely. For details, see:
// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
fnType := llvm.FunctionType(callee.Type().ElementType().ReturnType(), nil, false)
callee = llvm.ConstBitCast(callee, llvm.PointerType(fnType, b.funcPtrAddrSpace))
}
case *ssa.MakeClosure:
// A call on a func value, but the callee is trivial to find. For
// example: immediately applied functions.
@@ -1712,11 +1554,7 @@ func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
// 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.
elementSize := c.targetData.TypeAllocSize(elementType)
if elementSize == 0 {
elementSize = 1
}
maxSize := maxPointerValue / elementSize
maxSize := maxPointerValue / c.targetData.TypeAllocSize(elementType)
// len(slice) is an int. Make sure the length remains small enough to fit in
// an int.
@@ -2619,41 +2457,42 @@ func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Va
}
// createConst creates a LLVM constant value from a Go constant.
func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
func (b *builder) createConst(expr *ssa.Const) llvm.Value {
switch typ := expr.Type().Underlying().(type) {
case *types.Basic:
llvmType := c.getLLVMType(typ)
llvmType := b.getLLVMType(typ)
if typ.Info()&types.IsBoolean != 0 {
b := constant.BoolVal(expr.Value)
n := uint64(0)
if constant.BoolVal(expr.Value) {
if b {
n = 1
}
return llvm.ConstInt(llvmType, n, false)
} else if typ.Info()&types.IsString != 0 {
str := constant.StringVal(expr.Value)
strLen := llvm.ConstInt(c.uintptrType, uint64(len(str)), false)
strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
var strPtr llvm.Value
if str != "" {
objname := c.pkg.Path() + "$string"
global := llvm.AddGlobal(c.mod, llvm.ArrayType(c.ctx.Int8Type(), len(str)), objname)
global.SetInitializer(c.ctx.ConstString(str, false))
objname := b.pkg.Path() + "$string"
global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
global.SetInitializer(b.ctx.ConstString(str, false))
global.SetLinkage(llvm.InternalLinkage)
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
global.SetAlignment(1)
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
strPtr = llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero})
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
strPtr = b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
} else {
strPtr = llvm.ConstNull(c.i8ptrType)
strPtr = llvm.ConstNull(b.i8ptrType)
}
strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
return strObj
} else if typ.Kind() == types.UnsafePointer {
if !expr.IsNil() {
value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.i8ptrType)
return llvm.ConstIntToPtr(llvm.ConstInt(b.uintptrType, value, false), b.i8ptrType)
}
return llvm.ConstNull(c.i8ptrType)
return llvm.ConstNull(b.i8ptrType)
} else if typ.Info()&types.IsUnsigned != 0 {
n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
return llvm.ConstInt(llvmType, n, false)
@@ -2664,18 +2503,18 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
n, _ := constant.Float64Val(expr.Value)
return llvm.ConstFloat(llvmType, n)
} else if typ.Kind() == types.Complex64 {
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]))
i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]))
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false))
cplx = b.CreateInsertValue(cplx, r, 0, "")
cplx = b.CreateInsertValue(cplx, i, 1, "")
return cplx
} else if typ.Kind() == types.Complex128 {
r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
cplx = llvm.ConstInsertValue(cplx, r, []uint32{0})
cplx = llvm.ConstInsertValue(cplx, i, []uint32{1})
r := b.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]))
i := b.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]))
cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false))
cplx = b.CreateInsertValue(cplx, r, 0, "")
cplx = b.CreateInsertValue(cplx, i, 1, "")
return cplx
} else {
panic("unknown constant of basic type: " + expr.String())
@@ -2684,35 +2523,35 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
if expr.Value != nil {
panic("expected nil chan constant")
}
return llvm.ConstNull(c.getLLVMType(expr.Type()))
return llvm.ConstNull(b.getLLVMType(expr.Type()))
case *types.Signature:
if expr.Value != nil {
panic("expected nil signature constant")
}
return llvm.ConstNull(c.getLLVMType(expr.Type()))
return llvm.ConstNull(b.getLLVMType(expr.Type()))
case *types.Interface:
if expr.Value != nil {
panic("expected nil interface constant")
}
// Create a generic nil interface with no dynamic type (typecode=0).
fields := []llvm.Value{
llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstPointerNull(c.i8ptrType),
llvm.ConstInt(b.uintptrType, 0, false),
llvm.ConstPointerNull(b.i8ptrType),
}
return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
return llvm.ConstNamedStruct(b.getLLVMRuntimeType("_interface"), fields)
case *types.Pointer:
if expr.Value != nil {
panic("expected nil pointer constant")
}
return llvm.ConstPointerNull(c.getLLVMType(typ))
return llvm.ConstPointerNull(b.getLLVMType(typ))
case *types.Slice:
if expr.Value != nil {
panic("expected nil slice constant")
}
elemType := c.getLLVMType(typ.Elem())
elemType := b.getLLVMType(typ.Elem())
llvmPtr := llvm.ConstPointerNull(llvm.PointerType(elemType, 0))
llvmLen := llvm.ConstInt(c.uintptrType, 0, false)
slice := c.ctx.ConstStruct([]llvm.Value{
llvmLen := llvm.ConstInt(b.uintptrType, 0, false)
slice := b.ctx.ConstStruct([]llvm.Value{
llvmPtr, // backing array
llvmLen, // len
llvmLen, // cap
@@ -2723,7 +2562,7 @@ func (c *compilerContext) createConst(expr *ssa.Const) llvm.Value {
// I believe this is not allowed by the Go spec.
panic("non-nil map constant")
}
llvmType := c.getLLVMType(typ)
llvmType := b.getLLVMType(typ)
return llvm.ConstNull(llvmType)
default:
panic("unknown constant: " + expr.String())
+15 -4
View File
@@ -28,11 +28,18 @@ type testCase struct {
func TestCompiler(t *testing.T) {
t.Parallel()
// Determine LLVM version.
// Check LLVM version.
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
if err != nil {
t.Fatal("could not parse LLVM version:", llvm.Version)
}
if llvmMajor < 11 {
// It is likely this version needs to be bumped in the future.
// The goal is to at least test the LLVM version that's used by default
// in TinyGo and (if possible without too many workarounds) also some
// previous versions.
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
}
// Determine Go minor version (e.g. 16 in go1.16.3).
_, goMinor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
@@ -106,7 +113,6 @@ func TestCompiler(t *testing.T) {
if err != nil {
t.Fatal("failed to create target machine:", err)
}
defer machine.Dispose()
// Load entire program AST into memory.
lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
@@ -215,9 +221,14 @@ func filterIrrelevantIRLines(lines []string) []string {
if strings.HasPrefix(line, "source_filename = ") {
continue
}
if llvmVersion < 14 && strings.HasPrefix(line, "target datalayout = ") {
if llvmVersion < 12 && strings.HasPrefix(line, "attributes ") {
// Ignore attribute groups. These may change between LLVM versions.
// Right now test outputs are for LLVM 12 and higher.
continue
}
if llvmVersion < 13 && strings.HasPrefix(line, "target datalayout = ") {
// The datalayout string may vary betewen LLVM versions.
// Right now test outputs are for LLVM 14 and higher.
// Right now test outputs are for LLVM 13 and higher.
continue
}
out = append(out, line)
+1 -4
View File
@@ -98,10 +98,7 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
case llvm.IntegerTypeKind:
constraints = append(constraints, "r")
case llvm.PointerTypeKind:
// Memory references require a type in LLVM 14, probably as a
// preparation for opaque pointers.
err = b.makeError(instr.Pos(), "support for pointer operands was dropped in TinyGo 0.23")
return s
constraints = append(constraints, "*m")
default:
err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name)
return s
+45 -79
View File
@@ -152,27 +152,6 @@ func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
return structGlobal
}
var basicTypes = [...]string{
types.Bool: "bool",
types.Int: "int",
types.Int8: "int8",
types.Int16: "int16",
types.Int32: "int32",
types.Int64: "int64",
types.Uint: "uint",
types.Uint8: "uint8",
types.Uint16: "uint16",
types.Uint32: "uint32",
types.Uint64: "uint64",
types.Uintptr: "uintptr",
types.Float32: "float32",
types.Float64: "float64",
types.Complex64: "complex64",
types.Complex128: "complex128",
types.String: "string",
types.UnsafePointer: "unsafe.Pointer",
}
// getTypeCodeName returns a name for this type that can be used in the
// interface lowering pass to assign type codes as expected by the reflect
// package. See getTypeCodeNum.
@@ -183,7 +162,48 @@ func getTypeCodeName(t types.Type) string {
case *types.Array:
return "array:" + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
case *types.Basic:
return "basic:" + basicTypes[t.Kind()]
var kind string
switch t.Kind() {
case types.Bool:
kind = "bool"
case types.Int:
kind = "int"
case types.Int8:
kind = "int8"
case types.Int16:
kind = "int16"
case types.Int32:
kind = "int32"
case types.Int64:
kind = "int64"
case types.Uint:
kind = "uint"
case types.Uint8:
kind = "uint8"
case types.Uint16:
kind = "uint16"
case types.Uint32:
kind = "uint32"
case types.Uint64:
kind = "uint64"
case types.Uintptr:
kind = "uintptr"
case types.Float32:
kind = "float32"
case types.Float64:
kind = "float64"
case types.Complex64:
kind = "complex64"
case types.Complex128:
kind = "complex128"
case types.String:
kind = "string"
case types.UnsafePointer:
kind = "unsafeptr"
default:
panic("unknown basic type: " + t.Name())
}
return "basic:" + kind
case *types.Chan:
return "chan:" + getTypeCodeName(t.Elem())
case *types.Interface:
@@ -571,77 +591,23 @@ func signature(sig *types.Signature) string {
if i > 0 {
s += ", "
}
s += typestring(sig.Params().At(i).Type())
s += sig.Params().At(i).Type().String()
}
s += ")"
}
if sig.Results().Len() == 0 {
// keep as-is
} else if sig.Results().Len() == 1 {
s += " " + typestring(sig.Results().At(0).Type())
s += " " + sig.Results().At(0).Type().String()
} else {
s += " ("
for i := 0; i < sig.Results().Len(); i++ {
if i > 0 {
s += ", "
}
s += typestring(sig.Results().At(i).Type())
s += sig.Results().At(i).Type().String()
}
s += ")"
}
return s
}
// typestring returns a stable (human-readable) type string for the given type
// that can be used for interface equality checks. It is almost (but not
// exactly) the same as calling t.String(). The main difference is some
// normalization around `byte` vs `uint8` for example.
func typestring(t types.Type) string {
// See: https://github.com/golang/go/blob/master/src/go/types/typestring.go
switch t := t.(type) {
case *types.Array:
return "[" + strconv.FormatInt(t.Len(), 10) + "]" + typestring(t.Elem())
case *types.Basic:
return basicTypes[t.Kind()]
case *types.Chan:
switch t.Dir() {
case types.SendRecv:
return "chan (" + typestring(t.Elem()) + ")"
case types.SendOnly:
return "chan<- (" + typestring(t.Elem()) + ")"
case types.RecvOnly:
return "<-chan (" + typestring(t.Elem()) + ")"
default:
panic("unknown channel direction")
}
case *types.Interface:
methods := make([]string, t.NumMethods())
for i := range methods {
method := t.Method(i)
methods[i] = method.Name() + signature(method.Type().(*types.Signature))
}
return "interface{" + strings.Join(methods, ";") + "}"
case *types.Map:
return "map[" + typestring(t.Key()) + "]" + typestring(t.Elem())
case *types.Named:
return t.String()
case *types.Pointer:
return "*" + typestring(t.Elem())
case *types.Signature:
return "func" + signature(t)
case *types.Slice:
return "[]" + typestring(t.Elem())
case *types.Struct:
fields := make([]string, t.NumFields())
for i := range fields {
field := t.Field(i)
fields[i] = field.Name() + " " + typestring(field.Type())
if tag := t.Tag(i); tag != "" {
fields[i] += " " + strconv.Quote(tag)
}
}
return "struct{" + strings.Join(fields, ";") + "}"
default:
panic("unknown type: " + t.String())
}
}
+1 -1
View File
@@ -31,7 +31,7 @@ func (c *checker) checkType(t llvm.Type, checked map[llvm.Type]struct{}, special
return fmt.Errorf("type %q uses global context", t.String())
default:
// we used some other context by accident
return fmt.Errorf("type %q uses context %v instead of the main context %v", t.String(), t.Context(), c.ctx)
return fmt.Errorf("type %q uses context %v instead of the main context %v", t.Context(), c.ctx)
}
// if this is a composite type, check the components of the type
-2
View File
@@ -34,7 +34,6 @@ func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm
func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
ctx := t.Context()
targetData := llvm.NewTargetData(mod.DataLayout())
defer targetData.Dispose()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
alloca = CreateEntryBlockAlloca(builder, t, name)
bitcast = builder.CreateBitCast(alloca, i8ptrType, name+".bitcast")
@@ -47,7 +46,6 @@ func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, n
func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value {
ctx := mod.Context()
targetData := llvm.NewTargetData(mod.DataLayout())
defer targetData.Dispose()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
alloca := CreateEntryBlockAlloca(builder, t, name)
+2 -4
View File
@@ -15,9 +15,8 @@ import (
func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needsStackObjects bool, values []llvm.Value) llvm.Value {
ctx := mod.Context()
targetData := llvm.NewTargetData(mod.DataLayout())
defer targetData.Dispose()
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
valueTypes := make([]llvm.Type, len(values))
for i, value := range values {
@@ -128,9 +127,8 @@ func EmitPointerPack(builder llvm.Builder, mod llvm.Module, prefix string, needs
func EmitPointerUnpack(builder llvm.Builder, mod llvm.Module, ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
ctx := mod.Context()
targetData := llvm.NewTargetData(mod.DataLayout())
defer targetData.Dispose()
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
packedType := ctx.StructType(valueTypes, false)
+1 -13
View File
@@ -10,13 +10,6 @@ import (
"tinygo.org/x/go-llvm"
)
// constants for hashmap algorithms; must match src/runtime/hashmap.go
const (
hashmapAlgorithmBinary = iota
hashmapAlgorithmString
hashmapAlgorithmInterface
)
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
// initializing an appropriately sized object.
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
@@ -24,27 +17,22 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
keyType := mapType.Key().Underlying()
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
var llvmKeyType llvm.Type
var alg uint64 // must match values in src/runtime/hashmap.go
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// String keys.
llvmKeyType = b.getLLVMType(keyType)
alg = hashmapAlgorithmString
} else if hashmapIsBinaryKey(keyType) {
// Trivially comparable keys.
llvmKeyType = b.getLLVMType(keyType)
alg = hashmapAlgorithmBinary
} else {
// All other keys. Implemented as map[interface{}]valueType for ease of
// implementation.
llvmKeyType = b.getLLVMRuntimeType("_interface")
alg = hashmapAlgorithmInterface
}
keySize := b.targetData.TypeAllocSize(llvmKeyType)
valueSize := b.targetData.TypeAllocSize(llvmValueType)
llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false)
llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false)
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false)
if expr.Reserve != nil {
sizeHint = b.getValue(expr.Reserve)
var err error
@@ -53,7 +41,7 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
return llvm.Value{}, err
}
}
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint, algEnum}, "")
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
return hashmap, nil
}
-4
View File
@@ -45,11 +45,7 @@ func (s *stdSizes) Alignof(T types.Type) int64 {
if t.Info()&types.IsString != 0 {
return s.PtrSize
}
case *types.Signature:
// Even though functions in tinygo are 2 pointers, they are not 2 pointer aligned
return s.PtrSize
}
a := s.Sizeof(T) // may be 0
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
if a < 1 {
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'basic.go'
source_filename = "basic.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
%main.kv = type { float }
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'channel.go'
source_filename = "channel.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'float.go'
source_filename = "float.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'func.go'
source_filename = "func.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'gc.go'
source_filename = "gc.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'go1.17.go'
source_filename = "go1.17.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'goroutine.go'
source_filename = "goroutine.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
+2 -2
View File
@@ -1,6 +1,6 @@
; ModuleID = 'interface.go'
source_filename = "interface.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
@@ -128,5 +128,5 @@ declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"
attributes #0 = { nounwind }
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
attributes #2 = { "tinygo-methods"="reflect/methods.String() string" }
attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) byte" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) byte" }
attributes #4 = { "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'intrinsics.go'
source_filename = "intrinsics.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'pointer.go'
source_filename = "pointer.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'pragma.go'
source_filename = "pragma.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
@extern_global = external global [0 x i8], align 1
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'slice.go'
source_filename = "slice.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
+1 -1
View File
@@ -1,6 +1,6 @@
; ModuleID = 'string.go'
source_filename = "string.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
%runtime._string = type { i8*, i32 }
+2 -2
View File
@@ -1,6 +1,6 @@
module github.com/tinygo-org/tinygo
go 1.16
go 1.15
require (
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a
@@ -15,5 +15,5 @@ require (
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
gopkg.in/yaml.v2 v2.4.0
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f
tinygo.org/x/go-llvm v0.0.0-20220211075103-ee4aad45c3a1
)
+2 -2
View File
@@ -80,5 +80,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f h1:9/J+NpFTpAhYcbh1mC4dr9W/aAPnwrUh8dmq21xWdSM=
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
tinygo.org/x/go-llvm v0.0.0-20220211075103-ee4aad45c3a1 h1:6G8AxueDdqobCEqQrmHPLaEH1AZ1p6Y7rGElDNT7N98=
tinygo.org/x/go-llvm v0.0.0-20220211075103-ee4aad45c3a1/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
+3 -3
View File
@@ -12,7 +12,7 @@ import (
// Version of TinyGo.
// Update this value before release of new version of software.
const Version = "0.24.0-dev"
const Version = "0.23.0-dev"
var (
// This variable is set at build time using -ldflags parameters.
@@ -58,9 +58,9 @@ func GorootVersionString(goroot string) (string, error) {
return string(data), nil
} else if data, err := ioutil.ReadFile(filepath.Join(
goroot, "src", "internal", "buildcfg", "zbootstrap.go")); err == nil {
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
r := regexp.MustCompile("const version = `(.*)`")
r := regexp.MustCompile("const TheVersion = `(.*)`")
matches := r.FindSubmatch(data)
if len(matches) != 2 {
return "", errors.New("Invalid go version output:\n" + string(data))
+3 -18
View File
@@ -50,17 +50,10 @@ func newRunner(mod llvm.Module, debug bool) *runner {
return &r
}
// Dispose deallocates all alloated LLVM resources.
func (r *runner) dispose() {
r.targetData.Dispose()
r.targetData = llvm.TargetData{}
}
// Run evaluates runtime.initAll function as much as possible at compile time.
// Set debug to true if it should print output while running.
func Run(mod llvm.Module, debug bool) error {
r := newRunner(mod, debug)
defer r.dispose()
initAll := mod.NamedFunction("runtime.initAll")
bb := initAll.EntryBasicBlock()
@@ -128,10 +121,7 @@ func Run(mod llvm.Module, debug bool) error {
r.builder.CreateCall(fn, []llvm.Value{i8undef}, "")
// Make sure that any globals touched by the package
// initializer, won't be accessed by later package initializers.
err := r.markExternalLoad(fn)
if err != nil {
return fmt.Errorf("failed to interpret package %s: %w", r.pkgName, err)
}
r.markExternalLoad(fn)
continue
}
return callErr
@@ -203,7 +193,6 @@ func RunFunc(fn llvm.Value, debug bool) error {
// Create and initialize *runner object.
mod := fn.GlobalParent()
r := newRunner(mod, debug)
defer r.dispose()
initName := fn.Name()
if !strings.HasSuffix(initName, ".init") {
return errorAt(fn, "interp: unexpected function name (expected *.init)")
@@ -299,16 +288,12 @@ func (r *runner) getFunction(llvmFn llvm.Value) *function {
// 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) error {
func (r *runner) markExternalLoad(llvmValue llvm.Value) {
mem := memoryView{r: r}
err := mem.markExternalLoad(llvmValue)
if err != nil {
return err
}
mem.markExternalLoad(llvmValue)
for index, obj := range mem.objects {
if obj.marked > r.objects[index].marked {
r.objects[index].marked = obj.marked
}
}
return nil
}
+1 -13
View File
@@ -3,7 +3,6 @@ package interp
import (
"io/ioutil"
"os"
"strconv"
"strings"
"testing"
@@ -11,12 +10,6 @@ import (
)
func TestInterp(t *testing.T) {
llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
if err != nil {
// Note: this should never happen and if it does, it will always happen
// for a particular build because llvm.Version is a constant.
panic(err)
}
for _, name := range []string{
"basic",
"phi",
@@ -26,10 +19,7 @@ func TestInterp(t *testing.T) {
"revert",
"alloc",
} {
name := name // make local to this closure
if name == "slice-copy" && llvmVersion < 14 {
continue
}
name := name // make tc local to this closure
t.Run(name, func(t *testing.T) {
t.Parallel()
runTest(t, "testdata/"+name)
@@ -40,7 +30,6 @@ func TestInterp(t *testing.T) {
func runTest(t *testing.T, pathPrefix string) {
// Read the input IR.
ctx := llvm.NewContext()
defer ctx.Dispose()
buf, err := llvm.NewMemoryBufferFromFile(pathPrefix + ".ll")
os.Stat(pathPrefix + ".ll") // make sure this file is tracked by `go test` caching
if err != nil {
@@ -50,7 +39,6 @@ func runTest(t *testing.T, pathPrefix string) {
if err != nil {
t.Fatalf("could not load module:\n%v", err)
}
defer mod.Dispose()
// Perform the transform.
err = Run(mod, false)
+4 -32
View File
@@ -81,28 +81,12 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
if inst.opcode != llvm.PHI {
for _, v := range inst.operands {
if v, ok := v.(localValue); ok {
index, ok := fn.locals[v.value]
if !ok {
// This is a localValue that is not local to the
// function. An example would be an inline assembly call
// operand.
isRuntimeInst = true
break
}
localVal := locals[index]
if localVal == nil {
// Trying to read a function-local value before it is
// set.
if localVal := locals[fn.locals[v.value]]; localVal == nil {
return nil, mem, r.errorAt(inst, errors.New("interp: local not defined"))
} else {
operands = append(operands, localVal)
if _, ok := localVal.(localValue); ok {
// The function-local value is still just a
// localValue (which can't be interpreted at compile
// time). Not sure whether this ever happens in
// practice.
isRuntimeInst = true
break
}
continue
}
@@ -235,9 +219,6 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
if err != nil {
return nil, mem, err
}
case callFn.name == "internal/task.Pause":
// Task scheduling isn't possible at compile time.
return nil, mem, r.errorAt(inst, errUnsupportedRuntimeInst)
case callFn.name == "runtime.nanotime" && r.pkgName == "time":
// The time package contains a call to runtime.nanotime.
// This appears to be to work around a limitation in Windows
@@ -945,18 +926,12 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
args := operands[:len(operands)-1]
for _, arg := range args {
if arg.Type().TypeKind() == llvm.PointerTypeKind {
err := mem.markExternalStore(arg)
if err != nil {
return r.errorAt(inst, err)
}
mem.markExternalStore(arg)
}
}
result = r.builder.CreateCall(llvmFn, args, inst.name)
case llvm.Load:
err := mem.markExternalLoad(operands[0])
if err != nil {
return r.errorAt(inst, err)
}
mem.markExternalLoad(operands[0])
result = r.builder.CreateLoad(operands[0], inst.name)
if inst.llvmInst.IsVolatile() {
result.SetVolatile(true)
@@ -965,10 +940,7 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
result.SetOrdering(ordering)
}
case llvm.Store:
err := mem.markExternalStore(operands[1])
if err != nil {
return r.errorAt(inst, err)
}
mem.markExternalStore(operands[1])
result = r.builder.CreateStore(operands[0], operands[1])
if inst.llvmInst.IsVolatile() {
result.SetVolatile(true)
+14 -41
View File
@@ -17,7 +17,6 @@ package interp
import (
"encoding/binary"
"errors"
"fmt"
"math"
"math/big"
"strconv"
@@ -105,28 +104,28 @@ func (mv *memoryView) revert() {
// means that the interpreter can still read from it, but cannot write to it as
// that would mean the external read (done at runtime) reads from a state that
// would not exist had the whole initialization been done at runtime.
func (mv *memoryView) markExternalLoad(llvmValue llvm.Value) error {
return mv.markExternal(llvmValue, 1)
func (mv *memoryView) markExternalLoad(llvmValue llvm.Value) {
mv.markExternal(llvmValue, 1)
}
// markExternalStore marks the given LLVM value as having an external write.
// This means that the interpreter can no longer read from it or write to it, as
// that would happen in a different order than if all initialization were
// happening at runtime.
func (mv *memoryView) markExternalStore(llvmValue llvm.Value) error {
return mv.markExternal(llvmValue, 2)
func (mv *memoryView) markExternalStore(llvmValue llvm.Value) {
mv.markExternal(llvmValue, 2)
}
// markExternal is a helper for markExternalLoad and markExternalStore, and
// should not be called directly.
func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) {
if llvmValue.IsUndef() || llvmValue.IsNull() {
// Null and undef definitely don't contain (valid) pointers.
return nil
return
}
if !llvmValue.IsAInstruction().IsNil() || !llvmValue.IsAArgument().IsNil() {
// These are considered external by default, there is nothing to mark.
return nil
return
}
if !llvmValue.IsAGlobalValue().IsNil() {
@@ -145,10 +144,7 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
// Using mark '2' (which means read/write access) because
// even from an object that is only read from, the resulting
// loaded pointer can be written to.
err := mv.markExternal(initializer, 2)
if err != nil {
return err
}
mv.markExternal(initializer, 2)
}
} else {
// This is a function. Go through all instructions and mark all
@@ -174,10 +170,7 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
for i := 0; i < numOperands; i++ {
// Using mark '2' (which means read/write access)
// because this might be a store instruction.
err := mv.markExternal(inst.Operand(i), 2)
if err != nil {
return err
}
mv.markExternal(inst.Operand(i), 2)
}
}
}
@@ -186,22 +179,9 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
} else if !llvmValue.IsAConstantExpr().IsNil() {
switch llvmValue.Opcode() {
case llvm.IntToPtr, llvm.PtrToInt, llvm.BitCast, llvm.GetElementPtr:
err := mv.markExternal(llvmValue.Operand(0), mark)
if err != nil {
return err
}
case llvm.Add, llvm.Sub, llvm.Mul, llvm.UDiv, llvm.SDiv, llvm.URem, llvm.SRem, llvm.Shl, llvm.LShr, llvm.AShr, llvm.And, llvm.Or, llvm.Xor:
// Integer binary operators. Mark both operands.
err := mv.markExternal(llvmValue.Operand(0), mark)
if err != nil {
return err
}
err = mv.markExternal(llvmValue.Operand(1), mark)
if err != nil {
return err
}
mv.markExternal(llvmValue.Operand(0), mark)
default:
return fmt.Errorf("interp: unknown constant expression '%s'", instructionNameMap[llvmValue.Opcode()])
panic("interp: unknown constant expression")
}
} else if !llvmValue.IsAInlineAsm().IsNil() {
// Inline assembly can modify globals but only exported globals. Let's
@@ -216,25 +196,18 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
numElements := llvmType.StructElementTypesCount()
for i := 0; i < numElements; i++ {
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
err := mv.markExternal(element, mark)
if err != nil {
return err
}
mv.markExternal(element, mark)
}
case llvm.ArrayTypeKind:
numElements := llvmType.ArrayLength()
for i := 0; i < numElements; i++ {
element := llvm.ConstExtractValue(llvmValue, []uint32{uint32(i)})
err := mv.markExternal(element, mark)
if err != nil {
return err
}
mv.markExternal(element, mark)
}
default:
return errors.New("interp: unknown type kind in markExternalValue")
panic("interp: unknown type kind in markExternalValue")
}
}
return nil
}
// hasExternalLoadOrStore returns true if this object has an external load or
+4 -1
View File
@@ -1,6 +1,8 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
@"main$alloc.1" = internal unnamed_addr constant [6 x i8] c"\05\00{\00\00\04", align 8
declare void @runtime.printuint8(i8) local_unnamed_addr
declare void @runtime.printint16(i16) local_unnamed_addr
@@ -15,6 +17,7 @@ entry:
call void @runtime.printuint8(i8 3)
call void @runtime.printuint8(i8 3)
call void @runtime.printint16(i16 5)
call void @runtime.printint16(i16 5)
%int16SliceDst.val = load i16, i16* bitcast ([6 x i8]* @"main$alloc.1" to i16*), align 2
call void @runtime.printint16(i16 %int16SliceDst.val)
ret void
}
+1
Submodule lib/compiler-rt added at 5bc79797e1
-1
View File
@@ -230,7 +230,6 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool {
"device/": false,
"examples/": false,
"internal/": true,
"internal/fuzz/": false,
"internal/bytealg/": false,
"internal/reflectlite/": false,
"internal/task/": false,
+11 -276
View File
@@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"go/ast"
"go/constant"
"go/parser"
"go/scanner"
"go/token"
@@ -16,12 +15,10 @@ import (
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strconv"
"strings"
"unicode"
"github.com/tinygo-org/tinygo/cgo"
"github.com/tinygo-org/tinygo/compileopts"
@@ -64,9 +61,6 @@ type PackageJSON struct {
CgoFiles []string
CFiles []string
// Embedded files
EmbedFiles []string
// Dependency information
Imports []string
ImportMap map[string]string
@@ -83,22 +77,13 @@ type PackageJSON struct {
type Package struct {
PackageJSON
program *Program
Files []*ast.File
FileHashes map[string][]byte
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
CGoHeaders []string // text above 'import "C"' lines
EmbedGlobals map[string][]*EmbedFile
Pkg *types.Package
info types.Info
}
type EmbedFile struct {
Name string
Size uint64
Hash string // hash of the file (as a hex string)
NeedsData bool // true if this file is embedded as a byte slice
Data []byte // contents of this file (only if NeedsData is set)
program *Program
Files []*ast.File
FileHashes map[string][]byte
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
CGoHeaders []string // text above 'import "C"' lines
Pkg *types.Package
info types.Info
}
// Load loads the given package with all dependencies (including the runtime
@@ -152,9 +137,8 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
decoder := json.NewDecoder(buf)
for {
pkg := &Package{
program: p,
FileHashes: make(map[string][]byte),
EmbedGlobals: make(map[string][]*EmbedFile),
program: p,
FileHashes: make(map[string][]byte),
info: types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
@@ -373,15 +357,15 @@ func (p *Package) Check() error {
return nil // already typechecked
}
// Prepare some state used during type checking.
var typeErrors []error
checker := p.program.typeChecker // make a copy, because it will be modified
checker.Error = func(err error) {
typeErrors = append(typeErrors, err)
}
checker.Importer = p
// Do typechecking of the package.
checker.Importer = p
packageName := p.ImportPath
if p == p.program.MainPkg() {
if p.Name != "main" {
@@ -398,12 +382,6 @@ func (p *Package) Check() error {
return Errors{p, typeErrors}
}
p.Pkg = typesPkg
p.extractEmbedLines(checker.Error)
if len(typeErrors) != 0 {
return Errors{p, typeErrors}
}
return nil
}
@@ -462,249 +440,6 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
return files, nil
}
// extractEmbedLines finds all //go:embed lines in the package and matches them
// against EmbedFiles from `go list`.
func (p *Package) extractEmbedLines(addError func(error)) {
for _, file := range p.Files {
// Check for an `import "embed"` line at the start of the file.
// //go:embed lines are only valid if the given file itself imports the
// embed package. It is not valid if it is only imported in a separate
// Go file.
hasEmbed := false
for _, importSpec := range file.Imports {
if importSpec.Path.Value == `"embed"` {
hasEmbed = true
}
}
for _, decl := range file.Decls {
switch decl := decl.(type) {
case *ast.GenDecl:
if decl.Tok != token.VAR {
continue
}
for _, spec := range decl.Specs {
spec := spec.(*ast.ValueSpec)
var doc *ast.CommentGroup
if decl.Lparen == token.NoPos {
// Plain 'var' declaration, like:
// //go:embed hello.txt
// var hello string
doc = decl.Doc
} else {
// Bigger 'var' declaration like:
// var (
// //go:embed hello.txt
// hello string
// )
doc = spec.Doc
}
if doc == nil {
continue
}
// Look for //go:embed comments.
var allPatterns []string
for _, comment := range doc.List {
if comment.Text != "//go:embed" && !strings.HasPrefix(comment.Text, "//go:embed ") {
continue
}
if !hasEmbed {
addError(types.Error{
Fset: p.program.fset,
Pos: comment.Pos() + 2,
Msg: "//go:embed only allowed in Go files that import \"embed\"",
})
// Continue, because otherwise we might run into
// issues below.
continue
}
patterns, err := p.parseGoEmbed(comment.Text[len("//go:embed"):], comment.Slash)
if err != nil {
addError(err)
continue
}
if len(patterns) == 0 {
addError(types.Error{
Fset: p.program.fset,
Pos: comment.Pos() + 2,
Msg: "usage: //go:embed pattern...",
})
continue
}
for _, pattern := range patterns {
// Check that the pattern is well-formed.
// It must be valid: the Go toolchain has already
// checked for invalid patterns. But let's check
// anyway to be sure.
if _, err := path.Match(pattern, ""); err != nil {
addError(types.Error{
Fset: p.program.fset,
Pos: comment.Pos(),
Msg: "invalid pattern syntax",
})
continue
}
allPatterns = append(allPatterns, pattern)
}
}
if len(allPatterns) != 0 {
// This is a //go:embed global. Do a few more checks.
if len(spec.Names) != 1 {
addError(types.Error{
Fset: p.program.fset,
Pos: spec.Names[1].NamePos,
Msg: "//go:embed cannot apply to multiple vars",
})
}
if spec.Values != nil {
addError(types.Error{
Fset: p.program.fset,
Pos: spec.Values[0].Pos(),
Msg: "//go:embed cannot apply to var with initializer",
})
}
globalName := spec.Names[0].Name
globalType := p.Pkg.Scope().Lookup(globalName).Type()
valid, byteSlice := isValidEmbedType(globalType)
if !valid {
addError(types.Error{
Fset: p.program.fset,
Pos: spec.Type.Pos(),
Msg: "//go:embed cannot apply to var of type " + globalType.String(),
})
}
// Match all //go:embed patterns against the embed files
// provided by `go list`.
for _, name := range p.EmbedFiles {
for _, pattern := range allPatterns {
if matchPattern(pattern, name) {
p.EmbedGlobals[globalName] = append(p.EmbedGlobals[globalName], &EmbedFile{
Name: name,
NeedsData: byteSlice,
})
break
}
}
}
}
}
}
}
}
}
// matchPattern returns true if (and only if) the given pattern would match the
// filename. The pattern could also match a parent directory of name, in which
// case hidden files do not match.
func matchPattern(pattern, name string) bool {
// Match this file.
matched, _ := path.Match(pattern, name)
if matched {
return true
}
// Match parent directories.
dir := name
for {
dir, _ = path.Split(dir)
if dir == "" {
return false
}
dir = path.Clean(dir)
if matched, _ := path.Match(pattern, dir); matched {
// Pattern matches the directory.
suffix := name[len(dir):]
if strings.Contains(suffix, "/_") || strings.Contains(suffix, "/.") {
// Pattern matches a hidden file.
// Hidden files are included when listed directly as a
// pattern, but not when they are part of a directory tree.
// Source:
// > If a pattern names a directory, all files in the
// > subtree rooted at that directory are embedded
// > (recursively), except that files with names beginning
// > with . or _ are excluded.
return false
}
return true
}
}
}
// parseGoEmbed is like strings.Fields but for a //go:embed line. It parses
// regular fields and quoted fields (that may contain spaces).
func (p *Package) parseGoEmbed(args string, pos token.Pos) (patterns []string, err error) {
args = strings.TrimSpace(args)
initialLen := len(args)
for args != "" {
patternPos := pos + token.Pos(initialLen-len(args))
switch args[0] {
case '`', '"', '\\':
// Parse the next pattern using the Go scanner.
// This is perhaps a bit overkill, but it does correctly implement
// parsing of the various Go strings.
var sc scanner.Scanner
fset := &token.FileSet{}
file := fset.AddFile("", 0, len(args))
sc.Init(file, []byte(args), nil, 0)
_, tok, lit := sc.Scan()
if tok != token.STRING || sc.ErrorCount != 0 {
// Calculate start of token
return nil, types.Error{
Fset: p.program.fset,
Pos: patternPos,
Msg: "invalid quoted string in //go:embed",
}
}
pattern := constant.StringVal(constant.MakeFromLiteral(lit, tok, 0))
patterns = append(patterns, pattern)
args = strings.TrimLeftFunc(args[len(lit):], unicode.IsSpace)
default:
// The value is just a regular value.
// Split it at the first white space.
index := strings.IndexFunc(args, unicode.IsSpace)
if index < 0 {
index = len(args)
}
pattern := args[:index]
patterns = append(patterns, pattern)
args = strings.TrimLeftFunc(args[len(pattern):], unicode.IsSpace)
}
if _, err := path.Match(patterns[len(patterns)-1], ""); err != nil {
return nil, types.Error{
Fset: p.program.fset,
Pos: patternPos,
Msg: "invalid pattern syntax",
}
}
}
return patterns, nil
}
// isValidEmbedType returns whether the given Go type can be used as a
// //go:embed type. This is only true for embed.FS, strings, and byte slices.
// The second return value indicates that this is a byte slice, and therefore
// the contents of the file needs to be passed to the compiler.
func isValidEmbedType(typ types.Type) (valid, byteSlice bool) {
if typ.Underlying() == types.Typ[types.String] {
// string type
return true, false
}
if sliceType, ok := typ.Underlying().(*types.Slice); ok {
if elemType, ok := sliceType.Elem().Underlying().(*types.Basic); ok && elemType.Kind() == types.Byte {
// byte slice type
return true, true
}
}
if namedType, ok := typ.(*types.Named); ok && namedType.String() == "embed.FS" {
// embed.FS type
return true, false
}
return false, false
}
// Import implements types.Importer. It loads and parses packages it encounters
// along the way, if needed.
func (p *Package) Import(to string) (*types.Package, error) {
+135 -199
View File
@@ -2,7 +2,6 @@ package main
import (
"bytes"
"context"
"encoding/json"
"errors"
"flag"
@@ -200,26 +199,8 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
return false, err
}
// Pass test flags to the test binary.
var flags []string
if testVerbose {
flags = append(flags, "-test.v")
}
if testShort {
flags = append(flags, "-test.short")
}
if testRunRegexp != "" {
flags = append(flags, "-test.run="+testRunRegexp)
}
if testBenchRegexp != "" {
flags = append(flags, "-test.bench="+testBenchRegexp)
}
if testBenchTime != "" {
flags = append(flags, "-test.benchtime="+testBenchTime)
}
passed := false
err = buildAndRun(pkgName, config, os.Stdout, flags, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
err = builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error {
if testCompileOnly || outpath != "" {
// Write test binary to the specified file name.
if outpath == "" {
@@ -235,52 +216,27 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
return nil
}
// Tests are always run in the package directory.
cmd.Dir = result.MainDir
// Wasmtime needs a few extra flags to work.
if config.EmulatorName() == "wasmtime" {
// Add directories to the module root, but skip the current working
// directory which is already added by buildAndRun.
dirs := dirsToModuleRoot(result.MainDir, result.ModuleRoot)
var args []string
for _, d := range dirs[1:] {
args = append(args, "--dir="+d)
}
// create a new temp directory just for this run, announce it to os.TempDir() via TMPDIR
tmpdir, err := ioutil.TempDir("", "tinygotmp")
if err != nil {
return fmt.Errorf("failed to create temporary directory: %w", err)
}
args = append(args, "--dir="+tmpdir, "--env=TMPDIR="+tmpdir)
// TODO: add option to not delete temp dir for debugging?
defer os.RemoveAll(tmpdir)
// Insert new argments at the front of the command line argments.
args = append(args, cmd.Args[1:]...)
cmd.Args = append(cmd.Args[:1:1], args...)
}
// Run the test.
config.Options.Semaphore <- struct{}{}
defer func() {
<-config.Options.Semaphore
}()
start := time.Now()
err = cmd.Run()
var err error
passed, err = runPackageTest(config, stdout, stderr, result, testVerbose, testShort, testRunRegexp, testBenchRegexp, testBenchTime)
if err != nil {
return err
}
duration := time.Since(start)
// Print the result.
importPath := strings.TrimSuffix(result.ImportPath, ".test")
passed = err == nil
if passed {
fmt.Fprintf(stdout, "ok \t%s\t%.3fs\n", importPath, duration.Seconds())
} else {
fmt.Fprintf(stdout, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds())
}
if _, ok := err.(*exec.ExitError); ok {
// Binary exited with a non-zero exit code, which means the test
// failed.
return nil
}
return err
return nil
})
if err, ok := err.(loader.NoTestFilesError); ok {
fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", err.ImportPath)
@@ -303,6 +259,81 @@ func dirsToModuleRoot(maindir, modroot string) []string {
return dirs
}
// runPackageTest runs a test binary that was previously built. The return
// values are whether the test passed and any errors encountered while trying to
// run the binary.
func runPackageTest(config *compileopts.Config, stdout, stderr io.Writer, result builder.BuildResult, testVerbose, testShort bool, testRunRegexp string, testBenchRegexp string, testBenchTime string) (bool, error) {
var cmd *exec.Cmd
emulator := config.Emulator()
if len(emulator) == 0 {
// Run directly.
var flags []string
if testVerbose {
flags = append(flags, "-test.v")
}
if testShort {
flags = append(flags, "-test.short")
}
if testRunRegexp != "" {
flags = append(flags, "-test.run="+testRunRegexp)
}
if testBenchRegexp != "" {
flags = append(flags, "-test.bench="+testBenchRegexp)
}
if testBenchTime != "" {
flags = append(flags, "-test.benchtime="+testBenchTime)
}
cmd = executeCommand(config.Options, result.Binary, flags...)
} else {
// Run in an emulator.
args := append(emulator[1:], result.Binary)
if emulator[0] == "wasmtime" {
// create a new temp directory just for this run, announce it to os.TempDir() via TMPDIR
tmpdir, err := ioutil.TempDir("", "tinygotmp")
if err != nil {
return false, &commandError{"failed to create temporary directory", "tinygotmp", err}
}
args = append(args, "--dir="+tmpdir, "--env=TMPDIR="+tmpdir)
// TODO: add option to not delete temp dir for debugging?
defer os.RemoveAll(tmpdir)
// allow reading from directories up to module root
for _, d := range dirsToModuleRoot(result.MainDir, result.ModuleRoot) {
args = append(args, "--dir="+d)
}
// mark end of wasmtime arguments and start of program ones: --
args = append(args, "--")
if testVerbose {
args = append(args, "-test.v")
}
if testShort {
args = append(args, "-test.short")
}
if testRunRegexp != "" {
args = append(args, "-test.run="+testRunRegexp)
}
if testBenchRegexp != "" {
args = append(args, "-test.bench="+testBenchRegexp)
}
}
cmd = executeCommand(config.Options, emulator[0], args...)
}
cmd.Dir = result.MainDir
cmd.Stdout = stdout
cmd.Stderr = stderr
err := cmd.Run()
if err != nil {
if _, ok := err.(*exec.ExitError); ok {
// Binary exited with a non-zero exit code, which means the test
// failed.
return false, nil
}
return false, &commandError{"failed to run compiled binary", result.Binary, err}
}
return true, nil
}
// Flash builds and flashes the built binary to the given serial port.
func Flash(pkgName, port string, options *compileopts.Options) error {
config, err := builder.NewConfig(options)
@@ -422,11 +453,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
if err != nil {
return err
}
exit := " reset exit"
if config.Target.OpenOCDVerify != nil && *config.Target.OpenOCDVerify {
exit = " verify" + exit
}
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+exit)
args = append(args, "-c", "program "+filepath.ToSlash(result.Binary)+" reset exit")
cmd := executeCommand(config.Options, "openocd", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -483,19 +510,18 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
return err
}
format, fileExt := config.EmulatorFormat()
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
// Find a good way to run GDB.
gdbInterface, openocdInterface := config.Programmer()
switch gdbInterface {
case "msd", "command", "":
emulator := config.EmulatorName()
if emulator != "" {
if emulator == "mgba" {
emulator := config.Emulator()
if len(emulator) != 0 {
if emulator[0] == "mgba" {
gdbInterface = "mgba"
} else if emulator == "simavr" {
} else if emulator[0] == "simavr" {
gdbInterface = "simavr"
} else if strings.HasPrefix(emulator, "qemu-system-") {
} else if strings.HasPrefix(emulator[0], "qemu-system-") {
gdbInterface = "qemu"
} else {
// Assume QEMU as an emulator.
@@ -514,10 +540,6 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
port := ""
var gdbCommands []string
var daemon *exec.Cmd
emulator, err := config.Emulator(format, result.Binary)
if err != nil {
return err
}
switch gdbInterface {
case "native":
// Run GDB directly.
@@ -567,29 +589,33 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
}
case "qemu":
port = ":1234"
emulator := config.Emulator()
// Run in an emulator.
args := append(emulator[1:], "-s", "-S")
args := append(emulator[1:], result.Binary, "-s", "-S")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "qemu-user":
port = ":1234"
emulator := config.Emulator()
// Run in an emulator.
args := append(emulator[1:], "-g", "1234")
args := append(emulator[1:], "-g", "1234", result.Binary)
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "mgba":
port = ":2345"
emulator := config.Emulator()
// Run in an emulator.
args := append(emulator[1:], "-g")
args := append(emulator[1:], result.Binary, "-g")
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "simavr":
port = ":1234"
emulator := config.Emulator()
// Run in an emulator.
args := append(emulator[1:], "-g")
args := append(emulator[1:], "-g", result.Binary)
daemon = executeCommand(config.Options, emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
@@ -634,7 +660,7 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
// Construct and execute a gdb or lldb command.
// By default: gdb -ex run <binary>
// Exit the debugger with Ctrl-D.
params := []string{result.Executable}
params := []string{result.Binary}
switch debugger {
case "gdb":
if port != "" {
@@ -666,9 +692,9 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
err := cmd.Run()
if err != nil {
return &commandError{"failed to run " + cmdName + " with", result.Executable, err}
return &commandError{"failed to run " + cmdName + " with", result.Binary, err}
}
return nil
})
@@ -678,134 +704,44 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
// the options, it will run the program directly on the host or will run it in
// an emulator. For example, -target=wasm will cause the binary to be run inside
// of a WebAssembly VM.
func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error {
func Run(pkgName string, options *compileopts.Options) error {
config, err := builder.NewConfig(options)
if err != nil {
return err
}
return buildAndRun(pkgName, config, os.Stdout, cmdArgs, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error {
return cmd.Run()
})
}
// buildAndRun builds and runs the given program, writing output to stdout and
// errors to os.Stderr. It takes care of emulators (qemu, wasmtime, etc) and
// passes command line arguments and evironment variables in a way appropriate
// for the given emulator.
func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) error {
// 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.
needsEnvInVars := config.GOOS() == "js"
for _, tag := range config.BuildTags() {
if tag == "baremetal" {
needsEnvInVars = true
}
}
var args, env []string
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.
config.Options.GlobalValues = map[string]map[string]string{
"runtime": runtimeGlobals,
}
}
} else if config.EmulatorName() == "wasmtime" {
// Wasmtime needs some special flags to pass environment variables
// and allow reading from the current directory.
args = append(args, "--dir=.")
for _, v := range environmentVars {
args = append(args, "--env", v)
}
if len(cmdArgs) != 0 {
// mark end of wasmtime arguments and start of program ones: --
args = append(args, "--")
args = append(args, cmdArgs...)
}
} else {
// Pass environment variables and command line parameters as usual.
// This also works on qemu-aarch64 etc.
args = cmdArgs
env = environmentVars
}
format, fileExt := config.EmulatorFormat()
return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error {
// If needed, set a timeout on the command. This is done in tests so
// they don't waste resources on a stalled test.
var ctx context.Context
if timeout != 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(context.Background(), timeout)
defer cancel()
}
// Set up the command.
var name string
if config.Target.Emulator == "" {
name = result.Binary
} else {
emulator, err := config.Emulator(format, result.Binary)
return builder.Build(pkgName, ".elf", config, func(result builder.BuildResult) error {
emulator := config.Emulator()
if len(emulator) == 0 {
// Run directly.
cmd := executeCommand(config.Options, result.Binary)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return err
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
// Workaround for QEMU which always exits with an error.
return nil
}
return &commandError{"failed to run compiled binary", result.Binary, err}
}
name = emulator[0]
emuArgs := append([]string(nil), emulator[1:]...)
args = append(emuArgs, args...)
}
var cmd *exec.Cmd
if ctx != nil {
cmd = exec.CommandContext(ctx, name, args...)
return nil
} else {
cmd = exec.Command(name, args...)
}
cmd.Env = env
// Configure stdout/stderr. The stdout may go to a buffer, not a real
// stdout.
cmd.Stdout = stdout
cmd.Stderr = os.Stderr
if config.EmulatorName() == "simavr" {
cmd.Stdout = nil // don't print initial load commands
cmd.Stderr = stdout
}
// If this is a test, reserve CPU time for it so that increased
// parallelism doesn't blow up memory usage. If this isn't a test but
// simply `tinygo run`, then it is practically a no-op.
config.Options.Semaphore <- struct{}{}
defer func() {
<-config.Options.Semaphore
}()
// Run binary.
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(cmd.Path, cmd.Args...)
}
err := run(cmd, result)
if err != nil {
if ctx != nil && ctx.Err() == context.DeadlineExceeded {
stdout.Write([]byte(fmt.Sprintf("--- timeout of %s exceeded, terminating...\n", timeout)))
err = ctx.Err()
// Run in an emulator.
args := append(emulator[1:], result.Binary)
cmd := executeCommand(config.Options, emulator[0], args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
if err, ok := err.(*exec.ExitError); ok && err.Exited() {
// Workaround for QEMU which always exits with an error.
return nil
}
return &commandError{"failed to run emulator with", result.Binary, err}
}
return &commandError{"failed to run compiled binary", result.Binary, err}
return nil
}
return nil
})
}
@@ -1471,13 +1407,13 @@ func main() {
handleCompilerError(err)
}
case "run":
if flag.NArg() < 1 {
if flag.NArg() != 1 {
fmt.Fprintln(os.Stderr, "No package specified.")
usage(command)
os.Exit(1)
}
pkgName := filepath.ToSlash(flag.Arg(0))
err := Run(pkgName, options, flag.Args()[1:])
err := Run(pkgName, options)
handleCompilerError(err)
case "test":
var pkgNames []string
@@ -1573,7 +1509,7 @@ func main() {
os.Exit(1)
return
}
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == nil {
// This doesn't look like a regular target file, but rather like
// a parent target (such as targets/cortex-m.json).
continue
+143 -48
View File
@@ -6,6 +6,7 @@ package main
import (
"bufio"
"bytes"
"context"
"errors"
"flag"
"fmt"
@@ -13,6 +14,7 @@ import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"
@@ -48,7 +50,6 @@ func TestBuild(t *testing.T) {
"calls.go",
"cgo/",
"channel.go",
"embed/",
"float.go",
"gc.go",
"goroutines.go",
@@ -65,6 +66,7 @@ func TestBuild(t *testing.T) {
"stdlib.go",
"string.go",
"structs.go",
"testing.go",
"zeroalloc.go",
}
_, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT"))
@@ -74,11 +76,6 @@ func TestBuild(t *testing.T) {
if minor >= 17 {
tests = append(tests, "go1.17.go")
}
if minor >= 18 {
tests = append(tests, "testing_go118.go")
} else {
tests = append(tests, "testing.go")
}
if *testTarget != "" {
// This makes it possible to run one specific test (instead of all),
@@ -147,7 +144,46 @@ func TestBuild(t *testing.T) {
// LIBCLANG FATAL ERROR: Cannot select: t3: i16 = JumpTable<0>
// This bug is non-deterministic.
t.Skip("skipped due to non-deterministic backend bugs")
runPlatTests(optionsFromTarget("simavr", sema), tests, t)
var avrTests []string
for _, t := range tests {
switch t {
case "atomic.go":
// Requires GCC 11.2.0 or above for interface comparison.
// https://github.com/gcc-mirror/gcc/commit/f30dd607669212de135dec1f1d8a93b8954c327c
case "reflect.go":
// Reflect tests do not work due to type code issues.
case "gc.go":
// Does not pass due to high mark false positive rate.
case "json.go", "stdlib.go", "testing.go":
// Breaks interp.
case "map.go":
// Reflect size calculation crashes.
case "binop.go":
// Interface comparison results are inverted.
case "channel.go":
// Freezes after recv from closed channel.
case "float.go", "math.go", "print.go":
// Stuck in runtime.printfloat64.
case "interface.go":
// Several comparison tests fail.
case "cgo/":
// CGo does not work on AVR.
default:
avrTests = append(avrTests, t)
}
}
runPlatTests(optionsFromTarget("simavr", sema), avrTests, t)
})
if runtime.GOOS == "linux" {
@@ -179,44 +215,13 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
}
for _, name := range tests {
if options.Target == "simavr" {
// Not all tests are currently supported on AVR.
// Skip the ones that aren't.
switch name {
case "reflect.go":
// Reflect tests do not work due to type code issues.
continue
case "gc.go":
// Does not pass due to high mark false positive rate.
continue
case "json.go", "stdlib.go", "testing.go", "testing_go118.go":
// Breaks interp.
continue
case "channel.go":
// Freezes after recv from closed channel.
continue
case "math.go":
// Stuck somewhere, not sure what's happening.
continue
case "cgo/":
// CGo does not work on AVR.
continue
default:
}
}
name := name // redefine to avoid race condition
t.Run(name, func(t *testing.T) {
t.Parallel()
runTest(name, options, t, nil, nil)
})
}
if !strings.HasPrefix(spec.Emulator, "simavr ") {
if len(spec.Emulator) == 0 || spec.Emulator[0] != "simavr" {
t.Run("env.go", func(t *testing.T) {
t.Parallel()
runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
@@ -250,8 +255,8 @@ func emuCheck(t *testing.T, options compileopts.Options) {
if err != nil {
t.Fatal("failed to load target spec:", err)
}
if spec.Emulator != "" {
_, err := exec.LookPath(strings.SplitN(spec.Emulator, " ", 2)[0])
if len(spec.Emulator) != 0 {
_, err := exec.LookPath(spec.Emulator[0])
if err != nil {
if errors.Is(err, exec.ErrNotFound) {
t.Skipf("emulator not installed: %q", spec.Emulator[0])
@@ -315,27 +320,117 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
t.Fatal("could not read expected output file:", err)
}
config, err := builder.NewConfig(&options)
// Create a temporary directory for test output files.
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(&options)
if err != nil {
t.Fatal(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,
}
}
}
// Build the test binary.
stdout := &bytes.Buffer{}
err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
return cmd.Run()
})
binary := filepath.Join(tmpdir, "test")
if spec.GOOS == "windows" {
binary += ".exe"
}
err = Build("./"+path, binary, &options)
if err != nil {
printCompilerError(t.Log, err)
t.Fail()
return
}
// Reserve CPU time for the test to run.
// This attempts to ensure that the test is not CPU-starved.
options.Semaphore <- struct{}{}
defer func() { <-options.Semaphore }()
// Create the test command, taking care of emulators etc.
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
var cmd *exec.Cmd
// make sure any special vars in the emulator definition are rewritten
config := compileopts.Config{Target: spec}
emulator := config.Emulator()
if len(emulator) == 0 {
cmd = exec.CommandContext(ctx, binary)
} else {
args := append(emulator[1:], binary)
cmd = exec.CommandContext(ctx, emulator[0], args...)
}
if len(emulator) != 0 && emulator[0] == "wasmtime" {
// Allow reading from the current directory.
cmd.Args = append(cmd.Args, "--dir=.")
for _, v := range environmentVars {
cmd.Args = append(cmd.Args, "--env", v)
}
cmd.Args = append(cmd.Args, cmdArgs...)
} else {
if !needsEnvInVars {
cmd.Args = append(cmd.Args, cmdArgs...) // works on qemu-aarch64 etc
cmd.Env = append(cmd.Env, environmentVars...)
}
}
// Run the test.
stdout := &bytes.Buffer{}
if len(emulator) != 0 && emulator[0] == "simavr" {
cmd.Stdout = os.Stderr
cmd.Stderr = stdout
} else {
cmd.Stdout = stdout
cmd.Stderr = os.Stderr
}
err = cmd.Start()
if err != nil {
t.Fatal("failed to start:", err)
}
err = cmd.Wait()
if cerr := ctx.Err(); cerr == context.DeadlineExceeded {
stdout.WriteString("--- test ran too long, terminating...\n")
err = cerr
}
// putchar() prints CRLF, convert it to LF.
actual := bytes.Replace(stdout.Bytes(), []byte{'\r', '\n'}, []byte{'\n'}, -1)
expected = bytes.Replace(expected, []byte{'\r', '\n'}, []byte{'\n'}, -1) // for Windows
if config.EmulatorName() == "simavr" {
if len(emulator) != 0 && emulator[0] == "simavr" {
// Strip simavr log formatting.
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1)
+68 -164
View File
@@ -8,7 +8,6 @@
package nxp
import (
"device/arm"
"runtime/volatile"
"unsafe"
)
@@ -379,6 +378,30 @@ func (clk Clock) setCcm(value uint32) {
}
}
func setSysPfd(value ...uint32) {
for i, val := range value {
pfd528 := CCM_ANALOG.PFD_528.Get() &
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
}
}
func setUsb1Pfd(value ...uint32) {
for i, val := range value {
pfd480 := CCM_ANALOG.PFD_480.Get() &
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
}
}
// PLL configuration for ARM
type ClockConfigArmPll struct {
LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2.
@@ -449,178 +472,59 @@ func (cfg ClockConfigSysPll) Configure(pfd ...uint32) {
setSysPfd(pfd...)
}
func setSysPfd(value ...uint32) {
for i, val := range value {
pfd528 := CCM_ANALOG.PFD_528.Get() &
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
}
}
// PHY configuration for USB HS
type ClockConfigUsbPhy struct {
Instance uint8 // USB PHY number (1 or 2)
XtalFreq uint32 // External reference clock frequency (Hz)
DCal uint32 // Decode to trim nominal 17.78mA current source
TxCal45DP uint32 // Decode to trim nominal 45-Ohm series Rp on USB D+
TxCal45DM uint32 // Decode to trim nominal 45-Ohm series Rp on USB D-
PllConfig ClockConfigUsbPll
}
// Configure initializes the USB HS (480 Mbit/s) PHY and PLL clocks, including
// the USB +3V regulator (PMU), for use as either USB host or device.
func (cfg ClockConfigUsbPhy) Configure() {
var (
usb *USB_Type
phy *USBPHY_Type
chrgDetectReg *volatile.Register32
chrgDetectMsk uint32
)
// Select appropriate peripherals based on receiver Instance
switch cfg.Instance {
case 1:
usb = USB1 // Select USB1 HS PHY/PLL
phy = USBPHY1 //
chrgDetectReg = &USB_ANALOG.USB1_CHRG_DETECT_SET
chrgDetectMsk = USB_ANALOG_USB1_CHRG_DETECT_SET_CHK_CHRG_B |
USB_ANALOG_USB1_CHRG_DETECT_SET_EN_B
case 2:
usb = USB2 // Select USB2 HS PHY/PLL
phy = USBPHY2 //
chrgDetectReg = &USB_ANALOG.USB2_CHRG_DETECT_SET
chrgDetectMsk = USB_ANALOG_USB2_CHRG_DETECT_SET_CHK_CHRG_B |
USB_ANALOG_USB2_CHRG_DETECT_SET_EN_B
default:
panic("nxp: invalid USB PHY")
}
// Configure and enable USB PLL clocks
cfg.PllConfig.Configure()
// Release PHY from reset
phy.CTRL.ClearBits(USBPHY_CTRL_SFTRST)
phy.CTRL.ClearBits(USBPHY_CTRL_CLKGATE)
// Enable power to USB PHY
phy.PWD.Set(0)
phy.CTRL.SetBits(USBPHY_CTRL_ENAUTOCLR_PHY_PWD | USBPHY_CTRL_ENAUTOCLR_CLKGATE |
// enable support for low-speed device connection, direct and indirect (hub)
USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3)
// Enable USB HS clocks gate
ClockIpUsbOh3.Enable(true)
// Reset USB peripheral
usb.USBCMD.SetBits(USB_USBCMD_RST)
// Add a delay after RST to ensure there is a USB D+ pullup sequence
nopDelay(400000)
// Enable USB LDO
PMU.REG_3P0.Set((PMU.REG_3P0.Get() & ^uint32(PMU_REG_3P0_OUTPUT_TRG_Msk)) |
(0x17 << PMU_REG_3P0_OUTPUT_TRG_Pos) | PMU_REG_3P0_ENABLE_LINREG)
// check whether we are connected to USB charger
chrgDetectReg.Set(chrgDetectMsk)
// Decode to trim nominal 17.78mA source for HS TX on USB D+/D-
phy.TX.Set((phy.TX.Get() &
^uint32(USBPHY_TX_D_CAL_Msk|USBPHY_TX_TXCAL45DN_Msk|USBPHY_TX_TXCAL45DP_Msk)) |
((cfg.DCal << USBPHY_TX_D_CAL_Pos) & USBPHY_TX_D_CAL_Msk) |
((cfg.TxCal45DM << USBPHY_TX_TXCAL45DN_Pos) & USBPHY_TX_TXCAL45DN_Msk) |
((cfg.TxCal45DP << USBPHY_TX_TXCAL45DP_Pos) & USBPHY_TX_TXCAL45DP_Msk))
}
// PLL configuration for USB
type ClockConfigUsbPll struct {
Instance uint8 // USB PLL number (1 or 2)
LoopDivider uint8 // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
Src uint8 // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
Pfd []uint32 // Phase fractional divisors (len=4, or nil for boot default)
Instance uint8 // USB PLL number (1 or 2)
LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22
Src uint8 // Pll clock source, reference _clock_pll_clk_src
}
func (cfg ClockConfigUsbPll) Configure() {
// select USB peripheral registers based on receiver's Instance
switch cfg.Instance {
case 1: // USB1 PLL
if CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_ENABLE) {
// PLL already configured, enable USB clocks
CCM_ANALOG.PLL_USB1.SetBits(CCM_ANALOG_PLL_USB1_EN_USB_CLKS)
} else {
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) &
CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB1.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB1_BYPASS | src)
// reconfigure PLL
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) &
CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk
CCM_ANALOG.PLL_USB1.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB1_ENABLE | CCM_ANALOG_PLL_USB1_POWER |
CCM_ANALOG_PLL_USB1_EN_USB_CLKS | sel)
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK) {
}
// disable bypass
CCM_ANALOG.PLL_USB1.ClearBits(CCM_ANALOG_PLL_USB1_BYPASS)
func (cfg ClockConfigUsbPll) Configure(pfd ...uint32) {
// update PFDs (if provided)
if nil != cfg.Pfd {
setUsb1Pfd(cfg.Pfd...)
}
switch cfg.Instance {
case 1:
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB1.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB1_BYPASS_Msk | src)
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)
CCM_ANALOG.PLL_USB1_SET.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB1_ENABLE_Msk | CCM_ANALOG_PLL_USB1_POWER_Msk |
CCM_ANALOG_PLL_USB1_EN_USB_CLKS_Msk | sel)
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK_Msk) {
}
case 2: // USB2 PLL
if CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_ENABLE) {
// PLL already configured, enable USB clocks
CCM_ANALOG.PLL_USB2.SetBits(CCM_ANALOG_PLL_USB2_EN_USB_CLKS)
} else {
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) &
CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB2_BYPASS | src)
// reconfigure PLL
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) &
CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB2_ENABLE | CCM_ANALOG_PLL_USB2_POWER |
CCM_ANALOG_PLL_USB2_EN_USB_CLKS | sel)
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK) {
}
// disable bypass
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS)
// disable bypass
CCM_ANALOG.PLL_USB1_CLR.Set(CCM_ANALOG_PLL_USB1_BYPASS_Msk)
// update PFDs after update
setUsb1Pfd(pfd...)
case 2:
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB2_BYPASS_Msk | src)
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB2_ENABLE_Msk | CCM_ANALOG_PLL_USB2_POWER_Msk |
CCM_ANALOG_PLL_USB2_EN_USB_CLKS_Msk | sel)
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK_Msk) {
}
// disable bypass
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS_Msk)
default:
panic("nxp: invalid USB PLL")
}
}
func setUsb1Pfd(value ...uint32) {
for i, val := range value {
pfd480 := CCM_ANALOG.PFD_480.Get() &
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
}
}
// We cannot use the sleep timer from this context (import cycle), but we need
// an approximate method to spin CPU cycles for short periods of time.
// go:inline
func nopDelay(cycles uint32) {
for i := uint32(0); i < cycles; i++ {
arm.Asm(`nop`)
}
}
+47 -101
View File
@@ -142,14 +142,18 @@ func (mpu *MPU_Type) Enable(enable bool) {
if enable {
mpu.CTRL.Set(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk)
SystemControl.SHCSR.SetBits(SCB_SHCSR_MEMFAULTENA_Msk)
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
enableDcache(true)
enableIcache(true)
} else {
enableIcache(false)
enableDcache(false)
arm.Asm("dmb 0xF")
arm.AsmFull(`
dmb 0xF
`, nil)
SystemControl.SHCSR.ClearBits(SCB_SHCSR_MEMFAULTENA_Msk)
mpu.CTRL.ClearBits(MPU_CTRL_ENABLE_Msk)
}
@@ -184,21 +188,31 @@ func (mpu *MPU_Type) SetRASR(size RegionSize, access AccessPerms, ext Extension,
func enableIcache(enable bool) {
if enable != SystemControl.CCR.HasBits(SCB_CCR_IC_Msk) {
if enable {
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
SystemControl.ICIALLU.Set(0)
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
SystemControl.CCR.SetBits(SCB_CCR_IC_Msk)
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
} else {
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
SystemControl.CCR.ClearBits(SCB_CCR_IC_Msk)
SystemControl.ICIALLU.Set(0)
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
}
}
@@ -213,7 +227,9 @@ func enableDcache(enable bool) {
if enable != SystemControl.CCR.HasBits(SCB_CCR_DC_Msk) {
if enable {
SystemControl.CSSELR.Set(0)
arm.Asm("dsb 0xF")
arm.AsmFull(`
dsb 0xF
`, nil)
ccsidr := SystemControl.CCSIDR.Get()
sets := (ccsidr & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos
for sets != 0 {
@@ -226,15 +242,23 @@ func enableDcache(enable bool) {
}
sets--
}
arm.Asm("dsb 0xF")
arm.AsmFull(`
dsb 0xF
`, nil)
SystemControl.CCR.SetBits(SCB_CCR_DC_Msk)
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
} else {
SystemControl.CSSELR.Set(0)
arm.Asm("dsb 0xF")
arm.AsmFull(`
dsb 0xF
`, nil)
SystemControl.CCR.ClearBits(SCB_CCR_DC_Msk)
arm.Asm("dsb 0xF")
arm.AsmFull(`
dsb 0xF
`, nil)
dcacheCcsidr.Set(SystemControl.CCSIDR.Get())
dcacheSets.Set((dcacheCcsidr.Get() & SCB_CCSIDR_NUMSETS_Msk) >> SCB_CCSIDR_NUMSETS_Pos)
for dcacheSets.Get() != 0 {
@@ -247,88 +271,10 @@ func enableDcache(enable bool) {
}
dcacheSets.Set(dcacheSets.Get() - 1)
}
arm.Asm("dsb 0xF")
arm.Asm("isb 0xF")
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
}
}
// FlushDcache flushes data from cache to memory
//
// Normally FlushDcache is used when metadata written to memory will be used by
// a DMA or a bus-controller peripheral. Any data in the cache is written to
// memory. A copy remains in the cache, so this is typically used with special
// fields you will want to quickly access in the future. For data transmission,
// use FlushDeleteDcache.
//go:inline
func FlushDcache(addr, size uintptr) {
location := addr & 0xFFFFFFE0
endAddr := addr + size
arm.AsmFull(`
dsb 0xF
`, nil)
for {
SystemControl.DCCMVAC.Set(uint32(location))
location += 32
if location >= endAddr {
break
}
}
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
// DeleteDcache deletes data from the cache, without touching memory.
//
// Normally DeleteDcache is used before receiving data via DMA or from
// bus-controller peripherals which write to memory. You want to delete anything
// the cache may have stored, so your next read is certain to access the
// physical memory.
//go:inline
func DeleteDcache(addr, size uintptr) {
location := addr & 0xFFFFFFE0
endAddr := addr + size
arm.AsmFull(`
dsb 0xF
`, nil)
for {
SystemControl.DCIMVAC.Set(uint32(location))
location += 32
if location >= endAddr {
break
}
}
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
// FlushDeleteDcache flushes data from cache to memory, and delete it from the
// cache
//
// Normally FlushDeleteDcache is used when transmitting data via DMA or
// bus-controller peripherals which read from memory. You want any cached data
// written to memory, and then removed from the cache, because you no longer
// need to access the data after transmission.
//go:inline
func FlushDeleteDcache(addr, size uintptr) {
location := addr & 0xFFFFFFE0
endAddr := addr + size
arm.AsmFull(`
dsb 0xF
`, nil)
for {
SystemControl.DCCIMVAC.Set(uint32(location))
location += 32
if location >= endAddr {
break
}
}
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
-49
View File
@@ -1,49 +0,0 @@
// Reads multiple rp2040 ADC channels concurrently. Including the internal temperature sensor
package main
import (
"fmt"
"machine"
"time"
)
type celsius float32
func (c celsius) String() string {
return fmt.Sprintf("%4.1f℃", c)
}
// rp2040 ADC is 12 bits. Reading are shifted <<4 to fill the 16-bit range.
var adcReading [3]uint16
func readADC(a machine.ADC, w time.Duration, i int) {
for {
adcReading[i] = a.Get()
time.Sleep(w)
}
}
func main() {
machine.InitADC()
a0 := machine.ADC{machine.ADC0} // GPIO26 input
a1 := machine.ADC{machine.ADC1} // GPIO27 input
a2 := machine.ADC{machine.ADC2} // GPIO28 input
t := machine.ADC_TEMP_SENSOR // Internal Temperature sensor
// Configure sets the GPIOs to PinAnalog mode
a0.Configure(machine.ADCConfig{})
a1.Configure(machine.ADCConfig{})
a2.Configure(machine.ADCConfig{})
// Configure powers on the temperature sensor
t.Configure(machine.ADCConfig{})
// Safe to read concurrently
go readADC(a0, 10*time.Millisecond, 0)
go readADC(a1, 17*time.Millisecond, 1)
go readADC(a2, 29*time.Millisecond, 2)
for {
fmt.Printf("ADC0: %5d ADC1: %5d ADC2: %5d Temp: %v\n\r", adcReading[0], adcReading[1], adcReading[2], celsius(float32(t.ReadTemperature())/1000))
time.Sleep(1000 * time.Millisecond)
}
}
+31
View File
@@ -0,0 +1,31 @@
// This is a echo console running on the os.Stdin and os.Stdout.
// Stdin and os.Stdout are connected to machine.Serial in the baremetal target.
//
// Serial can be switched with the -serial option as follows
// 1. tinygo flash -target wioterminal -serial usb examples/echo2
// 2. tinygo flash -target wioterminal -serial uart examples/echo2
//
// This example will also work with standard Go.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
fmt.Printf("Echo console enabled. Type something then press enter:\r\n")
scanner := bufio.NewScanner(os.Stdin)
for {
msg := ""
fmt.Scanf("%s\n", &msg)
fmt.Printf("You typed (scanf) : %s\r\n", msg)
if scanner.Scan() {
fmt.Printf("You typed (scanner) : %s\r\n", scanner.Text())
}
}
}
View File
-258
View File
@@ -1,258 +0,0 @@
package main
import (
"machine"
"machine/usb"
"time"
)
var keyboard = machine.USB.Keyboard()
func main() {
for !machine.USB.Ready() {
}
println("USB HID keyboard demo")
for {
time.Sleep(5 * time.Second)
// Open a new text editor
keyboard.Down(usb.KeyModifierAlt)
keyboard.Press(usb.KeySpace)
keyboard.Up(usb.KeyModifierAlt)
time.Sleep(2 * time.Second)
keyboard.Write([]byte("kate"))
time.Sleep(time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(5 * time.Second)
// Use the io.Writer interface
keyboard.Write([]byte("TinyGo USB Keyboard Control Test\n"))
time.Sleep(2 * time.Second)
// Or manually specify keycodes and Unicode codepoints
testKeys([]Key{
// Print alphabet out-of-order
{Press: usb.KeyX},
{Press: usb.KeyY},
{Press: usb.KeyZ},
{Press: usb.KeyG},
{Press: usb.KeyH},
{Press: usb.KeyI},
{Press: usb.KeyJ},
{Press: usb.KeyK},
{Press: usb.KeyL},
{Press: usb.KeyM},
{Press: usb.KeyN},
{Press: usb.KeyO},
{Press: usb.KeyP},
{Press: usb.KeyQ},
{Press: usb.KeyR},
{Press: usb.KeyS},
{Press: usb.KeyT},
{Press: usb.KeyA},
{Press: usb.KeyB},
{Press: usb.KeyC},
{Press: usb.KeyD},
{Press: usb.KeyE},
{Press: usb.KeyF},
{Press: usb.KeyU},
{Press: usb.KeyV},
{Press: usb.KeyW},
// Pause 1 second
{Time: time.Second},
// Move cursor left x3
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
// Pause 1 second
{Time: time.Second},
// Highlight 6 symbols to the left
{Down: usb.KeyModifierShift},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Up: usb.KeyModifierShift},
// Pause 1 second
{Time: time.Second},
// Use Ctrl-X to cut
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Move to beginning of line
{Press: usb.KeyHome},
// Pause 1 second
{Time: time.Second},
// Use Ctrl-V to paste
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Highlight 3 symbols to the right
{Down: usb.KeyModifierShift},
{Press: usb.KeyRight},
{Press: usb.KeyRight},
{Press: usb.KeyRight},
{Up: usb.KeyModifierShift},
// Use Ctrl-X to cut
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Move to end of line
{Press: usb.KeyEnd},
// Pause 1 second
{Time: time.Second},
// Use Ctrl-V to paste
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Newline
{Press: usb.KeyEnter},
{Press: usb.KeyEnter},
}, 150*time.Millisecond)
// Highlight all text and delete
keyboard.Down(usb.KeyModifierCtrl)
keyboard.Press(usb.KeyA)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(time.Second)
keyboard.Press(usb.KeyDelete)
time.Sleep(time.Second)
// Close window
keyboard.Down(usb.KeyModifierCtrl)
keyboard.Press(usb.KeyQ)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(2 * time.Second)
// Confirm discard file
keyboard.Down(usb.KeyModifierAlt)
keyboard.Press(usb.KeyD)
keyboard.Up(usb.KeyModifierAlt)
time.Sleep(5 * time.Second)
// Open a new terminal
keyboard.Down(usb.KeyModifierAlt)
keyboard.Press(usb.KeySpace)
keyboard.Up(usb.KeyModifierAlt)
time.Sleep(2 * time.Second)
keyboard.Write([]byte("konsole"))
keyboard.Press(usb.KeyEnter)
time.Sleep(5 * time.Second)
// Open serial connection (GNU screen)
keyboard.Write([]byte("screen /dev/ttyACM0 115200"))
time.Sleep(2 * time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(4 * time.Second)
// Write to UART
keyboard.Write([]byte("hello!"))
time.Sleep(time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(2 * time.Second)
keyboard.Write([]byte("NO U"))
time.Sleep(time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(2 * time.Second)
// Close serial connection (GNU screen)
keyboard.Down(usb.KeyModifierCtrl)
// Ctrl-X is the prefix sequence in my GNU screen configuration
keyboard.Press(usb.KeyX)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(time.Second)
// Backslash (Prefix-\) is the GNU screen command to kill window
keyboard.Press(usb.KeyBackslash)
time.Sleep(time.Second)
// Confirm "Kill window (Y/N)?" prompt
keyboard.Press(usb.KeyY)
keyboard.Press(usb.KeyEnter)
time.Sleep(2 * time.Second)
// Close terminal
keyboard.Down(usb.KeyModifierCtrl)
// Ctrl-D exits the shell (sends a resemblance of EOF, I believe?)
keyboard.Press(usb.KeyD)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(25 * time.Second)
}
}
type Key struct {
Press usb.Keycode
Down usb.Keycode
Up usb.Keycode
Time time.Duration
}
func testKeys(key []Key, delay time.Duration) {
for _, k := range key {
if 0 != k.Down {
keyboard.Down(k.Down)
}
if 0 != k.Press {
keyboard.Press(k.Press)
}
if 0 != k.Up {
keyboard.Up(k.Up)
}
if 0 != k.Time {
time.Sleep(k.Time)
} else {
time.Sleep(delay)
}
}
}
func testInternationalLayout() {
// International keyboard layouts also supported
keyboard.Write([]byte("TinyGo USB Keyboard Layout Test\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Lowercase: abcdefghijklmnopqrstuvwxyz\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Uppercase: ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Numbers: 0123456789\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols1: !\"#$%&'()*+,-./\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols2: :;<=>?[\\]^_`{|}~\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols3: ¡¢£¤¥¦§¨©ª«¬­®¯°±\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols4: ²³´µ¶·¸¹º»¼½¾¿×÷\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Grave: ÀÈÌÒÙàèìòù\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Acute: ÁÉÍÓÚÝáéíóúý\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Circumflex: ÂÊÎÔÛâêîôû\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Tilde: ÃÑÕãñõ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Diaeresis: ÄËÏÖÜäëïöüÿ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Cedilla: Çç\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Ring Above: Åå\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("AE: Ææ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Thorn: Þþ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Sharp S: ß\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("O-Stroke: Øø\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Eth: Ðð\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Euro: €\n"))
}
-37
View File
@@ -1,37 +0,0 @@
// This is a echo console running on the device UART.
// Connect using default baudrate for this hardware, 8-N-1 with your terminal program.
package main
import (
"machine"
"time"
)
func main() {
uart := machine.Serial
uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n"))
input := make([]byte, 4096)
i := 0
for {
if uart.Buffered() > 0 {
data, _ := uart.ReadByte()
switch data {
case 13:
// return key
uart.Write([]byte("\r\n"))
uart.Write([]byte("You typed: "))
uart.Write(input[:i])
uart.Write([]byte("\r\n"))
i = 0
default:
// just echo the character
uart.WriteByte(data)
input[i] = data
i++
}
}
time.Sleep(10 * time.Millisecond)
}
}
-133
View File
@@ -1,133 +0,0 @@
// Package fuzz is a shim to allow compilation against Go 1.18.
// It only defines a single type to work with testing/internal/testdeps,
// and hide all the other new dependencies from this package.
package fuzz
import (
"context"
"errors"
"io"
"reflect"
"time"
)
// CorpusEntry represents an individual input for fuzzing.
//
// We must use an equivalent type in the testing and testing/internal/testdeps
// packages, but testing can't import this package directly, and we don't want
// to export this type from testing. Instead, we use the same struct type and
// use a type alias (not a defined type) for convenience.
type CorpusEntry = struct {
Parent string
// Path is the path of the corpus file, if the entry was loaded from disk.
// For other entries, including seed values provided by f.Add, Path is the
// name of the test, e.g. seed#0 or its hash.
Path string
// Data is the raw input data. Data should only be populated for seed
// values. For on-disk corpus files, Data will be nil, as it will be loaded
// from disk using Path.
Data []byte
// Values is the unmarshaled values from a corpus file.
Values []any
Generation int
// IsSeed indicates whether this entry is part of the seed corpus.
IsSeed bool
}
// CoordinateFuzzingOpts is a set of arguments for CoordinateFuzzing.
// The zero value is valid for each field unless specified otherwise.
type CoordinateFuzzingOpts struct {
// Log is a writer for logging progress messages and warnings.
// If nil, io.Discard will be used instead.
Log io.Writer
// Timeout is the amount of wall clock time to spend fuzzing after the corpus
// has loaded. If zero, there will be no time limit.
Timeout time.Duration
// Limit is the number of random values to generate and test. If zero,
// there will be no limit on the number of generated values.
Limit int64
// MinimizeTimeout is the amount of wall clock time to spend minimizing
// after discovering a crasher. If zero, there will be no time limit. If
// MinimizeTimeout and MinimizeLimit are both zero, then minimization will
// be disabled.
MinimizeTimeout time.Duration
// MinimizeLimit is the maximum number of calls to the fuzz function to be
// made while minimizing after finding a crash. If zero, there will be no
// limit. Calls to the fuzz function made when minimizing also count toward
// Limit. If MinimizeTimeout and MinimizeLimit are both zero, then
// minimization will be disabled.
MinimizeLimit int64
// parallel is the number of worker processes to run in parallel. If zero,
// CoordinateFuzzing will run GOMAXPROCS workers.
Parallel int
// Seed is a list of seed values added by the fuzz target with testing.F.Add
// and in testdata.
Seed []CorpusEntry
// Types is the list of types which make up a corpus entry.
// Types must be set and must match values in Seed.
Types []reflect.Type
// CorpusDir is a directory where files containing values that crash the
// code being tested may be written. CorpusDir must be set.
CorpusDir string
// CacheDir is a directory containing additional "interesting" values.
// The fuzzer may derive new values from these, and may write new values here.
CacheDir string
}
// CoordinateFuzzing creates several worker processes and communicates with
// them to test random inputs that could trigger crashes and expose bugs.
// The worker processes run the same binary in the same directory with the
// same environment variables as the coordinator process. Workers also run
// with the same arguments as the coordinator, except with the -test.fuzzworker
// flag prepended to the argument list.
//
// If a crash occurs, the function will return an error containing information
// about the crash, which can be reported to the user.
func CoordinateFuzzing(ctx context.Context, opts CoordinateFuzzingOpts) (err error) {
return errors.New("not implemented")
}
// ReadCorpus reads the corpus from the provided dir. The returned corpus
// entries are guaranteed to match the given types. Any malformed files will
// be saved in a MalformedCorpusError and returned, along with the most recent
// error.
func ReadCorpus(dir string, types []reflect.Type) ([]CorpusEntry, error) {
return nil, errors.New("not implemented")
}
// CheckCorpus verifies that the types in vals match the expected types
// provided.
func CheckCorpus(vals []any, types []reflect.Type) error {
return errors.New("not implemented")
}
func ResetCoverage() {}
func SnapshotCoverage() {}
// RunFuzzWorker is called in a worker process to communicate with the
// coordinator process in order to fuzz random inputs. RunFuzzWorker loops
// until the coordinator tells it to stop.
//
// fn is a wrapper on the fuzz function. It may return an error to indicate
// a given input "crashed". The coordinator will also record a crasher if
// the function times out or terminates the process.
//
// RunFuzzWorker returns an error if it could not communicate with the
// coordinator process.
func RunFuzzWorker(ctx context.Context, fn func(CorpusEntry) error) error {
return errors.New("not implemented")
}
-17
View File
@@ -1,12 +1,7 @@
#ifdef __MACH__
.global _tinygo_startTask
_tinygo_startTask:
#else
.section .text.tinygo_startTask
.global tinygo_startTask
.type tinygo_startTask, %function
tinygo_startTask:
#endif
.cfi_startproc
// Small assembly stub for starting a goroutine. This is already run on the
// new stack, with the callee-saved registers already loaded.
@@ -28,25 +23,13 @@ tinygo_startTask:
blr x19
// After return, exit this goroutine. This is a tail call.
#ifdef __MACH__
b _tinygo_pause
#else
b tinygo_pause
#endif
.cfi_endproc
#ifndef __MACH__
.size tinygo_startTask, .-tinygo_startTask
#endif
#ifdef __MACH__
.global _tinygo_swapTask
_tinygo_swapTask:
#else
.global tinygo_swapTask
.type tinygo_swapTask, %function
tinygo_swapTask:
#endif
// This function gets the following parameters:
// x0 = newStack uintptr
// x1 = oldStack *uintptr
-81
View File
@@ -1,81 +0,0 @@
//go:build badger2040
// +build badger2040
// This contains the pin mappings for the Badger 2040 Connect board.
//
// For more information, see: https://shop.pimoroni.com/products/badger-2040
// Also
// - Badger 2040 schematic: https://cdn.shopify.com/s/files/1/0174/1800/files/badger_2040_schematic.pdf?v=1645702148
//
package machine
const (
LED Pin = GPIO25
BUTTON_A Pin = GPIO12
BUTTON_B Pin = GPIO13
BUTTON_C Pin = GPIO14
BUTTON_UP Pin = GPIO15
BUTTON_DOWN Pin = GPIO11
BUTTON_USER Pin = GPIO23
EPD_BUSY_PIN Pin = GPIO26
EPD_RESET_PIN Pin = GPIO21
EPD_DC_PIN Pin = GPIO20
EPD_CS_PIN Pin = GPIO17
EPD_SCK_PIN Pin = GPIO18
EPD_SDO_PIN Pin = GPIO19
VBUS_DETECT Pin = GPIO24
BATTERY Pin = GPIO29
ENABLE_3V3 Pin = GPIO10
)
// I2C pins
const (
I2C0_SDA_PIN Pin = GPIO4
I2C0_SCL_PIN Pin = GPIO5
I2C1_SDA_PIN Pin = NoPin
I2C1_SCL_PIN Pin = NoPin
)
// SPI pins.
const (
SPI0_SCK_PIN Pin = GPIO18
SPI0_SDO_PIN Pin = GPIO19
SPI0_SDI_PIN Pin = GPIO16
SPI1_SCK_PIN Pin = NoPin
SPI1_SDO_PIN Pin = NoPin
SPI1_SDI_PIN Pin = NoPin
)
// QSPI pins¿?
const (
/* TODO
SPI0_SD0_PIN Pin = QSPI_SD0
SPI0_SD1_PIN Pin = QSPI_SD1
SPI0_SD2_PIN Pin = QSPI_SD2
SPI0_SD3_PIN Pin = QSPI_SD3
SPI0_SCK_PIN Pin = QSPI_SCLKGPIO6
SPI0_CS_PIN Pin = QSPI_CS
*/
)
// Onboard crystal oscillator frequency, in MHz.
const (
xoscFreq = 12 // MHz
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Badger 2040"
usb_STRING_MANUFACTURER = "Pimoroni"
)
var (
usb_VID uint16 = 0x2e8a
usb_PID uint16 = 0x0003
)
-3
View File
@@ -42,9 +42,6 @@ const (
SPIQ = IO17
U0RXD = IO20
U0TXD = IO21
UART_TX_PIN = U0TXD
UART_RX_PIN = U0RXD
)
const (
-56
View File
@@ -1,56 +0,0 @@
//go:build nrf52840 || circuitplay_bluefruit || reelboard || clue || itsybitsy_nrf52840
// +build nrf52840 circuitplay_bluefruit reelboard clue itsybitsy_nrf52840
package machine
// Hardware pins
const (
P0_00 Pin = 0
P0_01 Pin = 1
P0_02 Pin = 2
P0_03 Pin = 3
P0_04 Pin = 4
P0_05 Pin = 5
P0_06 Pin = 6
P0_07 Pin = 7
P0_08 Pin = 8
P0_09 Pin = 9
P0_10 Pin = 10
P0_11 Pin = 11
P0_12 Pin = 12
P0_13 Pin = 13
P0_14 Pin = 14
P0_15 Pin = 15
P0_16 Pin = 16
P0_17 Pin = 17
P0_18 Pin = 18
P0_19 Pin = 19
P0_20 Pin = 20
P0_21 Pin = 21
P0_22 Pin = 22
P0_23 Pin = 23
P0_24 Pin = 24
P0_25 Pin = 25
P0_26 Pin = 26
P0_27 Pin = 27
P0_28 Pin = 28
P0_29 Pin = 29
P0_30 Pin = 30
P0_31 Pin = 31
P1_00 Pin = 32
P1_01 Pin = 33
P1_02 Pin = 34
P1_03 Pin = 35
P1_04 Pin = 36
P1_05 Pin = 37
P1_06 Pin = 38
P1_07 Pin = 39
P1_08 Pin = 40
P1_09 Pin = 41
P1_10 Pin = 42
P1_11 Pin = 43
P1_12 Pin = 44
P1_13 Pin = 45
P1_14 Pin = 46
P1_15 Pin = 47
)
+15 -91
View File
@@ -5,7 +5,6 @@ package machine
import (
"device/nxp"
"machine/usb"
"runtime/interrupt"
)
@@ -57,21 +56,21 @@ const (
// Analog pins
const (
// = Pin // Dig [Pad] {ADC1/ADC2}
A0 = PA18 // D14 [AD_B1_02] { 7 / 7 }
A1 = PA19 // D15 [AD_B1_03] { 8 / 8 }
A2 = PA23 // D16 [AD_B1_07] { 12 / 12 }
A3 = PA22 // D17 [AD_B1_06] { 11 / 11 }
A4 = PA17 // D18 [AD_B1_01] { 6 / 6 }
A5 = PA16 // D19 [AD_B1_00] { 5 / 5 }
A6 = PA26 // D20 [AD_B1_10] { 15 / 15 }
A7 = PA27 // D21 [AD_B1_11] { 0 / 0 }
A8 = PA24 // D22 [AD_B1_08] { 13 / 13 }
A9 = PA25 // D23 [AD_B1_09] { 14 / 14 }
A10 = PA12 // D24 [AD_B0_12] { 1 / - }
A11 = PA13 // D25 [AD_B0_13] { 2 / - }
A12 = PA30 // D26 [AD_B1_14] { - / 3 }
A13 = PA31 // D27 [AD_B1_15] { - / 4 }
// = Pin // Dig | [Pad] {ADC1/ADC2}
A0 = PA18 // D14 | [AD_B1_02] { 7 / 7 }
A1 = PA19 // D15 | [AD_B1_03] { 8 / 8 }
A2 = PA23 // D16 | [AD_B1_07] { 12 / 12 }
A3 = PA22 // D17 | [AD_B1_06] { 11 / 11 }
A4 = PA17 // D18 | [AD_B1_01] { 6 / 6 }
A5 = PA16 // D19 | [AD_B1_00] { 5 / 5 }
A6 = PA26 // D20 | [AD_B1_10] { 15 / 15 }
A7 = PA27 // D21 | [AD_B1_11] { 0 / 0 }
A8 = PA24 // D22 | [AD_B1_08] { 13 / 13 }
A9 = PA25 // D23 | [AD_B1_09] { 14 / 14 }
A10 = PA12 // D24 | [AD_B0_12] { 1 / - }
A11 = PA13 // D25 | [AD_B0_13] { 2 / - }
A12 = PA30 // D26 | [AD_B1_14] { - / 3 }
A13 = PA31 // D27 | [AD_B1_15] { - / 4 }
)
// Default peripheral pins
@@ -106,15 +105,6 @@ func init() {
_UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
}
// #=====================================================#
// | USB |
// #=====================================================#
var (
UART0 = usb.UART{Port: 0}
// HID0 = usb.HID{Port: 0}
// UART0 = &UART1
)
// #=====================================================#
// | UART |
// #===========#===========#=============#===============#
@@ -247,8 +237,6 @@ var (
}
)
// #==================================================================#
// | SPI |
// #===========#==========#===============#===========================#
// | Interface | Hardware | Clock(Freq) | SDI/SDO/SCK/CS : Alt |
// #===========#==========#===============#=================-=========#
@@ -273,70 +261,6 @@ const (
SPI3_CS_PIN = D36
)
var (
SPI0 = SPI1 // SPI0 is an alias of SPI1 (LPSPI4)
SPI1 = &_SPI1
_SPI1 = SPI{
Bus: nxp.LPSPI4,
muxSDI: muxSelect{ // D12 (PB1 [B0_01])
mux: nxp.IOMUXC_LPSPI4_SDI_SELECT_INPUT_DAISY_GPIO_B0_01_ALT3,
sel: &nxp.IOMUXC.LPSPI4_SDI_SELECT_INPUT,
},
muxSDO: muxSelect{ // D11 (PB2 [B0_02])
mux: nxp.IOMUXC_LPSPI4_SDO_SELECT_INPUT_DAISY_GPIO_B0_02_ALT3,
sel: &nxp.IOMUXC.LPSPI4_SDO_SELECT_INPUT,
},
muxSCK: muxSelect{ // D13 (PB3 [B0_03])
mux: nxp.IOMUXC_LPSPI4_SCK_SELECT_INPUT_DAISY_GPIO_B0_03_ALT3,
sel: &nxp.IOMUXC.LPSPI4_SCK_SELECT_INPUT,
},
muxCS: muxSelect{ // D10 (PB0 [B0_00])
mux: nxp.IOMUXC_LPSPI4_PCS0_SELECT_INPUT_DAISY_GPIO_B0_00_ALT3,
sel: &nxp.IOMUXC.LPSPI4_PCS0_SELECT_INPUT,
},
}
SPI2 = &_SPI2
_SPI2 = SPI{
Bus: nxp.LPSPI3,
muxSDI: muxSelect{ // D1 (PA2 [AD_B0_02])
mux: nxp.IOMUXC_LPSPI3_SDI_SELECT_INPUT_DAISY_GPIO_AD_B0_02_ALT7,
sel: &nxp.IOMUXC.LPSPI3_SDI_SELECT_INPUT,
},
muxSDO: muxSelect{ // D26 (PA30 [AD_B1_14])
mux: nxp.IOMUXC_LPSPI3_SDO_SELECT_INPUT_DAISY_GPIO_AD_B1_14_ALT2,
sel: &nxp.IOMUXC.LPSPI3_SDO_SELECT_INPUT,
},
muxSCK: muxSelect{ // D27 (PA31 [AD_B1_15])
mux: nxp.IOMUXC_LPSPI3_SCK_SELECT_INPUT_DAISY_GPIO_AD_B1_15,
sel: &nxp.IOMUXC.LPSPI3_SCK_SELECT_INPUT,
},
muxCS: muxSelect{ // D0 (PA3 [AD_B0_03])
mux: nxp.IOMUXC_LPSPI3_PCS0_SELECT_INPUT_DAISY_GPIO_AD_B0_03_ALT7,
sel: &nxp.IOMUXC.LPSPI3_PCS0_SELECT_INPUT,
},
}
SPI3 = &_SPI3
_SPI3 = SPI{
Bus: nxp.LPSPI1,
muxSDI: muxSelect{ // D34 (PC15 [SD_B0_03])
mux: nxp.IOMUXC_LPSPI1_SDI_SELECT_INPUT_DAISY_GPIO_SD_B0_03_ALT4,
sel: &nxp.IOMUXC.LPSPI1_SDI_SELECT_INPUT,
},
muxSDO: muxSelect{ // D35 (PC14 [SD_B0_02])
mux: nxp.IOMUXC_LPSPI1_SDO_SELECT_INPUT_DAISY_GPIO_SD_B0_02_ALT4,
sel: &nxp.IOMUXC.LPSPI1_SDO_SELECT_INPUT,
},
muxSCK: muxSelect{ // D37 (PC12 [SD_B0_00])
mux: nxp.IOMUXC_LPSPI1_SCK_SELECT_INPUT_DAISY_GPIO_SD_B0_00_ALT4,
sel: &nxp.IOMUXC.LPSPI1_SCK_SELECT_INPUT,
},
muxCS: muxSelect{ // D36 (PC13 [SD_B0_01])
mux: nxp.IOMUXC_LPSPI1_PCS0_SELECT_INPUT_DAISY_GPIO_SD_B0_01_ALT4,
sel: &nxp.IOMUXC.LPSPI1_PCS0_SELECT_INPUT,
},
}
)
// #====================================================#
// | I2C |
// #===========#==========#=============#===============#
-81
View File
@@ -1,81 +0,0 @@
//go:build thingplus_rp2040
// +build thingplus_rp2040
package machine
const (
LED = GPIO25
// Onboard crystal oscillator frequency, in MHz.
xoscFreq = 12 // MHz
)
// GPIO Pins
const (
GP0 Pin = GPIO0 // TX
GP1 Pin = GPIO1 // RX
GP2 Pin = GPIO2 // SCK
GP3 Pin = GPIO3 // COPI
GP4 Pin = GPIO4 // CIPO
GP6 Pin = GPIO6 // SDA
GP7 Pin = GPIO7 // SCL (connected to GPIO23 as well)
GP8 Pin = GPIO8 // WS2812 RGB LED
GP9 Pin = GPIO9 // muSDcard DATA3 / CS
GP10 Pin = GPIO10 // muSDcard DATA2
GP11 Pin = GPIO11 // muSDcard DATA1
GP12 Pin = GPIO12 // muSDcard DATA0 / CIPO
GP14 Pin = GPIO14 // muSDcard CLK /SCLK
GP15 Pin = GPIO15 // muSDcard CMD / COPI
GP16 Pin = GPIO16 // 16
GP17 Pin = GPIO17 // 17
GP18 Pin = GPIO18 // 18
GP19 Pin = GPIO19 // 19
GP20 Pin = GPIO20 // 20
GP21 Pin = GPIO21 // 21
GP22 Pin = GPIO22 // 22
GP23 Pin = GPIO23 // Connected to GPIO7
GP25 Pin = GPIO25 // Status blue LED
GP26 Pin = GPIO26 // ADC0
GP27 Pin = GPIO27 // ADC1
GP28 Pin = GPIO28 // ADC2
GP29 Pin = GPIO29 // ADC3
)
// Analog pins
const (
A0 = GPIO26
A1 = GPIO27
A2 = GPIO28
A3 = GPIO29
)
// I2C Pins.
const (
I2C0_SCL_PIN = GPIO6 // N/A
I2C0_SDA_PIN = GPIO7 // N/A
I2C1_SDA_PIN = GPIO6
I2C1_SCL_PIN = GPIO7
SDA_PIN = I2C1_SDA_PIN
SCL_PIN = I2C1_SCL_PIN
)
// SPI default pins
const (
// Default Serial Clock Bus 0 for SPI communications
SPI0_SCK_PIN = GPIO2
// Default Serial Out Bus 0 for SPI communications
SPI0_SDO_PIN = GPIO3 // Tx
// Default Serial In Bus 0 for SPI communications
SPI0_SDI_PIN = GPIO4 // Rx
// Default Serial Clock Bus 1 for SPI communications to muSDcard
SPI1_SCK_PIN = GPIO14
// Default Serial Out Bus 1 for SPI communications to muSDcard
SPI1_SDO_PIN = GPIO15 // Tx
// Default Serial In Bus 1 for SPI communications to muSDcard
SPI1_SDI_PIN = GPIO12 // Rx
)
-113
View File
@@ -1,113 +0,0 @@
//go:build xiao_ble
// +build xiao_ble
// This file contains the pin mappings for the Seeed XIAO BLE nRF52840 [Sense] boards.
//
// Seeed XIAO BLE is an ultra-small size, ultra-low power Bluetooth development board based on the Nordic nRF52840.
// It features an onboard Bluetooth antenna, onboard battery charging chip, and 21*17.5mm thumb size, which makes it ideal for IoT projects.
//
// Seeed XIAO BLE nRF52840 Sense is a tiny Bluetooth LE development board designed for IoT and AI applications.
// It features an onboard antenna, 6 Dof IMU, microphone, all of which make it an ideal board to run AI using TinyML and TensorFlow Lite.
//
// SoftDevice (s140v7) is pre-flashed on this board already.
// See https://github.com/tinygo-org/bluetooth
//
// - https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html
// - https://www.seeedstudio.com/Seeed-XIAO-BLE-Sense-nRF52840-p-5253.html
//
// - https://wiki.seeedstudio.com/XIAO_BLE/
// - https://github.com/Seeed-Studio/ArduinoCore-mbed/tree/master/variants/SEEED_XIAO_NRF52840_SENSE
//
package machine
const HasLowFrequencyCrystal = true
// Digital Pins
const (
D0 Pin = P0_02
D1 Pin = P0_03
D2 Pin = P0_28
D3 Pin = P0_29
D4 Pin = P0_04
D5 Pin = P0_05
D6 Pin = P1_11
D7 Pin = P1_12
D8 Pin = P1_13
D9 Pin = P1_14
D10 Pin = P1_15
)
// Analog pins
const (
A0 Pin = P0_02
A1 Pin = P0_03
A2 Pin = P0_28
A3 Pin = P0_29
A4 Pin = P0_04
A5 Pin = P0_05
)
// Onboard LEDs
const (
LED = LED_CHG
LED1 = LED_RED
LED2 = LED_GREEN
LED3 = LED_BLUE
LED_CHG = P0_17
LED_RED = P0_26
LED_GREEN = P0_30
LED_BLUE = P0_06
)
// UART0 pins
const (
UART_RX_PIN = P1_12
UART_TX_PIN = P1_11
)
// I2C pins
const (
// Defaults to internal
SDA_PIN = SDA1_PIN
SCL_PIN = SCL1_PIN
// I2C0 (external) pins
SDA0_PIN = P0_04
SCL0_PIN = P0_05
// I2C1 (internal) pins
SDA1_PIN = P0_07
SCL1_PIN = P0_27
)
// SPI pins
const (
SPI0_SCK_PIN = P1_13
SPI0_SDO_PIN = P1_14
SPI0_SDI_PIN = P1_15
)
// Peripherals
const (
LSM_PWR = P1_08 // IMU (LSM6DS3TR) power
LSM_INT = P0_11 // IMU (LSM6DS3TR) interrupt
MIC_PWR = P1_10 // Microphone (MSM261D3526H1CPM) power
MIC_CLK = P1_00
MIC_DIN = P0_16
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "XIAO nRF52840 Sense"
usb_STRING_MANUFACTURER = "Seeed"
)
var (
usb_VID uint16 = 0x2886
usb_PID uint16 = 0x0045
)
var (
DefaultUART = UART0
)
+712 -2
View File
@@ -13,6 +13,7 @@ import (
"device/sam"
"errors"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
)
@@ -1974,10 +1975,719 @@ func (tcc *TCC) Set(channel uint8, value uint32) {
}
}
func initUSB() {
// Configure USB D+/D- pins.
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
type USBCDC struct {
Buffer *RingBuffer
TxIdx volatile.Register8
waitTxc bool
waitTxcRetryCount uint8
sent bool
configured bool
}
var (
// USB is a USB CDC interface.
USB = &USBCDC{Buffer: NewRingBuffer()}
)
const (
usbcdcTxSizeMask uint8 = 0x3F
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask
usbcdcTxBank1st uint8 = 0x00
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
usbcdcTxMaxRetriesAllowed uint8 = 5
)
// Flush flushes buffered data.
func (usbcdc *USBCDC) Flush() error {
if usbLineInfo.lineState > 0 {
idx := usbcdc.TxIdx.Get()
sz := idx & usbcdcTxSizeMask
bk := idx & usbcdcTxBankMask
if 0 < sz {
if usbcdc.waitTxc {
// waiting for the next flush(), because the transmission is not complete
usbcdc.waitTxcRetryCount++
return nil
}
usbcdc.waitTxc = true
usbcdc.waitTxcRetryCount = 0
// set the data
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk]))))
if bk == usbcdcTxBank1st {
usbcdc.TxIdx.Set(usbcdcTxBank2nd)
} else {
usbcdc.TxIdx.Set(usbcdcTxBank1st)
}
// clean multi packet size of bytes already sent
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
// set count of bytes to be sent
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((uint32(sz) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
// clear transfer complete flag
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
// send data by setting bank ready
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
usbcdc.sent = true
}
}
return nil
}
// WriteByte writes a byte of data to the USB CDC interface.
func (usbcdc *USBCDC) WriteByte(c byte) error {
// Supposedly to handle problem with Windows USB serial ports?
if usbLineInfo.lineState > 0 {
ok := false
for {
mask := interrupt.Disable()
idx := usbcdc.TxIdx.Get()
if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask {
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c
usbcdc.TxIdx.Set(idx + 1)
ok = true
}
interrupt.Restore(mask)
if ok {
break
} else if usbcdcTxMaxRetriesAllowed < usbcdc.waitTxcRetryCount {
mask := interrupt.Disable()
usbcdc.waitTxc = false
usbcdc.waitTxcRetryCount = 0
usbcdc.TxIdx.Set(0)
usbLineInfo.lineState = 0
interrupt.Restore(mask)
break
} else {
mask := interrupt.Disable()
if usbcdc.sent {
if usbcdc.waitTxc {
if (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) != 0 {
setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
usbcdc.waitTxc = false
usbcdc.Flush()
}
} else {
usbcdc.Flush()
}
}
interrupt.Restore(mask)
}
}
}
return nil
}
func (usbcdc *USBCDC) DTR() bool {
return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0
}
func (usbcdc *USBCDC) RTS() bool {
return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0
}
const (
// these are SAMD51 specific.
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF
usb_DEVICE_PCKSIZE_SIZE_Pos = 28
usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
)
var (
usbEndpointDescriptors [8]usbDeviceDescriptor
udd_ep_in_cache_buffer [7][128]uint8
udd_ep_out_cache_buffer [7][128]uint8
isEndpointHalt = false
isRemoteWakeUpEnabled = false
endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL,
(usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
(usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
(usb_ENDPOINT_TYPE_BULK | usbEndpointIn)}
usbConfiguration uint8
usbSetInterface uint8
usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00}
)
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
func (usbcdc *USBCDC) Configure(config UARTConfig) {
// reset USB interface
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST)
for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) ||
sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) {
}
sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors))))
// configure pins
USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom})
USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom})
// performs pad calibration from store fuses
handlePadCalibration()
// run in standby
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY)
// set full speed
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
// attach
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
// enable interrupt for end of reset
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
// enable interrupt for start of frame
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF)
// enable USB
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
// enable IRQ at highest priority
interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable()
interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable()
interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable()
interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable()
usbcdc.configured = true
}
// Configured returns whether usbcdc is configured or not.
func (usbcdc *USBCDC) Configured() bool {
return usbcdc.configured
}
func handlePadCalibration() {
// Load Pad Calibration data from non-volatile memory
// This requires registers that are not included in the SVD file.
// Modeled after defines from samd21g18a.h and nvmctrl.h:
//
// #define NVMCTRL_OTP4 0x00806020
//
// #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4)
// #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */
// #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos)
// #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos)))
// #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4)
// #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */
// #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos)
// #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos)))
// #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4)
// #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */
// #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos)
// #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos)))
//
fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
calibTransN := uint16(fuse>>13) & uint16(0x1f)
calibTransP := uint16(fuse>>18) & uint16(0x1f)
calibTrim := uint16(fuse>>23) & uint16(0x7)
if calibTransN == 0x1f {
calibTransN = 5
}
sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos)
if calibTransP == 0x1f {
calibTransP = 29
}
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
if calibTrim == 0x7 {
calibTrim = 3
}
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
}
func handleUSBIRQ(interrupt.Interrupt) {
// reset all interrupt flags
flags := sam.USB_DEVICE.INTFLAG.Get()
sam.USB_DEVICE.INTFLAG.Set(flags)
// End of reset
if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 {
// Configure control endpoint
initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL)
// Enable Setup-Received interrupt
setEPINTENSET(0, sam.USB_DEVICE_ENDPOINT_EPINTENSET_RXSTP)
usbConfiguration = 0
// ack the End-Of-Reset interrupt
sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST)
}
// Start of frame
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
USB.Flush()
// if you want to blink LED showing traffic, this would be the place...
}
// Endpoint 0 Setup interrupt
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP > 0 {
// ack setup received
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP)
// parse setup
setup := newUSBSetup(udd_ep_out_cache_buffer[0][:])
// Clear the Bank 0 ready flag on Control OUT
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
ok := false
if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
// Standard Requests
ok = handleStandardSetup(setup)
} else {
// Class Interface Requests
if setup.wIndex == usb_CDC_ACM_INTERFACE {
ok = cdcSetup(setup)
}
}
if ok {
// set Bank1 ready
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
} else {
// Stall endpoint
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
}
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1 > 0 {
// ack the stall
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
// clear stall request
setEPINTENCLR(0, sam.USB_DEVICE_ENDPOINT_EPINTENCLR_STALL1)
}
}
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
var i uint32
for i = 1; i < uint32(len(endPoints)); i++ {
// Check if endpoint has a pending interrupt
epFlags := getEPINTFLAG(i)
if (epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 ||
(epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 {
switch i {
case usb_CDC_ENDPOINT_OUT:
handleEndpoint(i)
setEPINTFLAG(i, epFlags)
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
setEPSTATUSCLR(i, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
setEPINTFLAG(i, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
if i == usb_CDC_ENDPOINT_IN {
USB.waitTxc = false
}
}
}
}
}
func initEndpoint(ep, config uint32) {
switch config {
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn:
// set packet size
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
// set data buffer address
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
// set endpoint type
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
// set packet size
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
// set data buffer address
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
// set endpoint type
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
// receive interrupts when current transfer complete
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
// set byte count to zero, we have not received anything yet
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
// ready for next transfer
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
// TODO: not really anything, seems like...
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
// set packet size
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
// set data buffer address
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
// set endpoint type
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
// NAK on endpoint IN, the bank is not yet filled in.
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
case usb_ENDPOINT_TYPE_CONTROL:
// Control OUT
// set packet size
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
// set data buffer address
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
// set endpoint type
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
// Control IN
// set packet size
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
// set data buffer address
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
// set endpoint type
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
// Prepare OUT endpoint for receive
// set multi packet size for expected number of receive bytes on control OUT
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
// set byte count to zero, we have not received anything yet
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
// NAK on endpoint OUT to show we are ready to receive control data
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK0RDY)
}
}
func handleStandardSetup(setup usbSetup) bool {
switch setup.bRequest {
case usb_GET_STATUS:
buf := []byte{0, 0}
if setup.bmRequestType != 0 { // endpoint
// TODO: actually check if the endpoint in question is currently halted
if isEndpointHalt {
buf[0] = 1
}
}
sendUSBPacket(0, buf)
return true
case usb_CLEAR_FEATURE:
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
isRemoteWakeUpEnabled = false
} else if setup.wValueL == 0 { // ENDPOINTHALT
isEndpointHalt = false
}
sendZlp()
return true
case usb_SET_FEATURE:
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
isRemoteWakeUpEnabled = true
} else if setup.wValueL == 0 { // ENDPOINTHALT
isEndpointHalt = true
}
sendZlp()
return true
case usb_SET_ADDRESS:
// set packet size 64 with auto Zlp after transfer
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.Set((epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) |
uint32(1<<31)) // autozlp
// ack the transfer is complete from the request
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
// set bank ready for data
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
// wait for transfer to complete
timeout := 3000
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
timeout--
if timeout == 0 {
return true
}
}
// last, set the device address to that requested by host
sam.USB_DEVICE.DADD.SetBits(setup.wValueL)
sam.USB_DEVICE.DADD.SetBits(sam.USB_DEVICE_DADD_ADDEN)
return true
case usb_GET_DESCRIPTOR:
sendDescriptor(setup)
return true
case usb_SET_DESCRIPTOR:
return false
case usb_GET_CONFIGURATION:
buff := []byte{usbConfiguration}
sendUSBPacket(0, buff)
return true
case usb_SET_CONFIGURATION:
if setup.bmRequestType&usb_REQUEST_RECIPIENT == usb_REQUEST_DEVICE {
for i := 1; i < len(endPoints); i++ {
initEndpoint(uint32(i), endPoints[i])
}
usbConfiguration = setup.wValueL
// Enable interrupt for CDC control messages from host (OUT packet)
setEPINTENSET(usb_CDC_ENDPOINT_ACM, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1)
// Enable interrupt for CDC data messages from host
setEPINTENSET(usb_CDC_ENDPOINT_OUT, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
sendZlp()
return true
} else {
return false
}
case usb_GET_INTERFACE:
buff := []byte{usbSetInterface}
sendUSBPacket(0, buff)
return true
case usb_SET_INTERFACE:
usbSetInterface = setup.wValueL
sendZlp()
return true
default:
return true
}
}
func cdcSetup(setup usbSetup) bool {
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
if setup.bRequest == usb_CDC_GET_LINE_CODING {
var b [cdcLineInfoSize]byte
b[0] = byte(usbLineInfo.dwDTERate)
b[1] = byte(usbLineInfo.dwDTERate >> 8)
b[2] = byte(usbLineInfo.dwDTERate >> 16)
b[3] = byte(usbLineInfo.dwDTERate >> 24)
b[4] = byte(usbLineInfo.bCharFormat)
b[5] = byte(usbLineInfo.bParityType)
b[6] = byte(usbLineInfo.bDataBits)
sendUSBPacket(0, b[:])
return true
}
}
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
if setup.bRequest == usb_CDC_SET_LINE_CODING {
b, err := receiveUSBControlPacket()
if err != nil {
return false
}
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
usbLineInfo.bCharFormat = b[4]
usbLineInfo.bParityType = b[5]
usbLineInfo.bDataBits = b[6]
}
if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
usbLineInfo.lineState = setup.wValueL
}
if setup.bRequest == usb_CDC_SET_LINE_CODING || setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
// auto-reset into the bootloader
if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
ResetProcessor()
} else {
// TODO: cancel any reset
}
sendZlp()
}
if setup.bRequest == usb_CDC_SEND_BREAK {
// TODO: something with this value?
// breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL;
// return false;
sendZlp()
}
return true
}
return false
}
//go:noinline
func sendUSBPacket(ep uint32, data []byte) {
copy(udd_ep_in_cache_buffer[ep][:], data)
// Set endpoint address for sending data
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
// clear multi-packet size which is total bytes already sent
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
// set byte count, which is total number of bytes to be sent
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos))
}
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
var b [cdcLineInfoSize]byte
// address
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
// set byte count to zero
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
// set ready for next data
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
// Wait until OUT transfer is ready.
timeout := 300000
for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 {
timeout--
if timeout == 0 {
return b, errUSBCDCReadTimeout
}
}
// Wait until OUT transfer is completed.
timeout = 300000
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
timeout--
if timeout == 0 {
return b, errUSBCDCReadTimeout
}
}
// get data
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
if bytesread != cdcLineInfoSize {
return b, errUSBCDCBytesRead
}
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
return b, nil
}
func handleEndpoint(ep uint32) {
// get data
count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >>
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
// move to ring buffer
for i := 0; i < count; i++ {
USB.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF)))
}
// set byte count to zero
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
// set multi packet size to 64
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
// set ready for next data
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
}
func sendZlp() {
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
}
func epPacketSize(size uint16) uint32 {
switch size {
case 8:
return 0
case 16:
return 1
case 32:
return 2
case 64:
return 3
case 128:
return 4
case 256:
return 5
case 512:
return 6
case 1023:
return 7
default:
return 0
}
}
func getEPCFG(ep uint32) uint8 {
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Get()
}
func setEPCFG(ep uint32, val uint8) {
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Set(val)
}
func setEPSTATUSCLR(ep uint32, val uint8) {
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSCLR.Set(val)
}
func setEPSTATUSSET(ep uint32, val uint8) {
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSSET.Set(val)
}
func getEPSTATUS(ep uint32) uint8 {
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUS.Get()
}
func getEPINTFLAG(ep uint32) uint8 {
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Get()
}
func setEPINTFLAG(ep uint32, val uint8) {
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Set(val)
}
func setEPINTENCLR(ep uint32, val uint8) {
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENCLR.Set(val)
}
func setEPINTENSET(ep uint32, val uint8) {
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val)
}
// ResetProcessor should perform a system reset in preparation
+4 -257
View File
@@ -5,8 +5,6 @@ package machine
import (
"device/esp"
"device/riscv"
"errors"
"runtime/interrupt"
"runtime/volatile"
"sync"
@@ -216,269 +214,18 @@ func setupPinInterrupt() error {
}).Enable()
}
var (
DefaultUART = UART0
var DefaultUART = UART0
var (
UART0 = &_UART0
_UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
UART1 = &_UART1
_UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()}
onceUart = sync.Once{}
errSamePins = errors.New("UART: invalid pin combination")
errWrongUART = errors.New("UART: unsupported UARTn")
errWrongBitSize = errors.New("UART: invalid data size")
errWrongStopBitSize = errors.New("UART: invalid bit size")
)
type UART struct {
Bus *esp.UART_Type
Buffer *RingBuffer
ParityErrorDetected bool // set when parity error detected
DataErrorDetected bool // set when data corruption detected
DataOverflowDetected bool // set when data overflow detected in UART FIFO buffer or RingBuffer
}
type UARTStopBits int
const (
UARTStopBits_Default UARTStopBits = iota
UARTStopBits_1
UARTStopBits_1_5
UARTStopBits_2
)
const (
defaultDataBits = 8
defaultStopBit = 1
defaultParity = ParityNone
uartInterrupts = esp.UART_INT_ENA_RXFIFO_FULL_INT_ENA |
esp.UART_INT_ENA_PARITY_ERR_INT_ENA |
esp.UART_INT_ENA_FRM_ERR_INT_ENA |
esp.UART_INT_ENA_RXFIFO_OVF_INT_ENA |
esp.UART_INT_ENA_GLITCH_DET_INT_ENA
pplClockFreq = 80e6
)
type registerSet struct {
interruptMapReg *volatile.Register32
uartClockBitMask uint32
gpioMatrixSignal uint32
}
func (uart *UART) Configure(config UARTConfig) error {
if config.BaudRate == 0 {
config.BaudRate = 115200
}
if config.TX == config.RX {
return errSamePins
}
switch {
case uart.Bus == esp.UART0:
return uart.configure(config, registerSet{
interruptMapReg: &esp.INTERRUPT_CORE0.UART_INTR_MAP,
uartClockBitMask: esp.SYSTEM_PERIP_CLK_EN0_UART_CLK_EN,
gpioMatrixSignal: 6,
})
case uart.Bus == esp.UART1:
return uart.configure(config, registerSet{
interruptMapReg: &esp.INTERRUPT_CORE0.UART1_INTR_MAP,
uartClockBitMask: esp.SYSTEM_PERIP_CLK_EN0_UART1_CLK_EN,
gpioMatrixSignal: 9,
})
}
return errWrongUART
}
func (uart *UART) configure(config UARTConfig, regs registerSet) error {
initUARTClock(uart.Bus, regs)
// - disbale TX/RX clock to make sure the UART transmitter or receiver is not at work during configuration
uart.Bus.SetCLK_CONF_TX_SCLK_EN(0)
uart.Bus.SetCLK_CONF_RX_SCLK_EN(0)
// Configure static registers (Ref: Configuring URATn Communication)
// - default clock source: 1=APB_CLK, 2=FOSC_CLK, 3=XTAL_CLK
uart.Bus.SetCLK_CONF_SCLK_SEL(1)
// reset divisor of the divider via UART_SCLK_DIV_NUM, UART_SCLK_DIV_A, and UART_SCLK_DIV_B
uart.Bus.SetCLK_CONF_SCLK_DIV_NUM(0)
uart.Bus.SetCLK_CONF_SCLK_DIV_A(0)
uart.Bus.SetCLK_CONF_SCLK_DIV_B(0)
// - the baud rate
uart.SetBaudRate(config.BaudRate)
// - the data format
uart.SetFormat(defaultDataBits, defaultStopBit, defaultParity)
// - set UART mode
uart.Bus.SetRS485_CONF_RS485_EN(0)
uart.Bus.SetRS485_CONF_RS485TX_RX_EN(0)
uart.Bus.SetRS485_CONF_RS485RXBY_TX_EN(0)
uart.Bus.SetCONF0_IRDA_EN(0)
// - disable hw-flow control
uart.Bus.SetCONF0_TX_FLOW_EN(0)
uart.Bus.SetCONF1_RX_FLOW_EN(0)
// synchronize values into Core Clock
uart.Bus.SetID_REG_UPDATE(1)
uart.setupPins(config, regs)
uart.configureInterrupt(regs.interruptMapReg)
uart.enableTransmitter()
uart.enableReceiver()
// Start TX/RX
uart.Bus.SetCLK_CONF_TX_SCLK_EN(1)
uart.Bus.SetCLK_CONF_RX_SCLK_EN(1)
return nil
}
func (uart *UART) SetFormat(dataBits, stopBits int, parity UARTParity) error {
if dataBits < 5 {
return errWrongBitSize
}
if stopBits > 1 {
return errWrongStopBitSize
}
// - data length
uart.Bus.SetCONF0_BIT_NUM(uint32(dataBits - 5))
// - stop bit
uart.Bus.SetCONF0_STOP_BIT_NUM(uint32(stopBits))
// - parity check
switch parity {
case ParityNone:
uart.Bus.SetCONF0_PARITY_EN(0)
case ParityEven:
uart.Bus.SetCONF0_PARITY_EN(1)
uart.Bus.SetCONF0_PARITY(0)
case ParityOdd:
uart.Bus.SetCONF0_PARITY_EN(1)
uart.Bus.SetCONF0_PARITY(1)
}
return nil
}
func initUARTClock(bus *esp.UART_Type, regs registerSet) {
uartClock := &esp.SYSTEM.PERIP_CLK_EN0
uartClockReset := &esp.SYSTEM.PERIP_RST_EN0
// Initialize/reset URATn (Ref: Initializing URATn)
// - enable the clock for UART RAM
uartClock.SetBits(esp.SYSTEM_PERIP_CLK_EN0_UART_MEM_CLK_EN)
// - enable APB_CLK for UARTn
uartClock.SetBits(regs.uartClockBitMask)
// - reset sequence
uartClockReset.ClearBits(regs.uartClockBitMask)
bus.SetCLK_CONF_RST_CORE(1)
uartClockReset.SetBits(regs.uartClockBitMask)
uartClockReset.ClearBits(regs.uartClockBitMask)
bus.SetCLK_CONF_RST_CORE(0)
// synchronize core register
bus.SetID_REG_UPDATE(0)
// enable RTC clock
esp.RTC_CNTL.SetRTC_CLK_CONF_DIG_CLK8M_EN(1)
// wait for Core Clock to ready for configuration
for bus.GetID_REG_UPDATE() > 0 {
riscv.Asm("nop")
}
}
func (uart *UART) SetBaudRate(baudRate uint32) {
// based on esp-idf
max_div := uint32((1 << 12) - 1)
sclk_div := (pplClockFreq + (max_div * baudRate) - 1) / (max_div * baudRate)
clk_div := (pplClockFreq << 4) / (baudRate * sclk_div)
uart.Bus.SetCLKDIV(clk_div >> 4)
uart.Bus.SetCLKDIV_FRAG(clk_div & 0xf)
uart.Bus.SetCLK_CONF_SCLK_DIV_NUM(sclk_div - 1)
}
func (uart *UART) setupPins(config UARTConfig, regs registerSet) {
config.RX.Configure(PinConfig{Mode: PinInputPullup})
config.TX.Configure(PinConfig{Mode: PinInputPullup})
// link TX with GPIO signal X (technical reference manual 5.10) (this is not interrupt signal!)
config.TX.outFunc().Set(regs.gpioMatrixSignal)
// link RX with GPIO signal X and route signals via GPIO matrix (GPIO_SIGn_IN_SEL 0x40)
inFunc(regs.gpioMatrixSignal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SIG_IN_SEL | uint32(config.RX))
}
func (uart *UART) configureInterrupt(intrMapReg *volatile.Register32) { // Disable all UART interrupts
// Disable all UART interrupts
uart.Bus.INT_ENA.ClearBits(0x0ffff)
intrMapReg.Set(7)
onceUart.Do(func() {
_ = interrupt.New(7, func(i interrupt.Interrupt) {
UART0.serveInterrupt(0)
UART1.serveInterrupt(1)
}).Enable()
})
}
func (uart *UART) serveInterrupt(num int) {
// get interrupt status
interrutFlag := uart.Bus.INT_ST.Get()
if (interrutFlag & uartInterrupts) == 0 {
return
}
// block UART interrupts while processing
uart.Bus.INT_ENA.ClearBits(uartInterrupts)
if interrutFlag&esp.UART_INT_ENA_RXFIFO_FULL_INT_ENA > 0 {
for uart.Bus.GetSTATUS_RXFIFO_CNT() > 0 {
b := uart.Bus.GetFIFO_RXFIFO_RD_BYTE()
if !uart.Buffer.Put(byte(b & 0xff)) {
uart.DataOverflowDetected = true
}
}
}
if interrutFlag&esp.UART_INT_ENA_PARITY_ERR_INT_ENA > 0 {
uart.ParityErrorDetected = true
}
if 0 != interrutFlag&esp.UART_INT_ENA_FRM_ERR_INT_ENA {
uart.DataErrorDetected = true
}
if 0 != interrutFlag&esp.UART_INT_ENA_RXFIFO_OVF_INT_ENA {
uart.DataOverflowDetected = true
}
if 0 != interrutFlag&esp.UART_INT_ENA_GLITCH_DET_INT_ENA {
uart.DataErrorDetected = true
}
// Clear the UART interrupt status
uart.Bus.INT_CLR.SetBits(interrutFlag)
uart.Bus.INT_CLR.ClearBits(interrutFlag)
// Enable interrupts
uart.Bus.INT_ENA.Set(uartInterrupts)
}
const uart_empty_thresh_default = 10
func (uart *UART) enableTransmitter() {
uart.Bus.SetCONF0_TXFIFO_RST(1)
uart.Bus.SetCONF0_TXFIFO_RST(0)
// TXINFO empty threshold is when txfifo_empty_int interrupt produced after the amount of data in Tx-FIFO is less than this register value.
uart.Bus.SetCONF1_TXFIFO_EMPTY_THRHD(uart_empty_thresh_default)
// we are not using interrut on TX since write we are waiting for FIFO to have space.
// uart.Bus.INT_ENA.SetBits(esp.UART_INT_ENA_TXFIFO_EMPTY_INT_ENA)
}
func (uart *UART) enableReceiver() {
uart.Bus.SetCONF0_RXFIFO_RST(1)
uart.Bus.SetCONF0_RXFIFO_RST(0)
// using value 1 so that we can start populate ring buffer with data as we get it
uart.Bus.SetCONF1_RXFIFO_FULL_THRHD(1)
// enable interrupts for:
uart.Bus.SetINT_ENA_RXFIFO_FULL_INT_ENA(1)
uart.Bus.SetINT_ENA_FRM_ERR_INT_ENA(1)
uart.Bus.SetINT_ENA_PARITY_ERR_INT_ENA(1)
uart.Bus.SetINT_ENA_GLITCH_DET_INT_ENA(1)
uart.Bus.SetINT_ENA_RXFIFO_OVF_INT_ENA(1)
Bus *esp.UART_Type
Buffer *RingBuffer
}
func (uart *UART) WriteByte(b byte) error {
+4 -4
View File
@@ -236,10 +236,10 @@ func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
kendryte.GPIOHS.RISE_IE.ClearBits(1 << pin)
// Acknowledge interrupt atomically.
riscv.AsmFull(
"amoor.w {}, {mask}, ({reg})",
"amoor.w {}, {mask}, {reg}",
map[string]interface{}{
"mask": uint32(1 << pin),
"reg": uintptr(unsafe.Pointer(&kendryte.GPIOHS.RISE_IP.Reg)),
"reg": &kendryte.GPIOHS.RISE_IP.Reg,
})
kendryte.GPIOHS.RISE_IE.SetBits(1 << pin)
}
@@ -248,10 +248,10 @@ func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
kendryte.GPIOHS.FALL_IE.ClearBits(1 << pin)
// Acknowledge interrupt atomically.
riscv.AsmFull(
"amoor.w {}, {mask}, ({reg})",
"amoor.w {}, {mask}, {reg}",
map[string]interface{}{
"mask": uint32(1 << pin),
"reg": uintptr(unsafe.Pointer(&kendryte.GPIOHS.FALL_IP.Reg)),
"reg": &kendryte.GPIOHS.FALL_IP.Reg,
})
kendryte.GPIOHS.FALL_IE.SetBits(1 << pin)
}
-176
View File
@@ -883,179 +883,3 @@ func (p Pin) getMuxMode(config PinConfig) uint32 {
panic("machine: invalid pin mode")
}
}
// maximum ADC value for the currently configured resolution (used for scaling)
var adcMaximum uint32
// InitADC is not used by this machine. Use `(ADC).Configure()`.
func InitADC() {}
// Configure initializes the receiver's ADC peripheral and pin for analog input.
func (a ADC) Configure(config ADCConfig) {
// if not specified, use defaults: 10-bit resolution, 4 samples/conversion
const (
defaultResolution = uint32(10)
defaultSamples = uint32(4)
)
a.Pin.Configure(PinConfig{Mode: PinInputAnalog})
resolution, samples := config.Resolution, config.Samples
if 0 == resolution {
resolution = defaultResolution
}
if 0 == samples {
samples = defaultSamples
}
if resolution > 12 {
resolution = 12 // maximum resolution of 12 bits
}
adcMaximum = (uint32(1) << resolution) - 1
mode, average := a.mode(resolution, samples)
nxp.ADC1.CFG.Set(mode | nxp.ADC_CFG_ADHSC) // configure ADC1
nxp.ADC2.CFG.Set(mode | nxp.ADC_CFG_ADHSC) // configure ADC2
// begin calibration
nxp.ADC1.GC.Set(average | nxp.ADC_GC_CAL)
nxp.ADC2.GC.Set(average | nxp.ADC_GC_CAL)
for a.isCalibrating() {
} // wait for calibration
}
// Get performs a single ADC conversion, returning a 16-bit unsigned integer.
// The value returned will be scaled (uniformly distributed) if necessary so
// that it is always in the range [0..65535], regardless of the ADC's configured
// bit size (resolution).
func (a ADC) Get() uint16 {
if ch1, ch2, ok := a.Pin.getADCChannel(); ok {
for a.isCalibrating() {
} // wait for calibration
var val uint32
if noADCChannel != ch1 {
nxp.ADC1.HC0.Set(uint32(ch1))
for !nxp.ADC1.HS.HasBits(nxp.ADC_HS_COCO0) {
}
val = nxp.ADC1.R0.Get() & 0xFFFF
} else {
nxp.ADC2.HC0.Set(uint32(ch2))
for !nxp.ADC2.HS.HasBits(nxp.ADC_HS_COCO0) {
}
val = nxp.ADC2.R0.Get() & 0xFFFF
}
// should never be zero, but just in case, use UINT16_MAX so that the scalar
// gets factored out of the conversion result, leaving the original reading
// to be returned unaltered/unscaled.
if adcMaximum == 0 {
adcMaximum = 0xFFFF
}
// scale up to a 16-bit value
return uint16((val * 0xFFFF) / adcMaximum)
}
return 0
}
// mode constructs bit masks for mode and average - used in ADC configuration
// registers - from a given ADC bit size (resolution) and sample count.
func (a ADC) mode(resolution, samples uint32) (mode, average uint32) {
// use asynchronous clock (ADACK) (0 = IPG, 1 = IPG/2, or 3 = ADACK)
mode = (nxp.ADC_CFG_ADICLK_ADICLK_3 << nxp.ADC_CFG_ADICLK_Pos) & nxp.ADC_CFG_ADICLK_Msk
// input clock DIV2 (0 = DIV1, 1 = DIV2, 2 = DIV4, or 3 = DIV8)
mode |= (nxp.ADC_CFG_ADIV_ADIV_1 << nxp.ADC_CFG_ADIV_Pos) & nxp.ADC_CFG_ADIV_Msk
switch resolution {
case 8: // 8-bit conversion, sample period (ADC clocks) = 8
mode |= (nxp.ADC_CFG_MODE_MODE_0 << nxp.ADC_CFG_MODE_Pos) & nxp.ADC_CFG_MODE_Msk
mode |= (nxp.ADC_CFG_ADSTS_ADSTS_3 << nxp.ADC_CFG_ADSTS_Pos) & nxp.ADC_CFG_ADSTS_Msk
case 12: // 12-bit conversion, sample period (ADC clocks) = 24
mode |= (nxp.ADC_CFG_MODE_MODE_2 << nxp.ADC_CFG_MODE_Pos) & nxp.ADC_CFG_MODE_Msk
mode |= (nxp.ADC_CFG_ADSTS_ADSTS_3 << nxp.ADC_CFG_ADSTS_Pos) & nxp.ADC_CFG_ADSTS_Msk
mode |= nxp.ADC_CFG_ADLSMP
default: // 10-bit conversion, sample period (ADC clocks) = 20
mode |= (nxp.ADC_CFG_MODE_MODE_1 << nxp.ADC_CFG_MODE_Pos) & nxp.ADC_CFG_MODE_Msk
mode |= (nxp.ADC_CFG_ADSTS_ADSTS_2 << nxp.ADC_CFG_ADSTS_Pos) & nxp.ADC_CFG_ADSTS_Msk
mode |= nxp.ADC_CFG_ADLSMP
}
if samples >= 4 {
if samples >= 32 {
// 32 samples averaged
mode |= (nxp.ADC_CFG_AVGS_AVGS_3 << nxp.ADC_CFG_AVGS_Pos) & nxp.ADC_CFG_AVGS_Msk
} else if samples >= 16 {
// 16 samples averaged
mode |= (nxp.ADC_CFG_AVGS_AVGS_2 << nxp.ADC_CFG_AVGS_Pos) & nxp.ADC_CFG_AVGS_Msk
} else if samples >= 8 {
// 8 samples averaged
mode |= (nxp.ADC_CFG_AVGS_AVGS_1 << nxp.ADC_CFG_AVGS_Pos) & nxp.ADC_CFG_AVGS_Msk
} else {
// 4 samples averaged
mode |= (nxp.ADC_CFG_AVGS_AVGS_0 << nxp.ADC_CFG_AVGS_Pos) & nxp.ADC_CFG_AVGS_Msk
}
average = nxp.ADC_GC_AVGE
}
return mode, average
}
// isCalibrating returns true if and only if either one (or both) of ADC1 and
// ADC2 have their calibrating flags set. ADC reads must wait until these flags
// are clear before attempting a conversion.
func (a ADC) isCalibrating() bool {
return nxp.ADC1.GC.HasBits(nxp.ADC_GC_CAL) || nxp.ADC2.GC.HasBits(nxp.ADC_GC_CAL)
}
const noADCChannel = uint8(0xFF)
// getADCChannel returns the input channel for ADC1/ADC2 of the receiver Pin p.
func (p Pin) getADCChannel() (adc1, adc2 uint8, ok bool) {
switch p {
case PA12: // [AD_B0_12]: ADC1_IN1 ~
return 1, noADCChannel, true
case PA13: // [AD_B0_13]: ADC1_IN2 ~
return 2, noADCChannel, true
case PA14: // [AD_B0_14]: ADC1_IN3 ~
return 3, noADCChannel, true
case PA15: // [AD_B0_15]: ADC1_IN4 ~
return 4, noADCChannel, true
case PA16: // [AD_B1_00]: ADC1_IN5 ADC2_IN5
return 5, 5, true
case PA17: // [AD_B1_01]: ADC1_IN6 ADC2_IN6
return 6, 6, true
case PA18: // [AD_B1_02]: ADC1_IN7 ADC2_IN7
return 7, 7, true
case PA19: // [AD_B1_03]: ADC1_IN8 ADC2_IN8
return 8, 8, true
case PA20: // [AD_B1_04]: ADC1_IN9 ADC2_IN9
return 9, 9, true
case PA21: // [AD_B1_05]: ADC1_IN10 ADC2_IN10
return 10, 10, true
case PA22: // [AD_B1_06]: ADC1_IN11 ADC2_IN11
return 11, 11, true
case PA23: // [AD_B1_07]: ADC1_IN12 ADC2_IN12
return 12, 12, true
case PA24: // [AD_B1_08]: ADC1_IN13 ADC2_IN13
return 13, 13, true
case PA25: // [AD_B1_09]: ADC1_IN14 ADC2_IN14
return 14, 14, true
case PA26: // [AD_B1_10]: ADC1_IN15 ADC2_IN15
return 15, 15, true
case PA27: // [AD_B1_11]: ADC1_IN0 ADC2_IN0
return 16, 16, true
case PA28: // [AD_B1_12]: ~ ADC2_IN1
return noADCChannel, 1, true
case PA29: // [AD_B1_13]: ~ ADC2_IN2
return noADCChannel, 2, true
case PA30: // [AD_B1_14]: ~ ADC2_IN3
return noADCChannel, 3, true
case PA31: // [AD_B1_15]: ~ ADC2_IN4
return noADCChannel, 4, true
default:
return noADCChannel, noADCChannel, false
}
}
-252
View File
@@ -1,252 +0,0 @@
//go:build mimxrt1062
// +build mimxrt1062
package machine
// SPI peripheral abstraction layer for the MIMXRT1062
import (
"device/nxp"
"errors"
"unsafe"
)
// SPIConfig is used to store config info for SPI.
type SPIConfig struct {
Frequency uint32
SDI Pin
SDO Pin
SCK Pin
CS Pin
LSBFirst bool
Mode uint8
}
func (c SPIConfig) getPins() (di, do, ck, cs Pin) {
if 0 == c.SDI && 0 == c.SDO && 0 == c.SCK && 0 == c.CS {
// default pins if none specified
return SPI_SDI_PIN, SPI_SDO_PIN, SPI_SCK_PIN, SPI_CS_PIN
}
return c.SDI, c.SDO, c.SCK, c.CS
}
type SPI struct {
Bus *nxp.LPSPI_Type
// these hold the input selector ("daisy chain") values that select which pins
// are connected to the LPSPI device, and should be defined where the SPI
// instance is declared (e.g., in the board definition). see the godoc
// comments on type muxSelect for more details.
muxSDI, muxSDO, muxSCK, muxCS muxSelect
// these are copied from SPIConfig, during (*SPI).Configure(SPIConfig), and
// should be considered read-only for internal reference (i.e., modifying them
// will have no desirable effect).
sdi, sdo, sck, cs Pin
frequency uint32
// auxiliary state data used internally
configured bool
}
const (
statusTxDataRequest = nxp.LPSPI_SR_TDF // Transmit data flag
statusRxDataReady = nxp.LPSPI_SR_RDF // Receive data flag
statusWordComplete = nxp.LPSPI_SR_WCF // Word Complete flag
statusFrameComplete = nxp.LPSPI_SR_FCF // Frame Complete flag
statusTransferComplete = nxp.LPSPI_SR_TCF // Transfer Complete flag
statusTransmitError = nxp.LPSPI_SR_TEF // Transmit Error flag (FIFO underrun)
statusReceiveError = nxp.LPSPI_SR_REF // Receive Error flag (FIFO overrun)
statusDataMatch = nxp.LPSPI_SR_DMF // Data Match flag
statusModuleBusy = nxp.LPSPI_SR_MBF // Module Busy flag
statusAll = nxp.LPSPI_SR_TDF | nxp.LPSPI_SR_RDF |
nxp.LPSPI_SR_WCF | nxp.LPSPI_SR_FCF | nxp.LPSPI_SR_TCF | nxp.LPSPI_SR_TEF |
nxp.LPSPI_SR_REF | nxp.LPSPI_SR_DMF | nxp.LPSPI_SR_MBF
)
var (
errSPINotConfigured = errors.New("SPI interface is not yet configured")
)
// Configure is intended to setup an SPI interface for transmit/receive.
func (spi *SPI) Configure(config SPIConfig) {
const defaultSpiFreq = 4000000 // 4 MHz
// init pins
spi.sdi, spi.sdo, spi.sck, spi.cs = config.getPins()
// configure the mux and pad control registers
spi.sdi.Configure(PinConfig{Mode: PinModeSPISDI})
spi.sdo.Configure(PinConfig{Mode: PinModeSPISDO})
spi.sck.Configure(PinConfig{Mode: PinModeSPICLK})
spi.cs.Configure(PinConfig{Mode: PinModeSPICS})
// configure the mux input selector
spi.muxSDI.connect()
spi.muxSDO.connect()
spi.muxSCK.connect()
spi.muxCS.connect()
// software reset of LPSPI state registers
spi.Bus.CR.SetBits(nxp.LPSPI_CR_RST)
// also reset FIFOs (not performed by software reset above)
spi.Bus.CR.SetBits(nxp.LPSPI_CR_RRF | nxp.LPSPI_CR_RTF)
spi.Bus.CR.Set(0)
// set controller mode, and input data is sampled on delayed SCK edge
spi.Bus.CFGR1.Set(nxp.LPSPI_CFGR1_MASTER | nxp.LPSPI_CFGR1_SAMPLE)
spi.frequency = config.Frequency
if 0 == spi.frequency {
spi.frequency = defaultSpiFreq
}
// configure LPSPI clock divisor and CS assertion delays
div := spi.getClockDivisor(config.Frequency)
ccr := (div << nxp.LPSPI_CCR_SCKDIV_Pos) & nxp.LPSPI_CCR_SCKDIV_Msk
ccr |= ((div / 2) << nxp.LPSPI_CCR_DBT_Pos) & nxp.LPSPI_CCR_DBT_Msk
ccr |= ((div / 2) << nxp.LPSPI_CCR_PCSSCK_Pos) & nxp.LPSPI_CCR_PCSSCK_Msk
spi.Bus.CCR.Set(ccr)
// 8-bit frame size (words)
tcr := uint32(7)
if config.LSBFirst {
tcr |= nxp.LPSPI_TCR_LSBF
}
// set polarity and phase
switch config.Mode {
case Mode1:
tcr |= nxp.LPSPI_TCR_CPHA
case Mode2:
tcr |= nxp.LPSPI_TCR_CPOL
case Mode3:
tcr |= nxp.LPSPI_TCR_CPOL
tcr |= nxp.LPSPI_TCR_CPHA
}
spi.Bus.TCR.Set(tcr)
// clear FIFO water marks
spi.setWatermark(0, 0)
// enable LPSPI module
spi.Bus.CR.Set(nxp.LPSPI_CR_MEN)
spi.configured = true
}
// Transfer writes/reads a single byte using the SPI interface.
func (spi *SPI) Transfer(w byte) (byte, error) {
if !spi.configured {
return 0, errSPINotConfigured
}
const readTryMax = 10000
for spi.Bus.SR.HasBits(statusModuleBusy) {
} // wait for SPI busy bit to clear
_, txFIFOSize := spi.getFIFOSize()
spi.flushFIFO(true, true)
spi.Bus.SR.Set(statusAll) // clear all status flags (W1C)
// enable LPSPI module
spi.Bus.CR.Set(nxp.LPSPI_CR_MEN)
// TODO: unnecessary since we just flushed the FIFO?
for { // wait for TX FIFO to not be full
if _, txFIFO := spi.getFIFOCount(); txFIFO < txFIFOSize {
break
}
}
// write out byte to TX FIFO
spi.Bus.TDR.Set(uint32(w))
// try to read from RX FIFO if anything exists
didRead := false
data := byte(0)
for i := 0; !didRead && (i < readTryMax); i++ {
rxFIFO, _ := spi.getFIFOCount()
didRead = rxFIFO > 0
if didRead {
data = byte(spi.Bus.RDR.Get())
}
}
// if nothing was read, then wait for transfer complete flag to decide when
// we are finished
if !didRead {
for !spi.Bus.SR.HasBits(nxp.LPSPI_SR_TCF) {
} // wait for all transfers complete flag to set
}
return data, nil
}
func (spi *SPI) isHardwareCSPin(pin Pin) bool {
switch unsafe.Pointer(spi.Bus) {
case unsafe.Pointer(nxp.LPSPI1):
return SPI1_CS_PIN == pin
case unsafe.Pointer(nxp.LPSPI2):
return SPI2_CS_PIN == pin
case unsafe.Pointer(nxp.LPSPI3):
return SPI3_CS_PIN == pin
}
return false
}
func (spi *SPI) hasHardwareCSPin() bool {
return spi.isHardwareCSPin(spi.cs)
}
// getClockDivisor finds the SPI prescalar that minimizes the error between
// requested frequency and possible frequencies available with the LPSPI clock.
// this routine is based on Teensyduino (libraries/SPI/SPI.cpp):
// `void SPIClass::setClockDivider_noInline(uint32_t clk)`
func (spi *SPI) getClockDivisor(freq uint32) uint32 {
const clock = 132000000 // LPSPI root clock frequency (PLL2)
d := uint32(clock)
if freq > 0 {
d /= freq
}
if d > 0 && clock/d > freq {
d++
}
if d > 257 {
return 255
}
if d > 2 {
return d - 2
}
return 0
}
func (spi *SPI) getFIFOSize() (rx, tx uint32) {
param := spi.Bus.PARAM.Get()
return uint32(1) << ((param & nxp.LPSPI_PARAM_RXFIFO_Msk) >> nxp.LPSPI_PARAM_RXFIFO_Pos),
uint32(1) << ((param & nxp.LPSPI_PARAM_TXFIFO_Msk) >> nxp.LPSPI_PARAM_TXFIFO_Pos)
}
func (spi *SPI) getFIFOCount() (rx, tx uint32) {
fsr := spi.Bus.FSR.Get()
return (fsr & nxp.LPSPI_FSR_RXCOUNT_Msk) >> nxp.LPSPI_FSR_RXCOUNT_Pos,
(fsr & nxp.LPSPI_FSR_TXCOUNT_Msk) >> nxp.LPSPI_FSR_TXCOUNT_Pos
}
func (spi *SPI) flushFIFO(rx, tx bool) {
var flush uint32
if rx {
flush |= nxp.LPSPI_CR_RRF
}
if tx {
flush |= nxp.LPSPI_CR_RTF
}
spi.Bus.CR.SetBits(flush)
}
func (spi *SPI) setWatermark(rx, tx uint32) {
spi.Bus.FCR.Set(((rx << nxp.LPSPI_FCR_RXWATER_Pos) & nxp.LPSPI_FCR_RXWATER_Msk) |
((tx << nxp.LPSPI_FCR_TXWATER_Pos) & nxp.LPSPI_FCR_TXWATER_Msk))
}
+52
View File
@@ -7,6 +7,58 @@ import (
"device/nrf"
)
// Hardware pins
const (
P0_00 Pin = 0
P0_01 Pin = 1
P0_02 Pin = 2
P0_03 Pin = 3
P0_04 Pin = 4
P0_05 Pin = 5
P0_06 Pin = 6
P0_07 Pin = 7
P0_08 Pin = 8
P0_09 Pin = 9
P0_10 Pin = 10
P0_11 Pin = 11
P0_12 Pin = 12
P0_13 Pin = 13
P0_14 Pin = 14
P0_15 Pin = 15
P0_16 Pin = 16
P0_17 Pin = 17
P0_18 Pin = 18
P0_19 Pin = 19
P0_20 Pin = 20
P0_21 Pin = 21
P0_22 Pin = 22
P0_23 Pin = 23
P0_24 Pin = 24
P0_25 Pin = 25
P0_26 Pin = 26
P0_27 Pin = 27
P0_28 Pin = 28
P0_29 Pin = 29
P0_30 Pin = 30
P0_31 Pin = 31
P1_00 Pin = 32
P1_01 Pin = 33
P1_02 Pin = 34
P1_03 Pin = 35
P1_04 Pin = 36
P1_05 Pin = 37
P1_06 Pin = 38
P1_07 Pin = 39
P1_08 Pin = 40
P1_09 Pin = 41
P1_10 Pin = 42
P1_11 Pin = 43
P1_12 Pin = 44
P1_13 Pin = 45
P1_14 Pin = 46
P1_15 Pin = 47
)
// Get peripheral and pin number for this GPIO pin.
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
if p >= 32 {
-3
View File
@@ -60,9 +60,6 @@ var (
ErrNotConfigured = errors.New("device has not been configured")
)
//go:linkname gosched runtime.Gosched
func gosched()
// PutcharUART writes a byte to the UART synchronously, without using interrupts
// or calling the scheduler
func PutcharUART(u *UART, c byte) {
-5
View File
@@ -86,11 +86,6 @@ func ticks() uint64 {
return timer.timeElapsed()
}
//go:linkname lightSleep runtime.machineLightSleep
func lightSleep(ticks uint64) {
timer.lightSleep(ticks)
}
// UART pins
const (
UART_TX_PIN = UART0_TX_PIN

Some files were not shown because too many files have changed in this diff Show More