Compare commits

..

11 Commits

Author SHA1 Message Date
Ayke van Laethem 06df31944c all: release v0.21.0 2021-11-18 14:20:59 +01:00
Damian Gryski 3ba8bc92cd testdata: update binop.go for string comparison tests 2021-11-18 11:07:45 +01:00
Damian Gryski 0e7a129de7 compiler: update testdata/string.ll
$ go test ./compiler -update
2021-11-18 11:07:45 +01:00
Damian Gryski aeddcd9c5f compiler: fix string compare functions
Before:
	x < x false
	x <= x true
	x == x true
	x >= x false
	x > x true

After:
	x < x false
	x <= x true
	x == x true
	x >= x true
	x > x false
2021-11-18 11:07:45 +01:00
Patricio Whittingslow bf0b05e32c machine/rp2040: refactor PWM code. fix Period calculation 2021-11-18 10:22:26 +01:00
Damian Gryski 348a02d697 src/runtime/debug: stub debug.Stack() 2021-11-17 19:25:52 +01:00
Damian Gryski 7273a2b5fd src/testing stub AllocsPerRun 2021-11-17 19:25:52 +01:00
Damian Gryski 44bb381220 src/runtime/debug: add SetMaxStack to allow compress/flate tests to build 2021-11-17 19:25:52 +01:00
Damian Gryski 2e6b92fc56 src/testing: add testing.Verbose() 2021-11-17 19:25:52 +01:00
Ayke van Laethem 51290e5842 wasm: support -scheduler=none
Previously, -scheduler=none wasn't possible for WASM targets:

    $ tinygo run -target=wasm -scheduler=none ./testdata/stdlib.go
    src/runtime/runtime_wasm_js.go:34:2: attempted to start a goroutine without a scheduler

With this commit, it works just fine:

    $ tinygo run -target=wasm -scheduler=none ./testdata/stdlib.go
    stdin:  /dev/stdin
    stdout: /dev/stdout
    stderr: /dev/stderr
    pseudorandom number: 1298498081
    strings.IndexByte: 2
    strings.Replace: An-example-string

Supporting `-scheduler=none` has some benefits:

  * it reduces file size a lot compared to having a scheduler
  * it allows JavaScript to call exported functions
2021-11-17 19:03:20 +01:00
Ayke van Laethem 22c35c8e31 ci: move all cross compilation tests to Linux
The idea here is as follows:
  - Run all Linux and cross compilation tests in the asser-test-linux
    job.
  - Only run native tests on MacOS and Windows.

This reduces testing time on MacOS and Windows, which are generally more
expensive in CI. Also, by not duplicating tests in Windows and MacOS we
can reduce overall CI usage a bit.

