mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-01 09:37:48 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b9ce6c3374 | |||
| cab3834bc9 | |||
| 7f970a45c2 | |||
| ebf70ab18e | |||
| 226744a538 | |||
| dc876c6ed4 | |||
| 5a09084c73 | |||
| 6c9074772c | |||
| e49663809b | |||
| 7d6d93f7aa |
@@ -165,6 +165,7 @@ jobs:
|
||||
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
|
||||
- run: make tinygo-test-wasip1-fast
|
||||
- run: make tinygo-test-wasip2-fast
|
||||
- run: make tinygo-test-wasm
|
||||
- run: make smoketest
|
||||
assert-test-linux:
|
||||
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
|
||||
@@ -277,7 +278,7 @@ jobs:
|
||||
run: make tinygo-test
|
||||
- run: make smoketest
|
||||
- run: make wasmtest
|
||||
- run: make tinygo-baremetal
|
||||
- run: make tinygo-test-baremetal
|
||||
build-linux-cross:
|
||||
# Build ARM Linux binaries, ready for release.
|
||||
# This intentionally uses an older Linux image, so that we compile against
|
||||
|
||||
@@ -11,6 +11,7 @@ name: LLVM
|
||||
on:
|
||||
push:
|
||||
branches: [ build-llvm-image ]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
+45
-5
@@ -407,6 +407,43 @@ TEST_PACKAGES_WINDOWS := \
|
||||
text/template/parse \
|
||||
$(nil)
|
||||
|
||||
|
||||
# These packages cannot be tested on wasm, mostly because these tests assume a
|
||||
# working filesystem. This could perhaps be fixed, by supporting filesystem
|
||||
# access when running inside Node.js.
|
||||
TEST_PACKAGES_WASM = $(filter-out $(TEST_PACKAGES_NONWASM), $(TEST_PACKAGES_FAST))
|
||||
TEST_PACKAGES_NONWASM = \
|
||||
compress/lzw \
|
||||
compress/zlib \
|
||||
crypto/ecdsa \
|
||||
debug/macho \
|
||||
embed/internal/embedtest \
|
||||
go/format \
|
||||
os \
|
||||
testing \
|
||||
$(nil)
|
||||
|
||||
# These packages cannot be tested on baremetal.
|
||||
#
|
||||
# Some reasons why the tests don't pass on baremetal:
|
||||
#
|
||||
# * No filesystem is available, so packages like compress/zlib can't be tested
|
||||
# (just like wasm).
|
||||
# * picolibc math functions apparently are less precise, the math package
|
||||
# fails on baremetal.
|
||||
# * Some packages fail or hang for an unknown reason, this should be
|
||||
# investigated and fixed.
|
||||
TEST_PACKAGES_BAREMETAL = $(filter-out $(TEST_PACKAGES_NONBAREMETAL), $(TEST_PACKAGES_FAST))
|
||||
TEST_PACKAGES_NONBAREMETAL = \
|
||||
$(TEST_PACKAGES_NONWASM) \
|
||||
crypto/elliptic \
|
||||
math \
|
||||
reflect \
|
||||
encoding/asn1 \
|
||||
encoding/base32 \
|
||||
go/ast \
|
||||
$(nil)
|
||||
|
||||
# Report platforms on which each standard library package is known to pass tests
|
||||
jointmp := $(shell echo /tmp/join.$$$$)
|
||||
report-stdlib-tests-pass:
|
||||
@@ -450,6 +487,8 @@ tinygo-bench-fast:
|
||||
$(TINYGO) test -bench . $(TEST_PACKAGES_HOST)
|
||||
|
||||
# Same thing, except for wasi rather than the current platform.
|
||||
tinygo-test-wasm:
|
||||
$(TINYGO) test -target wasm $(TEST_PACKAGES_WASM)
|
||||
tinygo-test-wasi:
|
||||
$(TINYGO) test -target wasip1 $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
|
||||
tinygo-test-wasip1:
|
||||
@@ -484,6 +523,10 @@ tinygo-bench-wasip2:
|
||||
tinygo-bench-wasip2-fast:
|
||||
$(TINYGO) test -target wasip2 -bench . $(TEST_PACKAGES_FAST)
|
||||
|
||||
# Run tests on riscv-qemu since that one provides a large amount of memory.
|
||||
tinygo-test-baremetal:
|
||||
$(TINYGO) test -target riscv-qemu $(TEST_PACKAGES_BAREMETAL)
|
||||
|
||||
# Test external packages in a large corpus.
|
||||
test-corpus:
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus . -corpus=testdata/corpus.yaml
|
||||
@@ -494,11 +537,6 @@ test-corpus-wasi: wasi-libc
|
||||
test-corpus-wasip2: wasi-libc
|
||||
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus . -corpus=testdata/corpus.yaml -target=wasip2
|
||||
|
||||
tinygo-baremetal:
|
||||
# Regression tests that run on a baremetal target and don't fit in either main_test.go or smoketest.
|
||||
# regression test for #2666: e.g. encoding/hex must pass on baremetal
|
||||
$(TINYGO) test -target cortex-m-qemu encoding/hex
|
||||
|
||||
.PHONY: testchdir
|
||||
testchdir:
|
||||
# test 'build' command with{,out} -C argument
|
||||
@@ -585,6 +623,8 @@ ifneq ($(WASM), 0)
|
||||
@$(MD5SUM) test.wasm
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=gopher_badge examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=pico examples/blinky1
|
||||
@$(MD5SUM) test.wasm
|
||||
endif
|
||||
# test all targets/boards
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040-s132v6 examples/blinky1
|
||||
|
||||
+1
-1
@@ -446,7 +446,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||
// The list of allowed types is based on this proposal:
|
||||
// https://github.com/golang/go/issues/59149
|
||||
func (c *compilerContext) checkWasmImportExport(f *ssa.Function, pragma string) {
|
||||
if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" || c.pkg.Path() == "syscall" {
|
||||
if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" || c.pkg.Path() == "syscall" || c.pkg.Path() == "crypto/internal/sysrand" {
|
||||
// The runtime is a special case. Allow all kinds of parameters
|
||||
// (importantly, including pointers).
|
||||
return
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || tkey
|
||||
//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || tkey || (tinygo.riscv32 && virt)
|
||||
|
||||
// If you update the above build constraint, you'll probably also need to update
|
||||
// src/runtime/rand_hwrng.go.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
cpuFreq = 125 * MHz
|
||||
cpuFreq = 200 * MHz
|
||||
_NUMBANK0_GPIOS = 30
|
||||
_NUMBANK0_IRQS = 4
|
||||
_NUMIRQ = 32
|
||||
@@ -208,3 +208,16 @@ func (clks *clocksType) initTicks() {} // No ticks on RP2040
|
||||
func (wd *watchdogImpl) startTick(cycles uint32) {
|
||||
rp.WATCHDOG.TICK.Set(cycles | rp.WATCHDOG_TICK_ENABLE)
|
||||
}
|
||||
|
||||
func adjustCoreVoltage() bool {
|
||||
if cpuFreq <= 133*MHz {
|
||||
return false
|
||||
}
|
||||
// The rp2040 is certified to run at 200MHz with the
|
||||
// core voltage set to 1150mV.
|
||||
const targetVoltage = 1150
|
||||
// 0b0101 maps to 800mV and each step is 50mV.
|
||||
const vreg = 0b0101 + (targetVoltage-800)/50
|
||||
rp.VREG_AND_CHIP_RESET.SetVREG_VSEL(vreg)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -222,3 +222,7 @@ func EnterBootloader() {
|
||||
func (wd *watchdogImpl) startTick(cycles uint32) {
|
||||
rp.TICKS.WATCHDOG_CTRL.SetBits(1)
|
||||
}
|
||||
|
||||
func adjustCoreVoltage() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -42,6 +42,10 @@ type clock struct {
|
||||
cix clockIndex
|
||||
}
|
||||
|
||||
// The delay in seconds for core voltage adjustments to
|
||||
// settle. Taken from the Pico SDK.
|
||||
const _VREG_VOLTAGE_AUTO_ADJUST_DELAY = 1 / 1e3
|
||||
|
||||
// clock returns the clock identified by cix.
|
||||
func (clks *clocksType) clock(cix clockIndex) clock {
|
||||
return clock{
|
||||
@@ -188,6 +192,14 @@ func (clks *clocksType) init() {
|
||||
xoscFreq,
|
||||
xoscFreq)
|
||||
|
||||
if adjustCoreVoltage() {
|
||||
// Wait for the voltage to settle.
|
||||
const cycles = _VREG_VOLTAGE_AUTO_ADJUST_DELAY * xoscFreq * MHz
|
||||
for i := 0; i < cycles; i++ {
|
||||
arm.Asm("nop")
|
||||
}
|
||||
}
|
||||
|
||||
// clkSys = pllSys (125MHz) / 1 = 125MHz
|
||||
csys := clks.clock(clkSys)
|
||||
csys.configure(rp.CLOCKS_CLK_SYS_CTRL_SRC_CLKSRC_CLK_SYS_AUX,
|
||||
|
||||
@@ -33,6 +33,7 @@ type irqSummary struct {
|
||||
|
||||
type ioBank0Type struct {
|
||||
io [_NUMBANK0_GPIOS]ioType
|
||||
_ [rp2350ExtraReg][128]byte
|
||||
irqsum [rp2350ExtraReg]irqSummary
|
||||
intR [_NUMBANK0_IRQS]volatile.Register32
|
||||
proc0IRQctrl irqCtrl
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build rp2040 || rp2350 || gopher_badge
|
||||
//go:build rp2040 || rp2350 || gopher_badge || pico
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ func (uart *UART) Configure(config UARTConfig) error {
|
||||
uart.Interrupt.SetPriority(0x80)
|
||||
uart.Interrupt.Enable()
|
||||
|
||||
// setup interrupt on receive
|
||||
// Setup interrupt on receive.
|
||||
uart.Bus.UARTIMSC.Set(rp.UART0_UARTIMSC_RXIM)
|
||||
|
||||
return nil
|
||||
@@ -153,7 +153,7 @@ func initUART(uart *UART) {
|
||||
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||
// this UART instance.
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
for uart.Bus.UARTFR.HasBits(rp.UART0_UARTFR_RXFE) {
|
||||
for !uart.Bus.UARTFR.HasBits(rp.UART0_UARTFR_RXFE) {
|
||||
uart.Receive(byte((uart.Bus.UARTDR.Get() & 0xFF)))
|
||||
}
|
||||
uart.Receive(byte((uart.Bus.UARTDR.Get() & 0xFF)))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
//go:build tinygo.riscv32 && virt
|
||||
|
||||
// Machine implementation for VirtIO targets.
|
||||
// At the moment only QEMU RISC-V is supported, but support for ARM for example
|
||||
// should not be difficult to add with a change to virtioFindDevice.
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime/volatile"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const deviceName = "riscv-qemu"
|
||||
|
||||
func (p Pin) Set(high bool) {
|
||||
// no pins defined
|
||||
}
|
||||
|
||||
var rngLock sync.Mutex
|
||||
var rngDevice *virtioDevice1
|
||||
var rngBuf volatile.Register32
|
||||
|
||||
var errNoRNG = errors.New("machine: no entropy source found")
|
||||
var errNoRNGData = errors.New("machine: entropy source didn't return enough data")
|
||||
|
||||
// GetRNG returns random numbers from a VirtIO entropy source.
|
||||
// When running in QEMU, it requires adding the RNG device:
|
||||
//
|
||||
// -device virtio-rng-device
|
||||
func GetRNG() (uint32, error) {
|
||||
rngLock.Lock()
|
||||
|
||||
// Initialize the device on first use.
|
||||
if rngDevice == nil {
|
||||
// Search for an available RNG.
|
||||
rngDevice = virtioFindDevice(virtioDeviceEntropySource)
|
||||
if rngDevice == nil {
|
||||
rngLock.Unlock()
|
||||
return 0, errNoRNG
|
||||
}
|
||||
|
||||
// Initialize the device.
|
||||
rngDevice.status.Set(0) // reset device
|
||||
rngDevice.status.Set(virtioDeviceStatusAcknowledge)
|
||||
rngDevice.status.Set(virtioDeviceStatusAcknowledge | virtioDeviceStatusDriver)
|
||||
rngDevice.hostFeaturesSel.Set(0)
|
||||
rngDevice.status.Set(virtioDeviceStatusAcknowledge | virtioDeviceStatusDriver | virtioDeviceStatusDriverOk)
|
||||
rngDevice.guestPageSize.Set(4096)
|
||||
|
||||
// Configure queue, according to section 4.2.4 "Legacy interface".
|
||||
// Note: we're skipping checks for queuePFM and queueNumMax.
|
||||
rngDevice.queueSel.Set(0) // use queue 0 (the only queue)
|
||||
rngDevice.queueNum.Set(1) // use a single buffer in the queue
|
||||
rngDevice.queueAlign.Set(4096) // default alignment appears to be 4096
|
||||
rngDevice.queuePFN.Set(uint32(uintptr(unsafe.Pointer(&rngQueue))) / 4096)
|
||||
|
||||
// Configure the only buffer in the queue (but don't increment
|
||||
// rngQueue.available yet).
|
||||
rngQueue.buffers[0].address = uint64(uintptr(unsafe.Pointer(&rngBuf)))
|
||||
rngQueue.buffers[0].length = uint32(unsafe.Sizeof(rngBuf))
|
||||
rngQueue.buffers[0].flags = 2 // 2 means write-only buffer
|
||||
}
|
||||
|
||||
// Increment the available ring buffer. This doesn't actually change the
|
||||
// buffer index (it's a ring with a single entry), but the number needs to
|
||||
// be incremented otherwise the device won't recognize a new buffer.
|
||||
index := rngQueue.available.index
|
||||
rngQueue.available.index = index + 1
|
||||
rngDevice.queueNotify.Set(0) // notify the device of the 'new' (reused) buffer
|
||||
for rngQueue.used.index.Get() != index+1 {
|
||||
// Busy wait until the RNG buffer is filled.
|
||||
// A better way would be to wait for an interrupt, but since this driver
|
||||
// implementation is mostly used for testing it's good enough for now.
|
||||
}
|
||||
|
||||
// Check that we indeed got 4 bytes back.
|
||||
if rngQueue.used.ring[0].length != 4 {
|
||||
rngLock.Unlock()
|
||||
return 0, errNoRNGData
|
||||
}
|
||||
|
||||
// Read the resulting random numbers.
|
||||
result := rngBuf.Get()
|
||||
|
||||
rngLock.Unlock()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Implement a driver for the VirtIO entropy device.
|
||||
// https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html
|
||||
// http://wiki.osdev.org/Virtio
|
||||
// http://www.dumais.io/index.php?article=aca38a9a2b065b24dfa1dee728062a12
|
||||
|
||||
const (
|
||||
virtioDeviceStatusAcknowledge = 1
|
||||
virtioDeviceStatusDriver = 2
|
||||
virtioDeviceStatusDriverOk = 4
|
||||
virtioDeviceStatusFeaturesOk = 8
|
||||
virtioDeviceStatusFailed = 128
|
||||
)
|
||||
|
||||
const (
|
||||
virtioDeviceReserved = iota
|
||||
virtioDeviceNetworkCard
|
||||
virtioDeviceBlockDevice
|
||||
virtioDeviceConsole
|
||||
virtioDeviceEntropySource
|
||||
// there are more device types
|
||||
)
|
||||
|
||||
// VirtIO device version 1
|
||||
type virtioDevice1 struct {
|
||||
magic volatile.Register32 // always 0x74726976
|
||||
version volatile.Register32
|
||||
deviceID volatile.Register32
|
||||
vendorID volatile.Register32
|
||||
hostFeatures volatile.Register32
|
||||
hostFeaturesSel volatile.Register32
|
||||
_ [2]uint32
|
||||
guestFeatures volatile.Register32
|
||||
guestFeaturesSel volatile.Register32
|
||||
guestPageSize volatile.Register32
|
||||
_ uint32
|
||||
queueSel volatile.Register32
|
||||
queueNumMax volatile.Register32
|
||||
queueNum volatile.Register32
|
||||
queueAlign volatile.Register32
|
||||
queuePFN volatile.Register32
|
||||
_ [3]uint32
|
||||
queueNotify volatile.Register32
|
||||
_ [3]uint32
|
||||
interruptStatus volatile.Register32
|
||||
interruptAck volatile.Register32
|
||||
_ [2]uint32
|
||||
status volatile.Register32
|
||||
}
|
||||
|
||||
// VirtIO queue, with a single buffer.
|
||||
type virtioQueue struct {
|
||||
buffers [1]struct {
|
||||
address uint64
|
||||
length uint32
|
||||
flags uint16
|
||||
next uint16
|
||||
} // 16 bytes
|
||||
|
||||
available struct {
|
||||
flags uint16
|
||||
index uint16
|
||||
ring [1]uint16
|
||||
eventIndex uint16
|
||||
} // 8 bytes
|
||||
|
||||
_ [4096 - 16*1 - 8*1]byte // padding (to align on a 4096 byte boundary)
|
||||
|
||||
used struct {
|
||||
flags uint16
|
||||
index volatile.Register16
|
||||
ring [1]struct {
|
||||
index uint32
|
||||
length uint32
|
||||
}
|
||||
availEvent uint16
|
||||
}
|
||||
}
|
||||
|
||||
func virtioFindDevice(deviceID uint32) *virtioDevice1 {
|
||||
// On RISC-V, QEMU defines 8 VirtIO devices starting at 0x10001000 and
|
||||
// repeating every 0x1000 bytes.
|
||||
// The memory map can be seen in the QEMU source code:
|
||||
// https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c
|
||||
for i := 0; i < 8; i++ {
|
||||
dev := (*virtioDevice1)(unsafe.Pointer(uintptr(0x10001000 + i*0x1000)))
|
||||
if dev.magic.Get() != 0x74726976 || dev.version.Get() != 1 || dev.deviceID.Get() != deviceID {
|
||||
continue
|
||||
}
|
||||
return dev
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A VirtIO queue needs to be page-aligned.
|
||||
//
|
||||
//go:align 4096
|
||||
var rngQueue virtioQueue
|
||||
@@ -134,6 +134,10 @@ func Pipe() (r *File, w *File, err error) {
|
||||
return nil, nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
func Symlink(oldname, newname string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func Readlink(name string) (string, error) {
|
||||
return "", ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build baremetal && (nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || tkey)
|
||||
//go:build baremetal && (nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || tkey || (tinygo.riscv32 && virt))
|
||||
|
||||
// If you update the above build constraint, you'll probably also need to update
|
||||
// src/crypto/rand/rand_baremetal.go.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build baremetal && !(nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || tkey)
|
||||
//go:build baremetal && !(nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || tkey || (tinygo.riscv32 && virt))
|
||||
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
"build-tags": ["virt", "qemu"],
|
||||
"default-stack-size": 4096,
|
||||
"linkerscript": "targets/riscv-qemu.ld",
|
||||
"emulator": "qemu-system-riscv32 -machine virt -nographic -bios none -kernel {}"
|
||||
"emulator": "qemu-system-riscv32 -machine virt -nographic -bios none -device virtio-rng-device -kernel {}"
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
|
||||
/* Memory map:
|
||||
* https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c
|
||||
* RAM and flash are set to 1MB each. That should be enough for the foreseeable
|
||||
* future. QEMU does not seem to limit the flash/RAM size and in fact doesn't
|
||||
* seem to differentiate between it.
|
||||
* Looks like we can use any address starting from 0x80000000 (so 2GB of space).
|
||||
* However, using a large space slows down tests.
|
||||
*/
|
||||
MEMORY
|
||||
{
|
||||
FLASH_TEXT (rw) : ORIGIN = 0x80000000, LENGTH = 0x100000
|
||||
RAM (xrw) : ORIGIN = 0x80100000, LENGTH = 0x100000
|
||||
RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 100M
|
||||
}
|
||||
|
||||
REGION_ALIAS("FLASH_TEXT", RAM)
|
||||
|
||||
_stack_size = 2K;
|
||||
|
||||
INCLUDE "targets/riscv.ld"
|
||||
|
||||
Reference in New Issue
Block a user