Compare commits

..

1 Commits

Author SHA1 Message Date
Ayke van Laethem c660750636 tools: process SVD files in parallel
This speeds up the generation of SVD files, for example as part of a
package build (where parallelism may not be desired, or tools should
auto-detect the number of threads to use).
2023-10-04 15:24:09 +02:00
84 changed files with 488 additions and 1510 deletions
+7 -16
View File
@@ -5,7 +5,7 @@ commands:
steps:
- run:
name: "Pull submodules"
command: git submodule update --init --recursive
command: git submodule update --init
llvm-source-linux:
steps:
- restore_cache:
@@ -55,7 +55,7 @@ commands:
- run:
name: "Install apt dependencies"
command: |
echo 'deb https://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<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-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-v4-{{ checksum "go.mod" }}
- go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-v3-{{ checksum "go.mod" }}
- llvm-source-linux
- run: go install -tags=llvm<<parameters.llvm>> .
- restore_cache:
@@ -89,10 +89,10 @@ commands:
# formatting of generated files.
name: Check Go code formatting
command: make fmt-check
- run: make gen-device -j4
- run: make gen-device
- run: make smoketest XTENSA=0
- save_cache:
key: go-cache-v4-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
key: go-cache-v3-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
- ~/.cache/go-build
- /go/pkg/mod
@@ -100,18 +100,11 @@ commands:
jobs:
test-llvm15-go118:
docker:
- image: golang:1.18-bullseye
- image: golang:1.18-buster
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:
@@ -119,5 +112,3 @@ 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
+4 -20
View File
@@ -88,7 +88,7 @@ jobs:
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- name: make gen-device
run: make -j3 gen-device
run: make gen-device
- name: Test TinyGo
shell: bash
run: make test GOTESTFLAGS="-short"
@@ -116,21 +116,11 @@ jobs:
test-macos-homebrew:
name: homebrew-install
runs-on: macos-latest
strategy:
matrix:
version: [16, 17]
steps:
- name: Update Homebrew
run: brew update
- name: Fix Python symlinks
run: |
# 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
shell: bash
run: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@${{ matrix.version }}
HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@16
- name: Checkout
uses: actions/checkout@v3
- name: Install Go
@@ -138,13 +128,7 @@ jobs:
with:
go-version: '1.21'
cache: true
- 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
- name: Build TinyGo
run: go install
- name: Check binary
if: matrix.version == 16
run: tinygo version
+7 -7
View File
@@ -140,8 +140,8 @@ jobs:
- name: Install wasmtime
run: |
mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
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/*
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/*
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/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/*
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/*
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
@@ -255,7 +255,7 @@ jobs:
- name: Build wasi-libc
if: steps.cache-wasi-libc.outputs.cache-hit != 'true'
run: make wasi-libc
- run: make gen-device -j4
- run: make gen-device
- name: Test TinyGo
run: make ASSERT=1 test
- name: Build TinyGo
@@ -356,13 +356,13 @@ jobs:
uses: actions/cache@v3
id: cache-binaryen
with:
key: binaryen-linux-${{ matrix.goarch }}-v3
key: binaryen-linux-${{ matrix.goarch }}-v1
path: build/wasm-opt
- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: |
sudo apt-get install --no-install-recommends ninja-build
git submodule update --init --recursive lib/binaryen
git submodule update --init lib/binaryen
make CROSS=${{ matrix.toolchain }} binaryen
- name: Install fpm
run: |
-43
View File
@@ -1,43 +0,0 @@
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"
+1 -1
View File
@@ -49,7 +49,7 @@ jobs:
path: |
~/.cache/go-build
~/go/pkg/mod
- run: make gen-device -j4
- run: make gen-device
- name: Download drivers repo
run: git clone https://github.com/tinygo-org/drivers.git
- name: Save HEAD
+3 -3
View File
@@ -96,9 +96,9 @@ jobs:
run: make wasi-libc
- name: Install wasmtime
run: |
scoop install wasmtime@14.0.4
scoop install wasmtime
- name: make gen-device
run: make -j3 gen-device
run: make gen-device
- name: Test TinyGo
shell: bash
run: make test GOTESTFLAGS="-short"
@@ -203,7 +203,7 @@ jobs:
- name: Install Dependencies
shell: bash
run: |
scoop install binaryen && scoop install wasmtime@14.0.4
scoop install binaryen wasmtime
- name: Checkout
uses: actions/checkout@v3
- name: Install Go
+1 -1
View File
@@ -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 --recursive
git submodule update --init
The release tarball is stored in build/release.tar.gz, and can be extracted with
the following command (for example in ~/lib):
+10 -12
View File
@@ -10,7 +10,7 @@ LLD_SRC ?= $(LLVM_PROJECTDIR)/lld
# Try to autodetect LLVM build tools.
# Versions are listed here in descending priority order.
LLVM_VERSIONS = 17 16 15
LLVM_VERSIONS = 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,6 +32,9 @@ 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)
@@ -127,14 +130,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
@@ -142,9 +145,6 @@ 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 --recursive"; 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"; 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 --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)
@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)
# Check for Node.js used during WASM tests.
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
@@ -506,8 +506,6 @@ 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
@@ -880,7 +878,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 -1
View File
@@ -1,6 +1,6 @@
# TinyGo - Go compiler for small places
[![Linux](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [![macOS](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [![Windows](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [![Docker](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [![Nix](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/nix.yml) [![CircleCI](https://circleci.com/gh/tinygo-org/tinygo/tree/dev.svg?style=svg)](https://circleci.com/gh/tinygo-org/tinygo/tree/dev)
[![Linux](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/linux.yml) [![macOS](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/build-macos.yml) [![Windows](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/windows.yml) [![Docker](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml/badge.svg?branch=dev)](https://github.com/tinygo-org/tinygo/actions/workflows/docker.yml) [![CircleCI](https://circleci.com/gh/tinygo-org/tinygo/tree/dev.svg?style=svg)](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.
+29 -31
View File
@@ -114,30 +114,6 @@ 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.
@@ -192,7 +168,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(),
@@ -211,7 +187,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, types.Config{
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
Sizes: compiler.Sizes(machine),
})
if err != nil {
@@ -240,12 +216,34 @@ 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 globalValues[pkg.Pkg.Path()] {
for name := range config.Options.GlobalValues[pkg.Pkg.Path()] {
undefinedGlobals = append(undefinedGlobals, name)
}
sort.Strings(undefinedGlobals)
@@ -573,7 +571,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, globalValues)
err := optimizeProgram(mod, config)
if err != nil {
return err
}
@@ -662,7 +660,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(false), config.Options.PrintCommands)
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.Options.PrintCommands)
job.result = result
return err
},
@@ -1042,7 +1040,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, globalValues map[string]map[string]string) error {
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
if err != nil {
return err
@@ -1061,7 +1059,7 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues m
}
// Insert values from -ldflags="-X ..." into the IR.
err = setGlobalValues(mod, globalValues)
err = setGlobalValues(mod, config.Options.GlobalValues)
if err != nil {
return err
}
+6 -3
View File
@@ -8,6 +8,7 @@ import (
"testing"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv"
"tinygo.org/x/go-llvm"
)
@@ -73,6 +74,7 @@ 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()
@@ -82,8 +84,9 @@ func testClangAttributes(t *testing.T, options *compileopts.Options) {
t.Fatalf("could not load target: %s", err)
}
config := compileopts.Config{
Options: options,
Target: target,
Options: options,
Target: target,
ClangHeaders: clangHeaderPath,
}
// Create a very simple C input file.
@@ -95,7 +98,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(false)...)
flags := append([]string{"-c", "-emit-llvm", "-o", outpath, srcpath}, config.CFlags()...)
if config.GOOS() == "darwin" {
// Silence some warnings that happen when testing GOOS=darwin on
// something other than MacOS.
+10 -34
View File
@@ -77,15 +77,6 @@ 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
@@ -97,31 +88,16 @@ 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
id, err := readRawGoBuildID(f, 4096)
if len(id) != 0 || err != nil {
return id, err
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
}
}
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
return nil, fmt.Errorf("could not find build ID in %s", executable)
}
+3
View File
@@ -33,10 +33,13 @@ 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
View File
@@ -0,0 +1,105 @@
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 ""
}
-4
View File
@@ -143,10 +143,6 @@ 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.
+79 -93
View File
@@ -3,7 +3,6 @@ package builder
import (
"os"
"path/filepath"
"strings"
"github.com/tinygo-org/tinygo/goenv"
)
@@ -42,23 +41,91 @@ var Picolibc = Library{
},
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
librarySources: func(target string) ([]string, error) {
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
return picolibcSources, 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",
@@ -162,87 +229,6 @@ 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",
+6
View File
@@ -1,6 +1,7 @@
package builder
import (
"errors"
"os"
"os/exec"
@@ -11,6 +12,11 @@ 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
+4 -1
View File
@@ -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) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string, clangHeaders string) (*ast.File, []string, []string, []string, map[string][]byte, []error) {
p := &cgoPackage{
currentDir: dir,
importPath: importPath,
@@ -292,6 +292,9 @@ 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
View File
@@ -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
+2 -2
View File
@@ -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
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/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 freebsd LDFLAGS: -L/usr/local/llvm15/lib -lclang
*/
import "C"
+4 -4
View File
@@ -1,8 +1,8 @@
//go:build !byollvm && !llvm15 && !llvm17
//go:build !byollvm && !llvm15
package cgo
// As of 2023-05-05, there is a packaging issue with LLVM 16 on Debian:
// As of 2023-05-05, there is a packaging issue 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
#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/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 freebsd LDFLAGS: -L/usr/local/llvm16/lib -lclang
*/
import "C"
-15
View File
@@ -1,15 +0,0 @@
//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"
+6 -21
View File
@@ -19,6 +19,7 @@ type Config struct {
Options *Options
Target *TargetSpec
GoMinorVersion int
ClangHeaders string // Clang built-in header include path
TestConfig TestConfig
}
@@ -258,39 +259,24 @@ 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(libclang bool) []string {
func (c *Config) CFlags() []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,
"-nostdlibinc",
"-isystem", filepath.Join(root, "lib/macos-minimal-sdk/src/usr/include"),
"--sysroot="+filepath.Join(root, "lib/macos-minimal-sdk/src"),
)
case "picolibc":
root := goenv.Get("TINYGOROOT")
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
path, _ := c.LibcPath("picolibc")
cflags = append(cflags,
"-nostdlibinc",
"-isystem", filepath.Join(path, "include"),
"--sysroot="+path,
"-isystem", filepath.Join(path, "include"), // necessary for Xtensa
"-isystem", filepath.Join(picolibcDir, "include"),
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
)
@@ -311,8 +297,7 @@ func (c *Config) CFlags(libclang bool) []string {
root := goenv.Get("TINYGOROOT")
path, _ := c.LibcPath("mingw-w64")
cflags = append(cflags,
"-nostdlibinc",
"-isystem", filepath.Join(path, "include"),
"--sysroot="+path,
"-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",
+2 -39
View File
@@ -246,43 +246,6 @@ 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.
@@ -381,12 +344,12 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
spec.Linker = "wasm-ld"
spec.RTLib = "compiler-rt"
spec.Libc = "wasi-libc"
spec.DefaultStackSize = 1024 * 64 // 64kB
spec.DefaultStackSize = 1024 * 32 // 32kB
spec.LDFlags = append(spec.LDFlags,
"--stack-first",
"--no-demangle",
)
spec.Emulator = "wasmtime --dir={tmpDir}::/tmp {}"
spec.Emulator = "wasmtime --mapdir=/tmp::{tmpDir} {}"
spec.ExtraFiles = append(spec.ExtraFiles,
"src/runtime/asm_tinygowasm.S",
"src/internal/task/task_asyncify_wasm.S",
+1 -1
View File
@@ -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, types.Config{
lprogram, err := loader.Load(config, "./testdata/"+file, config.ClangHeaders, types.Config{
Sizes: Sizes(machine),
})
if err != nil {
+5 -5
View File
@@ -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 65536, ptr undef) #9
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
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 65536, 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 32768, 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 65536, ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 32768, 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 65536, ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 32768, 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 65536, 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 32768, ptr undef) #9
ret void
}
Generated
-60
View File
@@ -1,60 +0,0 @@
{
"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
}
-85
View File
@@ -1,85 +0,0 @@
# 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"\"
'';
};
}
);
}
+1 -1
View File
@@ -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-20231014233752-75a8a9fe6f74
tinygo.org/x/go-llvm v0.0.0-20230920233244-32ed56c6be9c
)
require (
+2 -2
View File
@@ -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-20231014233752-75a8a9fe6f74 h1:tW8XhLI9gUZLL+2pG0HYb5dc6bpMj1aqtESpizXPnMY=
tinygo.org/x/go-llvm v0.0.0-20231014233752-75a8a9fe6f74/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
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=
-103
View File
@@ -14,8 +14,6 @@ import (
"runtime"
"strings"
"sync"
"tinygo.org/x/go-llvm"
)
// Keys is a slice of all available environment variable keys.
@@ -35,20 +33,11 @@ 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
@@ -295,95 +284,3 @@ 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 ""
}
-7
View File
@@ -1,7 +0,0 @@
//go:build byollvm
package goenv
func init() {
hasBuiltinTools = true
}
+1 -11
View File
@@ -9,7 +9,7 @@ import (
// Version of TinyGo.
// Update this value before release of new version of software.
const version = "0.31.0-dev"
const Version = "0.30.0"
var (
// This variable is set at build time using -ldflags parameters.
@@ -17,16 +17,6 @@ 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
View File
@@ -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 --recursive
git submodule update --init
+3 -2
View File
@@ -18,6 +18,7 @@ import (
"errors"
"io"
"io/fs"
"io/ioutil"
"os"
"os/exec"
"path"
@@ -156,7 +157,7 @@ func listGorootMergeLinks(goroot, tinygoroot string, overrides map[string]bool)
// Add files from TinyGo.
tinygoDir := filepath.Join(tinygoSrc, dir)
tinygoEntries, err := os.ReadDir(tinygoDir)
tinygoEntries, err := ioutil.ReadDir(tinygoDir)
if err != nil {
return nil, err
}
@@ -176,7 +177,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 := os.ReadDir(goDir)
goEntries, err := ioutil.ReadDir(goDir)
if err != nil {
return nil, err
}
+15 -13
View File
@@ -29,10 +29,11 @@ import (
// Program holds all packages and some metadata about the program as a whole.
type Program struct {
config *compileopts.Config
typeChecker types.Config
goroot string // synthetic GOROOT
workingDir string
config *compileopts.Config
clangHeaders string
typeChecker types.Config
goroot string // synthetic GOROOT
workingDir string
Packages map[string]*Package
sorted []*Package
@@ -102,7 +103,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, typeChecker types.Config) (*Program, error) {
func Load(config *compileopts.Config, inputPkg string, clangHeaders string, typeChecker types.Config) (*Program, error) {
goroot, err := GetCachedGoroot(config)
if err != nil {
return nil, err
@@ -117,12 +118,13 @@ func Load(config *compileopts.Config, inputPkg string, typeChecker types.Config)
}
}
p := &Program{
config: config,
typeChecker: typeChecker,
goroot: goroot,
workingDir: wd,
Packages: make(map[string]*Package),
fset: token.NewFileSet(),
config: config,
clangHeaders: clangHeaders,
typeChecker: typeChecker,
goroot: goroot,
workingDir: wd,
Packages: make(map[string]*Package),
fset: token.NewFileSet(),
}
// List the dependencies of this package, in raw JSON format.
@@ -436,9 +438,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(true)...)
initialCFlags = append(initialCFlags, p.program.config.CFlags()...)
initialCFlags = append(initialCFlags, "-I"+p.Dir)
generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags)
generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags, p.program.clangHeaders)
p.CFlags = append(initialCFlags, cflags...)
p.CGoHeaders = headerCode
for path, hash := range accessedFiles {
+41 -27
View File
@@ -11,6 +11,7 @@ import (
"go/scanner"
"go/types"
"io"
"io/ioutil"
"os"
"os/exec"
"os/signal"
@@ -797,7 +798,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
needsEnvInVars = true
}
}
var args, emuArgs, env []string
var args, env []string
var extraCmdEnv []string
if needsEnvInVars {
runtimeGlobals := make(map[string]string)
@@ -820,14 +821,13 @@ 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.
emuArgs = append(emuArgs, "--dir=.")
args = append(args, "--dir=.")
for _, v := range environmentVars {
emuArgs = append(emuArgs, "--env", v)
args = append(args, "--env", v)
}
if len(cmdArgs) != 0 {
// Use of '--' argument no longer necessary as of Wasmtime v14:
// https://github.com/bytecodealliance/wasmtime/pull/6946
// args = append(args, "--")
// mark end of wasmtime arguments and start of program ones: --
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(emuArgs, emulator[1:]...)
emuArgs := append([]string(nil), emulator[1:]...)
args = append(emuArgs, args...)
}
var cmd *exec.Cmd
@@ -1212,10 +1212,15 @@ 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:", goenv.Version())
fmt.Fprintln(os.Stderr, "version:", 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")
@@ -1437,7 +1442,6 @@ 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.
@@ -1733,29 +1737,35 @@ func main() {
os.Exit(1)
}
case "monitor":
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)
}
err := Monitor("", *port, options)
handleCompilerError(err)
case "targets":
specs, err := compileopts.GetTargetSpecs()
dir := filepath.Join(goenv.Get("TINYGOROOT"), "targets")
entries, err := ioutil.ReadDir(dir)
if err != nil {
fmt.Fprintln(os.Stderr, "could not list targets:", err)
os.Exit(1)
return
}
names := []string{}
for key := range specs {
names = append(names, key)
}
sort.Strings(names)
for _, name := range names {
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]
fmt.Println(name)
}
case "info":
@@ -1864,7 +1874,11 @@ func main() {
if s, err := goenv.GorootVersionString(); err == nil {
goversion = s
}
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", goenv.Version(), runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
version := goenv.Version
if strings.HasSuffix(goenv.Version, "-dev") && 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)
case "env":
if flag.NArg() == 0 {
// Show all environment variables.
-56
View File
@@ -13,15 +13,12 @@ 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.
@@ -131,59 +128,6 @@ 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.
-21
View File
@@ -1,21 +0,0 @@
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)
}
}
+1 -3
View File
@@ -24,9 +24,7 @@ func main() {
// Schedule and enable recurring interrupt.
// The callback function is executed in the context of an interrupt handler,
// 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/
// so regular restructions for this sort of code apply: no blocking, no memory allocation, etc.
delay := time.Minute + 12*time.Second
machine.RTC.SetInterrupt(uint32(delay.Seconds()), true, func() { println("Peekaboo!") })
-17
View File
@@ -1,17 +0,0 @@
//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)
+1 -13
View File
@@ -1,4 +1,4 @@
//go:build atmega || nrf || sam || stm32 || fe310 || k210 || rp2040 || mimxrt1062
//go:build atmega || nrf || sam || stm32 || fe310 || k210 || rp2040
package machine
@@ -6,17 +6,6 @@ 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.
@@ -36,7 +25,6 @@ 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
+1 -6
View File
@@ -31,11 +31,6 @@ 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))
@@ -43,7 +38,7 @@ func (i2c *I2C) SetBaudRate(br uint32) 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() / br) - 16) / 2))
avr.TWBR.Set(uint8(((CPUFrequency() / config.Frequency) - 16) / 2))
// Enable twi module.
avr.TWCR.Set(avr.TWCR_TWEN)
-31
View File
@@ -1,31 +0,0 @@
//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[:]
}
+2 -6
View File
@@ -18,9 +18,6 @@ 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
@@ -735,13 +732,12 @@ func (i2c *I2C) Configure(config I2CConfig) error {
return nil
}
// SetBaudRate sets the communication speed for I2C.
func (i2c *I2C) SetBaudRate(br uint32) error {
// SetBaudRate sets the communication speed for the I2C.
func (i2c *I2C) SetBaudRate(br uint32) {
// 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.
+2 -151
View File
@@ -18,9 +18,6 @@ import (
const deviceName = sam.Device
// DS60001507, Section 9.6: Serial Number
var deviceIDAddr = []uintptr{0x008061FC, 0x00806010, 0x00806014, 0x00806018}
func CPUFrequency() uint32 {
return 120000000
}
@@ -683,56 +680,6 @@ 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
@@ -1281,13 +1228,12 @@ func (i2c *I2C) Configure(config I2CConfig) error {
return nil
}
// SetBaudRate sets the communication speed for I2C.
func (i2c *I2C) SetBaudRate(br uint32) error {
// SetBaudRate sets the communication speed for the I2C.
func (i2c *I2C) SetBaudRate(br uint32) {
// 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.
@@ -1702,101 +1648,6 @@ 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
-21
View File
@@ -62,27 +62,6 @@ 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)
-21
View File
@@ -62,27 +62,6 @@ 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)
-21
View File
@@ -62,27 +62,6 @@ 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)
-25
View File
@@ -76,31 +76,6 @@ 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)
-25
View File
@@ -76,31 +76,6 @@ 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)
-21
View File
@@ -62,27 +62,6 @@ 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)
-25
View File
@@ -76,31 +76,6 @@ 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)
+5 -13
View File
@@ -229,10 +229,9 @@ 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
}
@@ -242,17 +241,7 @@ func (i2c *I2C) Configure(config I2CConfig) error {
config.SCL = I2C0_SCL_PIN
}
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
var prescaler = i2cClockFrequency/(5*config.Frequency) - 1
// disable controller before setting the prescale registers
i2c.Bus.CTR.ClearBits(sifive.I2C_CTR_EN)
@@ -264,6 +253,9 @@ func (i2c *I2C) SetBaudRate(br uint32) 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
}
+1 -11
View File
@@ -57,9 +57,8 @@ type SPIConfig struct {
Mode uint8
}
func (spi SPI) Configure(config SPIConfig) error {
func (spi SPI) Configure(config SPIConfig) {
spiConfigure(spi.Bus, config.SCK, config.SDO, config.SDI)
return nil
}
// Transfer writes/reads a single byte using the SPI interface.
@@ -108,12 +107,6 @@ 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))
@@ -124,9 +117,6 @@ 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
+6 -13
View File
@@ -563,19 +563,7 @@ func (i2c *I2C) Configure(config I2CConfig) error {
config.SCL.SetFPIOAFunction(FUNC_I2C2_SCLK)
}
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
div := CPUFrequency() / config.Frequency / 16
// Disable controller before setting the prescale register.
i2c.Bus.ENABLE.Set(0)
@@ -586,6 +574,11 @@ func (i2c *I2C) SetBaudRate(br uint32) 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
}
+19 -14
View File
@@ -6,6 +6,19 @@ 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.
@@ -137,7 +150,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) error {
func (i2c *I2C) Configure(config I2CConfig) {
// init pins
sda, scl := i2c.setPins(config)
@@ -156,14 +169,6 @@ func (i2c *I2C) Configure(config I2CConfig) error {
// 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 {
@@ -207,13 +212,13 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
return nil
}
// WriteRegisterEx transmits first the register and then the data to the
// WriteRegister 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) WriteRegisterEx(address uint8, register uint8, data []byte) error {
func (i2c I2C) WriteRegister(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
@@ -227,13 +232,13 @@ func (i2c I2C) WriteRegisterEx(address uint8, register uint8, data []byte) error
return nil
}
// ReadRegisterEx transmits the register, restarts the connection as a read
// ReadRegister 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) ReadRegisterEx(address uint8, register uint8, data []byte) error {
func (i2c I2C) ReadRegister(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
@@ -555,7 +560,7 @@ func (i2c *I2C) controllerReceive(rxBuffer []byte) resultFlag {
return result
}
// controllerTransmit performs a polling transmit transfer on the I2C bus.
// controllerReceive performs a polling transmit transfer on the I2C bus.
func (i2c *I2C) controllerTransmit(txBuffer []byte) resultFlag {
txSize := len(txBuffer)
for txSize > 0 {
+1 -3
View File
@@ -68,7 +68,7 @@ var (
)
// Configure is intended to setup an SPI interface for transmit/receive.
func (spi *SPI) Configure(config SPIConfig) error {
func (spi *SPI) Configure(config SPIConfig) {
const defaultSpiFreq = 4000000 // 4 MHz
@@ -132,8 +132,6 @@ func (spi *SPI) Configure(config SPIConfig) error {
spi.Bus.CR.Set(nxp.LPSPI_CR_MEN)
spi.configured = true
return nil
}
// Transfer writes/reads a single byte using the SPI interface.
+6 -46
View File
@@ -12,34 +12,6 @@ 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)
@@ -274,8 +246,13 @@ func (i2c *I2C) Configure(config I2CConfig) error {
i2c.setPins(config.SCL, config.SDA)
i2c.mode = config.Mode
if i2c.mode == I2CModeController {
i2c.SetBaudRate(config.Frequency)
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.enableAsController()
} else {
@@ -285,23 +262,6 @@ 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
+1 -3
View File
@@ -49,7 +49,7 @@ type SPIConfig struct {
}
// Configure is intended to setup the SPI interface.
func (spi SPI) Configure(config SPIConfig) error {
func (spi SPI) Configure(config SPIConfig) {
// Disable bus to configure it
spi.Bus.ENABLE.Set(nrf.SPI_ENABLE_ENABLE_Disabled)
@@ -117,8 +117,6 @@ func (spi SPI) Configure(config SPIConfig) error {
// 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.
+1 -3
View File
@@ -197,7 +197,7 @@ type SPIConfig struct {
}
// Configure is intended to setup the SPI interface.
func (spi SPI) Configure(config SPIConfig) error {
func (spi SPI) Configure(config SPIConfig) {
// Disable bus to configure it
spi.Bus.ENABLE.Set(nrf.SPIM_ENABLE_ENABLE_Disabled)
@@ -265,8 +265,6 @@ func (spi SPI) Configure(config SPIConfig) error {
// 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.
-26
View File
@@ -13,32 +13,6 @@ 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{}
-76
View File
@@ -93,33 +93,6 @@ 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)
{
@@ -159,42 +132,6 @@ 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"
@@ -202,19 +139,6 @@ 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
+1 -42
View File
@@ -289,26 +289,7 @@ func (spi SPI) isBusy() bool {
// tx writes buffer to SPI ignoring Rx.
func (spi SPI) tx(tx []byte) error {
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 {
if len(tx) == 0 {
// We don't have to do anything.
// This avoids a panic in &tx[0] when len(tx) == 0.
return nil
@@ -325,15 +306,6 @@ func (spi SPI) StartTx(tx, rx []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)
@@ -347,19 +319,6 @@ func (spi SPI) StartTx(tx, rx []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:
+1 -23
View File
@@ -2,12 +2,7 @@
package machine
import (
"device/stm32"
"runtime/volatile"
"unsafe"
)
import "device/stm32"
const deviceName = stm32.Device
@@ -85,20 +80,3 @@ 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[:]
}
-6
View File
@@ -157,12 +157,6 @@ 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 {
-6
View File
@@ -84,12 +84,6 @@ 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 {
+1 -3
View File
@@ -21,7 +21,7 @@ type SPIConfig struct {
}
// Configure is intended to setup the STM32 SPI1 interface.
func (spi SPI) Configure(config SPIConfig) error {
func (spi SPI) Configure(config SPIConfig) {
// -- CONFIGURING THE SPI IN MASTER MODE --
//
@@ -93,8 +93,6 @@ func (spi SPI) Configure(config SPIConfig) error {
// enable SPI
spi.Bus.CR1.SetBits(stm32.SPI_CR1_SPE)
return nil
}
// Transfer writes/reads a single byte using the SPI interface.
-2
View File
@@ -15,8 +15,6 @@ 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
-2
View File
@@ -14,8 +14,6 @@ import (
"unsafe"
)
var deviceIDAddr = []uintptr{0x1FFF7A10, 0x1FFF7A14, 0x1FFF7A18}
const (
PA0 = portA + 0
PA1 = portA + 1
-2
View File
@@ -11,8 +11,6 @@ import (
"unsafe"
)
var deviceIDAddr = []uintptr{0x1FF0F420, 0x1FF0F424, 0x1FF0F428}
// Alternative peripheral pin functions
const (
AF0_SYSTEM = 0
-2
View File
@@ -13,8 +13,6 @@ 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
-2
View File
@@ -13,8 +13,6 @@ import (
// Peripheral abstraction layer for the stm32l4
var deviceIDAddr = []uintptr{0x1FFF7590, 0x1FFF7594, 0x1FFF7598}
const (
AF0_SYSTEM = 0
AF1_TIM1_2_LPTIM1 = 1
-2
View File
@@ -11,8 +11,6 @@ import (
"unsafe"
)
var deviceIDAddr = []uintptr{0x0BFA0590, 0x0BFA0594, 0x0BFA0598}
const (
AF0_SYSTEM = 0
AF1_TIM1_2_5_8_LPTIM1 = 1
-2
View File
@@ -14,8 +14,6 @@ import (
"unsafe"
)
var deviceIDAddr = []uintptr{0x1FFF7590, 0x1FFF7594, 0x1FFF7598}
const (
AF0_SYSTEM = 0
AF1_TIM1_2_LPTIM1 = 1
-29
View File
@@ -1,29 +0,0 @@
//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
}
-14
View File
@@ -16,17 +16,3 @@ 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)
+2 -1
View File
@@ -10,7 +10,8 @@ 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().
// 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.
func Version() string {
return buildVersion
}
+5 -5
View File
@@ -17,11 +17,11 @@
//
// | object type | size | bitstring | note
// |-------------|------|-----------|------
// | 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
// | 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
//
// 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
View File
@@ -9,7 +9,7 @@
"libc": "wasi-libc",
"rtlib": "compiler-rt",
"scheduler": "asyncify",
"default-stack-size": 65536,
"default-stack-size": 32768,
"cflags": [
"-mbulk-memory",
"-mnontrapping-fptoint",
@@ -22,5 +22,5 @@
"extra-files": [
"src/runtime/asm_tinygowasm.S"
],
"emulator": "wasmtime --dir={tmpDir}::/tmp {}"
"emulator": "wasmtime --mapdir=/tmp::{tmpDir} {}"
}
+1 -1
View File
@@ -9,7 +9,7 @@
"libc": "wasi-libc",
"rtlib": "compiler-rt",
"scheduler": "asyncify",
"default-stack-size": 65536,
"default-stack-size": 32768,
"cflags": [
"-mbulk-memory",
"-mnontrapping-fptoint",
+65 -21
View File
@@ -10,9 +10,11 @@ import (
"os"
"path/filepath"
"regexp"
"runtime"
"sort"
"strconv"
"strings"
"sync"
"text/template"
"unicode"
)
@@ -1441,6 +1443,31 @@ __isr_vector:
return w.Flush()
}
// Process a single SVD file, synchronously.
func processFile(infile, outdir, sourceURL, interruptSystem string) error {
device, err := readSVD(infile, sourceURL)
if err != nil {
return fmt.Errorf("failed to read: %w", err)
}
err = writeGo(outdir, device, interruptSystem)
if err != nil {
return fmt.Errorf("failed to write Go file: %w", err)
}
switch interruptSystem {
case "software":
// Nothing to do.
case "hardware":
err = writeAsm(outdir, device)
if err != nil {
return fmt.Errorf("failed to write assembly file: %w", err)
}
default:
return fmt.Errorf("unknown interrupt system: %s", interruptSystem)
}
return nil
}
// Process an entire directory, in parallel.
func generate(indir, outdir, sourceURL, interruptSystem string) error {
if _, err := os.Stat(indir); errors.Is(err, fs.ErrNotExist) {
fmt.Fprintln(os.Stderr, "cannot find input directory:", indir)
@@ -1448,35 +1475,52 @@ func generate(indir, outdir, sourceURL, interruptSystem string) error {
}
os.MkdirAll(outdir, 0777)
// Read list of SVD files to process.
infiles, err := filepath.Glob(filepath.Join(indir, "*.svd"))
if err != nil {
fmt.Fprintln(os.Stderr, "could not read .svd files:", err)
os.Exit(1)
}
sort.Strings(infiles)
for _, infile := range infiles {
fmt.Println(infile)
device, err := readSVD(infile, sourceURL)
if err != nil {
return fmt.Errorf("failed to read: %w", err)
}
err = writeGo(outdir, device, interruptSystem)
if err != nil {
return fmt.Errorf("failed to write Go file: %w", err)
}
switch interruptSystem {
case "software":
// Nothing to do.
case "hardware":
err = writeAsm(outdir, device)
if err != nil {
return fmt.Errorf("failed to write assembly file: %w", err)
// Start worker goroutines.
var wg sync.WaitGroup
workChan := make(chan string)
errChan := make(chan error, 1)
for i := 0; i < runtime.NumCPU(); i++ {
go func() {
for infile := range workChan {
err := processFile(infile, outdir, sourceURL, interruptSystem)
if err != nil {
// Store error to errChan if no error was stored before.
select {
case errChan <- err:
default:
}
}
wg.Done()
}
default:
return fmt.Errorf("unknown interrupt system: %s", interruptSystem)
}
}()
}
// Submit all jobs to the goroutines.
wg.Add(len(infiles))
for _, filepath := range infiles {
fmt.Println(filepath)
workChan <- filepath
}
close(workChan)
// Wait until all workers have finished.
wg.Wait()
// Check for an error.
select {
case err := <-errChan:
return err
default:
return nil
}
return nil
}
func main() {
+1 -1
View File
@@ -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, types.Config{
lprogram, err := loader.Load(config, filename, config.ClangHeaders, types.Config{
Sizes: compiler.Sizes(machine),
})
if err != nil {