I've also changed the assert-test-linux job a bit to so that the tests
that are more likely to break and the tests that are only run in
assert-test-linux are run first.
2021-11-17 14:52:32 +01:00
16 changed files with 343 additions and 204 deletions
+40 -59
View File
@@ -163,20 +163,17 @@ commands:
command: |
sudo apt-get update
sudo apt-get install --no-install-recommends \
libgnutls30 libssl1.0.2 \
qemu-system-arm \
qemu-system-riscv32 \
qemu-user \
gcc-avr \
avr-libc \
ninja-build \
python3
- install-node
- install-chrome
- install-wasmtime
- install-cmake
- hack-ninja-jobs
- install-xtensa-toolchain:
variant: "linux-amd64"
- restore_cache:
keys:
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
@@ -201,12 +198,7 @@ commands:
key: llvm-build-11-linux-v4-assert
paths:
llvm-build
- build-binaryen-linux-stretch
- run:
name: "Build TinyGo"
command: |
make ASSERT=1
echo 'export PATH=$(pwd)/build:$PATH' >> $BASH_ENV
- build-binaryen-linux
- build-wasi-libc
- run:
name: "Test TinyGo"
@@ -217,73 +209,63 @@ commands:
# necessary to keep memory consumption down and avoid OOM (for a
# 2CPU/4GB executor).
GOFLAGS: -p=2
- run:
name: "Build TinyGo"
command: |
make ASSERT=1
echo 'export PATH=$(pwd)/build:$PATH' >> $BASH_ENV
- run: make tinygo-test
- save_cache:
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
- ~/.cache/go-build
- /go/pkg/mod
- install-xtensa-toolchain:
variant: "linux-amd64"
- run: make gen-device -j4
- run: make smoketest
- run: make tinygo-test
- install-chrome
- run: make wasmtest
build-linux:
steps:
- run:
name: "Install apk dependencies"
command: |
apk add git openssh make g++ gcompat
- checkout
- submodules
- run:
name: "Install Go"
name: "Install apt dependencies"
command: |
wget https://dl.google.com/go/go1.17.3.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.17.3.linux-amd64.tar.gz
ln -s /usr/local/go/bin/go /usr/local/bin/go
#- submodules
sudo apt-get update
sudo apt-get install --no-install-recommends \
libgnutls30 libssl1.0.2 \
ninja-build \
python3
- install-cmake
- hack-ninja-jobs
- restore_cache:
name: "Restore Go cache"
keys:
- go-cache-alpine-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-alpine-v2-{{ checksum "go.mod" }}
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
- go-cache-v2-{{ checksum "go.mod" }}
- llvm-source-linux
- restore_cache:
name: "Restore llvm-source cache"
keys:
- llvm-source-11-alpine-v2-x0
- run:
name: "Fetch LLVM source"
command: make llvm-source
- save_cache:
name: "Save llvm-source cache"
key: llvm-source-11-alpine-v2-x0
paths:
- llvm-project
#- llvm-project/clang/lib/Headers
#- llvm-project/clang/include
#- llvm-project/lld/include
#- llvm-project/llvm/include
- restore_cache:
name: "Restore llvm-build cache"
keys:
- llvm-build-11-alpine-v1-noassert-x0
- llvm-build-11-linux-v4-noassert
- run:
name: "Build LLVM"
command: |
if [ ! -f llvm-build/lib/liblldELF.a ]
then
# fetch LLVM source (may only have headers right now)
#rm -rf llvm-project
#make llvm-source
# install dependencies
apk add cmake samurai python3
rm -rf llvm-project
make llvm-source
# build!
SAMUFLAGS=-j3 make llvm-build
#find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
make llvm-build
find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \;
fi
- save_cache:
name: "Save llvm-build cache"
key: llvm-build-11-alpine-v1-noassert-x0
key: llvm-build-11-linux-v4-noassert
paths:
llvm-build
- build-binaryen-linux-stretch
- build-wasi-libc
- run:
name: "Install fpm"
command: |
@@ -304,8 +286,7 @@ commands:
- store_artifacts:
path: /tmp/tinygo_amd64.deb
- save_cache:
name: "Save Go cache"
key: go-cache-alpine-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
- ~/.cache/go-build
- /go/pkg/mod
@@ -342,7 +323,7 @@ commands:
curl https://dl.google.com/go/go1.17.darwin-amd64.tar.gz -o go1.17.darwin-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.17.darwin-amd64.tar.gz
ln -s /usr/local/go/bin/go /usr/local/bin/go
HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu cmake ninja
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake ninja
- install-xtensa-toolchain:
variant: "macos"
- restore_cache:
@@ -407,7 +388,7 @@ commands:
- lib/wasi-libc/sysroot
- run:
name: "Test TinyGo"
command: make test
command: make test GOTESTFLAGS="-v -short"
no_output_timeout: 20m
- run:
name: "Build TinyGo release"
@@ -445,12 +426,12 @@ jobs:
llvm: "11"
assert-test-linux:
docker:
- image: circleci/golang:1.17-stretch
- image: circleci/golang:1.17-buster
steps:
- assert-test-linux
build-linux:
docker:
- image: alpine:3.14
- image: circleci/golang:1.17-stretch
steps:
- build-linux
test-linux-build:
@@ -469,11 +450,11 @@ jobs:
workflows:
test-all:
jobs:
#- test-llvm11-go115
#- test-llvm11-go116
- test-llvm11-go115
- test-llvm11-go116
- build-linux
- test-linux-build:
requires:
- build-linux
#- build-macos
#- assert-test-linux
- build-macos
- assert-test-linux
+2 -7
View File
@@ -1,7 +1,7 @@
name: Windows
on:
#pull_request:
pull_request:
push:
branches:
- dev
@@ -15,11 +15,6 @@ jobs:
uses: actions/setup-go@v2
with:
go-version: '1.17'
- name: Install QEMU
shell: bash
run: |
choco install qemu --version=2020.06.12
echo "C:\Program Files\QEMU" >> $GITHUB_PATH
- name: Install Ninja
shell: bash
run: |
@@ -78,7 +73,7 @@ jobs:
run: make binaryen
- name: Test TinyGo
shell: bash
run: make test
run: make test GOTESTFLAGS="-v -short"
- name: Build TinyGo release tarball
run: make build/release -j4
- name: Make release artifact
+94
View File
@@ -1,3 +1,97 @@
0.21.0
---
* **command line**
- drop support for LLVM 10
- `build`: drop support for LLVM targets in the -target flag
- `build`: fix paths in error messages on Windows
- `build`: add -p flag to set parallelism
- `lldb`: implement `tinygo lldb` subcommand
- `test`: use emulator exit code instead of parsing test output
- `test`: pass testing arguments to wasmtime
* **compiler**
- use -opt flag for optimization level in CFlags (-Os, etc)
- `builder`: improve accuracy of the -size=full flag
- `builder`: hardcode some more frame sizes for __aeabi_* functions
- `builder`: add support for -size= flag for WebAssembly
- `cgo`: fix line/column reporting in syntax error messages
- `cgo`: support function definitions in CGo headers
- `cgo`: implement rudimentary C array decaying
- `cgo`: add support for stdio in picolibc and wasi-libc
- `cgo`: run CGo parser per file, not per CGo fragment
- `compiler`: fix unintentionally exported math functions
- `compiler`: properly implement div and rem operations
- `compiler`: add support for recursive function types
- `compiler`: add support for the `go` keyword on interface methods
- `compiler`: add minsize attribute for -Oz
- `compiler`: add "target-cpu" and "target-features" attributes
- `compiler`: fix indices into strings and arrays
- `compiler`: fix string compare functions
- `interp`: simplify some code to avoid some errors
- `interp`: support recursive globals (like linked lists) in globals
- `interp`: support constant globals
- `interp`: fix reverting of extractvalue/insertvalue with multiple indices
- `transform`: work around renamed return type after merging LLVM modules
* **standard library**
- `internal/bytealg`: fix indexing error in Compare()
- `machine`: support Pin.Get() function when the pin is configured as output
- `net`, `syscall`: Reduce code duplication by switching to internal/itoa.
- `os`: don't try to read executable path on baremetal
- `os`: implement Getwd
- `os`: add File.WriteString and File.WriteAt
- `reflect`: fix type.Size() to account for struct padding
- `reflect`: don't construct an interface-in-interface value
- `reflect`: implement Value.Elem() for interface values
- `reflect`: fix Value.Index() in a specific case
- `reflect`: add support for DeepEqual
- `runtime`: add another set of invalid unicode runes to encodeUTF8()
- `runtime`: only initialize os.runtime_args when needed
- `runtime`: only use CRLF on baremetal systems for println
- `runtime/debug`: stub `debug.SetMaxStack`
- `runtime/debug`: stub `debug.Stack`
- `testing`: add a stub for t.Parallel()
- `testing`: add support for -test.short flag
- `testing`: stub B.ReportAllocs()
- `testing`: add `testing.Verbose`
- `testing`: stub `testing.AllocsPerRun`
* **targets**
- fix gen-device-svd to handle 64-bit values
- add CPU and Features property to all targets
- match LLVM triple to the one Clang uses
- `atsam`: simplify definition of SERCOM UART, I2C and SPI peripherals
- `atsam`: move I2S0 to machine file
- `esp32`: fix SPI configuration
- `esp32c3`: add support for GDB debugging
- `esp32c3`: add support for CPU interrupts
- `esp32c3`: use tasks scheduler by default
- `fe310`: increase CPU frequency from 16MHz to 320MHz
- `fe310`: add support for bit banging drivers
- `linux`: build static binaries using musl
- `linux`: reduce binary size by calling `write` instead of `putchar`
- `linux`: add support for GOARM
- `riscv`: implement 32-bit atomic operations
- `riscv`: align the heap to 16 bytes
- `riscv`: switch to tasks-based scheduler
- `rp2040`: add CPUFrequency()
- `rp2040`: improve I2C baud rate configuration
- `rp2040`: add pin interrupt API
- `rp2040`: refactor PWM code and fix Period calculation
- `stm32f103`: fix SPI
- `stm32f103`: make SPI frequency selection more flexible
- `qemu`: signal correct exit code to QEMU
- `wasi`: run C/C++ constructors at startup
- `wasm`: ensure heapptr is aligned
- `wasm`: update wasi-libc dependency
- `wasm`: wasi: use asyncify
- `wasm`: support `-scheduler=none`
- `windows`: add support for Windows (amd64 only for now)
* **boards**
- `feather-stm32f405`, `feather-rp2040`: add I2C pin names
- `m5stack-core2`: add M5Stack Core2
- `nano-33-ble`: SoftDevice s140v7 support
- `nano-33-ble`: add constants for more on-board pins
0.20.0
---
+6 -3
View File
@@ -18,6 +18,9 @@ LLVM_NM ?= $(word 1,$(abspath $(call detect,llvm-build/bin/llvm-nm))$(call detec
GO ?= go
export GOROOT = $(shell $(GO) env GOROOT)
# Flags to pass to go test.
GOTESTFLAGS ?= -v
# md5sum binary
MD5SUM = md5sum
@@ -168,8 +171,8 @@ $(LLVM_BUILDDIR)/build.ninja: llvm-source
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
# Build LLVM.
$(LLVM_BUILDDIR): #$(LLVM_BUILDDIR)/build.ninja
cd $(LLVM_BUILDDIR) && ninja llvm-nm #$(NINJA_BUILD_TARGETS)
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
cd $(LLVM_BUILDDIR) && ninja $(NINJA_BUILD_TARGETS)
# Build Binaryen
.PHONY: binaryen
@@ -192,7 +195,7 @@ tinygo:
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X main.gitSha1=`git rev-parse --short HEAD`" .
test: wasi-libc
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform .
TEST_PACKAGES = \
compress/bzip2 \
+5 -5
View File
@@ -2276,16 +2276,16 @@ func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Va
case token.NEQ: // !=
result := b.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "")
return b.CreateNot(result, ""), nil
case token.LSS: // <
case token.LSS: // x < y
return b.createRuntimeCall("stringLess", []llvm.Value{x, y}, ""), nil
case token.LEQ: // <=
case token.LEQ: // x <= y becomes NOT (y < x)
result := b.createRuntimeCall("stringLess", []llvm.Value{y, x}, "")
return b.CreateNot(result, ""), nil
case token.GTR: // >
case token.GTR: // x > y becomes y < x
return b.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil
case token.GEQ: // x >= y becomes NOT (x < y)
result := b.createRuntimeCall("stringLess", []llvm.Value{x, y}, "")
return b.CreateNot(result, ""), nil
case token.GEQ: // >=
return b.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil
default:
panic("binop on string: " + op.String())
}
+2 -3
View File
@@ -71,9 +71,8 @@ entry:
; Function Attrs: nounwind
define hidden i1 @main.stringCompareLarger(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr #0 {
entry:
%0 = call i1 @runtime.stringLess(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null) #0
%1 = xor i1 %0, true
ret i1 %1
%0 = call i1 @runtime.stringLess(i8* %s2.data, i32 %s2.len, i8* %s1.data, i32 %s1.len, i8* undef, i8* null) #0
ret i1 %0
}
declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*, i8*)
+1 -1
View File
@@ -12,7 +12,7 @@ import (
// Version of TinyGo.
// Update this value before release of new version of software.
const Version = "0.21.0-dev"
const Version = "0.21.0"
// GetGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned.
+33 -35
View File
@@ -75,40 +75,6 @@ func TestCompiler(t *testing.T) {
runPlatTests(optionsFromTarget(""), tests, t)
})
if testing.Short() {
return
}
t.Run("EmulatedCortexM3", func(t *testing.T) {
runPlatTests(optionsFromTarget("cortex-m-qemu"), tests, t)
})
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
// Note: running only on Windows and macOS because Linux (as of 2020)
// usually has an outdated QEMU version that doesn't support RISC-V yet.
t.Run("EmulatedRISCV", func(t *testing.T) {
runPlatTests(optionsFromTarget("riscv-qemu"), tests, t)
})
}
if runtime.GOOS == "linux" {
t.Run("X86Linux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux/386"), tests, t)
})
t.Run("ARMLinux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux/arm/6"), tests, t)
})
t.Run("ARM64Linux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux/arm64"), tests, t)
})
t.Run("WebAssembly", func(t *testing.T) {
runPlatTests(optionsFromTarget("wasm"), tests, t)
})
t.Run("WASI", func(t *testing.T) {
runPlatTests(optionsFromTarget("wasi"), tests, t)
})
}
// Test a few build options.
t.Run("build-options", func(t *testing.T) {
t.Parallel()
@@ -150,6 +116,38 @@ func TestCompiler(t *testing.T) {
}, nil, nil)
})
})
if testing.Short() {
// Don't test other targets when the -short flag is used. Only test the
// host system.
return
}
t.Run("EmulatedCortexM3", func(t *testing.T) {
runPlatTests(optionsFromTarget("cortex-m-qemu"), tests, t)
})
t.Run("EmulatedRISCV", func(t *testing.T) {
runPlatTests(optionsFromTarget("riscv-qemu"), tests, t)
})
if runtime.GOOS == "linux" {
t.Run("X86Linux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux/386"), tests, t)
})
t.Run("ARMLinux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux/arm/6"), tests, t)
})
t.Run("ARM64Linux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux/arm64"), tests, t)
})
t.Run("WebAssembly", func(t *testing.T) {
runPlatTests(optionsFromTarget("wasm"), tests, t)
})
t.Run("WASI", func(t *testing.T) {
runPlatTests(optionsFromTarget("wasi"), tests, t)
})
}
}
func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
@@ -166,7 +164,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
t.Parallel()
runTest("env.go", options, t, []string{"first", "second"}, []string{"ENV1=VALUE1", "ENV2=VALUE2"})
})
if options.Target == "wasi" {
if options.Target == "wasi" || options.Target == "wasm" {
t.Run("alias.go-scheduler-none", func(t *testing.T) {
t.Parallel()
options := compileopts.Options(options)
+6
View File
@@ -18,6 +18,12 @@ func CPUFrequency() uint32 {
return 125 * MHz
}
// Returns the period of a clock cycle for the raspberry pi pico in nanoseconds.
// Used in PWM API.
func cpuPeriod() uint32 {
return 1e9 / CPUFrequency()
}
// clockIndex identifies a hardware clock
type clockIndex uint8
+77 -63
View File
@@ -1,3 +1,4 @@
//go:build rp2040
// +build rp2040
package machine
@@ -5,12 +6,13 @@ package machine
import (
"device/rp"
"errors"
"math"
"runtime/volatile"
"unsafe"
)
var (
ErrPeriodTooBig = errors.New("period outside valid range 1..4e9ns")
ErrBadPeriod = errors.New("period outside valid range 8ns..268ms")
)
const (
@@ -50,8 +52,21 @@ func getPWMGroup(index uintptr) *pwmGroup {
return (*pwmGroup)(unsafe.Pointer(uintptr(unsafe.Pointer(rp.PWM)) + 0x14*index))
}
// Hardware Pulse Width Modulation (PWM) API
// PWM peripherals available on RP2040. Each peripheral has 2 pins available for
// a total of 16 available PWM outputs. Some pins may not be available on some boards.
//
// The RP2040 PWM block has 8 identical slices. Each slice can drive two PWM output signals, or
// measure the frequency or duty cycle of an input signal. This gives a total of up to 16 controllable
// PWM outputs. All 30 GPIOs can be driven by the PWM block
//
// The PWM hardware functions by continuously comparing the input value to a free-running counter. This produces a
// toggling output where the amount of time spent at the high output level is proportional to the input value. The fraction of
// time spent at the high signal level is known as the duty cycle of the signal.
//
// The default behaviour of a PWM slice is to count upward until the wrap value (\ref pwm_config_set_wrap) is reached, and then
// immediately wrap to 0. PWM slices also offer a phase-correct mode, where the counter starts to count downward after
// reaching TOP, until it reaches 0 again.
var (
PWM0 = getPWMGroup(0)
PWM1 = getPWMGroup(1)
@@ -94,30 +109,23 @@ func (pwm *pwmGroup) peripheral() uint8 {
return uint8((uintptr(unsafe.Pointer(pwm)) - uintptr(unsafe.Pointer(rp.PWM))) / 0x14)
}
// SetPeriod updates the period of this PWM peripheral.
// SetPeriod updates the period of this PWM peripheral in nanoseconds.
// To set a particular frequency, use the following formula:
//
// period = 1e9 / frequency
//
// If you use a period of 0, a period that works well for LEDs will be picked.
// Where frequency is in hertz. If you use a period of 0, a period
// that works well for LEDs will be picked.
//
// SetPeriod will not change the prescaler, but also won't change the current
// value in any of the channels. This means that you may need to update the
// value for the particular channel.
//
// Note that you cannot pick any arbitrary period after the PWM peripheral has
// been configured. If you want to switch between frequencies, pick the lowest
// frequency (longest period) once when calling Configure and adjust the
// frequency here as needed.
// SetPeriod will try not to modify TOP if possible to reach the target period.
// If the period is unattainable with current TOP SetPeriod will modify TOP
// by the bare minimum to reach the target period. It will also enable phase
// correct to reach periods above 130ms.
func (p *pwmGroup) SetPeriod(period uint64) error {
if period > 0xffff_ffff {
return ErrPeriodTooBig
}
if period == 0 {
period = 1e5
}
p.setPeriod(period)
return nil
return p.setPeriod(period)
}
// Top returns the current counter top, for use in duty cycle calculation.
@@ -135,14 +143,14 @@ func (p *pwmGroup) Counter() uint32 {
return (p.CTR.Get() & rp.PWM_CH0_CTR_CH0_CTR_Msk) >> rp.PWM_CH0_CTR_CH0_CTR_Pos
}
// Period returns the used PWM period in nanoseconds. It might deviate slightly
// from the configured period due to rounding.
// Period returns the used PWM period in nanoseconds.
func (p *pwmGroup) Period() uint64 {
periodPerCycle := getPeriod()
periodPerCycle := cpuPeriod()
top := p.getWrap()
phc := p.getPhaseCorrect()
Int, frac := p.getClockDiv()
return uint64((Int + frac/16) * (top + 1) * (phc + 1) * periodPerCycle) // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
// Line below can overflow if operations done without care.
return (16*uint64(Int) + uint64(frac)) * uint64((top+1)*(phc+1)*periodPerCycle) / 16 // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
}
// SetInverting sets whether to invert the output of this channel.
@@ -180,6 +188,12 @@ func (p *pwmGroup) SetTop(top uint32) {
p.setWrap(uint16(top))
}
// SetCounter sets counter control register. Max value is 16bit (0xffff).
// Useful for synchronising two different PWM peripherals.
func (p *pwmGroup) SetCounter(ctr uint32) {
p.CTR.Set(ctr)
}
// Enable enables or disables PWM peripheral channels.
func (p *pwmGroup) Enable(enable bool) {
p.enable(enable)
@@ -190,29 +204,6 @@ func (p *pwmGroup) IsEnabled() (enabled bool) {
return (p.CSR.Get()&rp.PWM_CH0_CSR_EN_Msk)>>rp.PWM_CH0_CSR_EN_Pos != 0
}
// Hardware Pulse Width Modulation (PWM) API
//
// The RP2040 PWM block has 8 identical slices. Each slice can drive two PWM output signals, or
// measure the frequency or duty cycle of an input signal. This gives a total of up to 16 controllable
// PWM outputs. All 30 GPIOs can be driven by the PWM block
//
// The PWM hardware functions by continuously comparing the input value to a free-running counter. This produces a
// toggling output where the amount of time spent at the high output level is proportional to the input value. The fraction of
// time spent at the high signal level is known as the duty cycle of the signal.
//
// The default behaviour of a PWM slice is to count upward until the wrap value (\ref pwm_config_set_wrap) is reached, and then
// immediately wrap to 0. PWM slices also offer a phase-correct mode, where the counter starts to count downward after
// reaching TOP, until it reaches 0 again.
type pwms struct {
slice pwmGroup
hw *rp.PWM_Type
}
// Handle to all pwm peripheral registers.
var _PWM = pwms{
hw: rp.PWM,
}
// Initialise a PWM with settings from a configuration object.
// If start is true then PWM starts on initialization.
func (pwm *pwmGroup) init(config PWMConfig, start bool) error {
@@ -253,24 +244,53 @@ func (pwm *pwmGroup) setDivMode(mode uint32) {
pwm.CSR.ReplaceBits(mode<<rp.PWM_CH0_CSR_DIVMODE_Pos, rp.PWM_CH0_CSR_DIVMODE_Msk, 0)
}
// setPeriod sets the pwm peripheral period (frequency). Calculates DIV_INT and sets it from following equation:
// setPeriod sets the pwm peripheral period (frequency). Calculates DIV_INT,DIV_FRAC and sets it from following equation:
// cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
// where cycles is amount of clock cycles per PWM period.
func (pwm *pwmGroup) setPeriod(period uint64) {
targetPeriod := uint32(period)
periodPerCycle := getPeriod()
top := pwm.getWrap()
phc := pwm.getPhaseCorrect()
_, frac := pwm.getClockDiv()
func (pwm *pwmGroup) setPeriod(period uint64) error {
// This period calculation algorithm consists of
// 1. Calculating best-fit prescale at a slightly lower-than-max TOP value
// 2. Calculate TOP value to reach target period given the calculated prescale
// 3. Apply calculated Prescale from step 1 and calculated Top from step 2
const (
maxTop = math.MaxUint16
// start algorithm at 95% Top. This allows us to undershoot period with prescale.
topStart = 95 * maxTop / 100
milliseconds = 1_000_000_000
// Maximum Period is 268369920ns on rp2040, given by (16*255+15)*8*(1+0xffff)*(1+1)/16
// With no phase shift max period is half of this value.
maxPeriod = 268 * milliseconds
)
if period > maxPeriod || period < 8 {
return ErrBadPeriod
}
if period > maxPeriod/2 {
pwm.setPhaseCorrect(true) // Must enable Phase correct to reach large periods.
}
// clearing above expression:
// DIV_INT = cycles / ( (TOP+1) * (CSRPHCorrect+1) ) - DIV_FRAC/16
// DIV_INT + DIV_FRAC/16 = cycles / ( (TOP+1) * (CSRPHCorrect+1) ) // DIV_FRAC/16 is always 0 in this equation
// where cycles must be converted to time:
// target_period = cycles * period_per_cycle ==> cycles = target_period/period_per_cycle
Int := targetPeriod/((1+phc)*periodPerCycle*(1+top)) - frac/16
if Int > 0xff {
Int = 0xff
periodPerCycle := uint64(cpuPeriod())
phc := uint64(pwm.getPhaseCorrect())
rhs := 16 * period / ((1 + phc) * periodPerCycle * (1 + topStart)) // right-hand-side of equation, scaled so frac is not divided
whole := rhs / 16
frac := rhs % 16
if whole > 0xff {
whole = 0xff
}
pwm.setClockDiv(uint8(Int), 0)
// Step 2 is acquiring a better top value. Clearing the equation:
// TOP = cycles / ( (DIVINT+DIVFRAC/16) * (CSRPHCorrect+1) ) - 1
top := 16*period/((16*whole+frac)*periodPerCycle*(1+phc)) - 1
if top > maxTop {
top = maxTop
}
pwm.SetTop(uint32(top))
pwm.setClockDiv(uint8(whole), uint8(frac))
return nil
}
// Int is integer value to reduce counting rate by. Must be greater than or equal to 1. DIV_INT is bits 4:11 (8 bits).
@@ -360,9 +380,9 @@ func (pwm *pwmGroup) getPhaseCorrect() (phCorrect uint32) {
return (pwm.CSR.Get() & rp.PWM_CH0_CSR_PH_CORRECT_Msk) >> rp.PWM_CH0_CSR_PH_CORRECT_Pos
}
func (pwm *pwmGroup) getClockDiv() (Int, frac uint32) {
func (pwm *pwmGroup) getClockDiv() (Int, frac uint8) {
div := pwm.DIV.Get()
return (div & rp.PWM_CH0_DIV_INT_Msk) >> rp.PWM_CH0_DIV_INT_Pos, (div & rp.PWM_CH0_DIV_FRAC_Msk) >> rp.PWM_CH0_DIV_FRAC_Pos
return uint8((div & rp.PWM_CH0_DIV_INT_Msk) >> rp.PWM_CH0_DIV_INT_Pos), uint8((div & rp.PWM_CH0_DIV_FRAC_Msk) >> rp.PWM_CH0_DIV_FRAC_Pos)
}
// pwmGPIOToSlice Determine the PWM channel that is attached to the specified GPIO.
@@ -376,9 +396,3 @@ func pwmGPIOToSlice(gpio Pin) (slicenum uint8) {
func pwmGPIOToChannel(gpio Pin) (channel uint8) {
return uint8(gpio) & 1
}
// Returns the period of a clock cycle for the raspberry pi pico in nanoseconds.
func getPeriod() uint32 {
const periodIn uint32 = 1e9 / (125 * MHz)
return periodIn
}
+15
View File
@@ -1,2 +1,17 @@
// Package debug is a dummy package that is not yet implemented.
package debug
// SetMaxStack sets the maximum amount of memory that can be used by a single
// goroutine stack.
//
// Not implemented.
func SetMaxStack(n int) int {
return n
}
// Stack returns a formatted stack trace of the goroutine that calls it.
//
// Not implemented.
func Stack() []byte {
return nil
}
-28
View File
@@ -29,34 +29,6 @@ func setEventHandler(fn func()) {
handleEvent = fn
}
//export resume
func resume() {
go func() {
handleEvent()
}()
if wasmNested {
minSched()
return
}
wasmNested = true
scheduler()
wasmNested = false
}
//export go_scheduler
func go_scheduler() {
if wasmNested {
minSched()
return
}
wasmNested = true
scheduler()
wasmNested = false
}
func ticksToNanoseconds(ticks timeUnit) int64 {
// The JavaScript API works in float64 milliseconds, so convert to
// nanoseconds first before converting to a timeUnit (which is a float64),
+32
View File
@@ -0,0 +1,32 @@
//go:build wasm && !wasi && !scheduler.none
// +build wasm,!wasi,!scheduler.none
package runtime
//export resume
func resume() {
go func() {
handleEvent()
}()
if wasmNested {
minSched()
return
}
wasmNested = true
scheduler()
wasmNested = false
}
//export go_scheduler
func go_scheduler() {
if wasmNested {
minSched()
return
}
wasmNested = true
scheduler()
wasmNested = false
}
+18
View File
@@ -297,6 +297,24 @@ func Short() bool {
return flagShort
}
// Verbose reports whether the -test.v flag is set.
func Verbose() bool {
return flagVerbose
}
// AllocsPerRun returns the average number of allocations during calls to f.
// Although the return value has type float64, it will always be an integral
// value.
//
// Not implemented.
func AllocsPerRun(runs int, f func()) (avg float64) {
f()
for i := 0; i < runs; i++ {
f()
}
return 0
}
func TestMain(m *M) {
os.Exit(m.Run())
}
+7
View File
@@ -24,6 +24,13 @@ func main() {
println("ab" < "aa")
println("aa" < "ab")
h := "hello"
println("h < h", h < h)
println("h <= h", h <= h)
println("h == h", h == h)
println("h >= h", h >= h)
println("h > h", h > h)
println("array equality")
println(a1 == [2]int{1, 2})
println(a1 != [2]int{1, 2})
+5
View File
@@ -20,6 +20,11 @@ true
false
false
true
h < h false
h <= h true
h == h true
h >= h true
h > h false
array equality
true
false