mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-27 15:18:41 +00:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 65a059a973 | |||
| cb95259adf | |||
| 24d718fa05 | |||
| 174d492355 | |||
| 5355473dce | |||
| a531ed614a | |||
| 24ae6fdf29 | |||
| 938ce22307 | |||
| 9fb5a5b9a4 | |||
| 9fd9d9c05a | |||
| fd50227a3d | |||
| a90295430c | |||
| fa4ca63ff2 | |||
| 7019c4e8fc | |||
| b79e0e8528 | |||
| 51bed3afae | |||
| 4d4ccddad8 | |||
| f55f5315cc | |||
| 7468a00ef4 | |||
| 8d77278c6b | |||
| dde9b5ad3a | |||
| 2d4307647e | |||
| 935a293106 | |||
| 72b715fa99 | |||
| d801d0cd53 | |||
| c2f1965e03 | |||
| 75bba42b60 | |||
| 18b50db0dc | |||
| 499fce9cee | |||
| 88b29589d6 | |||
| 8cbfbcae5a | |||
| 5cd8ba2421 |
+15
-6
@@ -5,7 +5,7 @@ commands:
|
||||
steps:
|
||||
- run:
|
||||
name: "Pull submodules"
|
||||
command: git submodule update --init
|
||||
command: git submodule update --init --recursive
|
||||
llvm-source-linux:
|
||||
steps:
|
||||
- restore_cache:
|
||||
@@ -55,7 +55,7 @@ commands:
|
||||
- run:
|
||||
name: "Install apt dependencies"
|
||||
command: |
|
||||
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
|
||||
echo 'deb https://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
||||
apt-get update
|
||||
apt-get install --no-install-recommends -y \
|
||||
@@ -69,8 +69,8 @@ commands:
|
||||
- build-binaryen-linux
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-v3-{{ checksum "go.mod" }}
|
||||
- go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
|
||||
- go-cache-v4-{{ checksum "go.mod" }}
|
||||
- llvm-source-linux
|
||||
- run: go install -tags=llvm<<parameters.llvm>> .
|
||||
- restore_cache:
|
||||
@@ -92,7 +92,7 @@ commands:
|
||||
- run: make gen-device -j4
|
||||
- run: make smoketest XTENSA=0
|
||||
- save_cache:
|
||||
key: go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
key: go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
|
||||
paths:
|
||||
- ~/.cache/go-build
|
||||
- /go/pkg/mod
|
||||
@@ -100,11 +100,18 @@ commands:
|
||||
jobs:
|
||||
test-llvm15-go118:
|
||||
docker:
|
||||
- image: golang:1.18-buster
|
||||
- image: golang:1.18-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "15"
|
||||
resource_class: large
|
||||
test-llvm17-go121:
|
||||
docker:
|
||||
- image: golang:1.21-bullseye
|
||||
steps:
|
||||
- test-linux:
|
||||
llvm: "17"
|
||||
resource_class: large
|
||||
|
||||
workflows:
|
||||
test-all:
|
||||
@@ -112,3 +119,5 @@ workflows:
|
||||
# This tests our lowest supported versions of Go and LLVM, to make sure at
|
||||
# least the smoke tests still pass.
|
||||
- test-llvm15-go118
|
||||
# This tests the upcoming LLVM 17 support.
|
||||
- test-llvm17-go121
|
||||
|
||||
@@ -116,11 +116,21 @@ jobs:
|
||||
test-macos-homebrew:
|
||||
name: homebrew-install
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
version: [16, 17]
|
||||
steps:
|
||||
- name: Install LLVM
|
||||
shell: bash
|
||||
- name: Update Homebrew
|
||||
run: brew update
|
||||
- name: Fix Python symlinks
|
||||
run: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@16
|
||||
# Github runners have broken symlinks, so relink
|
||||
# see: https://github.com/actions/setup-python/issues/577
|
||||
brew unlink python
|
||||
brew link --overwrite python
|
||||
- name: Install LLVM
|
||||
run: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@${{ matrix.version }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Go
|
||||
@@ -128,7 +138,13 @@ jobs:
|
||||
with:
|
||||
go-version: '1.21'
|
||||
cache: true
|
||||
- name: Build TinyGo
|
||||
run: go install
|
||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||
run: go install -tags=llvm${{ matrix.version }}
|
||||
- name: Check binary
|
||||
run: tinygo version
|
||||
- name: Build TinyGo (default LLVM)
|
||||
if: matrix.version == 16
|
||||
run: go install
|
||||
- name: Check binary
|
||||
if: matrix.version == 16
|
||||
run: tinygo version
|
||||
|
||||
@@ -140,8 +140,8 @@ jobs:
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
|
||||
curl https://github.com/bytecodealliance/wasmtime/releases/download/v5.0.0/wasmtime-v5.0.0-x86_64-linux.tar.xz -o wasmtime-v5.0.0-x86_64-linux.tar.xz -SfL
|
||||
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v5.0.0-x86_64-linux.tar.xz --strip-components=1 wasmtime-v5.0.0-x86_64-linux/*
|
||||
curl https://github.com/bytecodealliance/wasmtime/releases/download/v14.0.4/wasmtime-v14.0.4-x86_64-linux.tar.xz -o wasmtime-v14.0.4-x86_64-linux.tar.xz -SfL
|
||||
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v14.0.4-x86_64-linux.tar.xz --strip-components=1 wasmtime-v14.0.4-x86_64-linux/*
|
||||
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
||||
- name: Download release artifact
|
||||
uses: actions/download-artifact@v3
|
||||
@@ -187,8 +187,8 @@ jobs:
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
|
||||
curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v5.0.0/wasmtime-v5.0.0-x86_64-linux.tar.xz -o wasmtime-v5.0.0-x86_64-linux.tar.xz -SfL
|
||||
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v5.0.0-x86_64-linux.tar.xz --strip-components=1 wasmtime-v5.0.0-x86_64-linux/*
|
||||
curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v14.0.4/wasmtime-v14.0.4-x86_64-linux.tar.xz -o wasmtime-v14.0.4-x86_64-linux.tar.xz -SfL
|
||||
tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v14.0.4-x86_64-linux.tar.xz --strip-components=1 wasmtime-v14.0.4-x86_64-linux/*
|
||||
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
@@ -356,13 +356,13 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
id: cache-binaryen
|
||||
with:
|
||||
key: binaryen-linux-${{ matrix.goarch }}-v1
|
||||
key: binaryen-linux-${{ matrix.goarch }}-v3
|
||||
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
|
||||
git submodule update --init --recursive lib/binaryen
|
||||
make CROSS=${{ matrix.toolchain }} binaryen
|
||||
- name: Install fpm
|
||||
run: |
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Nix
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
nix-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Pull musl
|
||||
run: |
|
||||
git submodule update --init lib/musl
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v3
|
||||
id: cache-llvm-source
|
||||
with:
|
||||
key: llvm-source-16-linux-nix-v1
|
||||
path: |
|
||||
llvm-project/compiler-rt
|
||||
- name: Download LLVM source
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
run: make llvm-source
|
||||
- name: Save LLVM source cache
|
||||
uses: actions/cache/save@v3
|
||||
if: steps.cache-llvm-source.outputs.cache-hit != 'true'
|
||||
with:
|
||||
key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }}
|
||||
path: |
|
||||
llvm-project/compiler-rt
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- name: Test
|
||||
run: |
|
||||
nix develop --ignore-environment --keep HOME --command bash -c "go install && ~/go/bin/tinygo version && ~/go/bin/tinygo build -o test ./testdata/cgo"
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
run: make wasi-libc
|
||||
- name: Install wasmtime
|
||||
run: |
|
||||
scoop install wasmtime
|
||||
scoop install wasmtime@14.0.4
|
||||
- name: make gen-device
|
||||
run: make -j3 gen-device
|
||||
- name: Test TinyGo
|
||||
@@ -203,7 +203,7 @@ jobs:
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
scoop install binaryen wasmtime
|
||||
scoop install binaryen && scoop install wasmtime@14.0.4
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Go
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ Now that we have a working static build, it's time to make a release tarball:
|
||||
|
||||
If you did not clone the repository with the `--recursive` option, you will get errors until you initialize the project submodules:
|
||||
|
||||
git submodule update --init
|
||||
git submodule update --init --recursive
|
||||
|
||||
The release tarball is stored in build/release.tar.gz, and can be extracted with
|
||||
the following command (for example in ~/lib):
|
||||
|
||||
+12
-10
@@ -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 = 16 15
|
||||
LLVM_VERSIONS = 17 16 15
|
||||
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)
|
||||
@@ -32,9 +32,6 @@ export GOROOT = $(shell $(GO) env GOROOT)
|
||||
# Flags to pass to go test.
|
||||
GOTESTFLAGS ?=
|
||||
|
||||
# md5sum binary
|
||||
MD5SUM = md5sum
|
||||
|
||||
# tinygo binary for tests
|
||||
TINYGO ?= $(call detect,tinygo,tinygo $(CURDIR)/build/tinygo)
|
||||
|
||||
@@ -130,14 +127,14 @@ ifeq ($(OS),Windows_NT)
|
||||
USE_SYSTEM_BINARYEN ?= 1
|
||||
|
||||
else ifeq ($(shell uname -s),Darwin)
|
||||
MD5SUM = md5
|
||||
MD5SUM ?= md5
|
||||
|
||||
CGO_LDFLAGS += -lxar
|
||||
|
||||
USE_SYSTEM_BINARYEN ?= 1
|
||||
|
||||
else ifeq ($(shell uname -s),FreeBSD)
|
||||
MD5SUM = md5
|
||||
MD5SUM ?= md5
|
||||
START_GROUP = -Wl,--start-group
|
||||
END_GROUP = -Wl,--end-group
|
||||
else
|
||||
@@ -145,6 +142,9 @@ else
|
||||
END_GROUP = -Wl,--end-group
|
||||
endif
|
||||
|
||||
# md5sum binary default, can be overridden by an environment variable
|
||||
MD5SUM ?= md5sum
|
||||
|
||||
# Libraries that should be linked in for the statically linked Clang.
|
||||
CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGen clangCrossTU clangDriver clangDynamicASTMatchers clangEdit clangExtractAPI clangFormat clangFrontend clangFrontendTool clangHandleCXX clangHandleLLVM clangIndex clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization clangSupport clangTooling clangToolingASTDiff clangToolingCore clangToolingInclusions
|
||||
CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++
|
||||
@@ -190,7 +190,7 @@ gen-device: gen-device-stm32
|
||||
endif
|
||||
|
||||
gen-device-avr:
|
||||
@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||
@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init --recursive"; exit 1; fi
|
||||
$(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/
|
||||
./build/gen-device-avr lib/avr/packs/atmega src/device/avr/
|
||||
./build/gen-device-avr lib/avr/packs/tiny src/device/avr/
|
||||
@@ -264,8 +264,8 @@ endif
|
||||
.PHONY: wasi-libc
|
||||
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
|
||||
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
|
||||
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||
cd lib/wasi-libc && $(MAKE) -j4 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC=$(CLANG) AR=$(LLVM_AR) NM=$(LLVM_NM)
|
||||
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init --recursive"; exit 1; fi
|
||||
cd lib/wasi-libc && $(MAKE) -j4 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC="$(CLANG)" AR=$(LLVM_AR) NM=$(LLVM_NM)
|
||||
|
||||
# Check for Node.js used during WASM tests.
|
||||
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
|
||||
@@ -506,6 +506,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id
|
||||
@$(MD5SUM) test.hex
|
||||
# test simulated boards on play.tinygo.org
|
||||
ifneq ($(WASM), 0)
|
||||
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||
@@ -878,7 +880,7 @@ deb:
|
||||
@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
|
||||
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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# TinyGo - Go compiler for small places
|
||||
|
||||
[](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
|
||||
[](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml) [](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
|
||||
|
||||
TinyGo is a Go compiler intended for use in small places such as microcontrollers, WebAssembly (wasm/wasi), and command-line tools.
|
||||
|
||||
|
||||
+31
-29
@@ -114,6 +114,30 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
cacheDir = tmpdir
|
||||
}
|
||||
|
||||
// Create default global values.
|
||||
globalValues := map[string]map[string]string{
|
||||
"runtime": {
|
||||
"buildVersion": goenv.Version(),
|
||||
},
|
||||
"testing": {},
|
||||
}
|
||||
if config.TestConfig.CompileTestBinary {
|
||||
// The testing.testBinary is set to "1" when in a test.
|
||||
// This is needed for testing.Testing() to work correctly.
|
||||
globalValues["testing"]["testBinary"] = "1"
|
||||
}
|
||||
|
||||
// Copy over explicitly set global values, like
|
||||
// -ldflags="-X main.Version="1.0"
|
||||
for pkgPath, vals := range config.Options.GlobalValues {
|
||||
if _, ok := globalValues[pkgPath]; !ok {
|
||||
globalValues[pkgPath] = map[string]string{}
|
||||
}
|
||||
for k, v := range vals {
|
||||
globalValues[pkgPath][k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Check for a libc dependency.
|
||||
// As a side effect, this also creates the headers for the given libc, if
|
||||
// the libc needs them.
|
||||
@@ -168,7 +192,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
CodeModel: config.CodeModel(),
|
||||
RelocationModel: config.RelocationModel(),
|
||||
SizeLevel: sizeLevel,
|
||||
TinyGoVersion: goenv.Version,
|
||||
TinyGoVersion: goenv.Version(),
|
||||
|
||||
Scheduler: config.Scheduler(),
|
||||
AutomaticStackSize: config.AutomaticStackSize(),
|
||||
@@ -187,7 +211,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
defer machine.Dispose()
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
|
||||
lprogram, err := loader.Load(config, pkgName, types.Config{
|
||||
Sizes: compiler.Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -216,34 +240,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
var packageJobs []*compileJob
|
||||
packageActionIDJobs := make(map[string]*compileJob)
|
||||
|
||||
if config.Options.GlobalValues == nil {
|
||||
config.Options.GlobalValues = make(map[string]map[string]string)
|
||||
}
|
||||
if config.Options.GlobalValues["runtime"]["buildVersion"] == "" {
|
||||
version := goenv.Version
|
||||
if strings.HasSuffix(goenv.Version, "-dev") && goenv.GitSha1 != "" {
|
||||
version += "-" + goenv.GitSha1
|
||||
}
|
||||
if config.Options.GlobalValues["runtime"] == nil {
|
||||
config.Options.GlobalValues["runtime"] = make(map[string]string)
|
||||
}
|
||||
config.Options.GlobalValues["runtime"]["buildVersion"] = version
|
||||
}
|
||||
if config.TestConfig.CompileTestBinary {
|
||||
// The testing.testBinary is set to "1" when in a test.
|
||||
// This is needed for testing.Testing() to work correctly.
|
||||
if config.Options.GlobalValues["testing"] == nil {
|
||||
config.Options.GlobalValues["testing"] = make(map[string]string)
|
||||
}
|
||||
config.Options.GlobalValues["testing"]["testBinary"] = "1"
|
||||
}
|
||||
|
||||
var embedFileObjects []*compileJob
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkg := pkg // necessary to avoid a race condition
|
||||
|
||||
var undefinedGlobals []string
|
||||
for name := range config.Options.GlobalValues[pkg.Pkg.Path()] {
|
||||
for name := range globalValues[pkg.Pkg.Path()] {
|
||||
undefinedGlobals = append(undefinedGlobals, name)
|
||||
}
|
||||
sort.Strings(undefinedGlobals)
|
||||
@@ -571,7 +573,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
|
||||
// Run all optimization passes, which are much more effective now
|
||||
// that the optimizer can see the whole program at once.
|
||||
err := optimizeProgram(mod, config)
|
||||
err := optimizeProgram(mod, config, globalValues)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -660,7 +662,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
job := &compileJob{
|
||||
description: "compile extra file " + path,
|
||||
run: func(job *compileJob) error {
|
||||
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.Options.PrintCommands)
|
||||
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(false), config.Options.PrintCommands)
|
||||
job.result = result
|
||||
return err
|
||||
},
|
||||
@@ -1040,7 +1042,7 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
|
||||
// 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.
|
||||
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues map[string]map[string]string) error {
|
||||
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1059,7 +1061,7 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
}
|
||||
|
||||
// Insert values from -ldflags="-X ..." into the IR.
|
||||
err = setGlobalValues(mod, config.Options.GlobalValues)
|
||||
err = setGlobalValues(mod, globalValues)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -74,7 +73,6 @@ func TestClangAttributes(t *testing.T) {
|
||||
|
||||
func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
||||
testDir := t.TempDir()
|
||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
|
||||
ctx := llvm.NewContext()
|
||||
defer ctx.Dispose()
|
||||
@@ -84,9 +82,8 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
||||
t.Fatalf("could not load target: %s", err)
|
||||
}
|
||||
config := compileopts.Config{
|
||||
Options: options,
|
||||
Target: target,
|
||||
ClangHeaders: clangHeaderPath,
|
||||
Options: options,
|
||||
Target: target,
|
||||
}
|
||||
|
||||
// Create a very simple C input file.
|
||||
@@ -98,7 +95,7 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
|
||||
|
||||
// Compile this file using Clang.
|
||||
outpath := filepath.Join(testDir, "test.bc")
|
||||
flags := append([]string{"-c", "-emit-llvm", "-o", outpath, srcpath}, config.CFlags()...)
|
||||
flags := append([]string{"-c", "-emit-llvm", "-o", outpath, srcpath}, config.CFlags(false)...)
|
||||
if config.GOOS() == "darwin" {
|
||||
// Silence some warnings that happen when testing GOOS=darwin on
|
||||
// something other than MacOS.
|
||||
|
||||
+34
-10
@@ -77,6 +77,15 @@ func ReadBuildID() ([]byte, error) {
|
||||
}
|
||||
return raw[4:], nil
|
||||
}
|
||||
|
||||
// Normally we would have found a build ID by now. But not on Nix,
|
||||
// unfortunately, because Nix adds -no_uuid for some reason:
|
||||
// https://github.com/NixOS/nixpkgs/issues/178366
|
||||
// Fall back to the same implementation that we use for Windows.
|
||||
id, err := readRawGoBuildID(f, 32*1024)
|
||||
if len(id) != 0 || err != nil {
|
||||
return id, err
|
||||
}
|
||||
default:
|
||||
// On other platforms (such as Windows) there isn't such a convenient
|
||||
// build ID. Luckily, Go does have an equivalent of the build ID, which
|
||||
@@ -88,16 +97,31 @@ func ReadBuildID() ([]byte, error) {
|
||||
// directly. Luckily the build ID is always at the start of the file.
|
||||
// For details, see:
|
||||
// https://github.com/golang/go/blob/master/src/cmd/internal/buildid/buildid.go
|
||||
fileStart := make([]byte, 4096)
|
||||
_, err := io.ReadFull(f, fileStart)
|
||||
index := bytes.Index(fileStart, []byte("\xff Go build ID: \""))
|
||||
if index < 0 || index > len(fileStart)-103 {
|
||||
return nil, fmt.Errorf("could not find build id in %s", err)
|
||||
}
|
||||
buf := fileStart[index : index+103]
|
||||
if bytes.HasPrefix(buf, []byte("\xff Go build ID: \"")) && bytes.HasSuffix(buf, []byte("\"\n \xff")) {
|
||||
return buf[len("\xff Go build ID: \"") : len(buf)-1], nil
|
||||
id, err := readRawGoBuildID(f, 4096)
|
||||
if len(id) != 0 || err != nil {
|
||||
return id, err
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("could not find build ID in %s", executable)
|
||||
return nil, fmt.Errorf("could not find build ID in %v", executable)
|
||||
}
|
||||
|
||||
// The Go toolchain stores a build ID in the binary that we can use, as a
|
||||
// fallback if binary file specific build IDs can't be obtained.
|
||||
// This function reads that build ID from the binary.
|
||||
func readRawGoBuildID(f *os.File, prefixSize int) ([]byte, error) {
|
||||
fileStart := make([]byte, prefixSize)
|
||||
_, err := io.ReadFull(f, fileStart)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read build id from %s: %v", f.Name(), err)
|
||||
}
|
||||
index := bytes.Index(fileStart, []byte("\xff Go build ID: \""))
|
||||
if index < 0 || index > len(fileStart)-103 {
|
||||
return nil, fmt.Errorf("could not find build id in %s", f.Name())
|
||||
}
|
||||
buf := fileStart[index : index+103]
|
||||
if bytes.HasPrefix(buf, []byte("\xff Go build ID: \"")) && bytes.HasSuffix(buf, []byte("\"\n \xff")) {
|
||||
return buf[len("\xff Go build ID: \"") : len(buf)-1], nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -33,13 +33,10 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
return nil, fmt.Errorf("requires go version 1.18 through 1.21, got go%d.%d", major, minor)
|
||||
}
|
||||
|
||||
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
|
||||
return &compileopts.Config{
|
||||
Options: options,
|
||||
Target: spec,
|
||||
GoMinorVersion: minor,
|
||||
ClangHeaders: clangHeaderPath,
|
||||
TestConfig: options.TestConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// getClangHeaderPath returns the path to the built-in Clang headers. It tries
|
||||
// multiple locations, which should make it find the directory when installed in
|
||||
// various ways.
|
||||
func getClangHeaderPath(TINYGOROOT string) string {
|
||||
// Check whether we're running from the source directory.
|
||||
path := filepath.Join(TINYGOROOT, "llvm-project", "clang", "lib", "Headers")
|
||||
if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) {
|
||||
return path
|
||||
}
|
||||
|
||||
// Check whether we're running from the installation directory.
|
||||
path = filepath.Join(TINYGOROOT, "lib", "clang", "include")
|
||||
if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) {
|
||||
return path
|
||||
}
|
||||
|
||||
// It looks like we are built with a system-installed LLVM. Do a last
|
||||
// attempt: try to use Clang headers relative to the clang binary.
|
||||
llvmMajor := strings.Split(llvm.Version, ".")[0]
|
||||
for _, cmdName := range commands["clang"] {
|
||||
binpath, err := exec.LookPath(cmdName)
|
||||
if err == nil {
|
||||
// This should be the command that will also be used by
|
||||
// execCommand. To avoid inconsistencies, make sure we use the
|
||||
// headers relative to this command.
|
||||
binpath, err = filepath.EvalSymlinks(binpath)
|
||||
if err != nil {
|
||||
// Unexpected.
|
||||
return ""
|
||||
}
|
||||
// Example executable:
|
||||
// /usr/lib/llvm-9/bin/clang
|
||||
// Example include path:
|
||||
// /usr/lib/llvm-9/lib64/clang/9.0.1/include/
|
||||
llvmRoot := filepath.Dir(filepath.Dir(binpath))
|
||||
clangVersionRoot := filepath.Join(llvmRoot, "lib64", "clang")
|
||||
dirs64, err64 := ioutil.ReadDir(clangVersionRoot)
|
||||
// Example include path:
|
||||
// /usr/lib/llvm-9/lib/clang/9.0.1/include/
|
||||
clangVersionRoot = filepath.Join(llvmRoot, "lib", "clang")
|
||||
dirs32, err32 := ioutil.ReadDir(clangVersionRoot)
|
||||
if err64 != nil && err32 != nil {
|
||||
// Unexpected.
|
||||
continue
|
||||
}
|
||||
dirnames := make([]string, len(dirs64)+len(dirs32))
|
||||
dirCount := 0
|
||||
for _, d := range dirs32 {
|
||||
name := d.Name()
|
||||
if name == llvmMajor || strings.HasPrefix(name, llvmMajor+".") {
|
||||
dirnames[dirCount] = filepath.Join(llvmRoot, "lib", "clang", name)
|
||||
dirCount++
|
||||
}
|
||||
}
|
||||
for _, d := range dirs64 {
|
||||
name := d.Name()
|
||||
if name == llvmMajor || strings.HasPrefix(name, llvmMajor+".") {
|
||||
dirnames[dirCount] = filepath.Join(llvmRoot, "lib64", "clang", name)
|
||||
dirCount++
|
||||
}
|
||||
}
|
||||
sort.Strings(dirnames)
|
||||
// Check for the highest version first.
|
||||
for i := dirCount - 1; i >= 0; i-- {
|
||||
path := filepath.Join(dirnames[i], "include")
|
||||
_, err := os.Stat(filepath.Join(path, "stdint.h"))
|
||||
if err == nil {
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On Arch Linux, the clang executable is stored in /usr/bin rather than being symlinked from there.
|
||||
// Search directly in /usr/lib for clang.
|
||||
if matches, err := filepath.Glob("/usr/lib/clang/" + llvmMajor + ".*.*"); err == nil {
|
||||
// Check for the highest version first.
|
||||
sort.Strings(matches)
|
||||
for i := len(matches) - 1; i >= 0; i-- {
|
||||
path := filepath.Join(matches[i], "include")
|
||||
_, err := os.Stat(filepath.Join(path, "stdint.h"))
|
||||
if err == nil {
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Could not find it.
|
||||
return ""
|
||||
}
|
||||
@@ -143,6 +143,10 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
||||
// reproducible. Otherwise the temporary directory is stored in the archive
|
||||
// itself, which varies each run.
|
||||
args := append(l.cflags(target, headerPath), "-c", "-Oz", "-gdwarf-4", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
||||
resourceDir := goenv.ClangResourceDir(false)
|
||||
if resourceDir != "" {
|
||||
args = append(args, "-resource-dir="+resourceDir)
|
||||
}
|
||||
cpu := config.CPU()
|
||||
if cpu != "" {
|
||||
// X86 has deprecated the -mcpu flag, so we need to use -march instead.
|
||||
|
||||
+93
-79
@@ -3,6 +3,7 @@ package builder
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
@@ -41,91 +42,23 @@ var Picolibc = Library{
|
||||
},
|
||||
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
|
||||
librarySources: func(target string) ([]string, error) {
|
||||
return picolibcSources, nil
|
||||
sources := append([]string(nil), picolibcSources...)
|
||||
if !strings.HasPrefix(target, "avr") {
|
||||
// Small chips without long jumps can't compile many files (printf,
|
||||
// pow, etc). Therefore exclude those source files for those chips.
|
||||
// Unfortunately it's difficult to exclude only some chips, so this
|
||||
// excludes those files on all AVR chips for now.
|
||||
// More information:
|
||||
// https://github.com/llvm/llvm-project/issues/67042
|
||||
sources = append(sources, picolibcSourcesLarge...)
|
||||
}
|
||||
return sources, nil
|
||||
},
|
||||
}
|
||||
|
||||
var picolibcSources = []string{
|
||||
"../../picolibc-stdio.c",
|
||||
|
||||
// srcs_tinystdio
|
||||
"libc/tinystdio/asprintf.c",
|
||||
"libc/tinystdio/bufio.c",
|
||||
"libc/tinystdio/clearerr.c",
|
||||
"libc/tinystdio/ecvt_r.c",
|
||||
"libc/tinystdio/ecvt.c",
|
||||
"libc/tinystdio/ecvtf_r.c",
|
||||
"libc/tinystdio/ecvtf.c",
|
||||
"libc/tinystdio/fcvt.c",
|
||||
"libc/tinystdio/fcvt_r.c",
|
||||
"libc/tinystdio/fcvtf.c",
|
||||
"libc/tinystdio/fcvtf_r.c",
|
||||
"libc/tinystdio/gcvt.c",
|
||||
"libc/tinystdio/gcvtf.c",
|
||||
"libc/tinystdio/fclose.c",
|
||||
"libc/tinystdio/fdevopen.c",
|
||||
"libc/tinystdio/feof.c",
|
||||
"libc/tinystdio/ferror.c",
|
||||
"libc/tinystdio/fflush.c",
|
||||
"libc/tinystdio/fgetc.c",
|
||||
"libc/tinystdio/fgets.c",
|
||||
"libc/tinystdio/fileno.c",
|
||||
"libc/tinystdio/filestrget.c",
|
||||
"libc/tinystdio/filestrput.c",
|
||||
"libc/tinystdio/filestrputalloc.c",
|
||||
"libc/tinystdio/fmemopen.c",
|
||||
"libc/tinystdio/fprintf.c",
|
||||
"libc/tinystdio/fputc.c",
|
||||
"libc/tinystdio/fputs.c",
|
||||
"libc/tinystdio/fread.c",
|
||||
//"libc/tinystdio/freopen.c", // crashes with AVR, see: https://github.com/picolibc/picolibc/pull/369
|
||||
"libc/tinystdio/fscanf.c",
|
||||
"libc/tinystdio/fseek.c",
|
||||
"libc/tinystdio/fseeko.c",
|
||||
"libc/tinystdio/ftell.c",
|
||||
"libc/tinystdio/ftello.c",
|
||||
"libc/tinystdio/fwrite.c",
|
||||
"libc/tinystdio/getchar.c",
|
||||
"libc/tinystdio/gets.c",
|
||||
"libc/tinystdio/matchcaseprefix.c",
|
||||
"libc/tinystdio/mktemp.c",
|
||||
"libc/tinystdio/perror.c",
|
||||
"libc/tinystdio/printf.c",
|
||||
"libc/tinystdio/putchar.c",
|
||||
"libc/tinystdio/puts.c",
|
||||
"libc/tinystdio/rewind.c",
|
||||
"libc/tinystdio/scanf.c",
|
||||
"libc/tinystdio/setbuf.c",
|
||||
"libc/tinystdio/setbuffer.c",
|
||||
"libc/tinystdio/setlinebuf.c",
|
||||
"libc/tinystdio/setvbuf.c",
|
||||
"libc/tinystdio/snprintf.c",
|
||||
"libc/tinystdio/sprintf.c",
|
||||
"libc/tinystdio/snprintfd.c",
|
||||
"libc/tinystdio/snprintff.c",
|
||||
"libc/tinystdio/sprintff.c",
|
||||
"libc/tinystdio/sprintfd.c",
|
||||
"libc/tinystdio/sscanf.c",
|
||||
"libc/tinystdio/strfromf.c",
|
||||
"libc/tinystdio/strfromd.c",
|
||||
"libc/tinystdio/strtof.c",
|
||||
"libc/tinystdio/strtof_l.c",
|
||||
"libc/tinystdio/strtod.c",
|
||||
"libc/tinystdio/strtod_l.c",
|
||||
"libc/tinystdio/ungetc.c",
|
||||
"libc/tinystdio/vasprintf.c",
|
||||
"libc/tinystdio/vfiprintf.c",
|
||||
"libc/tinystdio/vfprintf.c",
|
||||
"libc/tinystdio/vfprintff.c",
|
||||
"libc/tinystdio/vfscanf.c",
|
||||
"libc/tinystdio/vfiscanf.c",
|
||||
"libc/tinystdio/vfscanff.c",
|
||||
"libc/tinystdio/vprintf.c",
|
||||
"libc/tinystdio/vscanf.c",
|
||||
"libc/tinystdio/vsscanf.c",
|
||||
"libc/tinystdio/vsnprintf.c",
|
||||
"libc/tinystdio/vsprintf.c",
|
||||
|
||||
"libc/string/bcmp.c",
|
||||
"libc/string/bcopy.c",
|
||||
"libc/string/bzero.c",
|
||||
@@ -229,6 +162,87 @@ var picolibcSources = []string{
|
||||
"libc/string/wmempcpy.c",
|
||||
"libc/string/wmemset.c",
|
||||
"libc/string/xpg_strerror_r.c",
|
||||
}
|
||||
|
||||
// Parts of picolibc that are too large for small AVRs.
|
||||
var picolibcSourcesLarge = []string{
|
||||
// srcs_tinystdio
|
||||
"libc/tinystdio/asprintf.c",
|
||||
"libc/tinystdio/bufio.c",
|
||||
"libc/tinystdio/clearerr.c",
|
||||
"libc/tinystdio/ecvt_r.c",
|
||||
"libc/tinystdio/ecvt.c",
|
||||
"libc/tinystdio/ecvtf_r.c",
|
||||
"libc/tinystdio/ecvtf.c",
|
||||
"libc/tinystdio/fcvt.c",
|
||||
"libc/tinystdio/fcvt_r.c",
|
||||
"libc/tinystdio/fcvtf.c",
|
||||
"libc/tinystdio/fcvtf_r.c",
|
||||
"libc/tinystdio/gcvt.c",
|
||||
"libc/tinystdio/gcvtf.c",
|
||||
"libc/tinystdio/fclose.c",
|
||||
"libc/tinystdio/fdevopen.c",
|
||||
"libc/tinystdio/feof.c",
|
||||
"libc/tinystdio/ferror.c",
|
||||
"libc/tinystdio/fflush.c",
|
||||
"libc/tinystdio/fgetc.c",
|
||||
"libc/tinystdio/fgets.c",
|
||||
"libc/tinystdio/fileno.c",
|
||||
"libc/tinystdio/filestrget.c",
|
||||
"libc/tinystdio/filestrput.c",
|
||||
"libc/tinystdio/filestrputalloc.c",
|
||||
"libc/tinystdio/fmemopen.c",
|
||||
"libc/tinystdio/fprintf.c",
|
||||
"libc/tinystdio/fputc.c",
|
||||
"libc/tinystdio/fputs.c",
|
||||
"libc/tinystdio/fread.c",
|
||||
//"libc/tinystdio/freopen.c", // crashes with AVR, see: https://github.com/picolibc/picolibc/pull/369
|
||||
"libc/tinystdio/fscanf.c",
|
||||
"libc/tinystdio/fseek.c",
|
||||
"libc/tinystdio/fseeko.c",
|
||||
"libc/tinystdio/ftell.c",
|
||||
"libc/tinystdio/ftello.c",
|
||||
"libc/tinystdio/fwrite.c",
|
||||
"libc/tinystdio/getchar.c",
|
||||
"libc/tinystdio/gets.c",
|
||||
"libc/tinystdio/matchcaseprefix.c",
|
||||
"libc/tinystdio/mktemp.c",
|
||||
"libc/tinystdio/perror.c",
|
||||
"libc/tinystdio/printf.c",
|
||||
"libc/tinystdio/putchar.c",
|
||||
"libc/tinystdio/puts.c",
|
||||
"libc/tinystdio/rewind.c",
|
||||
"libc/tinystdio/scanf.c",
|
||||
"libc/tinystdio/setbuf.c",
|
||||
"libc/tinystdio/setbuffer.c",
|
||||
"libc/tinystdio/setlinebuf.c",
|
||||
"libc/tinystdio/setvbuf.c",
|
||||
"libc/tinystdio/snprintf.c",
|
||||
"libc/tinystdio/sprintf.c",
|
||||
"libc/tinystdio/snprintfd.c",
|
||||
"libc/tinystdio/snprintff.c",
|
||||
"libc/tinystdio/sprintff.c",
|
||||
"libc/tinystdio/sprintfd.c",
|
||||
"libc/tinystdio/sscanf.c",
|
||||
"libc/tinystdio/strfromf.c",
|
||||
"libc/tinystdio/strfromd.c",
|
||||
"libc/tinystdio/strtof.c",
|
||||
"libc/tinystdio/strtof_l.c",
|
||||
"libc/tinystdio/strtod.c",
|
||||
"libc/tinystdio/strtod_l.c",
|
||||
"libc/tinystdio/ungetc.c",
|
||||
"libc/tinystdio/vasprintf.c",
|
||||
"libc/tinystdio/vfiprintf.c",
|
||||
"libc/tinystdio/vfprintf.c",
|
||||
"libc/tinystdio/vfprintff.c",
|
||||
"libc/tinystdio/vfscanf.c",
|
||||
"libc/tinystdio/vfiscanf.c",
|
||||
"libc/tinystdio/vfscanff.c",
|
||||
"libc/tinystdio/vprintf.c",
|
||||
"libc/tinystdio/vscanf.c",
|
||||
"libc/tinystdio/vsscanf.c",
|
||||
"libc/tinystdio/vsnprintf.c",
|
||||
"libc/tinystdio/vsprintf.c",
|
||||
|
||||
"libm/common/sf_finite.c",
|
||||
"libm/common/sf_copysign.c",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
@@ -12,11 +11,6 @@ import (
|
||||
func runCCompiler(flags ...string) error {
|
||||
if hasBuiltinTools {
|
||||
// Compile this with the internal Clang compiler.
|
||||
headerPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))
|
||||
if headerPath == "" {
|
||||
return errors.New("could not locate Clang headers")
|
||||
}
|
||||
flags = append(flags, "-I"+headerPath)
|
||||
cmd := exec.Command(os.Args[0], append([]string{"clang"}, flags...)...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
+1
-4
@@ -165,7 +165,7 @@ func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
|
||||
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
|
||||
// hashes of the accessed C header files. If there is one or more error, it
|
||||
// returns these in the []error slice but still modifies the AST.
|
||||
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string, clangHeaders string) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
||||
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
||||
p := &cgoPackage{
|
||||
currentDir: dir,
|
||||
importPath: importPath,
|
||||
@@ -292,9 +292,6 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
||||
// have better alternatives anyway.
|
||||
cflagsForCGo := append([]string{"-D_FORTIFY_SOURCE=0"}, cflags...)
|
||||
cflagsForCGo = append(cflagsForCGo, p.cflags...)
|
||||
if clangHeaders != "" {
|
||||
cflagsForCGo = append(cflagsForCGo, "-isystem", clangHeaders)
|
||||
}
|
||||
|
||||
// Retrieve types such as C.int, C.longlong, etc from C.
|
||||
p.newCGoFile(nil, -1).readNames(builtinAliasTypedefs, cflagsForCGo, "", func(names map[string]clangCursor) {
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ func TestCGo(t *testing.T) {
|
||||
}
|
||||
|
||||
// Process the AST with CGo.
|
||||
cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags, "")
|
||||
cgoAST, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags)
|
||||
|
||||
// Check the AST for type errors.
|
||||
var typecheckErrors []error
|
||||
|
||||
@@ -8,8 +8,8 @@ package cgo
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@15/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm15/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-15/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@15/lib -lclang -lffi
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@15/lib -lclang -lffi
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@15/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@15/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm15/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//go:build !byollvm && !llvm15
|
||||
//go:build !byollvm && !llvm15 && !llvm17
|
||||
|
||||
package cgo
|
||||
|
||||
// As of 2023-05-05, there is a packaging issue on Debian:
|
||||
// As of 2023-05-05, there is a packaging issue with LLVM 16 on Debian:
|
||||
// https://github.com/llvm/llvm-project/issues/62199
|
||||
// A workaround is to fix this locally, using something like this:
|
||||
//
|
||||
@@ -14,8 +14,8 @@ package cgo
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@16/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm16/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-16/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@16/lib -lclang -lffi
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@16/lib -lclang -lffi
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@16/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@16/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm16/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build !byollvm && llvm17
|
||||
|
||||
package cgo
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -I/usr/include/llvm-17 -I/usr/include/llvm-c-17 -I/usr/lib/llvm-17/include
|
||||
#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@17/include
|
||||
#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@17/include
|
||||
#cgo freebsd CFLAGS: -I/usr/local/llvm17/include
|
||||
#cgo linux LDFLAGS: -L/usr/lib/llvm-17/lib -lclang
|
||||
#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@17/lib -lclang
|
||||
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@17/lib -lclang
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/llvm17/lib -lclang
|
||||
*/
|
||||
import "C"
|
||||
+21
-6
@@ -19,7 +19,6 @@ type Config struct {
|
||||
Options *Options
|
||||
Target *TargetSpec
|
||||
GoMinorVersion int
|
||||
ClangHeaders string // Clang built-in header include path
|
||||
TestConfig TestConfig
|
||||
}
|
||||
|
||||
@@ -259,24 +258,39 @@ func (c *Config) DefaultBinaryExtension() string {
|
||||
|
||||
// CFlags returns the flags to pass to the C compiler. This is necessary for CGo
|
||||
// preprocessing.
|
||||
func (c *Config) CFlags() []string {
|
||||
func (c *Config) CFlags(libclang bool) []string {
|
||||
var cflags []string
|
||||
for _, flag := range c.Target.CFlags {
|
||||
cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT")))
|
||||
}
|
||||
resourceDir := goenv.ClangResourceDir(libclang)
|
||||
if resourceDir != "" {
|
||||
// The resource directory contains the built-in clang headers like
|
||||
// stdbool.h, stdint.h, float.h, etc.
|
||||
// It is left empty if we're using an external compiler (that already
|
||||
// knows these headers).
|
||||
cflags = append(cflags,
|
||||
"-resource-dir="+resourceDir,
|
||||
)
|
||||
if strings.HasPrefix(c.Triple(), "xtensa") {
|
||||
// workaround needed in LLVM 16, see: https://github.com/espressif/llvm-project/issues/83
|
||||
cflags = append(cflags, "-isystem", filepath.Join(resourceDir, "include"))
|
||||
}
|
||||
}
|
||||
switch c.Target.Libc {
|
||||
case "darwin-libSystem":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
cflags = append(cflags,
|
||||
"--sysroot="+filepath.Join(root, "lib/macos-minimal-sdk/src"),
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(root, "lib/macos-minimal-sdk/src/usr/include"),
|
||||
)
|
||||
case "picolibc":
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
|
||||
path, _ := c.LibcPath("picolibc")
|
||||
cflags = append(cflags,
|
||||
"--sysroot="+path,
|
||||
"-isystem", filepath.Join(path, "include"), // necessary for Xtensa
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "include"),
|
||||
"-isystem", filepath.Join(picolibcDir, "include"),
|
||||
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
||||
)
|
||||
@@ -297,7 +311,8 @@ func (c *Config) CFlags() []string {
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
path, _ := c.LibcPath("mingw-w64")
|
||||
cflags = append(cflags,
|
||||
"--sysroot="+path,
|
||||
"-nostdlibinc",
|
||||
"-isystem", filepath.Join(path, "include"),
|
||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
|
||||
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
|
||||
"-D_UCRT",
|
||||
|
||||
+39
-2
@@ -246,6 +246,43 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
// GetTargetSpecs retrieves target specifications from the TINYGOROOT targets
|
||||
// directory. Only valid target JSON files are considered, and the function
|
||||
// returns a map of target names to their respective TargetSpec.
|
||||
func GetTargetSpecs() (map[string]*TargetSpec, error) {
|
||||
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not list targets: %w", err)
|
||||
}
|
||||
|
||||
maps := map[string]*TargetSpec{}
|
||||
for _, entry := range entries {
|
||||
entryInfo, err := entry.Info()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get entry info: %w", err)
|
||||
}
|
||||
if !entryInfo.Mode().IsRegular() || !strings.HasSuffix(entry.Name(), ".json") {
|
||||
// Only inspect JSON files.
|
||||
continue
|
||||
}
|
||||
path := filepath.Join(dir, entry.Name())
|
||||
spec, err := LoadTarget(&Options{Target: path})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not list target: %w", err)
|
||||
}
|
||||
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
|
||||
// This doesn't look like a regular target file, but rather like
|
||||
// a parent target (such as targets/cortex-m.json).
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
name = name[:len(name)-5]
|
||||
maps[name] = spec
|
||||
}
|
||||
return maps, nil
|
||||
}
|
||||
|
||||
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
// No target spec available. Use the default one, useful on most systems
|
||||
// with a regular OS.
|
||||
@@ -344,12 +381,12 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
spec.Linker = "wasm-ld"
|
||||
spec.RTLib = "compiler-rt"
|
||||
spec.Libc = "wasi-libc"
|
||||
spec.DefaultStackSize = 1024 * 32 // 32kB
|
||||
spec.DefaultStackSize = 1024 * 64 // 64kB
|
||||
spec.LDFlags = append(spec.LDFlags,
|
||||
"--stack-first",
|
||||
"--no-demangle",
|
||||
)
|
||||
spec.Emulator = "wasmtime --mapdir=/tmp::{tmpDir} {}"
|
||||
spec.Emulator = "wasmtime --dir={tmpDir}::/tmp {}"
|
||||
spec.ExtraFiles = append(spec.ExtraFiles,
|
||||
"src/runtime/asm_tinygowasm.S",
|
||||
"src/internal/task/task_asyncify_wasm.S",
|
||||
|
||||
@@ -243,7 +243,7 @@ func testCompilePackage(t *testing.T, options *compileopts.Options, file string)
|
||||
defer machine.Dispose()
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, "./testdata/"+file, config.ClangHeaders, types.Config{
|
||||
lprogram, err := loader.Load(config, "./testdata/"+file, types.Config{
|
||||
Sizes: Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
+5
-5
@@ -21,7 +21,7 @@ entry:
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 32768, ptr undef) #9
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #9
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 32768, ptr undef) #9
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #9
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ entry:
|
||||
store i32 5, ptr %0, align 4
|
||||
%1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1
|
||||
store ptr %n, ptr %1, align 4
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 32768, ptr undef) #9
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
||||
%2 = load i32, ptr %n, align 4
|
||||
call void @runtime.printint32(i32 %2, ptr undef) #9
|
||||
ret void
|
||||
@@ -113,7 +113,7 @@ entry:
|
||||
store ptr %fn.context, ptr %1, align 4
|
||||
%2 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 2
|
||||
store ptr %fn.funcptr, ptr %2, align 4
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 32768, ptr undef) #9
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ entry:
|
||||
store i32 4, ptr %.repack1, align 4
|
||||
%2 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 2
|
||||
store ptr %itf.typecode, ptr %2, align 4
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 32768, ptr undef) #9
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
|
||||
ret void
|
||||
}
|
||||
|
||||
|
||||
Generated
+60
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1694529238,
|
||||
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1696983906,
|
||||
"narHash": "sha256-L7GyeErguS7Pg4h8nK0wGlcUTbfUMDu+HMf1UcyP72k=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "bd1cde45c77891214131cbbea5b1203e485a9d51",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-23.05",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
# A Nix flake file, mainly intended for developing TinyGo.
|
||||
# You can download Nix here, for use on your Linux or macOS system:
|
||||
# https://nixos.org/download.html
|
||||
# After you have installed Nix, you can enter the development environment as
|
||||
# follows:
|
||||
#
|
||||
# nix develop
|
||||
#
|
||||
# This drops you into a bash shell, where you can install TinyGo simply using
|
||||
# the following command:
|
||||
#
|
||||
# go install
|
||||
#
|
||||
# That's all! Assuming you've set up your $PATH correctly, you can now use the
|
||||
# tinygo command as usual:
|
||||
#
|
||||
# tinygo version
|
||||
#
|
||||
# But you'll need a bit more to make TinyGo actually able to compile code:
|
||||
#
|
||||
# make llvm-source # fetch compiler-rt
|
||||
# git submodule update --init --recursive # fetch lots of other libraries and SVD files
|
||||
# make gen-device -j4 # build src/device/*/*.go files
|
||||
# make wasi-libc # build support for wasi/wasm
|
||||
#
|
||||
# With this, you should have an environment that can compile anything - except
|
||||
# for the Xtensa architecture (ESP8266/ESP32) because support for that lives in
|
||||
# a separate LLVM fork.
|
||||
#
|
||||
# You can also do many other things from this environment. Building and flashing
|
||||
# should work as you're used to: it's not a VM or container so there are no
|
||||
# access restrictions and you're running in the same host environment - just
|
||||
# with a slightly different set of tools available.
|
||||
{
|
||||
inputs = {
|
||||
# Use a recent stable release, but fix the version to make it reproducible.
|
||||
# This version should be updated from time to time.
|
||||
nixpkgs.url = "nixpkgs/nixos-23.05";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in
|
||||
with pkgs;
|
||||
{
|
||||
devShells.default = mkShell {
|
||||
buildInputs = [
|
||||
# These dependencies are required for building tinygo (go install).
|
||||
go
|
||||
llvmPackages_16.llvm
|
||||
llvmPackages_16.libclang
|
||||
# Additional dependencies needed at runtime, for building and/or
|
||||
# flashing.
|
||||
llvmPackages_16.lld
|
||||
avrdude
|
||||
binaryen
|
||||
# Additional dependencies needed for on-chip debugging.
|
||||
# These tools are rather big (especially GDB) and not frequently
|
||||
# used, so are commented out. On-chip debugging is still possible if
|
||||
# these tools are available in the host environment.
|
||||
#gdb
|
||||
#openocd
|
||||
];
|
||||
shellHook= ''
|
||||
# Configure CLANG, LLVM_AR, and LLVM_NM for `make wasi-libc`.
|
||||
# Without setting these explicitly, Homebrew versions might be used
|
||||
# or the default `ar` and `nm` tools might be used (which don't
|
||||
# support wasi).
|
||||
export CLANG="clang-16 -resource-dir ${llvmPackages_16.clang.cc.lib}/lib/clang/16"
|
||||
export LLVM_AR=llvm-ar
|
||||
export LLVM_NM=llvm-nm
|
||||
|
||||
# Make `make smoketest` work (the default is `md5`, while Nix only
|
||||
# has `md5sum`).
|
||||
export MD5SUM=md5sum
|
||||
|
||||
# Ugly hack to make the Clang resources directory available.
|
||||
export GOFLAGS="\"-ldflags=-X github.com/tinygo-org/tinygo/goenv.clangResourceDir=${llvmPackages_16.clang.cc.lib}/lib/clang/16"\"
|
||||
'';
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -18,7 +18,7 @@ require (
|
||||
golang.org/x/sys v0.11.0
|
||||
golang.org/x/tools v0.12.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
tinygo.org/x/go-llvm v0.0.0-20230920233244-32ed56c6be9c
|
||||
tinygo.org/x/go-llvm v0.0.0-20231014233752-75a8a9fe6f74
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -65,5 +65,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
tinygo.org/x/go-llvm v0.0.0-20230920233244-32ed56c6be9c h1:rS8mAFqf0CfxPCbtfsI3bWL4jkb0TBYA1wx7tY1nu28=
|
||||
tinygo.org/x/go-llvm v0.0.0-20230920233244-32ed56c6be9c/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||
tinygo.org/x/go-llvm v0.0.0-20231014233752-75a8a9fe6f74 h1:tW8XhLI9gUZLL+2pG0HYb5dc6bpMj1aqtESpizXPnMY=
|
||||
tinygo.org/x/go-llvm v0.0.0-20231014233752-75a8a9fe6f74/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
|
||||
|
||||
+103
@@ -14,6 +14,8 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// Keys is a slice of all available environment variable keys.
|
||||
@@ -33,11 +35,20 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
// Set to true if we're linking statically against LLVM.
|
||||
var hasBuiltinTools = false
|
||||
|
||||
// TINYGOROOT is the path to the final location for checking tinygo files. If
|
||||
// unset (by a -X ldflag), then sourceDir() will fallback to the original build
|
||||
// directory.
|
||||
var TINYGOROOT string
|
||||
|
||||
// If a particular Clang resource dir must always be used and TinyGo can't
|
||||
// figure out the directory using heuristics, this global can be set using a
|
||||
// linker flag.
|
||||
// This is needed for Nix.
|
||||
var clangResourceDir string
|
||||
|
||||
// Variables read from a `go env` command invocation.
|
||||
var goEnvVars struct {
|
||||
GOPATH string
|
||||
@@ -284,3 +295,95 @@ func isSourceDir(root string) bool {
|
||||
_, err = os.Stat(filepath.Join(root, "src/device/arm/arm.go"))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// ClangResourceDir returns the clang resource dir if available. This is the
|
||||
// -resource-dir flag. If it isn't available, an empty string is returned and
|
||||
// -resource-dir should be left unset.
|
||||
// The libclang flag must be set if the resource dir is read for use by
|
||||
// libclang.
|
||||
// In that case, the resource dir is always returned (even when linking
|
||||
// dynamically against LLVM) because libclang always needs this directory.
|
||||
func ClangResourceDir(libclang bool) string {
|
||||
if clangResourceDir != "" {
|
||||
// The resource dir is forced to a particular value at build time.
|
||||
// This is needed on Nix for example, where Clang and libclang don't
|
||||
// know their own resource dir.
|
||||
// Also see:
|
||||
// https://discourse.nixos.org/t/why-is-the-clang-resource-dir-split-in-a-separate-package/34114
|
||||
return clangResourceDir
|
||||
}
|
||||
|
||||
if !hasBuiltinTools && !libclang {
|
||||
// Using external tools, so the resource dir doesn't need to be
|
||||
// specified. Clang knows where to find it.
|
||||
return ""
|
||||
}
|
||||
|
||||
// Check whether we're running from a TinyGo release directory.
|
||||
// This is the case for release binaries on GitHub.
|
||||
root := Get("TINYGOROOT")
|
||||
releaseHeaderDir := filepath.Join(root, "lib", "clang")
|
||||
if _, err := os.Stat(releaseHeaderDir); !errors.Is(err, fs.ErrNotExist) {
|
||||
return releaseHeaderDir
|
||||
}
|
||||
|
||||
if hasBuiltinTools {
|
||||
// We are statically linked to LLVM.
|
||||
// Check whether we're running from the source directory.
|
||||
// This typically happens when TinyGo was built using `make` as part of
|
||||
// development.
|
||||
llvmMajor := strings.Split(llvm.Version, ".")[0]
|
||||
buildResourceDir := filepath.Join(root, "llvm-build", "lib", "clang", llvmMajor)
|
||||
if _, err := os.Stat(buildResourceDir); !errors.Is(err, fs.ErrNotExist) {
|
||||
return buildResourceDir
|
||||
}
|
||||
} else {
|
||||
// We use external tools, either when installed using `go install` or
|
||||
// when packaged in a Linux distribution (Linux distros typically prefer
|
||||
// dynamic linking).
|
||||
// Try to detect the system clang resources directory.
|
||||
resourceDir := findSystemClangResources(root)
|
||||
if resourceDir != "" {
|
||||
return resourceDir
|
||||
}
|
||||
}
|
||||
|
||||
// Resource directory not found.
|
||||
return ""
|
||||
}
|
||||
|
||||
// Find the Clang resource dir on this particular system.
|
||||
// Return the empty string when they aren't found.
|
||||
func findSystemClangResources(TINYGOROOT string) string {
|
||||
llvmMajor := strings.Split(llvm.Version, ".")[0]
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux", "android":
|
||||
// Header files are typically stored in /usr/lib/clang/<version>/include.
|
||||
// Tested on Fedora 39, Debian 12, and Arch Linux.
|
||||
path := filepath.Join("/usr/lib/clang", llvmMajor)
|
||||
_, err := os.Stat(filepath.Join(path, "include", "stdint.h"))
|
||||
if err == nil {
|
||||
return path
|
||||
}
|
||||
case "darwin":
|
||||
// This assumes a Homebrew installation, like in builder/commands.go.
|
||||
var prefix string
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
prefix = "/usr/local/opt/llvm@" + llvmMajor
|
||||
case "arm64":
|
||||
prefix = "/opt/homebrew/opt/llvm@" + llvmMajor
|
||||
default:
|
||||
return "" // very unlikely for now
|
||||
}
|
||||
path := fmt.Sprintf("%s/lib/clang/%s", prefix, llvmMajor)
|
||||
_, err := os.Stat(path + "/include/stdint.h")
|
||||
if err == nil {
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
// Could not find it.
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
//go:build byollvm
|
||||
|
||||
package goenv
|
||||
|
||||
func init() {
|
||||
hasBuiltinTools = true
|
||||
}
|
||||
+11
-1
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.30.0"
|
||||
const version = "0.31.0-dev"
|
||||
|
||||
var (
|
||||
// This variable is set at build time using -ldflags parameters.
|
||||
@@ -17,6 +17,16 @@ var (
|
||||
GitSha1 string
|
||||
)
|
||||
|
||||
// Return TinyGo version, either in the form 0.30.0 or as a development version
|
||||
// (like 0.30.0-dev-abcd012).
|
||||
func Version() string {
|
||||
v := version
|
||||
if strings.HasSuffix(version, "-dev") && GitSha1 != "" {
|
||||
v += "-" + GitSha1
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
||||
// If the goroot cannot be determined, (0, 0) is returned.
|
||||
func GetGorootVersion() (major, minor int, err error) {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
# Docker hub does a recursive clone, then checks the branch out,
|
||||
# so when a PR adds a submodule (or updates it), it fails.
|
||||
git submodule update --init
|
||||
git submodule update --init --recursive
|
||||
|
||||
+1
-1
Submodule lib/binaryen updated: 96f7acf09a...11dba9b1c2
+2
-3
@@ -18,7 +18,6 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@@ -157,7 +156,7 @@ func listGorootMergeLinks(goroot, tinygoroot string, overrides map[string]bool)
|
||||
|
||||
// Add files from TinyGo.
|
||||
tinygoDir := filepath.Join(tinygoSrc, dir)
|
||||
tinygoEntries, err := ioutil.ReadDir(tinygoDir)
|
||||
tinygoEntries, err := os.ReadDir(tinygoDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -177,7 +176,7 @@ func listGorootMergeLinks(goroot, tinygoroot string, overrides map[string]bool)
|
||||
// Add all directories from $GOROOT that are not part of the TinyGo
|
||||
// overrides.
|
||||
goDir := filepath.Join(goSrc, dir)
|
||||
goEntries, err := ioutil.ReadDir(goDir)
|
||||
goEntries, err := os.ReadDir(goDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+13
-15
@@ -29,11 +29,10 @@ import (
|
||||
|
||||
// Program holds all packages and some metadata about the program as a whole.
|
||||
type Program struct {
|
||||
config *compileopts.Config
|
||||
clangHeaders string
|
||||
typeChecker types.Config
|
||||
goroot string // synthetic GOROOT
|
||||
workingDir string
|
||||
config *compileopts.Config
|
||||
typeChecker types.Config
|
||||
goroot string // synthetic GOROOT
|
||||
workingDir string
|
||||
|
||||
Packages map[string]*Package
|
||||
sorted []*Package
|
||||
@@ -103,7 +102,7 @@ type EmbedFile struct {
|
||||
// Load loads the given package with all dependencies (including the runtime
|
||||
// package). Call .Parse() afterwards to parse all Go files (including CGo
|
||||
// processing, if necessary).
|
||||
func Load(config *compileopts.Config, inputPkg string, clangHeaders string, typeChecker types.Config) (*Program, error) {
|
||||
func Load(config *compileopts.Config, inputPkg string, typeChecker types.Config) (*Program, error) {
|
||||
goroot, err := GetCachedGoroot(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -118,13 +117,12 @@ func Load(config *compileopts.Config, inputPkg string, clangHeaders string, type
|
||||
}
|
||||
}
|
||||
p := &Program{
|
||||
config: config,
|
||||
clangHeaders: clangHeaders,
|
||||
typeChecker: typeChecker,
|
||||
goroot: goroot,
|
||||
workingDir: wd,
|
||||
Packages: make(map[string]*Package),
|
||||
fset: token.NewFileSet(),
|
||||
config: config,
|
||||
typeChecker: typeChecker,
|
||||
goroot: goroot,
|
||||
workingDir: wd,
|
||||
Packages: make(map[string]*Package),
|
||||
fset: token.NewFileSet(),
|
||||
}
|
||||
|
||||
// List the dependencies of this package, in raw JSON format.
|
||||
@@ -438,9 +436,9 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||
// to call cgo.Process in that case as it will only cause issues.
|
||||
if len(p.CgoFiles) != 0 && len(files) != 0 {
|
||||
var initialCFlags []string
|
||||
initialCFlags = append(initialCFlags, p.program.config.CFlags()...)
|
||||
initialCFlags = append(initialCFlags, p.program.config.CFlags(true)...)
|
||||
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
||||
generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags, p.program.clangHeaders)
|
||||
generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags)
|
||||
p.CFlags = append(initialCFlags, cflags...)
|
||||
p.CGoHeaders = headerCode
|
||||
for path, hash := range accessedFiles {
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"go/scanner"
|
||||
"go/types"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
@@ -798,7 +797,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
|
||||
needsEnvInVars = true
|
||||
}
|
||||
}
|
||||
var args, env []string
|
||||
var args, emuArgs, env []string
|
||||
var extraCmdEnv []string
|
||||
if needsEnvInVars {
|
||||
runtimeGlobals := make(map[string]string)
|
||||
@@ -821,13 +820,14 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
|
||||
} 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=.")
|
||||
emuArgs = append(emuArgs, "--dir=.")
|
||||
for _, v := range environmentVars {
|
||||
args = append(args, "--env", v)
|
||||
emuArgs = append(emuArgs, "--env", v)
|
||||
}
|
||||
if len(cmdArgs) != 0 {
|
||||
// mark end of wasmtime arguments and start of program ones: --
|
||||
args = append(args, "--")
|
||||
// Use of '--' argument no longer necessary as of Wasmtime v14:
|
||||
// https://github.com/bytecodealliance/wasmtime/pull/6946
|
||||
// args = append(args, "--")
|
||||
args = append(args, cmdArgs...)
|
||||
}
|
||||
|
||||
@@ -877,7 +877,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
|
||||
return result, err
|
||||
}
|
||||
name = emulator[0]
|
||||
emuArgs := append([]string(nil), emulator[1:]...)
|
||||
emuArgs = append(emuArgs, emulator[1:]...)
|
||||
args = append(emuArgs, args...)
|
||||
}
|
||||
var cmd *exec.Cmd
|
||||
@@ -1212,15 +1212,10 @@ func getBMPPorts() (gdbPort, uartPort string, err error) {
|
||||
}
|
||||
|
||||
func usage(command string) {
|
||||
version := goenv.Version
|
||||
if strings.HasSuffix(version, "-dev") && goenv.GitSha1 != "" {
|
||||
version += "-" + goenv.GitSha1
|
||||
}
|
||||
|
||||
switch command {
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
|
||||
fmt.Fprintln(os.Stderr, "version:", version)
|
||||
fmt.Fprintln(os.Stderr, "version:", goenv.Version())
|
||||
fmt.Fprintf(os.Stderr, "usage: %s <command> [arguments]\n", os.Args[0])
|
||||
fmt.Fprintln(os.Stderr, "\ncommands:")
|
||||
fmt.Fprintln(os.Stderr, " build: compile packages and dependencies")
|
||||
@@ -1442,6 +1437,7 @@ func main() {
|
||||
llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable")
|
||||
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
|
||||
monitor := flag.Bool("monitor", false, "enable serial monitor")
|
||||
info := flag.Bool("info", false, "print information")
|
||||
baudrate := flag.Int("baudrate", 115200, "baudrate of serial monitor")
|
||||
|
||||
// Internal flags, that are only intended for TinyGo development.
|
||||
@@ -1737,35 +1733,29 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
case "monitor":
|
||||
err := Monitor("", *port, options)
|
||||
handleCompilerError(err)
|
||||
if *info {
|
||||
serialPortInfo, err := ListSerialPorts()
|
||||
handleCompilerError(err)
|
||||
for _, s := range serialPortInfo {
|
||||
fmt.Printf("%s %4s %4s %s\n", s.Name, s.VID, s.PID, s.Target)
|
||||
}
|
||||
} else {
|
||||
err := Monitor("", *port, options)
|
||||
handleCompilerError(err)
|
||||
}
|
||||
case "targets":
|
||||
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
|
||||
entries, err := ioutil.ReadDir(dir)
|
||||
specs, err := compileopts.GetTargetSpecs()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "could not list targets:", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if !entry.Mode().IsRegular() || !strings.HasSuffix(entry.Name(), ".json") {
|
||||
// Only inspect JSON files.
|
||||
continue
|
||||
}
|
||||
path := filepath.Join(dir, entry.Name())
|
||||
spec, err := compileopts.LoadTarget(&compileopts.Options{Target: path})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "could not list target:", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
if spec.FlashMethod == "" && spec.FlashCommand == "" && spec.Emulator == "" {
|
||||
// This doesn't look like a regular target file, but rather like
|
||||
// a parent target (such as targets/cortex-m.json).
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
name = name[:len(name)-5]
|
||||
names := []string{}
|
||||
for key := range specs {
|
||||
names = append(names, key)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
fmt.Println(name)
|
||||
}
|
||||
case "info":
|
||||
@@ -1874,11 +1864,7 @@ func main() {
|
||||
if s, err := goenv.GorootVersionString(); err == nil {
|
||||
goversion = s
|
||||
}
|
||||
version := goenv.Version
|
||||
if strings.HasSuffix(goenv.Version, "-dev") && goenv.GitSha1 != "" {
|
||||
version += "-" + goenv.GitSha1
|
||||
}
|
||||
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", version, runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
|
||||
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", goenv.Version(), runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
|
||||
case "env":
|
||||
if flag.NArg() == 0 {
|
||||
// Show all environment variables.
|
||||
|
||||
+56
@@ -13,12 +13,15 @@ import (
|
||||
"os/signal"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mattn/go-tty"
|
||||
"github.com/tinygo-org/tinygo/builder"
|
||||
"github.com/tinygo-org/tinygo/compileopts"
|
||||
|
||||
"go.bug.st/serial"
|
||||
"go.bug.st/serial/enumerator"
|
||||
)
|
||||
|
||||
// Monitor connects to the given port and reads/writes the serial port.
|
||||
@@ -128,6 +131,59 @@ func Monitor(executable, port string, options *compileopts.Options) error {
|
||||
return <-errCh
|
||||
}
|
||||
|
||||
// SerialPortInfo is a structure that holds information about the port and its
|
||||
// associated TargetSpec.
|
||||
type SerialPortInfo struct {
|
||||
Name string
|
||||
IsUSB bool
|
||||
VID string
|
||||
PID string
|
||||
Target string
|
||||
Spec *compileopts.TargetSpec
|
||||
}
|
||||
|
||||
// ListSerialPort returns serial port information and any detected TinyGo
|
||||
// target.
|
||||
func ListSerialPorts() ([]SerialPortInfo, error) {
|
||||
maps, err := compileopts.GetTargetSpecs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
portsList, err := enumerator.GetDetailedPortsList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serialPortInfo := []SerialPortInfo{}
|
||||
for _, p := range portsList {
|
||||
info := SerialPortInfo{
|
||||
Name: p.Name,
|
||||
IsUSB: p.IsUSB,
|
||||
VID: p.VID,
|
||||
PID: p.PID,
|
||||
}
|
||||
vid := strings.ToLower(p.VID)
|
||||
pid := strings.ToLower(p.PID)
|
||||
for k, v := range maps {
|
||||
usbInterfaces := v.SerialPort
|
||||
for _, s := range usbInterfaces {
|
||||
parts := strings.Split(s, ":")
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
if vid == strings.ToLower(parts[0]) && pid == strings.ToLower(parts[1]) {
|
||||
info.Target = k
|
||||
info.Spec = v
|
||||
}
|
||||
}
|
||||
}
|
||||
serialPortInfo = append(serialPortInfo, info)
|
||||
}
|
||||
|
||||
return serialPortInfo, nil
|
||||
}
|
||||
|
||||
var addressMatch = regexp.MustCompile(`^panic: runtime error at 0x([0-9a-f]+): `)
|
||||
|
||||
// Extract the address from the "panic: runtime error at" message.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// For efficiency, it's best to get the device ID once and cache it
|
||||
// (e.g. on RP2040 XIP flash and interrupts disabled for period of
|
||||
// retrieving the hardware ID from ROM chip)
|
||||
id := machine.DeviceID()
|
||||
|
||||
for {
|
||||
println("Device ID:", hex.EncodeToString(id))
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,9 @@ func main() {
|
||||
|
||||
// Schedule and enable recurring interrupt.
|
||||
// The callback function is executed in the context of an interrupt handler,
|
||||
// so regular restructions for this sort of code apply: no blocking, no memory allocation, etc.
|
||||
// so regular restrictions for this sort of code apply: no blocking, no memory allocation, etc.
|
||||
// Please check the online documentation for the details about interrupts:
|
||||
// https://tinygo.org/docs/concepts/compiler-internals/interrupts/
|
||||
delay := time.Minute + 12*time.Second
|
||||
machine.RTC.SetInterrupt(uint32(delay.Seconds()), true, func() { println("Peekaboo!") })
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
//go:build rp2040 || nrf || sam
|
||||
|
||||
package machine
|
||||
|
||||
// DeviceID returns an identifier that is unique within
|
||||
// a particular chipset.
|
||||
//
|
||||
// The identity is one burnt into the MCU itself, or the
|
||||
// flash chip at time of manufacture.
|
||||
//
|
||||
// It's possible that two different vendors may allocate
|
||||
// the same DeviceID, so callers should take this into
|
||||
// account if needing to generate a globally unique id.
|
||||
//
|
||||
// The length of the hardware ID is vendor-specific, but
|
||||
// 8 bytes (64 bits) and 16 bytes (128 bits) are common.
|
||||
var _ = (func() []byte)(DeviceID)
|
||||
+13
-1
@@ -1,4 +1,4 @@
|
||||
//go:build atmega || nrf || sam || stm32 || fe310 || k210 || rp2040
|
||||
//go:build atmega || nrf || sam || stm32 || fe310 || k210 || rp2040 || mimxrt1062
|
||||
|
||||
package machine
|
||||
|
||||
@@ -6,6 +6,17 @@ import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// If you are getting a compile error on this line please check to see you've
|
||||
// correctly implemented the methods on the I2C type. They must match
|
||||
// the i2cController interface method signatures type to type perfectly.
|
||||
// If not implementing the I2C type please remove your target from the build tags
|
||||
// at the top of this file.
|
||||
var _ interface { // 2
|
||||
Configure(config I2CConfig) error
|
||||
Tx(addr uint16, w, r []byte) error
|
||||
SetBaudRate(br uint32) error
|
||||
} = (*I2C)(nil)
|
||||
|
||||
// TWI_FREQ is the I2C bus speed. Normally either 100 kHz, or 400 kHz for high-speed bus.
|
||||
//
|
||||
// Deprecated: use 100 * machine.KHz or 400 * machine.KHz instead.
|
||||
@@ -25,6 +36,7 @@ var (
|
||||
errI2CBusError = errors.New("I2C bus error")
|
||||
errI2COverflow = errors.New("I2C receive buffer overflow")
|
||||
errI2COverread = errors.New("I2C transmit buffer overflow")
|
||||
errI2CNotImplemented = errors.New("I2C operation not yet implemented")
|
||||
)
|
||||
|
||||
// I2CTargetEvent reflects events on the I2C bus
|
||||
|
||||
@@ -31,6 +31,11 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
// Activate internal pullups for twi.
|
||||
avr.PORTC.SetBits((avr.DIDR0_ADC4D | avr.DIDR0_ADC5D))
|
||||
|
||||
return i2c.SetBaudRate(config.Frequency)
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for I2C.
|
||||
func (i2c *I2C) SetBaudRate(br uint32) error {
|
||||
// Initialize twi prescaler and bit rate.
|
||||
avr.TWSR.SetBits((avr.TWSR_TWPS0 | avr.TWSR_TWPS1))
|
||||
|
||||
@@ -38,7 +43,7 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
||||
// NOTE: TWBR should be 10 or higher for controller mode.
|
||||
// It is 72 for a 16mhz board with 100kHz TWI
|
||||
avr.TWBR.Set(uint8(((CPUFrequency() / config.Frequency) - 16) / 2))
|
||||
avr.TWBR.Set(uint8(((CPUFrequency() / br) - 16) / 2))
|
||||
|
||||
// Enable twi module.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
//go:build sam
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var deviceID [16]byte
|
||||
|
||||
// DeviceID returns an identifier that is unique within
|
||||
// a particular chipset.
|
||||
//
|
||||
// The identity is one burnt into the MCU itself, or the
|
||||
// flash chip at time of manufacture.
|
||||
//
|
||||
// It's possible that two different vendors may allocate
|
||||
// the same DeviceID, so callers should take this into
|
||||
// account if needing to generate a globally unique id.
|
||||
//
|
||||
// The length of the hardware ID is vendor-specific, but
|
||||
// 8 bytes (64 bits) and 16 bytes (128 bits) are common.
|
||||
func DeviceID() []byte {
|
||||
for i := 0; i < len(deviceID); i++ {
|
||||
word := (*volatile.Register32)(unsafe.Pointer(deviceIDAddr[i/4])).Get()
|
||||
deviceID[i] = byte(word >> ((i % 4) * 8))
|
||||
}
|
||||
|
||||
return deviceID[:]
|
||||
}
|
||||
@@ -18,6 +18,9 @@ import (
|
||||
|
||||
const deviceName = sam.Device
|
||||
|
||||
// DS40001882F, Section 10.3.3: Serial Number
|
||||
var deviceIDAddr = []uintptr{0x0080A00C, 0x0080A040, 0x0080A044, 0x0080A048}
|
||||
|
||||
const (
|
||||
PinAnalog PinMode = 1
|
||||
PinSERCOM PinMode = 2
|
||||
@@ -732,12 +735,13 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the I2C.
|
||||
func (i2c *I2C) SetBaudRate(br uint32) {
|
||||
// SetBaudRate sets the communication speed for I2C.
|
||||
func (i2c *I2C) SetBaudRate(br uint32) error {
|
||||
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
|
||||
// SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
|
||||
baud := CPUFrequency()/(2*br) - 5 - (((CPUFrequency() / 1000000) * riseTimeNanoseconds) / (2 * 1000))
|
||||
i2c.Bus.BAUD.Set(baud)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
|
||||
@@ -18,6 +18,9 @@ import (
|
||||
|
||||
const deviceName = sam.Device
|
||||
|
||||
// DS60001507, Section 9.6: Serial Number
|
||||
var deviceIDAddr = []uintptr{0x008061FC, 0x00806010, 0x00806014, 0x00806018}
|
||||
|
||||
func CPUFrequency() uint32 {
|
||||
return 120000000
|
||||
}
|
||||
@@ -680,6 +683,56 @@ func (p Pin) getPinGrouping() (uint8, uint8) {
|
||||
return group, pin_in_group
|
||||
}
|
||||
|
||||
// Static DMA channel allocation.
|
||||
// If there are a lot of DMA using peripherals, we might need to switch to
|
||||
// dynamic allocation instead.
|
||||
const (
|
||||
dmaChannelSERCOM0 = iota
|
||||
dmaChannelSERCOM1
|
||||
dmaChannelSERCOM2
|
||||
dmaChannelSERCOM3
|
||||
dmaChannelSERCOM4
|
||||
dmaChannelSERCOM5
|
||||
dmaChannelSERCOM6
|
||||
dmaChannelSERCOM7
|
||||
dmaNumChannels
|
||||
)
|
||||
|
||||
// DMA descriptor structure. This structure is defined by the hardware, and is
|
||||
// described by "22.9 Register Summary - SRAM" in the datasheet.
|
||||
type dmaDescriptor struct {
|
||||
btctrl uint16
|
||||
btcnt uint16
|
||||
srcaddr unsafe.Pointer
|
||||
dstaddr unsafe.Pointer
|
||||
descaddr unsafe.Pointer
|
||||
}
|
||||
|
||||
//go:align 16
|
||||
var dmaDescriptorSection [dmaNumChannels]dmaDescriptor
|
||||
|
||||
//go:align 16
|
||||
var dmaDescriptorWritebackSection [dmaNumChannels]dmaDescriptor
|
||||
|
||||
// Enable and configure the DMAC peripheral if it hasn't been enabled already.
|
||||
func enableDMAC() {
|
||||
if !sam.DMAC.CTRL.HasBits(sam.DMAC_CTRL_DMAENABLE) {
|
||||
// Init DMAC.
|
||||
// First configure the clocks, then configure the DMA descriptors. Those
|
||||
// descriptors must live in SRAM and must be aligned on a 16-byte
|
||||
// boundary.
|
||||
// Some examples:
|
||||
// http://www.lucadavidian.com/2018/03/08/wifi-controlled-neo-pixels-strips/
|
||||
// https://svn.larosterna.com/oss/trunk/arduino/zerotimer/zerodma.cpp
|
||||
sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_DMAC_)
|
||||
sam.DMAC.BASEADDR.Set(uint32(uintptr(unsafe.Pointer(&dmaDescriptorSection))))
|
||||
sam.DMAC.WRBADDR.Set(uint32(uintptr(unsafe.Pointer(&dmaDescriptorWritebackSection))))
|
||||
|
||||
// Enable peripheral with all priorities.
|
||||
sam.DMAC.CTRL.SetBits(sam.DMAC_CTRL_DMAENABLE | sam.DMAC_CTRL_LVLEN0 | sam.DMAC_CTRL_LVLEN1 | sam.DMAC_CTRL_LVLEN2 | sam.DMAC_CTRL_LVLEN3)
|
||||
}
|
||||
}
|
||||
|
||||
// InitADC initializes the ADC.
|
||||
func InitADC() {
|
||||
// ADC Bias Calibration
|
||||
@@ -1228,12 +1281,13 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the I2C.
|
||||
func (i2c *I2C) SetBaudRate(br uint32) {
|
||||
// SetBaudRate sets the communication speed for I2C.
|
||||
func (i2c *I2C) SetBaudRate(br uint32) error {
|
||||
// Synchronous arithmetic baudrate, via Adafruit SAMD51 implementation:
|
||||
// sercom->I2CM.BAUD.bit.BAUD = SERCOM_FREQ_REF / ( 2 * baudrate) - 1 ;
|
||||
baud := SERCOM_FREQ_REF/(2*br) - 1
|
||||
i2c.Bus.BAUD.Set(baud)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
@@ -1648,6 +1702,101 @@ func (spi SPI) txrx(tx, rx []byte) {
|
||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
|
||||
// Channel to be used for SPI transfers.
|
||||
// These channels are currently statically allocated.
|
||||
func (spi SPI) dmaTxChannel() uint8 {
|
||||
return dmaChannelSERCOM0 + spi.SERCOM
|
||||
}
|
||||
|
||||
// IsAsync returns whether the SPI supports async operation (usually DMA).
|
||||
//
|
||||
// It returns true on the SAM D5x chips.
|
||||
func (spi SPI) IsAsync() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Start a transfer in the background.
|
||||
//
|
||||
// After this, another StartTx() or Wait() must be called. The provided byte
|
||||
// slices (tx and rx) may only be accessed again after Wait() was called.
|
||||
func (s SPI) StartTx(tx, rx []byte) error {
|
||||
// Check whether we support doing this transfer using DMA.
|
||||
if len(rx) != 0 {
|
||||
return s.Tx(tx, rx)
|
||||
}
|
||||
if len(tx) == 0 {
|
||||
return nil // nothing to send/receive
|
||||
}
|
||||
if len(tx) != int(uint16(len(tx))) {
|
||||
// The transfer size is a 16-bit field.
|
||||
// TODO: chain multiple transfers in some way when encountering these
|
||||
// large buffer sizes. They're not currently implemented because
|
||||
// transferring 64kB of data is less commonly done.
|
||||
return s.Tx(tx, rx)
|
||||
}
|
||||
|
||||
// Enable DMAC (if not already enabled).
|
||||
enableDMAC()
|
||||
|
||||
// Wait until a possible previous transfer has been completed.
|
||||
s.Wait()
|
||||
|
||||
// Configure the DMA channel, if it hasn't been configured already.
|
||||
dstaddr := unsafe.Pointer(&s.Bus.DATA.Reg)
|
||||
if dmaDescriptorSection[s.dmaTxChannel()].dstaddr != dstaddr {
|
||||
// Configure channel descriptor.
|
||||
dmaDescriptorSection[s.dmaTxChannel()] = dmaDescriptor{
|
||||
btctrl: (1 << 0) | // VALID: Descriptor Valid
|
||||
(0 << 3) | // BLOCKACT=NOACT: Block Action
|
||||
(1 << 10) | // SRCINC: Source Address Increment Enable
|
||||
(0 << 11) | // DSTINC: Destination Address Increment Enable
|
||||
(1 << 12) | // STEPSEL=SRC: Step Selection
|
||||
(0 << 13), // STEPSIZE=X1: Address Increment Step Size
|
||||
dstaddr: dstaddr,
|
||||
}
|
||||
|
||||
// Reset channel.
|
||||
sam.DMAC.CHANNEL[s.dmaTxChannel()].CHCTRLA.ClearBits(sam.DMAC_CHANNEL_CHCTRLA_ENABLE)
|
||||
sam.DMAC.CHANNEL[s.dmaTxChannel()].CHCTRLA.SetBits(sam.DMAC_CHANNEL_CHCTRLA_SWRST)
|
||||
|
||||
// Configure channel.
|
||||
sam.DMAC.CHANNEL[s.dmaTxChannel()].CHPRILVL.Set(0)
|
||||
sam.DMAC.CHANNEL[s.dmaTxChannel()].CHCTRLA.Set((sam.DMAC_CHANNEL_CHCTRLA_TRIGACT_BURST << sam.DMAC_CHANNEL_CHCTRLA_TRIGACT_Pos) |
|
||||
(s.triggerSource() << sam.DMAC_CHANNEL_CHCTRLA_TRIGSRC_Pos) |
|
||||
(sam.DMAC_CHANNEL_CHCTRLA_BURSTLEN_SINGLE << sam.DMAC_CHANNEL_CHCTRLA_BURSTLEN_Pos))
|
||||
}
|
||||
|
||||
// For some reason, you have to provide the address just past the end of the
|
||||
// array instead of the address of the array.
|
||||
descriptor := &dmaDescriptorSection[s.dmaTxChannel()]
|
||||
descriptor.srcaddr = unsafe.Pointer(uintptr(unsafe.Pointer(&tx[0])) + uintptr(len(tx)))
|
||||
descriptor.btcnt = uint16(len(tx)) // beat count
|
||||
|
||||
// Start the transfer.
|
||||
sam.DMAC.CHANNEL[s.dmaTxChannel()].CHCTRLA.SetBits(sam.DMAC_CHANNEL_CHCTRLA_ENABLE)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wait until all active transactions (started by StartTx) have finished. The
|
||||
// buffers provided in StartTx will be available after this method returns.
|
||||
func (spi SPI) Wait() error {
|
||||
// Wait until the previous SPI transfer completed.
|
||||
// This is basically the same thing as in SPI.tx.
|
||||
|
||||
// TODO: maybe block (and sleep) until the transfer has completed?
|
||||
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_TXC) {
|
||||
}
|
||||
|
||||
// read to clear RXC register
|
||||
for spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
|
||||
spi.Bus.DATA.Get()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// The QSPI peripheral on ATSAMD51 is only available on the following pins
|
||||
const (
|
||||
QSPI_SCK = PB10
|
||||
|
||||
@@ -62,6 +62,27 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// DMA trigger source
|
||||
func (spi SPI) triggerSource() (tx uint32) {
|
||||
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||
switch spi.Bus {
|
||||
case sam.SERCOM0_SPIM:
|
||||
return 0x05
|
||||
case sam.SERCOM1_SPIM:
|
||||
return 0x07
|
||||
case sam.SERCOM2_SPIM:
|
||||
return 0x09
|
||||
case sam.SERCOM3_SPIM:
|
||||
return 0x0A
|
||||
case sam.SERCOM4_SPIM:
|
||||
return 0x0C
|
||||
case sam.SERCOM5_SPIM:
|
||||
return 0x0E
|
||||
default:
|
||||
return 0 // should be unreachable
|
||||
}
|
||||
}
|
||||
|
||||
// This chip has three TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
|
||||
@@ -62,6 +62,27 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// DMA trigger source
|
||||
func (spi SPI) triggerSource() (tx uint32) {
|
||||
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||
switch spi.Bus {
|
||||
case sam.SERCOM0_SPIM:
|
||||
return 0x05
|
||||
case sam.SERCOM1_SPIM:
|
||||
return 0x07
|
||||
case sam.SERCOM2_SPIM:
|
||||
return 0x09
|
||||
case sam.SERCOM3_SPIM:
|
||||
return 0x0B
|
||||
case sam.SERCOM4_SPIM:
|
||||
return 0x0D
|
||||
case sam.SERCOM5_SPIM:
|
||||
return 0x0F
|
||||
default:
|
||||
return 0 // should be unreachable
|
||||
}
|
||||
}
|
||||
|
||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
|
||||
@@ -62,6 +62,27 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// DMA trigger source
|
||||
func (spi SPI) triggerSource() (tx uint32) {
|
||||
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||
switch spi.Bus {
|
||||
case sam.SERCOM0_SPIM:
|
||||
return 0x05
|
||||
case sam.SERCOM1_SPIM:
|
||||
return 0x07
|
||||
case sam.SERCOM2_SPIM:
|
||||
return 0x09
|
||||
case sam.SERCOM3_SPIM:
|
||||
return 0x0A
|
||||
case sam.SERCOM4_SPIM:
|
||||
return 0x0C
|
||||
case sam.SERCOM5_SPIM:
|
||||
return 0x0E
|
||||
default:
|
||||
return 0 // should be unreachable
|
||||
}
|
||||
}
|
||||
|
||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
|
||||
@@ -76,6 +76,31 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// DMA trigger source
|
||||
func (spi SPI) triggerSource() (tx uint32) {
|
||||
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||
switch spi.Bus {
|
||||
case sam.SERCOM0_SPIM:
|
||||
return 0x05
|
||||
case sam.SERCOM1_SPIM:
|
||||
return 0x07
|
||||
case sam.SERCOM2_SPIM:
|
||||
return 0x09
|
||||
case sam.SERCOM3_SPIM:
|
||||
return 0x0A
|
||||
case sam.SERCOM4_SPIM:
|
||||
return 0x0C
|
||||
case sam.SERCOM5_SPIM:
|
||||
return 0x0E
|
||||
case sam.SERCOM6_SPIM:
|
||||
return 0x10
|
||||
case sam.SERCOM7_SPIM:
|
||||
return 0x12
|
||||
default:
|
||||
return 0 // should be unreachable
|
||||
}
|
||||
}
|
||||
|
||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
|
||||
@@ -76,6 +76,31 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// DMA trigger source
|
||||
func (spi SPI) triggerSource() (tx uint32) {
|
||||
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||
switch spi.Bus {
|
||||
case sam.SERCOM0_SPIM:
|
||||
return 0x05
|
||||
case sam.SERCOM1_SPIM:
|
||||
return 0x07
|
||||
case sam.SERCOM2_SPIM:
|
||||
return 0x09
|
||||
case sam.SERCOM3_SPIM:
|
||||
return 0x0A
|
||||
case sam.SERCOM4_SPIM:
|
||||
return 0x0C
|
||||
case sam.SERCOM5_SPIM:
|
||||
return 0x0E
|
||||
case sam.SERCOM6_SPIM:
|
||||
return 0x10
|
||||
case sam.SERCOM7_SPIM:
|
||||
return 0x12
|
||||
default:
|
||||
return 0 // should be unreachable
|
||||
}
|
||||
}
|
||||
|
||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
|
||||
@@ -62,6 +62,27 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// DMA trigger source
|
||||
func (spi SPI) triggerSource() (tx uint32) {
|
||||
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||
switch spi.Bus {
|
||||
case sam.SERCOM0_SPIM:
|
||||
return 0x05
|
||||
case sam.SERCOM1_SPIM:
|
||||
return 0x07
|
||||
case sam.SERCOM2_SPIM:
|
||||
return 0x09
|
||||
case sam.SERCOM3_SPIM:
|
||||
return 0x0A
|
||||
case sam.SERCOM4_SPIM:
|
||||
return 0x0C
|
||||
case sam.SERCOM5_SPIM:
|
||||
return 0x0E
|
||||
default:
|
||||
return 0 // should be unreachable
|
||||
}
|
||||
}
|
||||
|
||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
|
||||
@@ -76,6 +76,31 @@ func setSERCOMClockGenerator(sercom uint8, gclk uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// DMA trigger source
|
||||
func (spi SPI) triggerSource() (tx uint32) {
|
||||
// See TRIGSRC field of CHCTRLA register for description of these constants.
|
||||
switch spi.Bus {
|
||||
case sam.SERCOM0_SPIM:
|
||||
return 0x05
|
||||
case sam.SERCOM1_SPIM:
|
||||
return 0x07
|
||||
case sam.SERCOM2_SPIM:
|
||||
return 0x09
|
||||
case sam.SERCOM3_SPIM:
|
||||
return 0x0A
|
||||
case sam.SERCOM4_SPIM:
|
||||
return 0x0C
|
||||
case sam.SERCOM5_SPIM:
|
||||
return 0x0E
|
||||
case sam.SERCOM6_SPIM:
|
||||
return 0x10
|
||||
case sam.SERCOM7_SPIM:
|
||||
return 0x12
|
||||
default:
|
||||
return 0 // should be unreachable
|
||||
}
|
||||
}
|
||||
|
||||
// This chip has five TCC peripherals, which have PWM as one feature.
|
||||
var (
|
||||
TCC0 = (*TCC)(sam.TCC0)
|
||||
|
||||
@@ -229,9 +229,10 @@ type I2CConfig struct {
|
||||
SDA Pin
|
||||
}
|
||||
|
||||
var i2cClockFrequency uint32 = 32000000
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
var i2cClockFrequency uint32 = 32000000
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = 100 * KHz
|
||||
}
|
||||
@@ -241,7 +242,17 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
config.SCL = I2C0_SCL_PIN
|
||||
}
|
||||
|
||||
var prescaler = i2cClockFrequency/(5*config.Frequency) - 1
|
||||
i2c.SetBaudRate(config.Frequency)
|
||||
|
||||
config.SDA.Configure(PinConfig{Mode: PinI2C})
|
||||
config.SCL.Configure(PinConfig{Mode: PinI2C})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for I2C.
|
||||
func (i2c *I2C) SetBaudRate(br uint32) error {
|
||||
var prescaler = i2cClockFrequency/(5*br) - 1
|
||||
|
||||
// disable controller before setting the prescale registers
|
||||
i2c.Bus.CTR.ClearBits(sifive.I2C_CTR_EN)
|
||||
@@ -253,9 +264,6 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
// enable controller
|
||||
i2c.Bus.CTR.SetBits(sifive.I2C_CTR_EN)
|
||||
|
||||
config.SDA.Configure(PinConfig{Mode: PinI2C})
|
||||
config.SCL.Configure(PinConfig{Mode: PinI2C})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -57,8 +57,9 @@ type SPIConfig struct {
|
||||
Mode uint8
|
||||
}
|
||||
|
||||
func (spi SPI) Configure(config SPIConfig) {
|
||||
func (spi SPI) Configure(config SPIConfig) error {
|
||||
spiConfigure(spi.Bus, config.SCK, config.SDO, config.SDI)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
@@ -107,6 +108,12 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the I2C frequency.
|
||||
func (i2c *I2C) SetBaudRate(br uint32) error {
|
||||
i2cSetBaudRate(i2c.Bus, br)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
i2cTransfer(i2c.Bus, &w[0], len(w), &r[0], len(r))
|
||||
@@ -117,6 +124,9 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
//export __tinygo_i2c_configure
|
||||
func i2cConfigure(bus uint8, scl Pin, sda Pin)
|
||||
|
||||
//export __tinygo_i2c_set_baud_rate
|
||||
func i2cSetBaudRate(bus uint8, br uint32)
|
||||
|
||||
//export __tinygo_i2c_transfer
|
||||
func i2cTransfer(bus uint8, w *byte, wlen int, r *byte, rlen int) int
|
||||
|
||||
|
||||
@@ -563,7 +563,19 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
config.SCL.SetFPIOAFunction(FUNC_I2C2_SCLK)
|
||||
}
|
||||
|
||||
div := CPUFrequency() / config.Frequency / 16
|
||||
i2c.SetBaudRate(config.Frequency)
|
||||
|
||||
i2c.Bus.INTR_MASK.Set(0)
|
||||
i2c.Bus.DMA_CR.Set(0x03)
|
||||
i2c.Bus.DMA_RDLR.Set(0)
|
||||
i2c.Bus.DMA_TDLR.Set(0x4)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for I2C.
|
||||
func (i2c *I2C) SetBaudRate(br uint32) error {
|
||||
div := CPUFrequency() / br / 16
|
||||
|
||||
// Disable controller before setting the prescale register.
|
||||
i2c.Bus.ENABLE.Set(0)
|
||||
@@ -574,11 +586,6 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
i2c.Bus.SS_SCL_HCNT.Set(uint32(div))
|
||||
i2c.Bus.SS_SCL_LCNT.Set(uint32(div))
|
||||
|
||||
i2c.Bus.INTR_MASK.Set(0)
|
||||
i2c.Bus.DMA_CR.Set(0x03)
|
||||
i2c.Bus.DMA_RDLR.Set(0)
|
||||
i2c.Bus.DMA_TDLR.Set(0x4)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,19 +6,6 @@ package machine
|
||||
|
||||
import (
|
||||
"device/nxp"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errI2CWriteTimeout = errors.New("I2C timeout during write")
|
||||
errI2CReadTimeout = errors.New("I2C timeout during read")
|
||||
errI2CBusReadyTimeout = errors.New("I2C timeout on bus ready")
|
||||
errI2CSignalStartTimeout = errors.New("I2C timeout on signal start")
|
||||
errI2CSignalReadTimeout = errors.New("I2C timeout on signal read")
|
||||
errI2CSignalStopTimeout = errors.New("I2C timeout on signal stop")
|
||||
errI2CAckExpected = errors.New("I2C error: expected ACK not NACK")
|
||||
errI2CBusError = errors.New("I2C bus error")
|
||||
errI2CNotConfigured = errors.New("I2C interface is not yet configured")
|
||||
)
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
@@ -150,7 +137,7 @@ func (i2c *I2C) setPins(c I2CConfig) (sda, scl Pin) {
|
||||
}
|
||||
|
||||
// Configure is intended to setup an I2C interface for transmit/receive.
|
||||
func (i2c *I2C) Configure(config I2CConfig) {
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
// init pins
|
||||
sda, scl := i2c.setPins(config)
|
||||
|
||||
@@ -169,6 +156,14 @@ func (i2c *I2C) Configure(config I2CConfig) {
|
||||
|
||||
// reset clock and registers, and enable LPI2C module interface
|
||||
i2c.reset(freq)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for I2C.
|
||||
func (i2c I2C) SetBaudRate(br uint32) error {
|
||||
// TODO: implement
|
||||
return errI2CNotImplemented
|
||||
}
|
||||
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
@@ -212,13 +207,13 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteRegister transmits first the register and then the data to the
|
||||
// WriteRegisterEx transmits first the register and then the data to the
|
||||
// peripheral device.
|
||||
//
|
||||
// Many I2C-compatible devices are organized in terms of registers. This method
|
||||
// is a shortcut to easily write to such registers. Also, it only works for
|
||||
// devices with 7-bit addresses, which is the vast majority.
|
||||
func (i2c I2C) WriteRegister(address uint8, register uint8, data []byte) error {
|
||||
func (i2c I2C) WriteRegisterEx(address uint8, register uint8, data []byte) error {
|
||||
option := transferOption{
|
||||
flags: transferDefault, // transfer options bit mask (0 = normal transfer)
|
||||
peripheral: uint16(address), // 7-bit peripheral address
|
||||
@@ -232,13 +227,13 @@ func (i2c I2C) WriteRegister(address uint8, register uint8, data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadRegister transmits the register, restarts the connection as a read
|
||||
// ReadRegisterEx transmits the register, restarts the connection as a read
|
||||
// operation, and reads the response.
|
||||
//
|
||||
// Many I2C-compatible devices are organized in terms of registers. This method
|
||||
// is a shortcut to easily read such registers. Also, it only works for devices
|
||||
// with 7-bit addresses, which is the vast majority.
|
||||
func (i2c I2C) ReadRegister(address uint8, register uint8, data []byte) error {
|
||||
func (i2c I2C) ReadRegisterEx(address uint8, register uint8, data []byte) error {
|
||||
option := transferOption{
|
||||
flags: transferDefault, // transfer options bit mask (0 = normal transfer)
|
||||
peripheral: uint16(address), // 7-bit peripheral address
|
||||
@@ -560,7 +555,7 @@ func (i2c *I2C) controllerReceive(rxBuffer []byte) resultFlag {
|
||||
return result
|
||||
}
|
||||
|
||||
// controllerReceive performs a polling transmit transfer on the I2C bus.
|
||||
// controllerTransmit performs a polling transmit transfer on the I2C bus.
|
||||
func (i2c *I2C) controllerTransmit(txBuffer []byte) resultFlag {
|
||||
txSize := len(txBuffer)
|
||||
for txSize > 0 {
|
||||
|
||||
@@ -68,7 +68,7 @@ var (
|
||||
)
|
||||
|
||||
// Configure is intended to setup an SPI interface for transmit/receive.
|
||||
func (spi *SPI) Configure(config SPIConfig) {
|
||||
func (spi *SPI) Configure(config SPIConfig) error {
|
||||
|
||||
const defaultSpiFreq = 4000000 // 4 MHz
|
||||
|
||||
@@ -132,6 +132,8 @@ func (spi *SPI) Configure(config SPIConfig) {
|
||||
spi.Bus.CR.Set(nxp.LPSPI_CR_MEN)
|
||||
|
||||
spi.configured = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
|
||||
@@ -12,6 +12,34 @@ import (
|
||||
|
||||
const deviceName = nrf.Device
|
||||
|
||||
var deviceID [8]byte
|
||||
|
||||
// DeviceID returns an identifier that is unique within
|
||||
// a particular chipset.
|
||||
//
|
||||
// The identity is one burnt into the MCU itself, or the
|
||||
// flash chip at time of manufacture.
|
||||
//
|
||||
// It's possible that two different vendors may allocate
|
||||
// the same DeviceID, so callers should take this into
|
||||
// account if needing to generate a globally unique id.
|
||||
//
|
||||
// The length of the hardware ID is vendor-specific, but
|
||||
// 8 bytes (64 bits) is common.
|
||||
func DeviceID() []byte {
|
||||
words := make([]uint32, 2)
|
||||
words[0] = nrf.FICR.DEVICEID[0].Get()
|
||||
words[1] = nrf.FICR.DEVICEID[1].Get()
|
||||
|
||||
for i := 0; i < 8; i++ {
|
||||
shift := (i % 4) * 8
|
||||
w := i / 4
|
||||
deviceID[i] = byte(words[w] >> shift)
|
||||
}
|
||||
|
||||
return deviceID[:]
|
||||
}
|
||||
|
||||
const (
|
||||
PinInput PinMode = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos)
|
||||
PinInputPullup PinMode = PinInput | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos)
|
||||
@@ -246,13 +274,8 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
i2c.setPins(config.SCL, config.SDA)
|
||||
|
||||
i2c.mode = config.Mode
|
||||
|
||||
if i2c.mode == I2CModeController {
|
||||
if config.Frequency >= 400*KHz {
|
||||
i2c.Bus.FREQUENCY.Set(nrf.TWI_FREQUENCY_FREQUENCY_K400)
|
||||
} else {
|
||||
i2c.Bus.FREQUENCY.Set(nrf.TWI_FREQUENCY_FREQUENCY_K100)
|
||||
}
|
||||
i2c.SetBaudRate(config.Frequency)
|
||||
|
||||
i2c.enableAsController()
|
||||
} else {
|
||||
@@ -262,6 +285,23 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the I2C frequency. It has the side effect of also
|
||||
// enabling the I2C hardware if disabled beforehand.
|
||||
//
|
||||
//go:inline
|
||||
func (i2c *I2C) SetBaudRate(br uint32) error {
|
||||
switch {
|
||||
case br >= 400*KHz:
|
||||
i2c.Bus.SetFREQUENCY(nrf.TWI_FREQUENCY_FREQUENCY_K400)
|
||||
case br >= 250*KHz:
|
||||
i2c.Bus.SetFREQUENCY(nrf.TWI_FREQUENCY_FREQUENCY_K250)
|
||||
default:
|
||||
i2c.Bus.SetFREQUENCY(nrf.TWI_FREQUENCY_FREQUENCY_K100)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// signalStop sends a stop signal to the I2C peripheral and waits for confirmation.
|
||||
func (i2c *I2C) signalStop() error {
|
||||
tries := 0
|
||||
|
||||
@@ -49,7 +49,7 @@ type SPIConfig struct {
|
||||
}
|
||||
|
||||
// Configure is intended to setup the SPI interface.
|
||||
func (spi SPI) Configure(config SPIConfig) {
|
||||
func (spi SPI) Configure(config SPIConfig) error {
|
||||
// Disable bus to configure it
|
||||
spi.Bus.ENABLE.Set(nrf.SPI_ENABLE_ENABLE_Disabled)
|
||||
|
||||
@@ -117,6 +117,8 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
|
||||
// Re-enable bus now that it is configured.
|
||||
spi.Bus.ENABLE.Set(nrf.SPI_ENABLE_ENABLE_Enabled)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
|
||||
@@ -197,7 +197,7 @@ type SPIConfig struct {
|
||||
}
|
||||
|
||||
// Configure is intended to setup the SPI interface.
|
||||
func (spi SPI) Configure(config SPIConfig) {
|
||||
func (spi SPI) Configure(config SPIConfig) error {
|
||||
// Disable bus to configure it
|
||||
spi.Bus.ENABLE.Set(nrf.SPIM_ENABLE_ENABLE_Disabled)
|
||||
|
||||
@@ -265,6 +265,8 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
|
||||
// Re-enable bus now that it is configured.
|
||||
spi.Bus.ENABLE.Set(nrf.SPIM_ENABLE_ENABLE_Enabled)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
|
||||
@@ -13,6 +13,32 @@ func EnterBootloader() {
|
||||
enterBootloader()
|
||||
}
|
||||
|
||||
// 13 = 1 + FLASH_RUID_DUMMY_BYTES(4) + FLASH_RUID_DATA_BYTES(8)
|
||||
var deviceIDBuf [13]byte
|
||||
|
||||
// DeviceID returns an identifier that is unique within
|
||||
// a particular chipset.
|
||||
//
|
||||
// The identity is one burnt into the MCU itself, or the
|
||||
// flash chip at time of manufacture.
|
||||
//
|
||||
// It's possible that two different vendors may allocate
|
||||
// the same DeviceID, so callers should take this into
|
||||
// account if needing to generate a globally unique id.
|
||||
//
|
||||
// The length of the hardware ID is vendor-specific, but
|
||||
// 8 bytes (64 bits) is common.
|
||||
func DeviceID() []byte {
|
||||
deviceIDBuf[0] = 0x4b // FLASH_RUID_CMD
|
||||
|
||||
err := doFlashCommand(deviceIDBuf[:], deviceIDBuf[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return deviceIDBuf[5:13]
|
||||
}
|
||||
|
||||
// compile-time check for ensuring we fulfill BlockDevice interface
|
||||
var _ BlockDevice = flashBlockDevice{}
|
||||
|
||||
|
||||
@@ -93,6 +93,33 @@ static ram_func void flash_enable_xip_via_boot2() {
|
||||
((void (*)(void))boot2_copyout+1)();
|
||||
}
|
||||
|
||||
#define IO_QSPI_BASE 0x40018000
|
||||
#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS 0x00000300
|
||||
#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_MSB 9
|
||||
#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB 8
|
||||
#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW 0x2
|
||||
#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH 0x3
|
||||
|
||||
#define XIP_SSI_BASE 0x18000000
|
||||
#define ssi_hw ((ssi_hw_t *)XIP_SSI_BASE)
|
||||
#define SSI_SR_OFFSET 0x00000028
|
||||
#define SSI_DR0_OFFSET 0x00000060
|
||||
#define SSI_SR_TFNF_BITS 0x00000002
|
||||
#define SSI_SR_RFNE_BITS 0x00000008
|
||||
|
||||
void ram_func flash_cs_force(bool high) {
|
||||
uint32_t field_val = high ?
|
||||
IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH :
|
||||
IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW;
|
||||
|
||||
// &ioqspi_hw->io[1].ctrl
|
||||
uint32_t *addr = (uint32_t*)(IO_QSPI_BASE + (1 * 8) + 4);
|
||||
|
||||
*addr = ((*addr) & !IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS)
|
||||
| (field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB);
|
||||
|
||||
}
|
||||
|
||||
// See https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_flash/flash.c#L86
|
||||
void ram_func flash_range_write(uint32_t offset, const uint8_t *data, size_t count)
|
||||
{
|
||||
@@ -132,6 +159,42 @@ void ram_func flash_erase_blocks(uint32_t offset, size_t count)
|
||||
flash_enable_xip_via_boot2();
|
||||
}
|
||||
|
||||
void ram_func flash_do_cmd(const uint8_t *txbuf, uint8_t *rxbuf, size_t count) {
|
||||
flash_connect_internal_fn flash_connect_internal_func = (flash_connect_internal_fn) rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH);
|
||||
flash_exit_xip_fn flash_exit_xip_func = (flash_exit_xip_fn) rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP);
|
||||
flash_flush_cache_fn flash_flush_cache_func = (flash_flush_cache_fn) rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE);
|
||||
|
||||
flash_init_boot2_copyout();
|
||||
|
||||
__compiler_memory_barrier();
|
||||
|
||||
flash_connect_internal_func();
|
||||
flash_exit_xip_func();
|
||||
|
||||
flash_cs_force(0);
|
||||
size_t tx_remaining = count;
|
||||
size_t rx_remaining = count;
|
||||
// We may be interrupted -- don't want FIFO to overflow if we're distracted.
|
||||
const size_t max_in_flight = 16 - 2;
|
||||
while (tx_remaining || rx_remaining) {
|
||||
uint32_t flags = *(uint32_t*)(XIP_SSI_BASE + SSI_SR_OFFSET);
|
||||
bool can_put = !!(flags & SSI_SR_TFNF_BITS);
|
||||
bool can_get = !!(flags & SSI_SR_RFNE_BITS);
|
||||
if (can_put && tx_remaining && rx_remaining - tx_remaining < max_in_flight) {
|
||||
*(uint32_t*)(XIP_SSI_BASE + SSI_DR0_OFFSET) = *txbuf++;
|
||||
--tx_remaining;
|
||||
}
|
||||
if (can_get && rx_remaining) {
|
||||
*rxbuf++ = (uint8_t)*(uint32_t*)(XIP_SSI_BASE + SSI_DR0_OFFSET);
|
||||
--rx_remaining;
|
||||
}
|
||||
}
|
||||
flash_cs_force(1);
|
||||
|
||||
flash_flush_cache_func();
|
||||
flash_enable_xip_via_boot2();
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -139,6 +202,19 @@ func enterBootloader() {
|
||||
C.reset_usb_boot(0, 0)
|
||||
}
|
||||
|
||||
func doFlashCommand(tx []byte, rx []byte) error {
|
||||
if len(tx) != len(rx) {
|
||||
return errFlashInvalidWriteLength
|
||||
}
|
||||
|
||||
C.flash_do_cmd(
|
||||
(*C.uint8_t)(unsafe.Pointer(&tx[0])),
|
||||
(*C.uint8_t)(unsafe.Pointer(&rx[0])),
|
||||
C.ulong(len(tx)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flash related code
|
||||
const memoryStart = C.XIP_BASE // memory start for purpose of erase
|
||||
|
||||
|
||||
@@ -289,7 +289,26 @@ func (spi SPI) isBusy() bool {
|
||||
|
||||
// tx writes buffer to SPI ignoring Rx.
|
||||
func (spi SPI) tx(tx []byte) error {
|
||||
if len(tx) == 0 {
|
||||
err := spi.StartTx(tx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return spi.Wait()
|
||||
}
|
||||
|
||||
// IsAsync returns whether the SPI supports async operation (usually DMA).
|
||||
//
|
||||
// It returns true on the rp2040.
|
||||
func (spi SPI) IsAsync() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Start a transfer in the background.
|
||||
//
|
||||
// After this, another StartTx() or Wait() must be called. The provided byte
|
||||
// slices (tx and rx) may only be accessed again after Wait() was called.
|
||||
func (spi SPI) StartTx(tx, rx []byte) error {
|
||||
if len(tx) == 0 && len(rx) == 0 {
|
||||
// We don't have to do anything.
|
||||
// This avoids a panic in &tx[0] when len(tx) == 0.
|
||||
return nil
|
||||
@@ -306,6 +325,15 @@ func (spi SPI) tx(tx []byte) error {
|
||||
dreq = 18 // DREQ_SPI1_TX
|
||||
}
|
||||
|
||||
// Wait for the previous transmission to complete.
|
||||
for ch.CTRL_TRIG.Get()&rp.DMA_CH0_CTRL_TRIG_BUSY != 0 {
|
||||
}
|
||||
|
||||
if len(rx) != 0 {
|
||||
// Fallback. We don't support receiving data using DMA yet.
|
||||
return spi.Tx(tx, rx)
|
||||
}
|
||||
|
||||
// Configure the DMA peripheral as follows:
|
||||
// - set read address, write address, and number of transfer units (bytes)
|
||||
// - increment read address (in memory), don't increment write address (SSPDR)
|
||||
@@ -319,6 +347,19 @@ func (spi SPI) tx(tx []byte) error {
|
||||
rp.DMA_CH0_CTRL_TRIG_DATA_SIZE_SIZE_BYTE<<rp.DMA_CH0_CTRL_TRIG_DATA_SIZE_Pos |
|
||||
dreq<<rp.DMA_CH0_CTRL_TRIG_TREQ_SEL_Pos |
|
||||
rp.DMA_CH0_CTRL_TRIG_EN)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wait until all active transactions (started by StartTx) have finished. The
|
||||
// buffers provided in StartTx will be available after this method returns.
|
||||
func (spi SPI) Wait() error {
|
||||
// Pick the DMA channel reserved for this SPI peripheral.
|
||||
var ch *dmaChannel
|
||||
if spi.Bus == rp.SPI0 {
|
||||
ch = &dmaChannels[spi0DMAChannel]
|
||||
} else { // SPI1
|
||||
ch = &dmaChannels[spi1DMAChannel]
|
||||
}
|
||||
|
||||
// Wait until the transfer is complete.
|
||||
// TODO: do this more efficiently:
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/stm32"
|
||||
import (
|
||||
"device/stm32"
|
||||
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const deviceName = stm32.Device
|
||||
|
||||
@@ -80,3 +85,20 @@ func (p Pin) PortMaskClear() (*uint32, uint32) {
|
||||
pin := uint8(p) % 16
|
||||
return &port.BSRR.Reg, 1 << (pin + 16)
|
||||
}
|
||||
|
||||
var deviceID [12]byte
|
||||
|
||||
// DeviceID returns an identifier that is unique within
|
||||
// a particular chipset.
|
||||
//
|
||||
// The identity is one burnt into the MCU itself.
|
||||
//
|
||||
// The length of the device ID for STM32 is 12 bytes (96 bits).
|
||||
func DeviceID() []byte {
|
||||
for i := 0; i < len(deviceID); i++ {
|
||||
word := (*volatile.Register32)(unsafe.Pointer(deviceIDAddr[i/4])).Get()
|
||||
deviceID[i] = byte(word >> ((i % 4) * 8))
|
||||
}
|
||||
|
||||
return deviceID[:]
|
||||
}
|
||||
|
||||
@@ -157,6 +157,12 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for I2C.
|
||||
func (i2c *I2C) SetBaudRate(br uint32) error {
|
||||
// TODO: implement
|
||||
return errI2CNotImplemented
|
||||
}
|
||||
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
|
||||
if err := i2c.controllerTransmit(addr, w); nil != err {
|
||||
|
||||
@@ -84,6 +84,12 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for I2C.
|
||||
func (i2c *I2C) SetBaudRate(br uint32) error {
|
||||
// TODO: implement
|
||||
return errI2CNotImplemented
|
||||
}
|
||||
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
if len(w) > 0 {
|
||||
if err := i2c.controllerTransmit(addr, w); nil != err {
|
||||
|
||||
@@ -21,7 +21,7 @@ type SPIConfig struct {
|
||||
}
|
||||
|
||||
// Configure is intended to setup the STM32 SPI1 interface.
|
||||
func (spi SPI) Configure(config SPIConfig) {
|
||||
func (spi SPI) Configure(config SPIConfig) error {
|
||||
|
||||
// -- CONFIGURING THE SPI IN MASTER MODE --
|
||||
//
|
||||
@@ -93,6 +93,8 @@ func (spi SPI) Configure(config SPIConfig) {
|
||||
|
||||
// enable SPI
|
||||
spi.Bus.CR1.SetBits(stm32.SPI_CR1_SPE)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transfer writes/reads a single byte using the SPI interface.
|
||||
|
||||
@@ -15,6 +15,8 @@ func CPUFrequency() uint32 {
|
||||
return 72000000
|
||||
}
|
||||
|
||||
var deviceIDAddr = []uintptr{0x1FFFF7E8, 0x1FFFF7EC, 0x1FFFF7F0}
|
||||
|
||||
// Internal use: configured speed of the APB1 and APB2 timers, this should be kept
|
||||
// in sync with any changes to runtime package which configures the oscillators
|
||||
// and clock frequencies
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var deviceIDAddr = []uintptr{0x1FFF7A10, 0x1FFF7A14, 0x1FFF7A18}
|
||||
|
||||
const (
|
||||
PA0 = portA + 0
|
||||
PA1 = portA + 1
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var deviceIDAddr = []uintptr{0x1FF0F420, 0x1FF0F424, 0x1FF0F428}
|
||||
|
||||
// Alternative peripheral pin functions
|
||||
const (
|
||||
AF0_SYSTEM = 0
|
||||
|
||||
@@ -13,6 +13,8 @@ func CPUFrequency() uint32 {
|
||||
return 32000000
|
||||
}
|
||||
|
||||
var deviceIDAddr = []uintptr{0x1FF80050, 0x1FF80054, 0x1FF80058}
|
||||
|
||||
// Internal use: configured speed of the APB1 and APB2 timers, this should be kept
|
||||
// in sync with any changes to runtime package which configures the oscillators
|
||||
// and clock frequencies
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
|
||||
// Peripheral abstraction layer for the stm32l4
|
||||
|
||||
var deviceIDAddr = []uintptr{0x1FFF7590, 0x1FFF7594, 0x1FFF7598}
|
||||
|
||||
const (
|
||||
AF0_SYSTEM = 0
|
||||
AF1_TIM1_2_LPTIM1 = 1
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var deviceIDAddr = []uintptr{0x0BFA0590, 0x0BFA0594, 0x0BFA0598}
|
||||
|
||||
const (
|
||||
AF0_SYSTEM = 0
|
||||
AF1_TIM1_2_5_8_LPTIM1 = 1
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var deviceIDAddr = []uintptr{0x1FFF7590, 0x1FFF7594, 0x1FFF7598}
|
||||
|
||||
const (
|
||||
AF0_SYSTEM = 0
|
||||
AF1_TIM1_2_LPTIM1 = 1
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
//go:build !baremetal || atmega || esp32 || fe310 || k210 || nrf || (nxp && !mk66f18) || atsamd21 || (stm32 && !stm32f7x2 && !stm32l5x2)
|
||||
|
||||
package machine
|
||||
|
||||
// This is a non-async implementation of the async SPI calls.
|
||||
// It is useful for devices that don't support DMA on SPI, or for which it
|
||||
// hasn't been implemented yet.
|
||||
|
||||
// IsAsync returns whether the SPI supports async operation (usually DMA).
|
||||
//
|
||||
// This SPI does not support async operations.
|
||||
func (s SPI) IsAsync() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Start a transfer in the background.
|
||||
//
|
||||
// Because this SPI implementation doesn't support async operation, it is an
|
||||
// alias for Tx.
|
||||
func (s SPI) StartTx(tx, rx []byte) error {
|
||||
return s.Tx(tx, rx)
|
||||
}
|
||||
|
||||
// Wait until all active transactions (started by StartTx) have finished.
|
||||
//
|
||||
// This is a no-op on this SPI implementation.
|
||||
func (s SPI) Wait() error {
|
||||
return nil
|
||||
}
|
||||
@@ -16,3 +16,17 @@ var (
|
||||
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
|
||||
errSPIInvalidMachineConfig = errors.New("SPI port was not configured properly by the machine")
|
||||
)
|
||||
|
||||
// If you are getting a compile error on this line please check to see you've
|
||||
// correctly implemented the methods on the SPI type. They must match
|
||||
// the interface method signatures type to type perfectly.
|
||||
// If not implementing the SPI type please remove your target from the build tags
|
||||
// at the top of this file.
|
||||
var _ interface { // 2
|
||||
Configure(config SPIConfig) error
|
||||
Tx(w, r []byte) error
|
||||
Transfer(w byte) (byte, error)
|
||||
IsAsync() bool
|
||||
StartTx(tx, rx []byte) error
|
||||
Wait() error
|
||||
} = (*SPI)(nil)
|
||||
|
||||
@@ -10,8 +10,7 @@ func Callers(skip int, pc []uintptr) int {
|
||||
var buildVersion string
|
||||
|
||||
// Version returns the Tinygo tree's version string.
|
||||
// It is the same as goenv.Version, or in case of a development build,
|
||||
// it will be the concatenation of goenv.Version and the git commit hash.
|
||||
// It is the same as goenv.Version().
|
||||
func Version() string {
|
||||
return buildVersion
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
//
|
||||
// | object type | size | bitstring | note
|
||||
// |-------------|------|-----------|------
|
||||
// | int | 1 | 0 | no pointers in this object
|
||||
// | string | 2 | 10 | {pointer, len} pair so there is one pointer
|
||||
// | []int | 3 | 100 | {pointer, len, cap}
|
||||
// | [4]*int | 1 | 1 | even though it contains 4 pointers, an array repeats so it can be stored with size=1
|
||||
// | [30]byte | 1 | 0 | there are no pointers so the layout is very simple
|
||||
// | int | 1 | 0 | no pointers in this object
|
||||
// | string | 2 | 01 | {pointer, len} pair so there is one pointer
|
||||
// | []int | 3 | 001 | {pointer, len, cap}
|
||||
// | [4]*int | 1 | 1 | even though it contains 4 pointers, an array repeats so it can be stored with size=1
|
||||
// | [30]byte | 1 | 0 | there are no pointers so the layout is very simple
|
||||
//
|
||||
// The garbage collector scans objects by starting at the first word value in
|
||||
// the object. If the least significant bit of the bitstring is clear, it is
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@
|
||||
"libc": "wasi-libc",
|
||||
"rtlib": "compiler-rt",
|
||||
"scheduler": "asyncify",
|
||||
"default-stack-size": 32768,
|
||||
"default-stack-size": 65536,
|
||||
"cflags": [
|
||||
"-mbulk-memory",
|
||||
"-mnontrapping-fptoint",
|
||||
@@ -22,5 +22,5 @@
|
||||
"extra-files": [
|
||||
"src/runtime/asm_tinygowasm.S"
|
||||
],
|
||||
"emulator": "wasmtime --mapdir=/tmp::{tmpDir} {}"
|
||||
"emulator": "wasmtime --dir={tmpDir}::/tmp {}"
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
"libc": "wasi-libc",
|
||||
"rtlib": "compiler-rt",
|
||||
"scheduler": "asyncify",
|
||||
"default-stack-size": 32768,
|
||||
"default-stack-size": 65536,
|
||||
"cflags": [
|
||||
"-mbulk-memory",
|
||||
"-mnontrapping-fptoint",
|
||||
|
||||
@@ -145,7 +145,7 @@ func compileGoFileForTesting(t *testing.T, filename string) llvm.Module {
|
||||
defer machine.Dispose()
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, filename, config.ClangHeaders, types.Config{
|
||||
lprogram, err := loader.Load(config, filename, types.Config{
|
||||
Sizes: compiler.Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user