mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-07 20:43:40 +00:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
@@ -1,3 +1,69 @@
|
||||
0.19.0
|
||||
---
|
||||
|
||||
* **command line**
|
||||
- don't consider compile-only tests as failing
|
||||
- add -test flag for `tinygo list`
|
||||
- escape commands while printing them with the -x flag
|
||||
- make flash-command portable and safer to use
|
||||
- use `extended-remote` instead of `remote` in GDB
|
||||
- detect specific serial port IDs based on USB vid/pid
|
||||
- add a flag to the command line to select the serial implementation
|
||||
* **compiler**
|
||||
- `cgo`: improve constant parser
|
||||
- `compiler`: support chained interrupt handlers
|
||||
- `compiler`: add support for running a builtin in a goroutine
|
||||
- `compiler`: do not emit nil checks for loading closure variables
|
||||
- `compiler`: skip context parameter when starting regular goroutine
|
||||
- `compiler`: refactor method names
|
||||
- `compiler`: add function and global section pragmas
|
||||
- `compiler`: implement `syscall.rawSyscallNoError` in inline assembly
|
||||
- `interp`: ignore inline assembly in markExternal
|
||||
- `interp`: fix a bug in pointer cast workaround
|
||||
- `loader`: fix testing a main package
|
||||
* **standard library**
|
||||
- `crypto/rand`: replace this package with a TinyGo version
|
||||
- `machine`: make USBCDC global a pointer
|
||||
- `machine`: make UART objects pointer receivers
|
||||
- `machine`: define Serial as the default output
|
||||
- `net`: add initial support for net.IP
|
||||
- `net`: add more net compatibility
|
||||
- `os`: add stub for os.ReadDir
|
||||
- `os`: add FileMode constants from Go 1.16
|
||||
- `os`: add stubs required for net/http
|
||||
- `os`: implement process related functions
|
||||
- `reflect`: implement AppendSlice
|
||||
- `reflect`: add stubs required for net/http
|
||||
- `runtime`: make task.Data a 64-bit integer to avoid overflow
|
||||
- `runtime`: expose memory stats
|
||||
- `sync`: implement NewCond
|
||||
- `syscall`: fix int type in libc version
|
||||
* **targets**
|
||||
- `cortexm`: do not disable interrupts on abort
|
||||
- `cortexm`: bump default stack size to 2048 bytes
|
||||
- `nrf`: avoid heap allocation in waitForEvent
|
||||
- `nrf`: don't trigger a heap allocation in SPI.Transfer
|
||||
- `nrf52840`: add support for flashing with the BOSSA tool
|
||||
- `rp2040`: add support for GPIO input
|
||||
- `rp2040`: add basic support for ADC
|
||||
- `rp2040`: gpio and adc pin definitions
|
||||
- `rp2040`: implement UART
|
||||
- `rp2040`: patch elf to checksum 2nd stage boot
|
||||
- `stm32`: add PWM for most chips
|
||||
- `stm32`: add support for pin interrupts
|
||||
- `stm32f103`: add support for PinInputPullup / PinInputPulldown
|
||||
- `wasi`: remove wasm build tag
|
||||
* **boards**
|
||||
- `feather-rp2040`: add support for this board
|
||||
- `feather-nrf52840-sense`: add board definition for this board
|
||||
- `pca10059`: support flashing from Windows
|
||||
- `nano-rp2040`: add this board
|
||||
- `nano-33-ble`: add support for this board
|
||||
- `pico`: add the Raspberry Pi Pico board with the new RP2040 chip
|
||||
- `qtpy`: add pin for neopixels
|
||||
- all: add definition for ws2812 for supported boards
|
||||
|
||||
|
||||
0.18.0
|
||||
---
|
||||
|
||||
|
||||
@@ -336,6 +336,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840-sense examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-nrf52840 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=qtpy examples/serial
|
||||
@@ -356,7 +358,11 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-nano33 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico examples/blinky1
|
||||
$(TINYGO) build -size short -o test.hex -target=pico examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nano-33-ble examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -432,6 +438,8 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=1 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -serial=none examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
|
||||
@$(MD5SUM) test.nro
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||
|
||||
@@ -43,7 +43,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
||||
|
||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
||||
|
||||
The following 63 microcontroller boards are currently supported:
|
||||
The following 68 microcontroller boards are currently supported:
|
||||
|
||||
* [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333)
|
||||
* [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333)
|
||||
@@ -52,6 +52,7 @@ The following 63 microcontroller boards are currently supported:
|
||||
* [Adafruit Feather M4](https://www.adafruit.com/product/3857)
|
||||
* [Adafruit Feather M4 CAN](https://www.adafruit.com/product/4759)
|
||||
* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
|
||||
* [Adafruit Feather nRF52840 Sense](https://www.adafruit.com/product/4516)
|
||||
* [Adafruit Feather RP2040](https://www.adafruit.com/product/4884)
|
||||
* [Adafruit Feather STM32F405 Express](https://www.adafruit.com/product/4382)
|
||||
* [Adafruit Grand Central M4](https://www.adafruit.com/product/4064)
|
||||
@@ -69,7 +70,10 @@ The following 63 microcontroller boards are currently supported:
|
||||
* [Arduino Mega 2560](https://store.arduino.cc/arduino-mega-2560-rev3)
|
||||
* [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000-wifi)
|
||||
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
|
||||
* [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot)
|
||||
* [Arduino Nano 33 BLE](https://store.arduino.cc/nano-33-ble)
|
||||
* [Arduino Nano 33 BLE Sense](https://store.arduino.cc/nano-33-ble-sense)
|
||||
* [Arduino Nano 33 IoT](https://store.arduino.cc/nano-33-iot)
|
||||
* [Arduino Nano RP2040 Connect](https://store.arduino.cc/nano-rp2040-connect)
|
||||
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
|
||||
* [Arduino Zero](https://store.arduino.cc/usa/arduino-zero)
|
||||
* [BBC micro:bit](https://microbit.org/)
|
||||
@@ -96,6 +100,7 @@ The following 63 microcontroller boards are currently supported:
|
||||
* [PJRC Teensy 3.6](https://www.pjrc.com/store/teensy36.html)
|
||||
* [PJRC Teensy 4.0](https://www.pjrc.com/store/teensy40.html)
|
||||
* [ProductivityOpen P1AM-100](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html)
|
||||
* [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/)
|
||||
* [Seeed Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html)
|
||||
* [Seeed Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)
|
||||
* [Seeed Sipeed MAix BiT](https://www.seeedstudio.com/Sipeed-MAix-BiT-for-RISC-V-AI-IoT-p-2872.html)
|
||||
|
||||
+78
-31
@@ -100,7 +100,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
AutomaticStackSize: config.AutomaticStackSize(),
|
||||
DefaultStackSize: config.Target.DefaultStackSize,
|
||||
NeedsStackObjects: config.NeedsStackObjects(),
|
||||
Debug: config.Debug(),
|
||||
Debug: true,
|
||||
LLVMFeatures: config.LLVMFeatures(),
|
||||
}
|
||||
|
||||
@@ -470,33 +470,10 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
}
|
||||
|
||||
// Add libc dependency if needed.
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
switch config.Target.Libc {
|
||||
case "picolibc":
|
||||
job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// The library needs to be compiled (cache miss).
|
||||
jobs = append(jobs, job.dependencies...)
|
||||
jobs = append(jobs, job)
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
case "wasi-libc":
|
||||
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
||||
}
|
||||
ldflags = append(ldflags, path)
|
||||
case "":
|
||||
// no library specified, so nothing to do
|
||||
default:
|
||||
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
||||
}
|
||||
|
||||
// Add jobs to compile extra files. These files are in C or assembly and
|
||||
// contain things like the interrupt vector table and low level operations
|
||||
// such as stack switching.
|
||||
root := goenv.Get("TINYGOROOT")
|
||||
for _, path := range config.ExtraFiles() {
|
||||
abspath := filepath.Join(root, path)
|
||||
job := &compileJob{
|
||||
@@ -537,6 +514,64 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
ldflags = append(ldflags, lprogram.LDFlags...)
|
||||
}
|
||||
|
||||
// Add libc dependency if needed.
|
||||
switch config.Target.Libc {
|
||||
case "picolibc":
|
||||
job, err := Picolibc.load(config.Triple(), config.CPU(), dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// The library needs to be compiled (cache miss).
|
||||
jobs = append(jobs, job.dependencies...)
|
||||
jobs = append(jobs, job)
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
case "wasi-libc":
|
||||
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
||||
}
|
||||
job := dummyCompileJob(path)
|
||||
jobs = append(jobs, job)
|
||||
linkerDependencies = append(linkerDependencies, job)
|
||||
case "":
|
||||
// no library specified, so nothing to do
|
||||
default:
|
||||
return fmt.Errorf("unknown libc: %s", config.Target.Libc)
|
||||
}
|
||||
|
||||
// Strip debug information with -no-debug.
|
||||
if !config.Debug() {
|
||||
for _, tag := range config.BuildTags() {
|
||||
if tag == "baremetal" {
|
||||
// Don't use -no-debug on baremetal targets. It makes no sense:
|
||||
// the debug information isn't flashed to the device anyway.
|
||||
return fmt.Errorf("stripping debug information is unnecessary for baremetal targets")
|
||||
}
|
||||
}
|
||||
if config.Target.Linker == "wasm-ld" {
|
||||
// Don't just strip debug information, also compress relocations
|
||||
// while we're at it. Relocations can only be compressed when debug
|
||||
// information is stripped.
|
||||
ldflags = append(ldflags, "--strip-debug", "--compress-relocations")
|
||||
} else {
|
||||
switch config.GOOS() {
|
||||
case "linux":
|
||||
// Either real linux or an embedded system (like AVR) that
|
||||
// pretends to be Linux. It's a ELF linker wrapped by GCC in any
|
||||
// case.
|
||||
ldflags = append(ldflags, "-Wl,--strip-debug")
|
||||
case "darwin":
|
||||
// MacOS (darwin) doesn't have a linker flag to strip debug
|
||||
// information. Apple expects you to use the strip command
|
||||
// instead.
|
||||
return errors.New("cannot remove debug information: MacOS doesn't suppor this linker flag")
|
||||
default:
|
||||
// Other OSes may have different flags.
|
||||
return errors.New("cannot remove debug information: unknown OS: " + config.GOOS())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a linker job, which links all object files together and does some
|
||||
// extra stuff that can only be done after linking.
|
||||
jobs = append(jobs, &compileJob{
|
||||
@@ -953,15 +988,19 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
|
||||
if fn.stackSizeType == stacksize.Bounded {
|
||||
stackSize := uint32(fn.stackSize)
|
||||
|
||||
// Adding 4 for the stack canary. Even though the size may be
|
||||
// automatically determined, stack overflow checking is still
|
||||
// important as the stack size cannot be determined for all
|
||||
// goroutines.
|
||||
stackSize += 4
|
||||
|
||||
// Add stack size used by interrupts.
|
||||
switch fileHeader.Machine {
|
||||
case elf.EM_ARM:
|
||||
if stackSize%8 != 0 {
|
||||
// If the stack isn't a multiple of 8, it means the leaf
|
||||
// function with the biggest stack depth doesn't have an aligned
|
||||
// stack. If the STKALIGN flag is set (which it is by default)
|
||||
// the interrupt controller will forcibly align the stack before
|
||||
// storing in-use registers. This will thus overwrite one word
|
||||
// past the end of the stack (off-by-one).
|
||||
stackSize += 4
|
||||
}
|
||||
|
||||
// On Cortex-M (assumed here), this stack size is 8 words or 32
|
||||
// bytes. This is only to store the registers that the interrupt
|
||||
// may modify, the interrupt will switch to the interrupt stack
|
||||
@@ -969,6 +1008,14 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
|
||||
// Some background:
|
||||
// https://interrupt.memfault.com/blog/cortex-m-rtos-context-switching
|
||||
stackSize += 32
|
||||
|
||||
// Adding 4 for the stack canary, and another 4 to keep the
|
||||
// stack aligned. Even though the size may be automatically
|
||||
// determined, stack overflow checking is still important as the
|
||||
// stack size cannot be determined for all goroutines.
|
||||
stackSize += 8
|
||||
default:
|
||||
return fmt.Errorf("unknown architecture: %s", fileHeader.Machine.String())
|
||||
}
|
||||
|
||||
// Finally write the stack size to the binary.
|
||||
|
||||
+18
-6
@@ -55,7 +55,7 @@ func (c *Config) GOARCH() string {
|
||||
|
||||
// BuildTags returns the complete list of build tags used during this build.
|
||||
func (c *Config) BuildTags() []string {
|
||||
tags := append(c.Target.BuildTags, []string{"tinygo", "gc." + c.GC(), "scheduler." + c.Scheduler()}...)
|
||||
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)
|
||||
for i := 1; i <= c.GoMinorVersion; i++ {
|
||||
tags = append(tags, fmt.Sprintf("go1.%d", i))
|
||||
}
|
||||
@@ -113,6 +113,18 @@ func (c *Config) Scheduler() string {
|
||||
return "coroutines"
|
||||
}
|
||||
|
||||
// Serial returns the serial implementation for this build configuration: uart,
|
||||
// usb (meaning USB-CDC), or none.
|
||||
func (c *Config) Serial() string {
|
||||
if c.Options.Serial != "" {
|
||||
return c.Options.Serial
|
||||
}
|
||||
if c.Target.Serial != "" {
|
||||
return c.Target.Serial
|
||||
}
|
||||
return "none"
|
||||
}
|
||||
|
||||
// OptLevels returns the optimization level (0-2), size level (0-2), and inliner
|
||||
// threshold as used in the LLVM optimization pipeline.
|
||||
func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
|
||||
@@ -197,9 +209,8 @@ func (c *Config) CFlags() []string {
|
||||
cflags = append(cflags, "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "picolibc", "newlib", "libc", "include"))
|
||||
cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include"))
|
||||
}
|
||||
if c.Debug() {
|
||||
cflags = append(cflags, "-g")
|
||||
}
|
||||
// Always emit debug information. It is optionally stripped at link time.
|
||||
cflags = append(cflags, "-g")
|
||||
return cflags
|
||||
}
|
||||
|
||||
@@ -238,8 +249,9 @@ func (c *Config) VerifyIR() bool {
|
||||
return c.Options.VerifyIR
|
||||
}
|
||||
|
||||
// Debug returns whether to add debug symbols to the IR, for debugging with GDB
|
||||
// and similar.
|
||||
// Debug returns whether debug (DWARF) information should be retained by the
|
||||
// linker. By default, debug information is retained but it can be removed with
|
||||
// the -no-debug flag.
|
||||
func (c *Config) Debug() bool {
|
||||
return c.Options.Debug
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
var (
|
||||
validGCOptions = []string{"none", "leaking", "extalloc", "conservative"}
|
||||
validSchedulerOptions = []string{"none", "tasks", "coroutines"}
|
||||
validSerialOptions = []string{"none", "uart", "usb"}
|
||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||
validPanicStrategyOptions = []string{"print", "trap"}
|
||||
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
||||
@@ -22,6 +23,7 @@ type Options struct {
|
||||
GC string
|
||||
PanicStrategy string
|
||||
Scheduler string
|
||||
Serial string
|
||||
PrintIR bool
|
||||
DumpSSA bool
|
||||
VerifyIR bool
|
||||
@@ -59,6 +61,15 @@ func (o *Options) Verify() error {
|
||||
}
|
||||
}
|
||||
|
||||
if o.Serial != "" {
|
||||
valid := isInArray(validSerialOptions, o.Serial)
|
||||
if !valid {
|
||||
return fmt.Errorf(`invalid serial option '%s': valid values are %s`,
|
||||
o.Serial,
|
||||
strings.Join(validSerialOptions, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
if o.PrintSizes != "" {
|
||||
valid := isInArray(validPrintSizeOptions, o.PrintSizes)
|
||||
if !valid {
|
||||
|
||||
@@ -31,6 +31,7 @@ type TargetSpec struct {
|
||||
BuildTags []string `json:"build-tags"`
|
||||
GC string `json:"gc"`
|
||||
Scheduler string `json:"scheduler"`
|
||||
Serial string `json:"serial"` // which serial output to use (uart, usb, none)
|
||||
Linker string `json:"linker"`
|
||||
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
|
||||
Libc string `json:"libc"`
|
||||
|
||||
+12
-4
@@ -23,7 +23,7 @@ import (
|
||||
// Version of the compiler pacakge. Must be incremented each time the compiler
|
||||
// package changes in a way that affects the generated LLVM module.
|
||||
// This version is independent of the TinyGo version number.
|
||||
const Version = 11 // last change: change method name globals
|
||||
const Version = 12 // last change: implement syscall.rawSyscallNoError
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargets()
|
||||
@@ -715,11 +715,11 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
member := pkg.Members[name]
|
||||
switch member := member.(type) {
|
||||
case *ssa.Function:
|
||||
// Create the function definition.
|
||||
b := newBuilder(c, irbuilder, member)
|
||||
if member.Blocks == nil {
|
||||
continue // external function
|
||||
}
|
||||
// Create the function definition.
|
||||
b := newBuilder(c, irbuilder, member)
|
||||
b.createFunction()
|
||||
case *ssa.Type:
|
||||
if types.IsInterface(member.Type()) {
|
||||
@@ -758,10 +758,13 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
case *ssa.Global:
|
||||
// Global variable.
|
||||
info := c.getGlobalInfo(member)
|
||||
global := c.getGlobal(member)
|
||||
if !info.extern {
|
||||
global := c.getGlobal(member)
|
||||
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
if info.section != "" {
|
||||
global.SetSection(info.section)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -787,6 +790,9 @@ func (b *builder) createFunction() {
|
||||
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
||||
b.llvmFn.SetUnnamedAddr(true)
|
||||
}
|
||||
if b.info.section != "" {
|
||||
b.llvmFn.SetSection(b.info.section)
|
||||
}
|
||||
if b.info.exported && strings.HasPrefix(b.Triple, "wasm") {
|
||||
// Set the exported name. This is necessary for WebAssembly because
|
||||
// otherwise the function is not exported.
|
||||
@@ -1304,6 +1310,8 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||
return b.emitCSROperation(instr)
|
||||
case strings.HasPrefix(name, "syscall.Syscall"):
|
||||
return b.createSyscall(instr)
|
||||
case strings.HasPrefix(name, "syscall.rawSyscallNoError"):
|
||||
return b.createRawSyscallNoError(instr)
|
||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||
return b.createVolatileLoad(instr)
|
||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||
|
||||
@@ -45,6 +45,7 @@ func TestCompiler(t *testing.T) {
|
||||
{"float.go", ""},
|
||||
{"interface.go", ""},
|
||||
{"func.go", ""},
|
||||
{"pragma.go", ""},
|
||||
{"goroutine.go", "wasm"},
|
||||
{"goroutine.go", "cortex-m-qemu"},
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ type functionInfo struct {
|
||||
module string // go:wasm-module
|
||||
importName string // go:linkname, go:export - The name the developer assigns
|
||||
linkName string // go:linkname, go:export - The name that we map for the particular module -> importName
|
||||
section string // go:section - object file section name
|
||||
exported bool // go:export, CGo
|
||||
nobounds bool // go:nobounds
|
||||
variadic bool // go:variadic (CGo only)
|
||||
@@ -270,6 +271,10 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
|
||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||
info.linkName = parts[2]
|
||||
}
|
||||
case "//go:section":
|
||||
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
|
||||
info.section = parts[1]
|
||||
}
|
||||
case "//go:nobounds":
|
||||
// Skip bounds checking in this function. Useful for some
|
||||
// runtime functions.
|
||||
@@ -325,6 +330,7 @@ type globalInfo struct {
|
||||
linkName string // go:extern
|
||||
extern bool // go:extern
|
||||
align int // go:align
|
||||
section string // go:section
|
||||
}
|
||||
|
||||
// loadASTComments loads comments on globals from the AST, for use later in the
|
||||
@@ -438,6 +444,10 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
|
||||
if err == nil {
|
||||
info.align = align
|
||||
}
|
||||
case "//go:section":
|
||||
if len(parts) == 2 {
|
||||
info.section = parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
-8
@@ -10,11 +10,11 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// createSyscall emits an inline system call instruction, depending on the
|
||||
// target OS/arch.
|
||||
func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
// createRawSyscall creates a system call with the provided system call number
|
||||
// and returns the result as a single integer (the system call result). The
|
||||
// result is not further interpreted.
|
||||
func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
num := b.getValue(call.Args[0])
|
||||
var syscallResult llvm.Value
|
||||
switch {
|
||||
case b.GOARCH == "amd64":
|
||||
if b.GOOS == "darwin" {
|
||||
@@ -57,7 +57,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
constraints += ",~{rcx},~{r11}"
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||
syscallResult = b.CreateCall(target, args, "")
|
||||
return b.CreateCall(target, args, ""), nil
|
||||
case b.GOARCH == "386" && b.GOOS == "linux":
|
||||
// Sources:
|
||||
// syscall(2) man page
|
||||
@@ -83,7 +83,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
}
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||
syscallResult = b.CreateCall(target, args, "")
|
||||
return b.CreateCall(target, args, ""), nil
|
||||
case b.GOARCH == "arm" && b.GOOS == "linux":
|
||||
// Implement the EABI system call convention for Linux.
|
||||
// Source: syscall(2) man page.
|
||||
@@ -115,7 +115,7 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
}
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||
syscallResult = b.CreateCall(target, args, "")
|
||||
return b.CreateCall(target, args, ""), nil
|
||||
case b.GOARCH == "arm64" && b.GOOS == "linux":
|
||||
// Source: syscall(2) man page.
|
||||
args := []llvm.Value{}
|
||||
@@ -147,10 +147,19 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
constraints += ",~{x16},~{x17}" // scratch registers
|
||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||
syscallResult = b.CreateCall(target, args, "")
|
||||
return b.CreateCall(target, args, ""), nil
|
||||
default:
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||
}
|
||||
}
|
||||
|
||||
// createSyscall emits instructions for the syscall.Syscall* family of
|
||||
// functions, depending on the target OS/arch.
|
||||
func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
syscallResult, err := b.createRawSyscall(call)
|
||||
if err != nil {
|
||||
return syscallResult, err
|
||||
}
|
||||
switch b.GOOS {
|
||||
case "linux", "freebsd":
|
||||
// Return values: r0, r1 uintptr, err Errno
|
||||
@@ -190,3 +199,16 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||
}
|
||||
}
|
||||
|
||||
// createRawSyscallNoError emits instructions for the Linux-specific
|
||||
// syscall.rawSyscallNoError function.
|
||||
func (b *builder) createRawSyscallNoError(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
syscallResult, err := b.createRawSyscall(call)
|
||||
if err != nil {
|
||||
return syscallResult, err
|
||||
}
|
||||
retval := llvm.ConstNull(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType}, false))
|
||||
retval = b.CreateInsertValue(retval, syscallResult, 0, "")
|
||||
retval = b.CreateInsertValue(retval, llvm.ConstInt(b.uintptrType, 0, false), 1, "")
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
// Creates an external global with name extern_global.
|
||||
//go:extern extern_global
|
||||
var externGlobal [0]byte
|
||||
|
||||
// Creates a
|
||||
//go:align 32
|
||||
var alignedGlobal [4]uint32
|
||||
|
||||
// Test conflicting pragmas (the last one counts).
|
||||
//go:align 64
|
||||
//go:align 16
|
||||
var alignedGlobal16 [4]uint32
|
||||
|
||||
// Test exported functions.
|
||||
//export extern_func
|
||||
func externFunc() {
|
||||
}
|
||||
|
||||
// Define a function in a different package using go:linkname.
|
||||
//go:linkname withLinkageName1 somepkg.someFunction1
|
||||
func withLinkageName1() {
|
||||
}
|
||||
|
||||
// Import a function from a different package using go:linkname.
|
||||
//go:linkname withLinkageName2 somepkg.someFunction2
|
||||
func withLinkageName2()
|
||||
|
||||
// Function has an 'inline hint', similar to the inline keyword in C.
|
||||
//go:inline
|
||||
func inlineFunc() {
|
||||
}
|
||||
|
||||
// Function should never be inlined, equivalent to GCC
|
||||
// __attribute__((noinline)).
|
||||
//go:noinline
|
||||
func noinlineFunc() {
|
||||
}
|
||||
|
||||
// This function should have the specified section.
|
||||
//go:section .special_function_section
|
||||
func functionInSection() {
|
||||
}
|
||||
|
||||
//export exportedFunctionInSection
|
||||
//go:section .special_function_section
|
||||
func exportedFunctionInSection() {
|
||||
}
|
||||
|
||||
// This function should not: it's only a declaration and not a definition.
|
||||
//go:section .special_function_section
|
||||
func undefinedFunctionNotInSection()
|
||||
|
||||
//go:section .special_global_section
|
||||
var globalInSection uint32
|
||||
|
||||
//go:section .special_global_section
|
||||
//go:extern undefinedGlobalNotInSection
|
||||
var undefinedGlobalNotInSection uint32
|
||||
|
||||
//go:align 1024
|
||||
//go:section .global_section
|
||||
var multipleGlobalPragmas uint32
|
||||
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
; ModuleID = 'pragma.go'
|
||||
source_filename = "pragma.go"
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32--wasi"
|
||||
|
||||
@extern_global = external global [0 x i8], align 1
|
||||
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
|
||||
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16
|
||||
@main.globalInSection = hidden global i32 0, section ".special_global_section", align 4
|
||||
@undefinedGlobalNotInSection = external global i32, align 4
|
||||
@main.multipleGlobalPragmas = hidden global i32 0, section ".global_section", align 1024
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @extern_func() #0 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @somepkg.someFunction1(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @somepkg.someFunction2(i8*, i8*)
|
||||
|
||||
; Function Attrs: inlinehint
|
||||
define hidden void @main.inlineFunc(i8* %context, i8* %parentHandle) unnamed_addr #1 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline
|
||||
define hidden void @main.noinlineFunc(i8* %context, i8* %parentHandle) unnamed_addr #2 {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden void @main.functionInSection(i8* %context, i8* %parentHandle) unnamed_addr section ".special_function_section" {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @exportedFunctionInSection() #3 section ".special_function_section" {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @main.undefinedFunctionNotInSection(i8*, i8*)
|
||||
|
||||
attributes #0 = { "wasm-export-name"="extern_func" }
|
||||
attributes #1 = { inlinehint }
|
||||
attributes #2 = { noinline }
|
||||
attributes #3 = { "wasm-export-name"="exportedFunctionInSection" }
|
||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
// Version of TinyGo.
|
||||
// Update this value before release of new version of software.
|
||||
const Version = "0.19.0-dev"
|
||||
const Version = "0.20.0-dev"
|
||||
|
||||
// GetGorootVersion returns the major and minor version for a given GOROOT path.
|
||||
// If the goroot cannot be determined, (0, 0) is returned.
|
||||
|
||||
@@ -57,6 +57,7 @@ func (r *runner) errorAt(inst instruction, err error) *Error {
|
||||
pos := getPosition(inst.llvmInst)
|
||||
return &Error{
|
||||
ImportPath: r.pkgName,
|
||||
Inst: inst.llvmInst,
|
||||
Pos: pos,
|
||||
Err: err,
|
||||
Traceback: []ErrorLine{{pos, inst.llvmInst}},
|
||||
|
||||
+27
-2
@@ -15,7 +15,7 @@ import (
|
||||
// package is changed in a way that affects the output so that cached package
|
||||
// builds will be invalidated.
|
||||
// This version is independent of the TinyGo version number.
|
||||
const Version = 1
|
||||
const Version = 2 // last change: fix GEP on untyped pointers
|
||||
|
||||
// Enable extra checks, which should be disabled by default.
|
||||
// This may help track down bugs by adding a few more sanity checks.
|
||||
@@ -110,17 +110,26 @@ func Run(mod llvm.Module, debug bool) error {
|
||||
fmt.Fprintln(os.Stderr, "call:", fn.Name())
|
||||
}
|
||||
_, mem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
|
||||
call.EraseFromParentAsInstruction()
|
||||
if callErr != nil {
|
||||
if isRecoverableError(callErr.Err) {
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, "not interpreting", r.pkgName, "because of error:", callErr.Error())
|
||||
}
|
||||
// Remove instructions that were created as part of interpreting
|
||||
// the package.
|
||||
mem.revert()
|
||||
// Create a call to the package initializer (which was
|
||||
// previously deleted).
|
||||
i8undef := llvm.Undef(r.i8ptrType)
|
||||
r.builder.CreateCall(fn, []llvm.Value{i8undef, i8undef}, "")
|
||||
// Make sure that any globals touched by the package
|
||||
// initializer, won't be accessed by later package initializers.
|
||||
r.markExternalLoad(fn)
|
||||
continue
|
||||
}
|
||||
return callErr
|
||||
}
|
||||
call.EraseFromParentAsInstruction()
|
||||
for index, obj := range mem.objects {
|
||||
r.objects[index] = obj
|
||||
}
|
||||
@@ -270,3 +279,19 @@ func (r *runner) getFunction(llvmFn llvm.Value) *function {
|
||||
r.functionCache[llvmFn] = fn
|
||||
return fn
|
||||
}
|
||||
|
||||
// markExternalLoad marks the given llvmValue as being loaded externally. This
|
||||
// is primarily used to mark package initializers that could not be run at
|
||||
// compile time. As an example, a package initialize might store to a global
|
||||
// variable. Another package initializer might read from the same global
|
||||
// variable. By marking this function as being run at runtime, that load
|
||||
// instruction will need to be run at runtime instead of at compile time.
|
||||
func (r *runner) markExternalLoad(llvmValue llvm.Value) {
|
||||
mem := memoryView{r: r}
|
||||
mem.markExternalLoad(llvmValue)
|
||||
for index, obj := range mem.objects {
|
||||
if obj.marked > r.objects[index].marked {
|
||||
r.objects[index].marked = obj.marked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ func TestInterp(t *testing.T) {
|
||||
"slice-copy",
|
||||
"consteval",
|
||||
"interface",
|
||||
"revert",
|
||||
} {
|
||||
name := name // make tc local to this closure
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
||||
@@ -572,6 +572,17 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
|
||||
}
|
||||
|
||||
if llvmType.IsNil() {
|
||||
if v.offset() != 0 {
|
||||
// If there is an offset, make sure to use a GEP to index into the
|
||||
// pointer. Because there is no expected type, we use whatever is
|
||||
// most convenient: an *i8 type. It is trivial to index byte-wise.
|
||||
if llvmValue.Type() != mem.r.i8ptrType {
|
||||
llvmValue = llvm.ConstBitCast(llvmValue, mem.r.i8ptrType)
|
||||
}
|
||||
llvmValue = llvm.ConstInBoundsGEP(llvmValue, []llvm.Value{
|
||||
llvm.ConstInt(llvmValue.Type().Context().Int32Type(), uint64(v.offset()), false),
|
||||
})
|
||||
}
|
||||
return llvmValue, nil
|
||||
}
|
||||
|
||||
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
declare void @externalCall(i64)
|
||||
|
||||
@foo.knownAtRuntime = global i64 0
|
||||
@bar.knownAtRuntime = global i64 0
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call void @foo.init(i8* undef, i8* undef)
|
||||
call void @bar.init(i8* undef, i8* undef)
|
||||
call void @main.init(i8* undef, i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
store i64 5, i64* @foo.knownAtRuntime
|
||||
unreachable ; this triggers a revert of @foo.init.
|
||||
}
|
||||
|
||||
define internal void @bar.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
%val = load i64, i64* @foo.knownAtRuntime
|
||||
store i64 %val, i64* @bar.knownAtRuntime
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
call void @externalCall(i64 3)
|
||||
ret void
|
||||
}
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
@foo.knownAtRuntime = local_unnamed_addr global i64 0
|
||||
@bar.knownAtRuntime = local_unnamed_addr global i64 0
|
||||
|
||||
declare void @externalCall(i64) local_unnamed_addr
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call fastcc void @foo.init(i8* undef, i8* undef)
|
||||
%val = load i64, i64* @foo.knownAtRuntime, align 8
|
||||
store i64 %val, i64* @bar.knownAtRuntime, align 8
|
||||
call void @externalCall(i64 3)
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal fastcc void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
store i64 5, i64* @foo.knownAtRuntime, align 8
|
||||
unreachable
|
||||
}
|
||||
@@ -1009,6 +1009,7 @@ func main() {
|
||||
gc := flag.String("gc", "", "garbage collector to use (none, leaking, extalloc, conservative)")
|
||||
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
|
||||
scheduler := flag.String("scheduler", "", "which scheduler to use (none, coroutines, tasks)")
|
||||
serial := flag.String("serial", "", "which serial output to use (none, uart, usb)")
|
||||
printIR := flag.Bool("printir", false, "print LLVM IR")
|
||||
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
|
||||
verifyIR := flag.Bool("verifyir", false, "run extra verification steps on LLVM IR")
|
||||
@@ -1018,7 +1019,7 @@ func main() {
|
||||
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
|
||||
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
|
||||
printCommands := flag.Bool("x", false, "Print commands")
|
||||
nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation")
|
||||
nodebug := flag.Bool("no-debug", false, "strip debug information")
|
||||
ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)")
|
||||
ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug")
|
||||
port := flag.String("port", "", "flash port (can specify multiple candidates separated by commas)")
|
||||
@@ -1081,6 +1082,7 @@ func main() {
|
||||
GC: *gc,
|
||||
PanicStrategy: *panicStrategy,
|
||||
Scheduler: *scheduler,
|
||||
Serial: *serial,
|
||||
PrintIR: *printIR,
|
||||
DumpSSA: *dumpSSA,
|
||||
VerifyIR: *verifyIR,
|
||||
|
||||
+14
-15
@@ -125,7 +125,7 @@ func TestCompiler(t *testing.T) {
|
||||
// Test with few optimizations enabled (no inlining, etc).
|
||||
t.Run("opt=1", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTestWithConfig("stdlib.go", "", t, &compileopts.Options{
|
||||
runTestWithConfig("stdlib.go", "", t, compileopts.Options{
|
||||
Opt: "1",
|
||||
}, nil, nil)
|
||||
})
|
||||
@@ -134,15 +134,14 @@ func TestCompiler(t *testing.T) {
|
||||
// TODO: fix this for stdlib.go, which currently fails.
|
||||
t.Run("opt=0", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTestWithConfig("print.go", "", t, &compileopts.Options{
|
||||
runTestWithConfig("print.go", "", t, compileopts.Options{
|
||||
Opt: "0",
|
||||
}, nil, nil)
|
||||
})
|
||||
|
||||
t.Run("ldflags", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTestWithConfig("ldflags.go", "", t, &compileopts.Options{
|
||||
Opt: "z",
|
||||
runTestWithConfig("ldflags.go", "", t, compileopts.Options{
|
||||
GlobalValues: map[string]map[string]string{
|
||||
"main": {
|
||||
"someGlobal": "foobar",
|
||||
@@ -188,20 +187,20 @@ func runBuild(src, out string, opts *compileopts.Options) error {
|
||||
}
|
||||
|
||||
func runTest(name, target string, t *testing.T, cmdArgs, environmentVars []string) {
|
||||
options := &compileopts.Options{
|
||||
Target: target,
|
||||
Opt: "z",
|
||||
PrintIR: false,
|
||||
DumpSSA: false,
|
||||
VerifyIR: true,
|
||||
Debug: true,
|
||||
PrintSizes: "",
|
||||
WasmAbi: "",
|
||||
options := compileopts.Options{
|
||||
Target: target,
|
||||
}
|
||||
runTestWithConfig(name, target, t, options, cmdArgs, environmentVars)
|
||||
}
|
||||
|
||||
func runTestWithConfig(name, target string, t *testing.T, options *compileopts.Options, cmdArgs, environmentVars []string) {
|
||||
func runTestWithConfig(name, target string, t *testing.T, options compileopts.Options, cmdArgs, environmentVars []string) {
|
||||
// Set default config.
|
||||
options.Debug = true
|
||||
options.VerifyIR = true
|
||||
if options.Opt == "" {
|
||||
options.Opt = "z"
|
||||
}
|
||||
|
||||
// Get the expected output for this test.
|
||||
// Note: not using filepath.Join as it strips the path separator at the end
|
||||
// of the path.
|
||||
@@ -230,7 +229,7 @@ func runTestWithConfig(name, target string, t *testing.T, options *compileopts.O
|
||||
|
||||
// Build the test binary.
|
||||
binary := filepath.Join(tmpdir, "test")
|
||||
err = runBuild("./"+path, binary, options)
|
||||
err = runBuild("./"+path, binary, &options)
|
||||
if err != nil {
|
||||
printCompilerError(t.Log, err)
|
||||
t.Fail()
|
||||
|
||||
@@ -47,8 +47,6 @@ const (
|
||||
LED = D6
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
// USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN Pin = PA24
|
||||
|
||||
@@ -35,8 +35,6 @@ const (
|
||||
LED3 Pin = PB03 // RX LED
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
// ADC pins
|
||||
const (
|
||||
AREF Pin = PA03
|
||||
|
||||
@@ -74,5 +74,3 @@ const (
|
||||
PB30 Pin = 62
|
||||
PB31 Pin = 63
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
@@ -15,8 +15,6 @@ const (
|
||||
BUTTON = PB31
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
const (
|
||||
// https://ww1.microchip.com/downloads/en/DeviceDoc/70005321A.pdf
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ const (
|
||||
BUTTON = PA0
|
||||
)
|
||||
|
||||
var Serial = UART1
|
||||
var DefaultUART = UART1
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
|
||||
@@ -57,8 +57,6 @@ const (
|
||||
UART_RX_PIN = P0_30 // PORTB
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = P0_05 // I2C0 external
|
||||
|
||||
@@ -104,11 +104,6 @@ const (
|
||||
UART_TX_PIN = D1
|
||||
)
|
||||
|
||||
// Serial is the USB device
|
||||
var (
|
||||
Serial = USB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = D20 // I2C0 external
|
||||
|
||||
@@ -68,8 +68,6 @@ const (
|
||||
ADC3 Pin = IO39
|
||||
)
|
||||
|
||||
var Serial = UART0
|
||||
|
||||
// UART0 pins
|
||||
const (
|
||||
UART_TX_PIN = IO1
|
||||
|
||||
@@ -47,8 +47,6 @@ const (
|
||||
WS2812 = D8
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
// USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
|
||||
@@ -40,8 +40,6 @@ const (
|
||||
WS2812 = D8
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
// USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
// +build feather_nrf52840_sense
|
||||
|
||||
package machine
|
||||
|
||||
const HasLowFrequencyCrystal = true
|
||||
|
||||
// GPIO Pins
|
||||
const (
|
||||
D0 = P0_25 // UART TX
|
||||
D1 = P0_24 // UART RX
|
||||
D2 = P0_10 // NFC2
|
||||
D3 = P1_11
|
||||
D4 = P1_10 // LED2
|
||||
D5 = P1_08
|
||||
D6 = P0_07
|
||||
D7 = P1_02 // Button
|
||||
D8 = P0_16 // NeoPixel
|
||||
D9 = P0_26
|
||||
D10 = P0_27
|
||||
D11 = P0_06
|
||||
D12 = P0_08
|
||||
D13 = P1_09 // LED1
|
||||
D14 = P0_04 // A0
|
||||
D15 = P0_05 // A1
|
||||
D16 = P0_30 // A2
|
||||
D17 = P0_28 // A3
|
||||
D18 = P0_02 // A4
|
||||
D19 = P0_03 // A5
|
||||
D20 = P0_29 // Battery
|
||||
D21 = P0_31 // AREF
|
||||
D22 = P0_12 // I2C SDA
|
||||
D23 = P0_11 // I2C SCL
|
||||
D24 = P0_15 // SPI MISO
|
||||
D25 = P0_13 // SPI MOSI
|
||||
D26 = P0_14 // SPI SCK
|
||||
D27 = P0_19 // QSPI CLK
|
||||
D28 = P0_20 // QSPI CS
|
||||
D29 = P0_17 // QSPI Data 0
|
||||
D30 = P0_22 // QSPI Data 1
|
||||
D31 = P0_23 // QSPI Data 2
|
||||
D32 = P0_21 // QSPI Data 3
|
||||
D33 = P0_09 // NFC1 (test point on bottom of board)
|
||||
)
|
||||
|
||||
// Analog Pins
|
||||
const (
|
||||
A0 = D14
|
||||
A1 = D15
|
||||
A2 = D16
|
||||
A3 = D17
|
||||
A4 = D18
|
||||
A5 = D19
|
||||
A6 = D20 // Battery
|
||||
A7 = D21 // ARef
|
||||
)
|
||||
|
||||
const (
|
||||
LED = D13
|
||||
LED1 = LED
|
||||
LED2 = D4
|
||||
NEOPIXEL = D8
|
||||
WS2812 = D8
|
||||
BUTTON = D7
|
||||
|
||||
QSPI_SCK = D27
|
||||
QSPI_CS = D28
|
||||
QSPI_DATA0 = D29
|
||||
QSPI_DATA1 = D30
|
||||
QSPI_DATA2 = D31
|
||||
QSPI_DATA3 = D32
|
||||
)
|
||||
|
||||
// UART0 pins (logical UART1)
|
||||
const (
|
||||
UART_RX_PIN = D1
|
||||
UART_TX_PIN = D0
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = D22 // I2C0 external
|
||||
SCL_PIN = D23 // I2C0 external
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = D26 // SCK
|
||||
SPI0_SDO_PIN = D25 // SDO
|
||||
SPI0_SDI_PIN = D24 // SDI
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Feather nRF52840 Express"
|
||||
usb_STRING_MANUFACTURER = "Adafruit Industries LLC"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x8088
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultUART = UART0
|
||||
)
|
||||
@@ -72,13 +72,8 @@ const (
|
||||
|
||||
// UART0 pins (logical UART1)
|
||||
const (
|
||||
UART_RX_PIN = D0
|
||||
UART_TX_PIN = D1
|
||||
)
|
||||
|
||||
// Serial is the USB device
|
||||
var (
|
||||
Serial = USB
|
||||
UART_RX_PIN = D1
|
||||
UART_TX_PIN = D0
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
@@ -104,3 +99,7 @@ var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x802A
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultUART = UART0
|
||||
)
|
||||
|
||||
@@ -141,7 +141,7 @@ var (
|
||||
TxAltFuncSelector: AF7_USART1_2_3,
|
||||
RxAltFuncSelector: AF7_USART1_2_3,
|
||||
}
|
||||
Serial = UART1
|
||||
DefaultUART = UART1
|
||||
)
|
||||
|
||||
func initUART() {
|
||||
|
||||
@@ -8,3 +8,20 @@ const (
|
||||
// Onboard crystal oscillator frequency, in MHz.
|
||||
xoscFreq = 12 // MHz
|
||||
)
|
||||
|
||||
// SPI default pins
|
||||
const (
|
||||
// Default Serial Clock Bus 0 for SPI communications
|
||||
SPI0_SCK_PIN = GPIO18
|
||||
// Default Serial Out Bus 0 for SPI communications
|
||||
SPI0_SDO_PIN = GPIO19 // Tx
|
||||
// Default Serial In Bus 0 for SPI communications
|
||||
SPI0_SDI_PIN = GPIO20 // Rx
|
||||
|
||||
// Default Serial Clock Bus 1 for SPI communications
|
||||
SPI1_SCK_PIN = GPIO10
|
||||
// Default Serial Out Bus 1 for SPI communications
|
||||
SPI1_SDO_PIN = GPIO11 // Tx
|
||||
// Default Serial In Bus 1 for SPI communications
|
||||
SPI1_SDI_PIN = GPIO12 // Rx
|
||||
)
|
||||
|
||||
@@ -142,8 +142,6 @@ const (
|
||||
WS2812 = NEOPIXEL_PIN
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART1_RX_PIN = D0 // (PB25)
|
||||
|
||||
@@ -35,7 +35,7 @@ const (
|
||||
LED_BLUE = P21
|
||||
)
|
||||
|
||||
var Serial = UART0
|
||||
var DefaultUART = UART0
|
||||
|
||||
const (
|
||||
// TODO: figure out the pin numbers for these.
|
||||
|
||||
@@ -37,8 +37,6 @@ const (
|
||||
LED = D13
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
// USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
|
||||
@@ -70,11 +70,6 @@ const (
|
||||
UART_TX_PIN = D1
|
||||
)
|
||||
|
||||
// Serial is the USB device
|
||||
var (
|
||||
Serial = USB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = D21 // I2C0 external
|
||||
|
||||
@@ -54,7 +54,7 @@ const (
|
||||
I2C0_SDA_PIN = PA10
|
||||
)
|
||||
|
||||
var Serial = UART0
|
||||
var DefaultUART = UART0
|
||||
|
||||
var (
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ const (
|
||||
LED_BLUE = D14
|
||||
)
|
||||
|
||||
var Serial = UART0
|
||||
var DefaultUART = UART0
|
||||
|
||||
// Default pins for UARTHS.
|
||||
const (
|
||||
|
||||
@@ -41,8 +41,6 @@ const (
|
||||
WS2812 = D40
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
// USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
|
||||
@@ -12,7 +12,7 @@ const (
|
||||
BUTTONB Pin = P11
|
||||
)
|
||||
|
||||
var Serial = UART0
|
||||
var DefaultUART = UART0
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
|
||||
@@ -12,7 +12,7 @@ const (
|
||||
BUTTONB Pin = 26
|
||||
)
|
||||
|
||||
var Serial = UART0
|
||||
var DefaultUART = UART0
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
|
||||
@@ -70,11 +70,6 @@ const (
|
||||
UART_TX_PIN = P1_03
|
||||
)
|
||||
|
||||
// Serial is the USB device
|
||||
var (
|
||||
Serial = USB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = P0_31
|
||||
|
||||
@@ -53,11 +53,16 @@ const (
|
||||
SCL_PIN Pin = GPIO13
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
// SPI pins. SPI1 not available on Nano RP2040 Connect.
|
||||
const (
|
||||
SPI0_SCK_PIN Pin = GPIO6
|
||||
SPI0_SDO_PIN Pin = GPIO7
|
||||
SPI0_SDI_PIN Pin = GPIO4
|
||||
|
||||
// GPIO22 does not have SPI functionality so we set it to avoid interfering with NINA.
|
||||
SPI1_SCK_PIN Pin = GPIO22
|
||||
SPI1_SDO_PIN Pin = GPIO22
|
||||
SPI1_SDI_PIN Pin = GPIO22
|
||||
)
|
||||
|
||||
// NINA-W102 Pins
|
||||
|
||||
@@ -54,11 +54,6 @@ const (
|
||||
UART_TX_PIN = P0_08
|
||||
)
|
||||
|
||||
// Serial is the USB device
|
||||
var (
|
||||
Serial = USB
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = P0_17 // I2C0 external
|
||||
|
||||
@@ -20,8 +20,6 @@ const (
|
||||
// Onboard blue LED (on the AI-Thinker module).
|
||||
const LED = D4
|
||||
|
||||
var Serial = UART0
|
||||
|
||||
// SPI pins
|
||||
const (
|
||||
SPI0_SCK_PIN = D5
|
||||
|
||||
@@ -23,9 +23,6 @@ const (
|
||||
UART_RX_PIN Pin = NoPin
|
||||
)
|
||||
|
||||
// Serial is the USB device
|
||||
var Serial = USB
|
||||
|
||||
// I2C pins (unused)
|
||||
const (
|
||||
SDA_PIN = NoPin
|
||||
|
||||
@@ -18,9 +18,6 @@ const (
|
||||
UART_RX_PIN Pin = 19
|
||||
)
|
||||
|
||||
// Serial is the USB device
|
||||
var Serial = USB
|
||||
|
||||
// I2C pins (unused)
|
||||
const (
|
||||
SDA_PIN = NoPin
|
||||
|
||||
@@ -34,7 +34,7 @@ var (
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART2,
|
||||
}
|
||||
Serial = UART2
|
||||
DefaultUART = UART2
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -38,7 +38,7 @@ var (
|
||||
TxAltFuncSelector: UART_ALT_FN,
|
||||
RxAltFuncSelector: UART_ALT_FN,
|
||||
}
|
||||
Serial = UART1
|
||||
DefaultUART = UART1
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -76,7 +76,7 @@ var (
|
||||
TxAltFuncSelector: 4,
|
||||
RxAltFuncSelector: 4,
|
||||
}
|
||||
Serial = UART1
|
||||
DefaultUART = UART1
|
||||
|
||||
// I2C1 is documented, alias to I2C0 as well
|
||||
I2C1 = &I2C{
|
||||
|
||||
@@ -78,7 +78,7 @@ var (
|
||||
TxAltFuncSelector: 7,
|
||||
RxAltFuncSelector: 3,
|
||||
}
|
||||
Serial = UART1
|
||||
DefaultUART = UART1
|
||||
|
||||
// I2C1 is documented, alias to I2C0 as well
|
||||
I2C1 = &I2C{
|
||||
|
||||
@@ -38,7 +38,7 @@ var (
|
||||
TxAltFuncSelector: UART_ALT_FN,
|
||||
RxAltFuncSelector: UART_ALT_FN,
|
||||
}
|
||||
Serial = UART1
|
||||
DefaultUART = UART1
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -41,7 +41,7 @@ const (
|
||||
|
||||
// UART
|
||||
var (
|
||||
Serial = UART0
|
||||
DefaultUART = UART0
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -41,7 +41,7 @@ const (
|
||||
|
||||
// UART
|
||||
var (
|
||||
Serial = UART0
|
||||
DefaultUART = UART0
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -41,7 +41,7 @@ const (
|
||||
|
||||
// UART
|
||||
var (
|
||||
Serial = UART0
|
||||
DefaultUART = UART0
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -19,7 +19,7 @@ const (
|
||||
LED_BLUE Pin = 23
|
||||
)
|
||||
|
||||
var Serial = UART0
|
||||
var DefaultUART = UART0
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
|
||||
@@ -23,7 +23,7 @@ const (
|
||||
BUTTON4 Pin = 16
|
||||
)
|
||||
|
||||
var Serial = UART0
|
||||
var DefaultUART = UART0
|
||||
|
||||
// UART pins for NRF52840-DK
|
||||
const (
|
||||
|
||||
@@ -22,7 +22,7 @@ const (
|
||||
BUTTON4 Pin = 25
|
||||
)
|
||||
|
||||
var Serial = UART0
|
||||
var DefaultUART = UART0
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
|
||||
@@ -34,11 +34,6 @@ const (
|
||||
UART_RX_PIN Pin = NoPin
|
||||
)
|
||||
|
||||
// Serial is the USB device
|
||||
var (
|
||||
Serial = USB
|
||||
)
|
||||
|
||||
// I2C pins (unused)
|
||||
const (
|
||||
SDA_PIN = NoPin
|
||||
|
||||
@@ -37,3 +37,20 @@ const (
|
||||
// Onboard crystal oscillator frequency, in MHz.
|
||||
xoscFreq = 12 // MHz
|
||||
)
|
||||
|
||||
// SPI default pins
|
||||
const (
|
||||
// Default Serial Clock Bus 0 for SPI communications
|
||||
SPI0_SCK_PIN = GPIO18
|
||||
// Default Serial Out Bus 0 for SPI communications
|
||||
SPI0_SDO_PIN = GPIO19 // Tx
|
||||
// Default Serial In Bus 0 for SPI communications
|
||||
SPI0_SDI_PIN = GPIO16 // Rx
|
||||
|
||||
// Default Serial Clock Bus 1 for SPI communications
|
||||
SPI1_SCK_PIN = GPIO10
|
||||
// Default Serial Out Bus 1 for SPI communications
|
||||
SPI1_SDO_PIN = GPIO11 // Tx
|
||||
// Default Serial In Bus 1 for SPI communications
|
||||
SPI1_SDI_PIN = GPIO12 // Rx
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ const (
|
||||
LED3 = LCD_BACKLIGHT_LOW
|
||||
)
|
||||
|
||||
var Serial = UART0
|
||||
var DefaultUART = UART0
|
||||
|
||||
// UART pins for PineTime. Note that RX is set to NoPin as RXD is not listed in
|
||||
// the PineTime schematic 1.0:
|
||||
|
||||
@@ -67,8 +67,6 @@ const (
|
||||
BUTTON_B_MASK = 128
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
// USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
|
||||
@@ -70,8 +70,6 @@ const (
|
||||
BUTTON_B_MASK = 128
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
// USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
|
||||
@@ -95,8 +95,6 @@ const (
|
||||
LED = D13
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
// USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PA24
|
||||
|
||||
@@ -29,7 +29,7 @@ const (
|
||||
BUTTON Pin = 7
|
||||
)
|
||||
|
||||
var Serial = UART0
|
||||
var DefaultUART = UART0
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
|
||||
@@ -38,7 +38,7 @@ var (
|
||||
TxAltFuncSelector: AF7_USART1_2_3,
|
||||
RxAltFuncSelector: AF7_USART1_2_3,
|
||||
}
|
||||
Serial = UART1
|
||||
DefaultUART = UART1
|
||||
)
|
||||
|
||||
// set up RX IRQ handler. Follow similar pattern for other UARTx instances
|
||||
|
||||
@@ -87,6 +87,8 @@ var (
|
||||
TeensyUART5 = UART4
|
||||
)
|
||||
|
||||
var DefaultUART = UART0
|
||||
|
||||
const (
|
||||
defaultUART0RX = D00
|
||||
defaultUART0TX = D01
|
||||
|
||||
@@ -136,9 +136,9 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
Serial = UART1
|
||||
UART1 = &_UART1
|
||||
_UART1 = UART{
|
||||
DefaultUART = UART1
|
||||
UART1 = &_UART1
|
||||
_UART1 = UART{
|
||||
Bus: nxp.LPUART6,
|
||||
Buffer: NewRingBuffer(),
|
||||
txBuffer: NewRingBuffer(),
|
||||
|
||||
@@ -325,8 +325,6 @@ const (
|
||||
OUTPUT_CTR_3V3 = PC15
|
||||
)
|
||||
|
||||
var Serial = USB
|
||||
|
||||
// USBCDC pins
|
||||
const (
|
||||
USBCDC_DM_PIN = PIN_USB_DM
|
||||
|
||||
@@ -27,4 +27,4 @@ const (
|
||||
|
||||
const HasLowFrequencyCrystal = true
|
||||
|
||||
var Serial = UART0
|
||||
var DefaultUART = UART0
|
||||
|
||||
@@ -122,7 +122,7 @@ func (i2c *I2C) readByte() byte {
|
||||
}
|
||||
|
||||
// Always use UART0 as the serial output.
|
||||
var Serial = UART0
|
||||
var DefaultUART = UART0
|
||||
|
||||
// UART
|
||||
var (
|
||||
|
||||
@@ -251,6 +251,8 @@ func (p Pin) mux() *volatile.Register32 {
|
||||
}
|
||||
}
|
||||
|
||||
var DefaultUART = UART0
|
||||
|
||||
var (
|
||||
UART0 = &_UART0
|
||||
_UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
|
||||
|
||||
@@ -139,6 +139,8 @@ func (p Pin) PortMaskClear() (*uint32, uint32) {
|
||||
return &esp.GPIO.GPIO_OUT_W1TC.Reg, 1 << p
|
||||
}
|
||||
|
||||
var DefaultUART = UART0
|
||||
|
||||
// UART0 is a hardware UART that supports both TX and RX.
|
||||
var UART0 = &_UART0
|
||||
var _UART0 = UART{Buffer: NewRingBuffer()}
|
||||
|
||||
@@ -11,6 +11,12 @@ var (
|
||||
USB = &UART{100}
|
||||
)
|
||||
|
||||
// The Serial port always points to the default UART in a simulated environment.
|
||||
//
|
||||
// TODO: perhaps this should be a special serial object that outputs via WASI
|
||||
// stdout calls.
|
||||
var Serial = UART0
|
||||
|
||||
const (
|
||||
PinInput PinMode = iota
|
||||
PinOutput
|
||||
@@ -118,12 +124,6 @@ type UART struct {
|
||||
Bus uint8
|
||||
}
|
||||
|
||||
type UARTConfig struct {
|
||||
BaudRate uint32
|
||||
TX Pin
|
||||
RX Pin
|
||||
}
|
||||
|
||||
// Configure the UART.
|
||||
func (uart *UART) Configure(config UARTConfig) {
|
||||
uartConfigure(uart.Bus, config.TX, config.RX)
|
||||
|
||||
@@ -108,7 +108,7 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
var Serial = UART0
|
||||
var DefaultUART = UART0
|
||||
|
||||
func init() {
|
||||
UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt)
|
||||
|
||||
@@ -68,6 +68,7 @@ const (
|
||||
PinInputPullup
|
||||
PinAnalog
|
||||
PinUART
|
||||
PinSPI
|
||||
)
|
||||
|
||||
// set drives the pin high
|
||||
@@ -155,6 +156,8 @@ func (p Pin) Configure(config PinConfig) {
|
||||
p.pulloff()
|
||||
case PinUART:
|
||||
p.setFunc(fnUART)
|
||||
case PinSPI:
|
||||
p.setFunc(fnSPI)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
// +build rp2040
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/rp"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// SPI on the RP2040
|
||||
var (
|
||||
SPI0 = &_SPI0
|
||||
_SPI0 = SPI{
|
||||
Bus: rp.SPI0,
|
||||
}
|
||||
SPI1 = &_SPI1
|
||||
_SPI1 = SPI{
|
||||
Bus: rp.SPI1,
|
||||
}
|
||||
)
|
||||
|
||||
// SPIConfig is used to store config info for SPI.
|
||||
type SPIConfig struct {
|
||||
Frequency uint32
|
||||
// LSB not supported on rp2040.
|
||||
LSBFirst bool
|
||||
// Mode's two most LSB are CPOL and CPHA. i.e. Mode==2 (0b10) is CPOL=1, CPHA=0
|
||||
Mode uint8
|
||||
// Number of data bits per transfer. Valid values 4..16. Default and recommended is 8.
|
||||
DataBits uint8
|
||||
// Serial clock pin
|
||||
SCK Pin
|
||||
// TX or Serial Data Out (MOSI if rp2040 is master)
|
||||
SDO Pin
|
||||
// RX or Serial Data In (MISO if rp2040 is master)
|
||||
SDI Pin
|
||||
}
|
||||
|
||||
var (
|
||||
ErrLSBNotSupported = errors.New("SPI LSB unsupported on PL022")
|
||||
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
|
||||
ErrSPITimeout = errors.New("SPI timeout")
|
||||
ErrSPIBaud = errors.New("SPI baud too low or above 66.5Mhz")
|
||||
)
|
||||
|
||||
type SPI struct {
|
||||
Bus *rp.SPI0_Type
|
||||
}
|
||||
|
||||
// time to wait on a transaction before dropping. Unit in Microseconds for compatibility with ticks().
|
||||
const _SPITimeout = 10 * 1000 // 10 ms
|
||||
|
||||
// Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read
|
||||
// interface, there must always be the same number of bytes written as bytes read.
|
||||
// The Tx method knows about this, and offers a few different ways of calling it.
|
||||
//
|
||||
// This form sends the bytes in tx buffer, putting the resulting bytes read into the rx buffer.
|
||||
// Note that the tx and rx buffers must be the same size:
|
||||
//
|
||||
// spi.Tx(tx, rx)
|
||||
//
|
||||
// This form sends the tx buffer, ignoring the result. Useful for sending "commands" that return zeros
|
||||
// until all the bytes in the command packet have been received:
|
||||
//
|
||||
// spi.Tx(tx, nil)
|
||||
//
|
||||
// This form sends zeros, putting the result into the rx buffer. Good for reading a "result packet":
|
||||
//
|
||||
// spi.Tx(nil, rx)
|
||||
//
|
||||
// Remark: This implementation (RP2040) allows reading into buffer with a custom repeated
|
||||
// value on tx.
|
||||
//
|
||||
// spi.Tx([]byte{0xff}, rx) // may cause unwanted heap allocations.
|
||||
//
|
||||
// This form sends 0xff and puts the result into rx buffer. Useful for reading from SD cards
|
||||
// which require 0xff input on SI.
|
||||
func (spi SPI) Tx(w, r []byte) (err error) {
|
||||
switch {
|
||||
case w == nil:
|
||||
// read only, so write zero and read a result.
|
||||
err = spi.rx(r, 0)
|
||||
case r == nil:
|
||||
// write only
|
||||
err = spi.tx(w)
|
||||
case len(w) == 1 && len(r) > 1:
|
||||
// Read with custom repeated value.
|
||||
err = spi.rx(r, w[0])
|
||||
default:
|
||||
// write/read
|
||||
err = spi.txrx(w, r)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Write a single byte and read a single byte from TX/RX FIFO.
|
||||
func (spi SPI) Transfer(w byte) (byte, error) {
|
||||
var deadline = ticks() + _SPITimeout
|
||||
for !spi.isWritable() {
|
||||
if ticks() > deadline {
|
||||
return 0, ErrSPITimeout
|
||||
}
|
||||
}
|
||||
|
||||
spi.Bus.SSPDR.Set(uint32(w))
|
||||
|
||||
for !spi.isReadable() {
|
||||
if ticks() > deadline {
|
||||
return 0, ErrSPITimeout
|
||||
}
|
||||
}
|
||||
return uint8(spi.Bus.SSPDR.Get()), nil
|
||||
}
|
||||
|
||||
func (spi SPI) SetBaudRate(br uint32) error {
|
||||
const freqin uint32 = 125 * MHz
|
||||
const maxBaud uint32 = 66.5 * MHz // max output frequency is 66.5MHz on rp2040. see Note page 527.
|
||||
// Find smallest prescale value which puts output frequency in range of
|
||||
// post-divide. Prescale is an even number from 2 to 254 inclusive.
|
||||
var prescale, postdiv uint32
|
||||
for prescale = 2; prescale < 255; prescale += 2 {
|
||||
if freqin < (prescale+2)*256*br {
|
||||
break
|
||||
}
|
||||
}
|
||||
if prescale > 254 || br > maxBaud {
|
||||
return ErrSPIBaud
|
||||
}
|
||||
// Find largest post-divide which makes output <= baudrate. Post-divide is
|
||||
// an integer in the range 1 to 256 inclusive.
|
||||
for postdiv = 256; postdiv > 1; postdiv-- {
|
||||
if freqin/(prescale*(postdiv-1)) > br {
|
||||
break
|
||||
}
|
||||
}
|
||||
spi.Bus.SSPCPSR.Set(prescale)
|
||||
spi.Bus.SSPCR0.ReplaceBits((postdiv-1)<<rp.SPI0_SSPCR0_SCR_Pos, rp.SPI0_SSPCR0_SCR_Msk, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (spi SPI) GetBaudRate() uint32 {
|
||||
const freqin uint32 = 125 * MHz
|
||||
prescale := spi.Bus.SSPCPSR.Get()
|
||||
postdiv := ((spi.Bus.SSPCR0.Get() & rp.SPI0_SSPCR0_SCR_Msk) >> rp.SPI0_SSPCR0_SCR_Pos) + 1
|
||||
return freqin / (prescale * postdiv)
|
||||
}
|
||||
|
||||
// Configure is intended to setup/initialize the SPI interface.
|
||||
// Default baudrate of 115200 is used if Frequency == 0. Default
|
||||
// word length (data bits) is 8.
|
||||
// Below is a list of GPIO pins corresponding to SPI0 bus on the rp2040:
|
||||
// SI : 0, 4, 17 a.k.a RX and MISO (if rp2040 is master)
|
||||
// SO : 3, 7, 19 a.k.a TX and MOSI (if rp2040 is master)
|
||||
// SCK: 2, 6, 18
|
||||
// SPI1 bus GPIO pins:
|
||||
// SI : 8, 12
|
||||
// SO : 11, 15
|
||||
// SCK: 10, 14
|
||||
// No pin configuration is needed of SCK, SDO and SDI needed after calling Configure.
|
||||
func (spi SPI) Configure(config SPIConfig) error {
|
||||
const defaultBaud uint32 = 115200
|
||||
if config.SCK == 0 {
|
||||
// set default pins if config zero valued or invalid clock pin supplied.
|
||||
switch spi.Bus {
|
||||
case rp.SPI0:
|
||||
config.SCK = SPI0_SCK_PIN
|
||||
config.SDO = SPI0_SDO_PIN
|
||||
config.SDI = SPI0_SDI_PIN
|
||||
case rp.SPI1:
|
||||
config.SCK = SPI1_SCK_PIN
|
||||
config.SDO = SPI1_SDO_PIN
|
||||
config.SDI = SPI1_SDI_PIN
|
||||
}
|
||||
}
|
||||
if config.DataBits < 4 || config.DataBits > 16 {
|
||||
config.DataBits = 8
|
||||
}
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = defaultBaud
|
||||
}
|
||||
// SPI pin configuration
|
||||
config.SCK.setFunc(fnSPI)
|
||||
config.SDO.setFunc(fnSPI)
|
||||
config.SDI.setFunc(fnSPI)
|
||||
|
||||
return spi.initSPI(config)
|
||||
}
|
||||
|
||||
func (spi SPI) initSPI(config SPIConfig) (err error) {
|
||||
spi.reset()
|
||||
// LSB-first not supported on PL022:
|
||||
if config.LSBFirst {
|
||||
return ErrLSBNotSupported
|
||||
}
|
||||
err = spi.SetBaudRate(config.Frequency)
|
||||
// Set SPI Format (CPHA and CPOL) and frame format (default is Motorola)
|
||||
spi.setFormat(config.DataBits, config.Mode, rp.XIP_SSI_CTRLR0_SPI_FRF_STD)
|
||||
|
||||
// Always enable DREQ signals -- harmless if DMA is not listening
|
||||
spi.Bus.SSPDMACR.SetBits(rp.SPI0_SSPDMACR_TXDMAE | rp.SPI0_SSPDMACR_RXDMAE)
|
||||
// Finally enable the SPI
|
||||
spi.Bus.SSPCR1.SetBits(rp.SPI0_SSPCR1_SSE)
|
||||
return err
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (spi SPI) setFormat(databits, mode uint8, frameFormat uint32) {
|
||||
cpha := uint32(mode) & 1
|
||||
cpol := uint32(mode>>1) & 1
|
||||
spi.Bus.SSPCR0.ReplaceBits(
|
||||
(cpha<<rp.SPI0_SSPCR0_SPH_Pos)|
|
||||
(cpol<<rp.SPI0_SSPCR0_SPO_Pos)|
|
||||
(uint32(databits-1)<<rp.SPI0_SSPCR0_DSS_Pos)| // Set databits (SPI word length). Valid inputs are 4-16.
|
||||
(frameFormat&0b11)<<rp.SPI0_SSPCR0_FRF_Pos, // Frame format bits 4:5
|
||||
rp.SPI0_SSPCR0_SPH_Msk|rp.SPI0_SSPCR0_SPO_Msk|rp.SPI0_SSPCR0_DSS_Msk|rp.SPI0_SSPCR0_FRF_Msk, 0)
|
||||
}
|
||||
|
||||
// reset resets SPI and waits until reset is done.
|
||||
//go:inline
|
||||
func (spi SPI) reset() {
|
||||
resetVal := spi.deinit()
|
||||
rp.RESETS.RESET.ClearBits(resetVal)
|
||||
// Wait until reset is done.
|
||||
for !rp.RESETS.RESET_DONE.HasBits(resetVal) {
|
||||
}
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (spi SPI) deinit() (resetVal uint32) {
|
||||
switch spi.Bus {
|
||||
case rp.SPI0:
|
||||
resetVal = rp.RESETS_RESET_SPI0
|
||||
case rp.SPI1:
|
||||
resetVal = rp.RESETS_RESET_SPI1
|
||||
}
|
||||
// Perform SPI reset.
|
||||
rp.RESETS.RESET.SetBits(resetVal)
|
||||
return resetVal
|
||||
}
|
||||
|
||||
// isWritable returns false if no space is available to write. True if a write is possible
|
||||
//go:inline
|
||||
func (spi SPI) isWritable() bool {
|
||||
return spi.Bus.SSPSR.HasBits(rp.SPI0_SSPSR_TNF)
|
||||
}
|
||||
|
||||
// isReadable returns true if a read is possible i.e. data is present
|
||||
//go:inline
|
||||
func (spi SPI) isReadable() bool {
|
||||
return spi.Bus.SSPSR.HasBits(rp.SPI0_SSPSR_RNE)
|
||||
}
|
||||
|
||||
// PrintRegs prints SPI's peripheral common registries current values
|
||||
func (spi SPI) PrintRegs() {
|
||||
cr0 := spi.Bus.SSPCR0.Get()
|
||||
cr1 := spi.Bus.SSPCR1.Get()
|
||||
dmacr := spi.Bus.SSPDMACR.Get()
|
||||
cpsr := spi.Bus.SSPCPSR.Get()
|
||||
dr := spi.Bus.SSPDR.Get()
|
||||
ris := spi.Bus.SSPRIS.Get()
|
||||
println("CR0:", cr0)
|
||||
println("CR1:", cr1)
|
||||
println("DMACR:", dmacr)
|
||||
println("CPSR:", cpsr)
|
||||
println("DR:", dr)
|
||||
println("RIS:", ris)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (spi SPI) isBusy() bool {
|
||||
return spi.Bus.SSPSR.HasBits(rp.SPI0_SSPSR_BSY)
|
||||
}
|
||||
|
||||
// tx writes buffer to SPI ignoring Rx.
|
||||
func (spi SPI) tx(tx []byte) error {
|
||||
var deadline = ticks() + _SPITimeout
|
||||
// Write to TX FIFO whilst ignoring RX, then clean up afterward. When RX
|
||||
// is full, PL022 inhibits RX pushes, and sets a sticky flag on
|
||||
// push-on-full, but continues shifting. Safe if SSPIMSC_RORIM is not set.
|
||||
for i := range tx {
|
||||
for !spi.isWritable() {
|
||||
if ticks() > deadline {
|
||||
return ErrSPITimeout
|
||||
}
|
||||
}
|
||||
spi.Bus.SSPDR.Set(uint32(tx[i]))
|
||||
}
|
||||
// Drain RX FIFO, then wait for shifting to finish (which may be *after*
|
||||
// TX FIFO drains), then drain RX FIFO again
|
||||
for spi.isReadable() {
|
||||
spi.Bus.SSPDR.Get()
|
||||
}
|
||||
for spi.isBusy() {
|
||||
if ticks() > deadline {
|
||||
return ErrSPITimeout
|
||||
}
|
||||
}
|
||||
for spi.isReadable() {
|
||||
spi.Bus.SSPDR.Get()
|
||||
}
|
||||
// Don't leave overrun flag set
|
||||
spi.Bus.SSPICR.Set(rp.SPI0_SSPICR_RORIC)
|
||||
return nil
|
||||
}
|
||||
|
||||
// rx reads buffer to SPI ignoring x.
|
||||
// txrepeat is output repeatedly on SO as data is read in from SI.
|
||||
// Generally this can be 0, but some devices require a specific value here,
|
||||
// e.g. SD cards expect 0xff
|
||||
func (spi SPI) rx(rx []byte, txrepeat byte) error {
|
||||
var deadline = ticks() + _SPITimeout
|
||||
plen := len(rx)
|
||||
const fifoDepth = 8 // see txrx
|
||||
var rxleft, txleft = plen, plen
|
||||
for txleft != 0 || rxleft != 0 {
|
||||
if txleft != 0 && spi.isWritable() && rxleft < txleft+fifoDepth {
|
||||
spi.Bus.SSPDR.Set(uint32(txrepeat))
|
||||
txleft--
|
||||
}
|
||||
if rxleft != 0 && spi.isReadable() {
|
||||
rx[plen-rxleft] = uint8(spi.Bus.SSPDR.Get())
|
||||
rxleft--
|
||||
continue // if reading succesfully in rx there is no need to check deadline.
|
||||
}
|
||||
if ticks() > deadline {
|
||||
return ErrSPITimeout
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write len bytes from src to SPI. Simultaneously read len bytes from SPI to dst.
|
||||
// Note this function is guaranteed to exit in a known amount of time (bits sent * time per bit)
|
||||
func (spi SPI) txrx(tx, rx []byte) error {
|
||||
var deadline = ticks() + _SPITimeout
|
||||
plen := len(tx)
|
||||
if plen != len(rx) {
|
||||
return ErrTxInvalidSliceSize
|
||||
}
|
||||
// Never have more transfers in flight than will fit into the RX FIFO,
|
||||
// else FIFO will overflow if this code is heavily interrupted.
|
||||
const fifoDepth = 8
|
||||
var rxleft, txleft = plen, plen
|
||||
for (txleft != 0 || rxleft != 0) && ticks() <= deadline {
|
||||
if txleft != 0 && spi.isWritable() && rxleft < txleft+fifoDepth {
|
||||
spi.Bus.SSPDR.Set(uint32(tx[plen-txleft]))
|
||||
txleft--
|
||||
}
|
||||
if rxleft != 0 && spi.isReadable() {
|
||||
rx[plen-rxleft] = uint8(spi.Bus.SSPDR.Get())
|
||||
rxleft--
|
||||
}
|
||||
}
|
||||
|
||||
if txleft != 0 || rxleft != 0 {
|
||||
// Transaction ended early due to timeout
|
||||
return ErrSPITimeout
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// +build baremetal,serial.none
|
||||
|
||||
package machine
|
||||
|
||||
// Serial is a null device: writes to it are ignored.
|
||||
var Serial = NullSerial{}
|
||||
@@ -0,0 +1,6 @@
|
||||
// +build baremetal,serial.uart
|
||||
|
||||
package machine
|
||||
|
||||
// Serial is implemented via the default (usually the first) UART on the chip.
|
||||
var Serial = DefaultUART
|
||||
@@ -0,0 +1,6 @@
|
||||
// +build baremetal,serial.usb
|
||||
|
||||
package machine
|
||||
|
||||
// Serial is implemented via USB (USB-CDC).
|
||||
var Serial = USB
|
||||
@@ -0,0 +1,46 @@
|
||||
package machine
|
||||
|
||||
import "errors"
|
||||
|
||||
var errNoByte = errors.New("machine: no byte read")
|
||||
|
||||
// UARTConfig is a struct with which a UART (or similar object) can be
|
||||
// configured. The baud rate is usually respected, but TX and RX may be ignored
|
||||
// depending on the chip and the type of object.
|
||||
type UARTConfig struct {
|
||||
BaudRate uint32
|
||||
TX Pin
|
||||
RX Pin
|
||||
}
|
||||
|
||||
// NullSerial is a serial version of /dev/null (or null router): it drops
|
||||
// everything that is written to it.
|
||||
type NullSerial struct {
|
||||
}
|
||||
|
||||
// Configure does nothing: the null serial has no configuration.
|
||||
func (ns NullSerial) Configure(config UARTConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteByte is a no-op: the null serial doesn't write bytes.
|
||||
func (ns NullSerial) WriteByte(b byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadByte always returns an error because there aren't any bytes to read.
|
||||
func (ns NullSerial) ReadByte() (byte, error) {
|
||||
return 0, errNoByte
|
||||
}
|
||||
|
||||
// Buffered returns how many bytes are buffered in the UART. It always returns 0
|
||||
// as there are no bytes to read.
|
||||
func (ns NullSerial) Buffered() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Write is a no-op: none of the data is being written and it will not return an
|
||||
// error.
|
||||
func (ns NullSerial) Write(p []byte) (n int, err error) {
|
||||
return len(p), nil
|
||||
}
|
||||
@@ -23,12 +23,6 @@ const (
|
||||
ParityOdd UARTParity = 2
|
||||
)
|
||||
|
||||
type UARTConfig struct {
|
||||
BaudRate uint32
|
||||
TX Pin
|
||||
RX Pin
|
||||
}
|
||||
|
||||
// To implement the UART interface for a board, you must declare a concrete type as follows:
|
||||
//
|
||||
// type UART struct {
|
||||
|
||||
@@ -7,3 +7,81 @@
|
||||
package net
|
||||
|
||||
const hexDigit = "0123456789abcdef"
|
||||
|
||||
// A HardwareAddr represents a physical hardware address.
|
||||
type HardwareAddr []byte
|
||||
|
||||
func (a HardwareAddr) String() string {
|
||||
if len(a) == 0 {
|
||||
return ""
|
||||
}
|
||||
buf := make([]byte, 0, len(a)*3-1)
|
||||
for i, b := range a {
|
||||
if i > 0 {
|
||||
buf = append(buf, ':')
|
||||
}
|
||||
buf = append(buf, hexDigit[b>>4])
|
||||
buf = append(buf, hexDigit[b&0xF])
|
||||
}
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
// ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, EUI-64, or a 20-octet
|
||||
// IP over InfiniBand link-layer address using one of the following formats:
|
||||
// 00:00:5e:00:53:01
|
||||
// 02:00:5e:10:00:00:00:01
|
||||
// 00:00:00:00:fe:80:00:00:00:00:00:00:02:00:5e:10:00:00:00:01
|
||||
// 00-00-5e-00-53-01
|
||||
// 02-00-5e-10-00-00-00-01
|
||||
// 00-00-00-00-fe-80-00-00-00-00-00-00-02-00-5e-10-00-00-00-01
|
||||
// 0000.5e00.5301
|
||||
// 0200.5e10.0000.0001
|
||||
// 0000.0000.fe80.0000.0000.0000.0200.5e10.0000.0001
|
||||
func ParseMAC(s string) (hw HardwareAddr, err error) {
|
||||
if len(s) < 14 {
|
||||
goto err
|
||||
}
|
||||
|
||||
if s[2] == ':' || s[2] == '-' {
|
||||
if (len(s)+1)%3 != 0 {
|
||||
goto err
|
||||
}
|
||||
n := (len(s) + 1) / 3
|
||||
if n != 6 && n != 8 && n != 20 {
|
||||
goto err
|
||||
}
|
||||
hw = make(HardwareAddr, n)
|
||||
for x, i := 0, 0; i < n; i++ {
|
||||
var ok bool
|
||||
if hw[i], ok = xtoi2(s[x:], s[2]); !ok {
|
||||
goto err
|
||||
}
|
||||
x += 3
|
||||
}
|
||||
} else if s[4] == '.' {
|
||||
if (len(s)+1)%5 != 0 {
|
||||
goto err
|
||||
}
|
||||
n := 2 * (len(s) + 1) / 5
|
||||
if n != 6 && n != 8 && n != 20 {
|
||||
goto err
|
||||
}
|
||||
hw = make(HardwareAddr, n)
|
||||
for x, i := 0, 0; i < n; i += 2 {
|
||||
var ok bool
|
||||
if hw[i], ok = xtoi2(s[x:x+2], 0); !ok {
|
||||
goto err
|
||||
}
|
||||
if hw[i+1], ok = xtoi2(s[x+2:], s[4]); !ok {
|
||||
goto err
|
||||
}
|
||||
x += 5
|
||||
}
|
||||
} else {
|
||||
goto err
|
||||
}
|
||||
return hw, nil
|
||||
|
||||
err:
|
||||
return nil, &AddrError{Err: "invalid MAC address", Addr: s}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,18 @@ func xtoi(s string) (n int, i int, ok bool) {
|
||||
return n, i, true
|
||||
}
|
||||
|
||||
// xtoi2 converts the next two hex digits of s into a byte.
|
||||
// If s is longer than 2 bytes then the third byte must be e.
|
||||
// If the first two bytes of s are not hex digits or the third byte
|
||||
// does not match e, false is returned.
|
||||
func xtoi2(s string, e byte) (byte, bool) {
|
||||
if len(s) > 2 && s[2] != e {
|
||||
return 0, false
|
||||
}
|
||||
n, ei, ok := xtoi(s[:2])
|
||||
return byte(n), ok && ei == 2
|
||||
}
|
||||
|
||||
// Convert unsigned integer to decimal string.
|
||||
func uitoa(val uint) string {
|
||||
if val == 0 { // avoid string allocation
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
package os
|
||||
|
||||
import "syscall"
|
||||
|
||||
type Signal interface {
|
||||
String() string
|
||||
Signal() // to distinguish from other Stringers
|
||||
}
|
||||
|
||||
// Getpid returns the process id of the caller, or -1 if unavailable.
|
||||
func Getpid() int {
|
||||
return syscall.Getpid()
|
||||
}
|
||||
|
||||
// Getppid returns the process id of the caller's parent, or -1 if unavailable.
|
||||
func Getppid() int {
|
||||
return syscall.Getppid()
|
||||
}
|
||||
|
||||
@@ -196,8 +196,3 @@ func Readlink(name string) (string, error) {
|
||||
func TempDir() string {
|
||||
return "/tmp"
|
||||
}
|
||||
|
||||
// Getpid is a stub (for now), always returning 1
|
||||
func Getpid() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -24,3 +24,31 @@ func runtime_args() []string // in package runtime
|
||||
func Exit(code int) {
|
||||
syscall.Exit(code)
|
||||
}
|
||||
|
||||
// Getuid returns the numeric user id of the caller.
|
||||
//
|
||||
// On non-POSIX systems, it returns -1.
|
||||
func Getuid() int {
|
||||
return syscall.Getuid()
|
||||
}
|
||||
|
||||
// Geteuid returns the numeric effective user id of the caller.
|
||||
//
|
||||
// On non-POSIX systems, it returns -1.
|
||||
func Geteuid() int {
|
||||
return syscall.Geteuid()
|
||||
}
|
||||
|
||||
// Getgid returns the numeric group id of the caller.
|
||||
//
|
||||
// On non-POSIX systems, it returns -1.
|
||||
func Getgid() int {
|
||||
return syscall.Getgid()
|
||||
}
|
||||
|
||||
// Getegid returns the numeric effective group id of the caller.
|
||||
//
|
||||
// On non-POSIX systems, it returns -1.
|
||||
func Getegid() int {
|
||||
return syscall.Getegid()
|
||||
}
|
||||
|
||||
@@ -32,9 +32,11 @@ var (
|
||||
|
||||
const wasmPageSize = 64 * 1024
|
||||
|
||||
// Align on word boundary.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 3) &^ 3
|
||||
// Align to 16, which is the alignment of max_align_t:
|
||||
// https://godbolt.org/z/dYqTsWrGq
|
||||
const heapAlign = 16
|
||||
return (ptr + heapAlign - 1) &^ (heapAlign - 1)
|
||||
}
|
||||
|
||||
func getCurrentStackPointer() uintptr
|
||||
@@ -55,3 +57,51 @@ func growHeap() bool {
|
||||
// Heap has grown successfully.
|
||||
return true
|
||||
}
|
||||
|
||||
// The below functions override the default allocator of wasi-libc.
|
||||
// Most functions are defined but unimplemented to make sure that if there is
|
||||
// any code using them, they will get an error instead of (incorrectly) using
|
||||
// the wasi-libc dlmalloc heap implementation instead. If they are needed by any
|
||||
// program, they can certainly be implemented.
|
||||
|
||||
//export malloc
|
||||
func libc_malloc(size uintptr) unsafe.Pointer {
|
||||
return alloc(size)
|
||||
}
|
||||
|
||||
//export free
|
||||
func libc_free(ptr unsafe.Pointer) {
|
||||
free(ptr)
|
||||
}
|
||||
|
||||
//export calloc
|
||||
func libc_calloc(nmemb, size uintptr) unsafe.Pointer {
|
||||
// Note: we could be even more correct here and check that nmemb * size
|
||||
// doesn't overflow. However the current implementation should normally work
|
||||
// fine.
|
||||
return alloc(nmemb * size)
|
||||
}
|
||||
|
||||
//export realloc
|
||||
func libc_realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
|
||||
runtimePanic("unimplemented: realloc")
|
||||
return nil
|
||||
}
|
||||
|
||||
//export posix_memalign
|
||||
func libc_posix_memalign(memptr *unsafe.Pointer, alignment, size uintptr) int {
|
||||
runtimePanic("unimplemented: posix_memalign")
|
||||
return 0
|
||||
}
|
||||
|
||||
//export aligned_alloc
|
||||
func libc_aligned_alloc(alignment, bytes uintptr) unsafe.Pointer {
|
||||
runtimePanic("unimplemented: aligned_alloc")
|
||||
return nil
|
||||
}
|
||||
|
||||
//export malloc_usable_size
|
||||
func libc_malloc_usable_size(ptr unsafe.Pointer) uintptr {
|
||||
runtimePanic("unimplemented: malloc_usable_size")
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -52,3 +52,24 @@ func syscall_Exit(code int) {
|
||||
}
|
||||
|
||||
const baremetal = true
|
||||
|
||||
// timeOffset is how long the monotonic clock started after the Unix epoch. It
|
||||
// should be a positive integer under normal operation or zero when it has not
|
||||
// been set.
|
||||
var timeOffset int64
|
||||
|
||||
//go:linkname now time.now
|
||||
func now() (sec int64, nsec int32, mono int64) {
|
||||
mono = nanotime()
|
||||
sec = (mono + timeOffset) / (1000 * 1000 * 1000)
|
||||
nsec = int32((mono + timeOffset) - sec*(1000*1000*1000))
|
||||
return
|
||||
}
|
||||
|
||||
// AdjustTimeOffset adds the given offset to the built-in time offset. A
|
||||
// positive value adds to the time (skipping some time), a negative value moves
|
||||
// the clock into the past.
|
||||
func AdjustTimeOffset(offset int64) {
|
||||
// TODO: do this atomically?
|
||||
timeOffset += offset
|
||||
}
|
||||
|
||||
@@ -228,6 +228,14 @@ func setHeapEnd(newHeapEnd uintptr) {
|
||||
// This function can be called again when the heap size increases. The caller is
|
||||
// responsible for copying the metadata to the new location.
|
||||
func calculateHeapAddresses() {
|
||||
if GOARCH == "wasm" {
|
||||
// This is a workaround for a bug in wasm-ld: wasm-ld doesn't always
|
||||
// align __heap_base and when this memory is shared through an API, it
|
||||
// might result in unaligned memory. For details, see:
|
||||
// https://reviews.llvm.org/D106499
|
||||
// It should be removed once we switch to LLVM 13, where this is fixed.
|
||||
heapStart = align(heapStart)
|
||||
}
|
||||
totalSize := heapEnd - heapStart
|
||||
|
||||
// Allocate some memory to keep 2 bits of information about every block.
|
||||
|
||||
@@ -11,3 +11,9 @@ const (
|
||||
flag_MAP_PRIVATE = 0x2
|
||||
flag_MAP_ANONYMOUS = 0x1000 // MAP_ANON
|
||||
)
|
||||
|
||||
// Source: https://opensource.apple.com/source/Libc/Libc-1439.100.3/include/time.h.auto.html
|
||||
const (
|
||||
clock_REALTIME = 0
|
||||
clock_MONOTONIC_RAW = 4
|
||||
)
|
||||
|
||||
@@ -11,3 +11,9 @@ const (
|
||||
flag_MAP_PRIVATE = 0x2
|
||||
flag_MAP_ANONYMOUS = 0x20
|
||||
)
|
||||
|
||||
// Source: https://github.com/torvalds/linux/blob/master/include/uapi/linux/time.h
|
||||
const (
|
||||
clock_REALTIME = 0
|
||||
clock_MONOTONIC_RAW = 4
|
||||
)
|
||||
|
||||
@@ -68,27 +68,6 @@ func nanotime() int64 {
|
||||
return ticksToNanoseconds(ticks())
|
||||
}
|
||||
|
||||
// timeOffset is how long the monotonic clock started after the Unix epoch. It
|
||||
// should be a positive integer under normal operation or zero when it has not
|
||||
// been set.
|
||||
var timeOffset int64
|
||||
|
||||
//go:linkname now time.now
|
||||
func now() (sec int64, nsec int32, mono int64) {
|
||||
mono = nanotime()
|
||||
sec = (mono + timeOffset) / (1000 * 1000 * 1000)
|
||||
nsec = int32((mono + timeOffset) - sec*(1000*1000*1000))
|
||||
return
|
||||
}
|
||||
|
||||
// AdjustTimeOffset adds the given offset to the built-in time offset. A
|
||||
// positive value adds to the time (skipping some time), a negative value moves
|
||||
// the clock into the past.
|
||||
func AdjustTimeOffset(offset int64) {
|
||||
// TODO: do this atomically?
|
||||
timeOffset += offset
|
||||
}
|
||||
|
||||
// Copied from the Go runtime source code.
|
||||
//go:linkname os_sigpipe os.sigpipe
|
||||
func os_sigpipe() {
|
||||
|
||||
@@ -50,6 +50,14 @@ func putchar(c byte) {
|
||||
}
|
||||
}
|
||||
|
||||
//go:linkname now time.now
|
||||
func now() (sec int64, nsec int32, mono int64) {
|
||||
mono = nanotime()
|
||||
sec = mono / (1000 * 1000 * 1000)
|
||||
nsec = int32(mono - sec*(1000*1000*1000))
|
||||
return
|
||||
}
|
||||
|
||||
// Abort executes the wasm 'unreachable' instruction.
|
||||
func abort() {
|
||||
trap()
|
||||
|
||||
@@ -38,8 +38,6 @@ type timespec struct {
|
||||
tv_nsec int // long: on Linux and macOS, follows the platform bitness
|
||||
}
|
||||
|
||||
const CLOCK_MONOTONIC_RAW = 4
|
||||
|
||||
var stackTop uintptr
|
||||
|
||||
func postinit() {}
|
||||
@@ -138,19 +136,31 @@ func sleepTicks(d timeUnit) {
|
||||
usleep(uint(d) / 1000)
|
||||
}
|
||||
|
||||
// Return monotonic time in nanoseconds.
|
||||
//
|
||||
// TODO: noescape
|
||||
func monotime() uint64 {
|
||||
func getTime(clock int32) uint64 {
|
||||
ts := timespec{}
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts)
|
||||
clock_gettime(clock, &ts)
|
||||
return uint64(ts.tv_sec)*1000*1000*1000 + uint64(ts.tv_nsec)
|
||||
}
|
||||
|
||||
// Return monotonic time in nanoseconds.
|
||||
func monotime() uint64 {
|
||||
return getTime(clock_MONOTONIC_RAW)
|
||||
}
|
||||
|
||||
func ticks() timeUnit {
|
||||
return timeUnit(monotime())
|
||||
}
|
||||
|
||||
//go:linkname now time.now
|
||||
func now() (sec int64, nsec int32, mono int64) {
|
||||
ts := timespec{}
|
||||
clock_gettime(clock_REALTIME, &ts)
|
||||
sec = int64(ts.tv_sec)
|
||||
nsec = int32(ts.tv_nsec)
|
||||
mono = nanotime()
|
||||
return
|
||||
}
|
||||
|
||||
//go:linkname syscall_Exit syscall.Exit
|
||||
func syscall_Exit(code int) {
|
||||
exit(code)
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// +build baremetal wasi wasm
|
||||
|
||||
// This file emulates some process-related functions that are only available
|
||||
// under a real operating system.
|
||||
|
||||
package syscall
|
||||
|
||||
func Getuid() int { return -1 }
|
||||
func Geteuid() int { return -1 }
|
||||
func Getgid() int { return -1 }
|
||||
func Getegid() int { return -1 }
|
||||
func Getpid() int { return -1 }
|
||||
func Getppid() int { return -1 }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user