From cfbc9be9bfac79aca8f2dfe259a295c6ea5fd101 Mon Sep 17 00:00:00 2001 From: Kenneth Bell Date: Sat, 29 May 2021 10:13:41 -0700 Subject: [PATCH 01/70] rp2040: git ignore generated device files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 20a0a2fb0..483c789c7 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ src/device/stm32/*.go src/device/stm32/*.s src/device/kendryte/*.go src/device/kendryte/*.s +src/device/rp/*.go +src/device/rp/*.s vendor llvm-build llvm-project From 22eeed2da121c9119441c619fe2594b73e8e032b Mon Sep 17 00:00:00 2001 From: sago35 Date: Sun, 30 May 2021 11:43:16 +0900 Subject: [PATCH 02/70] qtpy: add pin for neopixels --- Makefile | 2 +- src/machine/board_qtpy.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index da699cc3c..0d731774a 100644 --- a/Makefile +++ b/Makefile @@ -336,7 +336,7 @@ smoketest: @$(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/blinky1 + $(TINYGO) build -size short -o test.hex -target=qtpy examples/serial @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=teensy40 examples/blinky1 @$(MD5SUM) test.hex diff --git a/src/machine/board_qtpy.go b/src/machine/board_qtpy.go index 23c7becad..b17e8070e 100644 --- a/src/machine/board_qtpy.go +++ b/src/machine/board_qtpy.go @@ -42,7 +42,8 @@ const ( ) const ( - LED = D13 + NEOPIXELS = D11 + NEOPIXELS_POWER = D12 ) // USBCDC pins From 8b79e826863db75ab2f250fdbcc691576d71dbea Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 30 May 2021 17:14:23 +0200 Subject: [PATCH 03/70] nrf: avoid heap allocation in waitForEvent Because arm.SVCall1 lets pointers escape, the return value of sd_softdevice_is_enabled (passed as a pointer in a parameter) will escape and thus this value will be heap allocated. Use a global variable for this purpose instead to avoid the heap allocation. This is safe as waitForEvent may only be called outside of interrupts. --- src/runtime/runtime_nrf_softdevice.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/runtime/runtime_nrf_softdevice.go b/src/runtime/runtime_nrf_softdevice.go index 36269e11e..b1c970333 100644 --- a/src/runtime/runtime_nrf_softdevice.go +++ b/src/runtime/runtime_nrf_softdevice.go @@ -10,6 +10,9 @@ import ( //export sd_app_evt_wait func sd_app_evt_wait() +// This is a global variable to avoid a heap allocation in waitForEvents. +var softdeviceEnabled uint8 + func waitForEvents() { // Call into the SoftDevice to sleep. This is necessary here because a // normal wfe will not put the chip in low power mode (it still consumes @@ -18,10 +21,9 @@ func waitForEvents() { // First check whether the SoftDevice is enabled. Unfortunately, // sd_app_evt_wait cannot be called when the SoftDevice is not enabled. - var enabled uint8 - arm.SVCall1(0x12, &enabled) // sd_softdevice_is_enabled + arm.SVCall1(0x12, &softdeviceEnabled) // sd_softdevice_is_enabled - if enabled != 0 { + if softdeviceEnabled != 0 { // Now pick the appropriate SVCall number. Hopefully they won't change // in the future with a different SoftDevice version. if nrf.Device == "nrf51" { From e8c4c4a865ca024951116a1bd6995cba7c4d2e0b Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 30 May 2021 18:11:51 +0200 Subject: [PATCH 04/70] nrf: don't trigger a heap allocation in SPI.Transfer By using a 1-byte buffer, two heap allocations each `SPI.Transfer` call can be avoided. --- src/machine/machine_nrf528xx.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/machine/machine_nrf528xx.go b/src/machine/machine_nrf528xx.go index 6c22463e2..2cf2e10bd 100644 --- a/src/machine/machine_nrf528xx.go +++ b/src/machine/machine_nrf528xx.go @@ -115,13 +115,14 @@ func (a ADC) Get() uint16 { // SPI on the NRF. type SPI struct { Bus *nrf.SPIM_Type + buf *[1]byte // 1-byte buffer for the Transfer method } // There are 3 SPI interfaces on the NRF528xx. var ( - SPI0 = SPI{Bus: nrf.SPIM0} - SPI1 = SPI{Bus: nrf.SPIM1} - SPI2 = SPI{Bus: nrf.SPIM2} + SPI0 = SPI{Bus: nrf.SPIM0, buf: new([1]byte)} + SPI1 = SPI{Bus: nrf.SPIM1, buf: new([1]byte)} + SPI2 = SPI{Bus: nrf.SPIM2, buf: new([1]byte)} ) // SPIConfig is used to store config info for SPI. @@ -207,10 +208,10 @@ func (spi SPI) Configure(config SPIConfig) { // Transfer writes/reads a single byte using the SPI interface. func (spi SPI) Transfer(w byte) (byte, error) { - var wbuf, rbuf [1]byte - wbuf[0] = w - err := spi.Tx(wbuf[:], rbuf[:]) - return rbuf[0], err + buf := spi.buf[:] + buf[0] = w + err := spi.Tx(buf[:], buf[:]) + return buf[0], err } // Tx handles read/write operation for SPI interface. Since SPI is a syncronous From 36dffb55545b9e64ca53b408a9343a7a56bbbbc1 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Sat, 29 May 2021 10:41:19 +0200 Subject: [PATCH 05/70] machine/rp2040: add support for GPIO input --- src/machine/machine_rp2040_gpio.go | 32 +++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/machine/machine_rp2040_gpio.go b/src/machine/machine_rp2040_gpio.go index 1d35254c9..b82e0435c 100644 --- a/src/machine/machine_rp2040_gpio.go +++ b/src/machine/machine_rp2040_gpio.go @@ -63,6 +63,9 @@ const ( const ( PinOutput PinMode = iota + PinInput + PinInputPulldown + PinInputPullup ) // set drives the pin high @@ -83,6 +86,11 @@ func (p Pin) xor() { rp.SIO.GPIO_OUT_XOR.Set(mask) } +// get returns the pin value +func (p Pin) get() bool { + return rp.SIO.GPIO_IN.HasBits(uint32(1) << p) +} + func (p Pin) ioCtrl() *volatile.Register32 { return &ioBank0.io[p].ctrl } @@ -91,6 +99,16 @@ func (p Pin) padCtrl() *volatile.Register32 { return &padsBank0.io[p] } +func (p Pin) pullup() { + p.padCtrl().SetBits(rp.PADS_BANK0_GPIO0_PUE) + p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PDE) +} + +func (p Pin) pulldown() { + p.padCtrl().SetBits(rp.PADS_BANK0_GPIO0_PDE) + p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE) +} + // setFunc will set pin function to fn. func (p Pin) setFunc(fn pinFunc) { // Set input enable, Clear output disable @@ -108,7 +126,6 @@ func (p Pin) init() { rp.SIO.GPIO_OE_CLR.Set(mask) p.clr() p.setFunc(fnSIO) - } // Configure configures the gpio pin as per mode. @@ -118,6 +135,14 @@ func (p Pin) Configure(config PinConfig) { switch config.Mode { case PinOutput: rp.SIO.GPIO_OE_SET.Set(mask) + case PinInput: + rp.SIO.GPIO_OE_CLR.Set(mask) + case PinInputPulldown: + rp.SIO.GPIO_OE_CLR.Set(mask) + p.pulldown() + case PinInputPullup: + rp.SIO.GPIO_OE_CLR.Set(mask) + p.pullup() } } @@ -129,3 +154,8 @@ func (p Pin) Set(value bool) { p.clr() } } + +// Get reads the pin value. +func (p Pin) Get() bool { + return p.get() +} From c5ea1fde612e39d591a150e7ee130b99056060da Mon Sep 17 00:00:00 2001 From: sago35 Date: Tue, 1 Jun 2021 08:41:02 +0900 Subject: [PATCH 06/70] increase stack size for access to sdcard --- targets/grandcentral-m4.json | 3 ++- targets/p1am-100.json | 3 ++- targets/pygamer.json | 3 ++- targets/pyportal.json | 3 ++- targets/wioterminal.json | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/targets/grandcentral-m4.json b/targets/grandcentral-m4.json index 4545dfd9f..6229d4565 100644 --- a/targets/grandcentral-m4.json +++ b/targets/grandcentral-m4.json @@ -5,5 +5,6 @@ "flash-method": "msd", "msd-volume-name": "GCM4BOOT", "msd-firmware-name": "firmware.uf2", - "openocd-interface": "jlink" + "openocd-interface": "jlink", + "default-stack-size": 2048 } diff --git a/targets/p1am-100.json b/targets/p1am-100.json index fcfaff1ac..be9b0e446 100644 --- a/targets/p1am-100.json +++ b/targets/p1am-100.json @@ -2,5 +2,6 @@ "inherits": ["atsamd21g18a"], "build-tags": ["sam", "atsamd21g18a", "p1am_100"], "flash-command": "bossac -d -i -e -w -v -R --port={port} --offset=0x2000 {bin}", - "flash-1200-bps-reset": "true" + "flash-1200-bps-reset": "true", + "default-stack-size": 2048 } diff --git a/targets/pygamer.json b/targets/pygamer.json index 63dc34a18..71244afe7 100644 --- a/targets/pygamer.json +++ b/targets/pygamer.json @@ -4,5 +4,6 @@ "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "PYGAMERBOOT", - "msd-firmware-name": "arcade.uf2" + "msd-firmware-name": "arcade.uf2", + "default-stack-size": 2048 } diff --git a/targets/pyportal.json b/targets/pyportal.json index e03efcb6d..14b95ef8e 100644 --- a/targets/pyportal.json +++ b/targets/pyportal.json @@ -4,5 +4,6 @@ "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "PORTALBOOT", - "msd-firmware-name": "firmware.uf2" + "msd-firmware-name": "firmware.uf2", + "default-stack-size": 2048 } diff --git a/targets/wioterminal.json b/targets/wioterminal.json index 093947e16..8595b7c93 100644 --- a/targets/wioterminal.json +++ b/targets/wioterminal.json @@ -4,5 +4,6 @@ "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "Arduino", - "msd-firmware-name": "firmware.uf2" + "msd-firmware-name": "firmware.uf2", + "default-stack-size": 2048 } From 2f248bbf8b3150cd0ef96859d234b2257fdabfa7 Mon Sep 17 00:00:00 2001 From: Kenneth Bell Date: Sun, 16 May 2021 11:05:23 -0700 Subject: [PATCH 07/70] scheduler: task.Data made 64bit to avoid overflow --- compiler/testdata/goroutine-cortex-m-qemu.ll | 2 +- compiler/testdata/goroutine-wasm.ll | 2 +- src/internal/task/task.go | 2 +- src/runtime/scheduler.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/testdata/goroutine-cortex-m-qemu.ll b/compiler/testdata/goroutine-cortex-m-qemu.ll index a00d49be6..a98592c15 100644 --- a/compiler/testdata/goroutine-cortex-m-qemu.ll +++ b/compiler/testdata/goroutine-cortex-m-qemu.ll @@ -5,7 +5,7 @@ target triple = "armv7m-none-eabi" %runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* } %runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } } -%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i32, %"internal/task.state" } +%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" } %"internal/task.state" = type { i32, i32* } %runtime.chanSelectState = type { %runtime.channel*, i8* } diff --git a/compiler/testdata/goroutine-wasm.ll b/compiler/testdata/goroutine-wasm.ll index 212bd714e..1d840e6da 100644 --- a/compiler/testdata/goroutine-wasm.ll +++ b/compiler/testdata/goroutine-wasm.ll @@ -6,7 +6,7 @@ target triple = "wasm32--wasi" %runtime.funcValueWithSignature = type { i32, i8* } %runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* } %runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } } -%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i32, %"internal/task.state" } +%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" } %"internal/task.state" = type { i8* } %runtime.chanSelectState = type { %runtime.channel*, i8* } diff --git a/src/internal/task/task.go b/src/internal/task/task.go index 489400dfc..bad501b61 100644 --- a/src/internal/task/task.go +++ b/src/internal/task/task.go @@ -13,7 +13,7 @@ type Task struct { Ptr unsafe.Pointer // Data is a field which can be used for storing state information. - Data uint + Data uint64 // state is the underlying running state of the task. state state diff --git a/src/runtime/scheduler.go b/src/runtime/scheduler.go index c5286d33b..44b07f75d 100644 --- a/src/runtime/scheduler.go +++ b/src/runtime/scheduler.go @@ -88,7 +88,7 @@ func addSleepTask(t *task.Task, duration timeUnit) { panic("runtime: addSleepTask: expected next task to be nil") } } - t.Data = uint(duration) // TODO: longer durations + t.Data = uint64(duration) now := ticks() if sleepQueue == nil { scheduleLog(" -> sleep new queue") From 5b82125765bf3613d1e7ce98e39d538288f437f9 Mon Sep 17 00:00:00 2001 From: "Federico G. Schwindt" Date: Fri, 28 May 2021 16:58:00 +0100 Subject: [PATCH 08/70] Add sync.NewCond Required by net/http. --- src/sync/cond.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sync/cond.go b/src/sync/cond.go index e392bc6e2..e65e86ed1 100644 --- a/src/sync/cond.go +++ b/src/sync/cond.go @@ -16,6 +16,10 @@ type earlySignal struct { signaled bool } +func NewCond(l Locker) *Cond { + return &Cond{L: l} +} + func (c *Cond) trySignal() bool { // Pop a blocked task off of the stack, and schedule it if applicable. t := c.blocked.Pop() From 62f9f6166465761cb4f3d6b794f72fdcf8bd58c4 Mon Sep 17 00:00:00 2001 From: "Federico G. Schwindt" Date: Fri, 28 May 2021 17:11:24 +0100 Subject: [PATCH 09/70] Add runtime stubs required for net/http Continued from #1911. --- src/runtime/extern.go | 5 +++++ src/runtime/symtab.go | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/runtime/extern.go create mode 100644 src/runtime/symtab.go diff --git a/src/runtime/extern.go b/src/runtime/extern.go new file mode 100644 index 000000000..12de02840 --- /dev/null +++ b/src/runtime/extern.go @@ -0,0 +1,5 @@ +package runtime + +func Callers(skip int, pc []uintptr) int { + return 0 +} diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go new file mode 100644 index 000000000..0a3ca3da8 --- /dev/null +++ b/src/runtime/symtab.go @@ -0,0 +1,20 @@ +package runtime + +type Frames struct { + // +} + +type Frame struct { + Function string + + File string + Line int +} + +func CallersFrames(callers []uintptr) *Frames { + return nil +} + +func (ci *Frames) Next() (frame Frame, more bool) { + return Frame{}, false +} From 78d030aa7a7dfc19ca40860fea1bcbd7e93b8e1e Mon Sep 17 00:00:00 2001 From: "Federico G. Schwindt" Date: Fri, 28 May 2021 23:40:55 +0100 Subject: [PATCH 10/70] Add os stubs required for net/http --- src/os/file.go | 5 +++++ src/os/tempfile.go | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 src/os/tempfile.go diff --git a/src/os/file.go b/src/os/file.go index 99977fa4e..a7aa40430 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -116,6 +116,11 @@ func (f *File) Readdirnames(n int) (names []string, err error) { return nil, &PathError{"readdirnames", f.name, ErrNotImplemented} } +// Seek is a stub, not yet implemented +func (f *File) Seek(offset int64, whence int) (ret int64, err error) { + return 0, &PathError{"seek", f.name, ErrNotImplemented} +} + // Stat is a stub, not yet implemented func (f *File) Stat() (FileInfo, error) { return nil, &PathError{"stat", f.name, ErrNotImplemented} diff --git a/src/os/tempfile.go b/src/os/tempfile.go new file mode 100644 index 000000000..27949f7fa --- /dev/null +++ b/src/os/tempfile.go @@ -0,0 +1,5 @@ +package os + +func CreateTemp(dir, pattern string) (*File, error) { + return nil, &PathError{"createtemp", pattern, ErrNotImplemented} +} From 793a3175d3f91423d48d750a9318dabe0ed778da Mon Sep 17 00:00:00 2001 From: "Federico G. Schwindt" Date: Wed, 2 Jun 2021 12:28:38 +0100 Subject: [PATCH 11/70] reflect: add stubs required for net/http --- src/reflect/makefunc.go | 5 +++++ src/reflect/value.go | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 src/reflect/makefunc.go diff --git a/src/reflect/makefunc.go b/src/reflect/makefunc.go new file mode 100644 index 000000000..2234f2591 --- /dev/null +++ b/src/reflect/makefunc.go @@ -0,0 +1,5 @@ +package reflect + +func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { + panic("unimplemented: reflect.MakeFunc()") +} diff --git a/src/reflect/value.go b/src/reflect/value.go index 11a54279e..88f8137af 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -840,3 +840,7 @@ func (v Value) FieldByName(name string) Value { func MakeMap(typ Type) Value { panic("unimplemented: reflect.MakeMap()") } + +func (v Value) Call(in []Value) []Value { + panic("unimplemented: (reflect.Value).Call()") +} From 4c95febeee0babe066eae5e9ccb0410061635b1e Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 2 Jun 2021 12:44:02 +0200 Subject: [PATCH 12/70] main: don't consider compile-only tests as failing Previously a command like the following would incorrectly print FAIL: tinygo test -c math This commit fixes this issue by defaulting to a passing test (the test is marked as passed if it isn't run). --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index c56c0752f..e6fc92c12 100644 --- a/main.go +++ b/main.go @@ -145,7 +145,7 @@ func Test(pkgName string, options *compileopts.Options, testCompileOnly bool, ou return false, err } - var passed bool + passed := true err = builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error { if testCompileOnly || outpath != "" { // Write test binary to the specified file name. From 98f117fca4581836c3e9bf29c8c726b75096502a Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 2 Jun 2021 13:13:34 +0200 Subject: [PATCH 13/70] main: add -test flag for `tinygo list` This matches `go list` and is convenient for debugging. --- main.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index e6fc92c12..1f34add39 100644 --- a/main.go +++ b/main.go @@ -947,10 +947,11 @@ func main() { wasmAbi := flag.String("wasm-abi", "", "WebAssembly ABI conventions: js (no i64 params) or generic") llvmFeatures := flag.String("llvm-features", "", "comma separated LLVM features to enable") - var flagJSON, flagDeps *bool + var flagJSON, flagDeps, flagTest *bool if command == "help" || command == "list" { flagJSON = flag.Bool("json", false, "print data in JSON format") - flagDeps = flag.Bool("deps", false, "") + flagDeps = flag.Bool("deps", false, "supply -deps flag to go list") + flagTest = flag.Bool("test", false, "supply -test flag to go list") } var outpath string if command == "help" || command == "build" || command == "build-library" || command == "test" { @@ -1200,6 +1201,9 @@ func main() { if *flagDeps { extraArgs = append(extraArgs, "-deps") } + if *flagTest { + extraArgs = append(extraArgs, "-test") + } cmd, err := loader.List(config, extraArgs, flag.Args()) if err != nil { fmt.Fprintln(os.Stderr, "failed to run `go list`:", err) From af65c006e6875a08362b4e826e300b14702c7fb3 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 2 Jun 2021 13:14:13 +0200 Subject: [PATCH 14/70] loader: fix testing a main package This was broken because multiple packages in the program were named 'main', even one that was imported (by the generated main package). This fixes tests for main packages. --- loader/loader.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/loader/loader.go b/loader/loader.go index 27d5ea76e..eed57e6d6 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -343,12 +343,11 @@ func (p *Package) Check() error { checker.Importer = p packageName := p.ImportPath - if p.Name == "main" { - // The main package normally has a different import path, such as - // "command-line-arguments" or "./testdata/cgo". Therefore, use the name - // "main" in such a case: this package isn't imported from anywhere. - // This is safe as it isn't possible to import a package with the name - // "main". + if p == p.program.MainPkg() { + if p.Name != "main" { + // Sanity check. Should not ever trigger. + panic("expected main package to have name 'main'") + } packageName = "main" } typesPkg, err := checker.Check(packageName, p.program.fset, p.Files, &p.info) From 4e610a0ee72eba476b5b4102df62385ef7b73e39 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 2 Jun 2021 14:39:43 +0200 Subject: [PATCH 15/70] main: escape commands while printing them with the -x flag This should make issues such as the one in https://github.com/tinygo-org/tinygo/issues/1910 more obvious. --- builder/build.go | 4 ++-- builder/cc.go | 6 +++--- compileopts/options.go | 2 +- main.go | 27 ++++++++++++++++++++++++--- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/builder/build.go b/builder/build.go index 9d26df816..bdb5a1e70 100644 --- a/builder/build.go +++ b/builder/build.go @@ -547,8 +547,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil } ldflags = append(ldflags, dependency.result) } - if config.Options.PrintCommands { - fmt.Printf("%s %s\n", config.Target.Linker, strings.Join(ldflags, " ")) + if config.Options.PrintCommands != nil { + config.Options.PrintCommands(config.Target.Linker, ldflags...) } err = link(config.Target.Linker, ldflags...) if err != nil { diff --git a/builder/cc.go b/builder/cc.go index a450cc6e8..7f0dad28a 100644 --- a/builder/cc.go +++ b/builder/cc.go @@ -56,7 +56,7 @@ import ( // depfile but without invalidating its name. For this reason, the depfile is // written on each new compilation (even when it seems unnecessary). However, it // could in rare cases lead to a stale file fetched from the cache. -func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands bool) (string, error) { +func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands func(string, ...string)) (string, error) { // Hash input file. fileHash, err := hashFile(abspath) if err != nil { @@ -128,8 +128,8 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands // flags (for the assembler) is a compiler error. flags = append(flags, "-Qunused-arguments") } - if printCommands { - fmt.Printf("clang %s\n", strings.Join(flags, " ")) + if printCommands != nil { + printCommands("clang", flags...) } err = runCCompiler(flags...) if err != nil { diff --git a/compileopts/options.go b/compileopts/options.go index ee5621003..10c143b80 100644 --- a/compileopts/options.go +++ b/compileopts/options.go @@ -25,7 +25,7 @@ type Options struct { PrintIR bool DumpSSA bool VerifyIR bool - PrintCommands bool + PrintCommands func(cmd string, args ...string) Debug bool PrintSizes string PrintAllocs *regexp.Regexp // regexp string diff --git a/main.go b/main.go index 1f34add39..88d7cfac5 100644 --- a/main.go +++ b/main.go @@ -95,12 +95,31 @@ func copyFile(src, dst string) error { // executeCommand is a simple wrapper to exec.Cmd func executeCommand(options *compileopts.Options, name string, arg ...string) *exec.Cmd { - if options.PrintCommands { - fmt.Printf("%s %s\n", name, strings.Join(arg, " ")) + if options.PrintCommands != nil { + options.PrintCommands(name, arg...) } return exec.Command(name, arg...) } +// printCommand prints a command to stdout while formatting it like a real +// command (escaping characters etc). The resulting command should be easy to +// run directly in a shell, although it is not guaranteed to be a safe shell +// escape. That's not a problem as the primary use case is printing the command, +// not running it. +func printCommand(cmd string, args ...string) { + command := append([]string{cmd}, args...) + for i, arg := range command { + // Source: https://www.oreilly.com/library/view/learning-the-bash/1565923472/ch01s09.html + const specialChars = "~`#$&*()\\|[]{};'\"<>?! " + if strings.ContainsAny(arg, specialChars) { + // See: https://stackoverflow.com/questions/15783701/which-characters-need-to-be-escaped-when-using-bash + arg = "'" + strings.ReplaceAll(arg, `'`, `'\''`) + "'" + command[i] = arg + } + } + fmt.Fprintln(os.Stderr, strings.Join(command, " ")) +} + // Build compiles and links the given package and writes it to outpath. func Build(pkgName, outpath string, options *compileopts.Options) error { config, err := builder.NewConfig(options) @@ -1008,7 +1027,6 @@ func main() { PrintSizes: *printSize, PrintStacks: *printStacks, PrintAllocs: printAllocs, - PrintCommands: *printCommands, Tags: *tags, GlobalValues: globalVarValues, WasmAbi: *wasmAbi, @@ -1016,6 +1034,9 @@ func main() { OpenOCDCommands: ocdCommands, LLVMFeatures: *llvmFeatures, } + if *printCommands { + options.PrintCommands = printCommand + } os.Setenv("CC", "clang -target="+*target) From 42ec3e2469b87f4bf4f402b744c056c02b266a1f Mon Sep 17 00:00:00 2001 From: deadprogram Date: Wed, 2 Jun 2021 18:55:33 +0200 Subject: [PATCH 16/70] docker: use github actions to build/publish tinygo-dev dockerfile Signed-off-by: deadprogram --- .github/workflows/build-tinygo-dev-docker.yml | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/build-tinygo-dev-docker.yml diff --git a/.github/workflows/build-tinygo-dev-docker.yml b/.github/workflows/build-tinygo-dev-docker.yml new file mode 100644 index 000000000..ba8ba220e --- /dev/null +++ b/.github/workflows/build-tinygo-dev-docker.yml @@ -0,0 +1,45 @@ +name: CI for tinygo-dev docker container +on: + push: + branches: [ dev ] + +jobs: + push_to_registry: + name: Push Docker image to GHCR/Docker Hub + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - name: Check out the repo + uses: actions/checkout@v2 + with: + submodules: recursive + - name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + images: | + tinygo/tinygo-dev + ghcr.io/${{ github.repository }}/tinygo-dev + tags: | + type=sha,format=long + type=raw,value=latest + - name: Log in to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + - name: Log in to Github Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} From 9912dd6db1496eefd806d9cceb9527b6478b7485 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Sat, 5 Jun 2021 13:15:43 +0200 Subject: [PATCH 17/70] machine/rp2040: add basic support for ADC Signed-off-by: deadprogram --- src/machine/board_pico.go | 6 +++ src/machine/machine_rp2040_adc.go | 61 ++++++++++++++++++++++++++++++ src/machine/machine_rp2040_gpio.go | 17 +++++++-- 3 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 src/machine/machine_rp2040_adc.go diff --git a/src/machine/board_pico.go b/src/machine/board_pico.go index b1be4b913..6ab32c394 100644 --- a/src/machine/board_pico.go +++ b/src/machine/board_pico.go @@ -38,6 +38,12 @@ const ( // Onboard LED LED Pin = GP25 + // Analog pins + ADC0 = GP26 + ADC1 = GP27 + ADC2 = GP28 + ADC3 = GP29 + // Onboard crystal oscillator frequency, in MHz. xoscFreq = 12 // MHz ) diff --git a/src/machine/machine_rp2040_adc.go b/src/machine/machine_rp2040_adc.go new file mode 100644 index 000000000..2101abbaf --- /dev/null +++ b/src/machine/machine_rp2040_adc.go @@ -0,0 +1,61 @@ +// +build rp2040 + +package machine + +import ( + "device/rp" +) + +func InitADC() { + // reset ADC + rp.RESETS.RESET.SetBits(rp.RESETS_RESET_ADC) + rp.RESETS.RESET.ClearBits(rp.RESETS_RESET_ADC) + for !rp.RESETS.RESET_DONE.HasBits(rp.RESETS_RESET_ADC) { + } + + // enable ADC + rp.ADC.CS.Set(rp.ADC_CS_EN) + + waitForReady() +} + +// Configure configures a ADC pin to be able to be used to read data. +func (a ADC) Configure(config ADCConfig) { + switch a.Pin { + case GP26, GP27, GP28, GP29: + a.Pin.Configure(PinConfig{Mode: PinAnalog}) + default: + // invalid ADC + return + } +} + +func (a ADC) Get() uint16 { + rp.ADC.CS.SetBits(uint32(a.getADCChannel()) << rp.ADC_CS_AINSEL_Pos) + rp.ADC.CS.SetBits(rp.ADC_CS_START_ONCE) + + waitForReady() + + // rp2040 uses 12-bit sampling, so scale to 16-bit + return uint16(rp.ADC.RESULT.Get() << 4) +} + +func waitForReady() { + for !rp.ADC.CS.HasBits(rp.ADC_CS_READY) { + } +} + +func (a ADC) getADCChannel() uint8 { + switch a.Pin { + case GP26: + return 0 + case GP27: + return 1 + case GP28: + return 2 + case GP29: + return 3 + default: + return 0 + } +} diff --git a/src/machine/machine_rp2040_gpio.go b/src/machine/machine_rp2040_gpio.go index b82e0435c..62c6c3350 100644 --- a/src/machine/machine_rp2040_gpio.go +++ b/src/machine/machine_rp2040_gpio.go @@ -66,6 +66,7 @@ const ( PinInput PinInputPulldown PinInputPullup + PinAnalog ) // set drives the pin high @@ -109,6 +110,11 @@ func (p Pin) pulldown() { p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE) } +func (p Pin) pulloff() { + p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PDE) + p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE) +} + // setFunc will set pin function to fn. func (p Pin) setFunc(fn pinFunc) { // Set input enable, Clear output disable @@ -125,7 +131,6 @@ func (p Pin) init() { mask := uint32(1) << p rp.SIO.GPIO_OE_CLR.Set(mask) p.clr() - p.setFunc(fnSIO) } // Configure configures the gpio pin as per mode. @@ -134,15 +139,19 @@ func (p Pin) Configure(config PinConfig) { mask := uint32(1) << p switch config.Mode { case PinOutput: + p.setFunc(fnSIO) rp.SIO.GPIO_OE_SET.Set(mask) case PinInput: - rp.SIO.GPIO_OE_CLR.Set(mask) + p.setFunc(fnSIO) case PinInputPulldown: - rp.SIO.GPIO_OE_CLR.Set(mask) + p.setFunc(fnSIO) p.pulldown() case PinInputPullup: - rp.SIO.GPIO_OE_CLR.Set(mask) + p.setFunc(fnSIO) p.pullup() + case PinAnalog: + p.setFunc(fnNULL) + p.pulloff() } } From 1f5e4e79aa6b691a54fbfac76b8d1f83650aff4d Mon Sep 17 00:00:00 2001 From: Olaf Flebbe Date: Thu, 3 Jun 2021 14:27:33 +0200 Subject: [PATCH 18/70] support flashing pca10059 from windows --- builder/build.go | 12 ++++++++++++ builder/nrfutil.go | 27 +++++++++++++++++++++++++++ compileopts/config.go | 5 +++++ main.go | 2 ++ targets/pca10059.json | 3 ++- 5 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 builder/nrfutil.go diff --git a/builder/build.go b/builder/build.go index bdb5a1e70..5ef7b3e51 100644 --- a/builder/build.go +++ b/builder/build.go @@ -639,6 +639,18 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil if err != nil { return err } + case "nrf-dfu": + // special format for nrfutil for Nordic chips + tmphexpath := filepath.Join(dir, "main.hex") + err := objcopy(executable, tmphexpath, "hex") + if err != nil { + return err + } + tmppath = filepath.Join(dir, "main"+outext) + err = makeDFUFirmwareImage(config.Options, tmphexpath, tmppath) + if err != nil { + return err + } default: return fmt.Errorf("unknown output binary format: %s", outputBinaryFormat) } diff --git a/builder/nrfutil.go b/builder/nrfutil.go new file mode 100644 index 000000000..55aef45e3 --- /dev/null +++ b/builder/nrfutil.go @@ -0,0 +1,27 @@ +package builder + +import ( + "fmt" + "io/ioutil" + "os/exec" + + "github.com/tinygo-org/tinygo/compileopts" +) + +// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fug_nrfutil%2FUG%2Fnrfutil%2Fnrfutil_intro.html + +func makeDFUFirmwareImage(options *compileopts.Options, infile, outfile string) error { + cmdLine := []string{"nrfutil", "pkg", "generate", "--hw-version", "52", "--sd-req", "0x0", "--debug-mode", "--application", infile, outfile} + + if options.PrintCommands != nil { + options.PrintCommands(cmdLine[0], cmdLine[1:]...) + } + + cmd := exec.Command(cmdLine[0], cmdLine[1:]...) + cmd.Stdout = ioutil.Discard + err := cmd.Run() + if err != nil { + return fmt.Errorf("could not run nrfutil pkg generate: %w", err) + } + return nil +} diff --git a/compileopts/config.go b/compileopts/config.go index d5f392664..d29616f0c 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -254,6 +254,11 @@ func (c *Config) BinaryFormat(ext string) string { // More information: // https://github.com/Microsoft/uf2 return "uf2" + case ".zip": + if c.Target.BinaryFormat != "" { + return c.Target.BinaryFormat + } + return "zip" default: // Use the ELF format for unrecognized file formats. return "elf" diff --git a/main.go b/main.go index 88d7cfac5..c85feb697 100644 --- a/main.go +++ b/main.go @@ -275,6 +275,8 @@ func Flash(pkgName, port string, options *compileopts.Options) error { fileExt = ".bin" case strings.Contains(config.Target.FlashCommand, "{uf2}"): fileExt = ".uf2" + case strings.Contains(config.Target.FlashCommand, "{zip}"): + fileExt = ".zip" default: return errors.New("invalid target file - did you forget the {hex} token in the 'flash-command' section?") } diff --git a/targets/pca10059.json b/targets/pca10059.json index efab1b6e3..5a8eb8265 100644 --- a/targets/pca10059.json +++ b/targets/pca10059.json @@ -2,5 +2,6 @@ "inherits": ["nrf52840"], "build-tags": ["pca10059"], "linkerscript": "targets/pca10059.ld", - "flash-command": "nrfutil pkg generate --hw-version 52 --sd-req 0x0 --application {hex} --application-version 1 /tmp/tinygo_$$.zip && nrfutil dfu usb-serial -pkg /tmp/tinygo_$$.zip -p {port} -b 115200 && rm -f /tmp/tinygo_$$.zip" + "binary-format": "nrf-dfu", + "flash-command": "nrfutil dfu usb-serial -pkg {zip} -p {port} -b 115200" } From 95af44489634d769d95af838b85a5fddc3e0829e Mon Sep 17 00:00:00 2001 From: Yurii Soldak Date: Wed, 9 Jun 2021 02:59:57 +0200 Subject: [PATCH 19/70] machine/rp2040: gpio and adc pin definitions --- src/machine/board_pico.go | 64 +++++++++++++------------------ src/machine/machine_rp2040.go | 40 +++++++++++++++++++ src/machine/machine_rp2040_adc.go | 10 ++--- 3 files changed, 72 insertions(+), 42 deletions(-) diff --git a/src/machine/board_pico.go b/src/machine/board_pico.go index 6ab32c394..3a4d67b45 100644 --- a/src/machine/board_pico.go +++ b/src/machine/board_pico.go @@ -4,45 +4,35 @@ package machine // GPIO pins const ( - GP0 Pin = 0 - GP1 Pin = 1 - GP2 Pin = 2 - GP3 Pin = 3 - GP4 Pin = 4 - GP5 Pin = 5 - GP6 Pin = 6 - GP7 Pin = 7 - GP8 Pin = 8 - GP9 Pin = 9 - GP10 Pin = 10 - GP11 Pin = 11 - GP12 Pin = 12 - GP13 Pin = 13 - GP14 Pin = 14 - GP15 Pin = 15 - GP16 Pin = 16 - GP17 Pin = 17 - GP18 Pin = 18 - GP19 Pin = 19 - GP20 Pin = 20 - GP21 Pin = 21 - GP22 Pin = 22 - GP23 Pin = 23 - GP24 Pin = 24 - GP25 Pin = 25 - GP26 Pin = 26 - GP27 Pin = 27 - GP28 Pin = 28 - GP29 Pin = 29 + GP0 Pin = GPIO0 + GP1 Pin = GPIO1 + GP2 Pin = GPIO2 + GP3 Pin = GPIO3 + GP4 Pin = GPIO4 + GP5 Pin = GPIO5 + GP6 Pin = GPIO6 + GP7 Pin = GPIO7 + GP8 Pin = GPIO8 + GP9 Pin = GPIO9 + GP10 Pin = GPIO10 + GP11 Pin = GPIO11 + GP12 Pin = GPIO12 + GP13 Pin = GPIO13 + GP14 Pin = GPIO14 + GP15 Pin = GPIO15 + GP16 Pin = GPIO16 + GP17 Pin = GPIO17 + GP18 Pin = GPIO18 + GP19 Pin = GPIO19 + GP20 Pin = GPIO20 + GP21 Pin = GPIO21 + GP22 Pin = GPIO22 + GP26 Pin = GPIO26 + GP27 Pin = GPIO27 + GP28 Pin = GPIO28 // Onboard LED - LED Pin = GP25 - - // Analog pins - ADC0 = GP26 - ADC1 = GP27 - ADC2 = GP28 - ADC3 = GP29 + LED Pin = GPIO25 // Onboard crystal oscillator frequency, in MHz. xoscFreq = 12 // MHz diff --git a/src/machine/machine_rp2040.go b/src/machine/machine_rp2040.go index ecab6688d..7f0a71c43 100644 --- a/src/machine/machine_rp2040.go +++ b/src/machine/machine_rp2040.go @@ -7,6 +7,46 @@ import ( _ "unsafe" ) +const ( + // GPIO pins + GPIO0 Pin = 0 + GPIO1 Pin = 1 + GPIO2 Pin = 2 + GPIO3 Pin = 3 + GPIO4 Pin = 4 + GPIO5 Pin = 5 + GPIO6 Pin = 6 + GPIO7 Pin = 7 + GPIO8 Pin = 8 + GPIO9 Pin = 9 + GPIO10 Pin = 10 + GPIO11 Pin = 11 + GPIO12 Pin = 12 + GPIO13 Pin = 13 + GPIO14 Pin = 14 + GPIO15 Pin = 15 + GPIO16 Pin = 16 + GPIO17 Pin = 17 + GPIO18 Pin = 18 + GPIO19 Pin = 19 + GPIO20 Pin = 20 + GPIO21 Pin = 21 + GPIO22 Pin = 22 + GPIO23 Pin = 23 + GPIO24 Pin = 24 + GPIO25 Pin = 25 + GPIO26 Pin = 26 + GPIO27 Pin = 27 + GPIO28 Pin = 28 + GPIO29 Pin = 29 + + // Analog pins + ADC0 Pin = GPIO26 + ADC1 Pin = GPIO27 + ADC2 Pin = GPIO28 + ADC3 Pin = GPIO29 +) + //go:linkname machineInit runtime.machineInit func machineInit() { // Reset all peripherals to put system into a known state, diff --git a/src/machine/machine_rp2040_adc.go b/src/machine/machine_rp2040_adc.go index 2101abbaf..5598c6b73 100644 --- a/src/machine/machine_rp2040_adc.go +++ b/src/machine/machine_rp2040_adc.go @@ -22,7 +22,7 @@ func InitADC() { // Configure configures a ADC pin to be able to be used to read data. func (a ADC) Configure(config ADCConfig) { switch a.Pin { - case GP26, GP27, GP28, GP29: + case ADC0, ADC1, ADC2, ADC3: a.Pin.Configure(PinConfig{Mode: PinAnalog}) default: // invalid ADC @@ -47,13 +47,13 @@ func waitForReady() { func (a ADC) getADCChannel() uint8 { switch a.Pin { - case GP26: + case ADC0: return 0 - case GP27: + case ADC1: return 1 - case GP28: + case ADC2: return 2 - case GP29: + case ADC3: return 3 default: return 0 From 15d77119c93ec634cd0631f07ce86c7dfc892ec0 Mon Sep 17 00:00:00 2001 From: Yurii Soldak Date: Tue, 8 Jun 2021 01:19:02 +0200 Subject: [PATCH 20/70] board/nano-rp2040: pins and blinking led --- src/machine/board_nano-rp2040.go | 99 ++++++++++++++++++++++++++++++++ targets/nano-rp2040.json | 10 ++++ 2 files changed, 109 insertions(+) create mode 100644 src/machine/board_nano-rp2040.go create mode 100644 targets/nano-rp2040.json diff --git a/src/machine/board_nano-rp2040.go b/src/machine/board_nano-rp2040.go new file mode 100644 index 000000000..2863f672a --- /dev/null +++ b/src/machine/board_nano-rp2040.go @@ -0,0 +1,99 @@ +// +build nano_rp2040 + +// This contains the pin mappings for the Arduino Nano RP2040 Connect board. +// +// Sometimes the board is not detected even when the board is connected to your computer. +// To solve this, place a jumper wire between the REC and GND pins, then connect the board to your computer. +// +// For more information, see: https://store.arduino.cc/nano-rp2040-connect +// Also +// - Datasheets: https://docs.arduino.cc/hardware/nano-rp2040-connect +// - Nano RP2040 Connect technical reference: https://docs.arduino.cc/tutorials/nano-rp2040-connect/rp2040-01-technical-reference +// +package machine + +// Digital Pins +const ( + D2 Pin = GPIO25 + D3 Pin = GPIO15 + D4 Pin = GPIO16 + D5 Pin = GPIO17 + D6 Pin = GPIO18 + D7 Pin = GPIO19 + D8 Pin = GPIO20 + D9 Pin = GPIO21 + D10 Pin = GPIO5 + D11 Pin = GPIO7 + D12 Pin = GPIO4 + D13 Pin = GPIO6 + D14 Pin = GPIO26 + D15 Pin = GPIO27 + D16 Pin = GPIO28 + D17 Pin = GPIO29 + D18 Pin = GPIO12 + D19 Pin = GPIO13 +) + +// Analog pins +const ( + A0 Pin = ADC0 + A1 Pin = ADC1 + A2 Pin = ADC2 + A3 Pin = ADC3 +) + +// Onboard LED +const ( + LED = GPIO6 +) + +// UART1 pins +const ( + UART_TX_PIN Pin = GPIO0 + UART_RX_PIN Pin = GPIO1 +) + +// I2C pins +const ( + SDA_PIN Pin = GPIO12 + SCL_PIN Pin = GPIO13 +) + +// SPI pins +const ( + SPI0_SCK_PIN Pin = GPIO6 + SPI0_SDO_PIN Pin = GPIO7 + SPI0_SDI_PIN Pin = GPIO4 +) + +// NINA-W102 Pins +const ( + NINA_SCK Pin = GPIO14 + NINA_SDO Pin = GPIO11 + NINA_SDI Pin = GPIO8 + + NINA_CS Pin = GPIO9 + NINA_ACK Pin = GPIO10 + NINA_GPIO0 Pin = GPIO0 + NINA_RESETN Pin = GPIO3 + + NINA_TX Pin = GPIO9 + NINA_RX Pin = GPIO8 +) + +// Onboard crystal oscillator frequency, in MHz. +const ( + xoscFreq = 12 // MHz +) + +// USB CDC identifiers +// https://github.com/arduino/ArduinoCore-mbed/blob/master/variants/NANO_RP2040_CONNECT/pins_arduino.h +const ( + usb_STRING_PRODUCT = "Nano RP2040 Connect" + usb_STRING_MANUFACTURER = "Arduino" +) + +var ( + usb_VID uint16 = 0x2341 + usb_PID uint16 = 0x005e +) diff --git a/targets/nano-rp2040.json b/targets/nano-rp2040.json new file mode 100644 index 000000000..fb74a2e74 --- /dev/null +++ b/targets/nano-rp2040.json @@ -0,0 +1,10 @@ +{ + "inherits": [ + "rp2040" + ], + "build-tags": ["nano_rp2040"], + "linkerscript": "targets/pico.ld", + "extra-files": [ + "targets/pico_boot_stage2.S" + ] +} From b092856238bcb968d2fb9bc7074e87328cec2b8e Mon Sep 17 00:00:00 2001 From: "Federico G. Schwindt" Date: Tue, 1 Jun 2021 14:48:15 +0100 Subject: [PATCH 21/70] Add more net compatibility Required for net/http. --- src/net/dial.go | 24 +++++++ src/net/errors.go | 10 +++ src/net/iprawsock.go | 13 ++++ src/net/net.go | 160 +++++++++++++++++++++++++++++++++++++++++++ src/net/tcpsock.go | 11 +++ 5 files changed, 218 insertions(+) create mode 100644 src/net/dial.go create mode 100644 src/net/errors.go create mode 100644 src/net/iprawsock.go create mode 100644 src/net/tcpsock.go diff --git a/src/net/dial.go b/src/net/dial.go new file mode 100644 index 000000000..763096d9e --- /dev/null +++ b/src/net/dial.go @@ -0,0 +1,24 @@ +package net + +import ( + "context" + "time" +) + +type Dialer struct { + Timeout time.Duration + Deadline time.Time + KeepAlive time.Duration +} + +func Dial(network, address string) (Conn, error) { + return nil, ErrNotImplemented +} + +func Listen(network, address string) (Listener, error) { + return nil, ErrNotImplemented +} + +func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error) { + return nil, ErrNotImplemented +} diff --git a/src/net/errors.go b/src/net/errors.go new file mode 100644 index 000000000..c1dc7b31c --- /dev/null +++ b/src/net/errors.go @@ -0,0 +1,10 @@ +package net + +import "errors" + +var ( + // copied from poll.ErrNetClosing + errClosed = errors.New("use of closed network connection") + + ErrNotImplemented = errors.New("operation not implemented") +) diff --git a/src/net/iprawsock.go b/src/net/iprawsock.go new file mode 100644 index 000000000..8fac37916 --- /dev/null +++ b/src/net/iprawsock.go @@ -0,0 +1,13 @@ +// The following is copied from Go 1.16 official implementation. + +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +// IPAddr represents the address of an IP end point. +type IPAddr struct { + IP IP + Zone string // IPv6 scoped addressing zone +} diff --git a/src/net/net.go b/src/net/net.go index 53372e677..d1bea59e0 100644 --- a/src/net/net.go +++ b/src/net/net.go @@ -6,6 +6,159 @@ package net +import "time" + +// Addr represents a network end point address. +// +// The two methods Network and String conventionally return strings +// that can be passed as the arguments to Dial, but the exact form +// and meaning of the strings is up to the implementation. +type Addr interface { + Network() string // name of the network (for example, "tcp", "udp") + String() string // string form of address (for example, "192.0.2.1:25", "[2001:db8::1]:80") +} + +// Conn is a generic stream-oriented network connection. +// +// Multiple goroutines may invoke methods on a Conn simultaneously. +type Conn interface { + // Read reads data from the connection. + // Read can be made to time out and return an error after a fixed + // time limit; see SetDeadline and SetReadDeadline. + Read(b []byte) (n int, err error) + + // Write writes data to the connection. + // Write can be made to time out and return an error after a fixed + // time limit; see SetDeadline and SetWriteDeadline. + Write(b []byte) (n int, err error) + + // Close closes the connection. + // Any blocked Read or Write operations will be unblocked and return errors. + Close() error + + // LocalAddr returns the local network address. + LocalAddr() Addr + + // RemoteAddr returns the remote network address. + RemoteAddr() Addr + + // SetDeadline sets the read and write deadlines associated + // with the connection. It is equivalent to calling both + // SetReadDeadline and SetWriteDeadline. + // + // A deadline is an absolute time after which I/O operations + // fail instead of blocking. The deadline applies to all future + // and pending I/O, not just the immediately following call to + // Read or Write. After a deadline has been exceeded, the + // connection can be refreshed by setting a deadline in the future. + // + // If the deadline is exceeded a call to Read or Write or to other + // I/O methods will return an error that wraps os.ErrDeadlineExceeded. + // This can be tested using errors.Is(err, os.ErrDeadlineExceeded). + // The error's Timeout method will return true, but note that there + // are other possible errors for which the Timeout method will + // return true even if the deadline has not been exceeded. + // + // An idle timeout can be implemented by repeatedly extending + // the deadline after successful Read or Write calls. + // + // A zero value for t means I/O operations will not time out. + SetDeadline(t time.Time) error + + // SetReadDeadline sets the deadline for future Read calls + // and any currently-blocked Read call. + // A zero value for t means Read will not time out. + SetReadDeadline(t time.Time) error + + // SetWriteDeadline sets the deadline for future Write calls + // and any currently-blocked Write call. + // Even if write times out, it may return n > 0, indicating that + // some of the data was successfully written. + // A zero value for t means Write will not time out. + SetWriteDeadline(t time.Time) error +} + +type conn struct { + // +} + +// A Listener is a generic network listener for stream-oriented protocols. +// +// Multiple goroutines may invoke methods on a Listener simultaneously. +type Listener interface { + // Accept waits for and returns the next connection to the listener. + Accept() (Conn, error) + + // Close closes the listener. + // Any blocked Accept operations will be unblocked and return errors. + Close() error + + // Addr returns the listener's network address. + Addr() Addr +} + +// An Error represents a network error. +type Error interface { + error + Timeout() bool // Is the error a timeout? + Temporary() bool // Is the error temporary? +} + +// OpError is the error type usually returned by functions in the net +// package. It describes the operation, network type, and address of +// an error. +type OpError struct { + // Op is the operation which caused the error, such as + // "read" or "write". + Op string + + // Net is the network type on which this error occurred, + // such as "tcp" or "udp6". + Net string + + // For operations involving a remote network connection, like + // Dial, Read, or Write, Source is the corresponding local + // network address. + Source Addr + + // Addr is the network address for which this error occurred. + // For local operations, like Listen or SetDeadline, Addr is + // the address of the local endpoint being manipulated. + // For operations involving a remote network connection, like + // Dial, Read, or Write, Addr is the remote address of that + // connection. + Addr Addr + + // Err is the error that occurred during the operation. + // The Error method panics if the error is nil. + Err error +} + +func (e *OpError) Unwrap() error { return e.Err } + +func (e *OpError) Error() string { + if e == nil { + return "" + } + s := e.Op + if e.Net != "" { + s += " " + e.Net + } + if e.Source != nil { + s += " " + e.Source.String() + } + if e.Addr != nil { + if e.Source != nil { + s += "->" + } else { + s += " " + } + s += e.Addr.String() + } + s += ": " + e.Err.Error() + return s +} + // A ParseError is the error type of literal network address parsers. type ParseError struct { // Type is the type of string that was expected, such as @@ -36,3 +189,10 @@ func (e *AddrError) Error() string { func (e *AddrError) Timeout() bool { return false } func (e *AddrError) Temporary() bool { return false } + +// ErrClosed is the error returned by an I/O call on a network +// connection that has already been closed, or that is closed by +// another goroutine before the I/O is completed. This may be wrapped +// in another error, and should normally be tested using +// errors.Is(err, net.ErrClosed). +var ErrClosed = errClosed diff --git a/src/net/tcpsock.go b/src/net/tcpsock.go new file mode 100644 index 000000000..4af06a857 --- /dev/null +++ b/src/net/tcpsock.go @@ -0,0 +1,11 @@ +package net + +// TCPConn is an implementation of the Conn interface for TCP network +// connections. +type TCPConn struct { + conn +} + +func (c *TCPConn) CloseWrite() error { + return &OpError{"close", "", nil, nil, ErrNotImplemented} +} From d62a9e24e579dccc6ea849cf9044cec837e923f1 Mon Sep 17 00:00:00 2001 From: Yurii Soldak Date: Sat, 5 Jun 2021 13:33:26 +0200 Subject: [PATCH 22/70] runtime: expose memory stats --- Makefile | 2 + src/examples/memstats/memstats.go | 32 ++++++++++++++ src/runtime/gc_conservative.go | 2 +- src/runtime/mstats.go | 69 +++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/examples/memstats/memstats.go create mode 100644 src/runtime/mstats.go diff --git a/Makefile b/Makefile index 0d731774a..09977ccd0 100644 --- a/Makefile +++ b/Makefile @@ -230,6 +230,8 @@ smoketest: @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008 @$(MD5SUM) test.hex + $(TINYGO) build -size short -o test.hex -target=pca10040 examples/memstats + @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt diff --git a/src/examples/memstats/memstats.go b/src/examples/memstats/memstats.go new file mode 100644 index 000000000..f0224ac0f --- /dev/null +++ b/src/examples/memstats/memstats.go @@ -0,0 +1,32 @@ +package main + +import ( + "math/rand" + "runtime" + "time" +) + +func main() { + + ms := runtime.MemStats{} + + for { + escapesToHeap() + runtime.ReadMemStats(&ms) + println("Heap before GC. Used: ", ms.HeapInuse, " Free: ", ms.HeapIdle, " Meta: ", ms.GCSys) + runtime.GC() + runtime.ReadMemStats(&ms) + println("Heap after GC. Used: ", ms.HeapInuse, " Free: ", ms.HeapIdle, " Meta: ", ms.GCSys) + time.Sleep(5 * time.Second) + } + +} + +func escapesToHeap() { + n := rand.Intn(100) + println("Doing ", n, " iterations") + for i := 0; i < n; i++ { + s := make([]byte, i) + _ = append(s, 42) + } +} diff --git a/src/runtime/gc_conservative.go b/src/runtime/gc_conservative.go index 7638fb0d6..e96825922 100644 --- a/src/runtime/gc_conservative.go +++ b/src/runtime/gc_conservative.go @@ -51,7 +51,7 @@ const ( ) var ( - metadataStart unsafe.Pointer // pointer to the start of the heap + metadataStart unsafe.Pointer // pointer to the start of the heap metadata nextAlloc gcBlock // the next block that should be tried by the allocator endBlock gcBlock // the block just past the end of the available space ) diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go new file mode 100644 index 000000000..d2723fe93 --- /dev/null +++ b/src/runtime/mstats.go @@ -0,0 +1,69 @@ +// +build gc.conservative + +package runtime + +// Memory statistics + +// Subset of memory statistics from upstream Go. +// Works with conservative gc only. + +// A MemStats records statistics about the memory allocator. +type MemStats struct { + // General statistics. + + // Sys is the total bytes of memory obtained from the OS. + // + // Sys is the sum of the XSys fields below. Sys measures the + // address space reserved by the runtime for the + // heap, stacks, and other internal data structures. + Sys uint64 + + // Heap memory statistics. + + // HeapSys is bytes of heap memory, total. + // + // In TinyGo unlike upstream Go, we make no distinction between + // regular heap blocks used by escaped-to-the-heap variables and + // blocks occupied by goroutine stacks, + // all such blocks are marked as in-use, see HeapInuse below. + HeapSys uint64 + + // HeapIdle is bytes in idle (unused) blocks. + HeapIdle uint64 + + // HeapInuse is bytes in in-use blocks. + HeapInuse uint64 + + // HeapReleased is bytes of physical memory returned to the OS. + HeapReleased uint64 + + // Off-heap memory statistics. + // + // The following statistics measure runtime-internal + // structures that are not allocated from heap memory (usually + // because they are part of implementing the heap). + + // GCSys is bytes of memory in garbage collection metadata. + GCSys uint64 +} + +// ReadMemStats populates m with memory statistics. +// +// The returned memory statistics are up to date as of the +// call to ReadMemStats. This would not do GC implicitly for you. +func ReadMemStats(m *MemStats) { + m.HeapIdle = 0 + m.HeapInuse = 0 + for block := gcBlock(0); block < endBlock; block++ { + bstate := block.state() + if bstate == blockStateFree { + m.HeapIdle += uint64(bytesPerBlock) + } else { + m.HeapInuse += uint64(bytesPerBlock) + } + } + m.HeapReleased = 0 // always 0, we don't currently release memory back to the OS. + m.HeapSys = m.HeapInuse + m.HeapIdle + m.GCSys = uint64(heapEnd - uintptr(metadataStart)) + m.Sys = uint64(heapEnd - heapStart) +} From c017ed224247c1627dcc81dad2d69fb266671678 Mon Sep 17 00:00:00 2001 From: Kenneth Bell Date: Mon, 31 May 2021 16:46:53 -0700 Subject: [PATCH 23/70] bluepill: GPIO PinInputPullup / PinInputPulldown Other chips support explicit control of pull-up vs pull-down for GPIO input. Support that with bluepill also. PinInputPullUpDown is maintained for back-compat. It is implicit pull-down. --- src/machine/machine_stm32f103.go | 56 ++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/machine/machine_stm32f103.go b/src/machine/machine_stm32f103.go index 614697674..86ed14113 100644 --- a/src/machine/machine_stm32f103.go +++ b/src/machine/machine_stm32f103.go @@ -37,6 +37,11 @@ const ( PinOutputModeGPOpenDrain PinMode = 4 // Output mode general purpose open drain PinOutputModeAltPushPull PinMode = 8 // Output mode alt. purpose push/pull PinOutputModeAltOpenDrain PinMode = 12 // Output mode alt. purpose open drain + + // Pull-up vs Pull down is not part of the CNF0 / CNF1 bits, but is + // controlled by PxODR. Encoded using the 'spare' bit 5. + PinInputPulldown PinMode = PinInputModePullUpDown + PinInputPullup PinMode = PinInputModePullUpDown | 0x10 ) // Pin constants for all stm32f103 package sizes @@ -157,6 +162,16 @@ func (p Pin) Configure(config PinConfig) { } else { port.CRH.ReplaceBits(uint32(config.Mode), 0xf, pos) } + + // If configured for input pull-up or pull-down, set ODR + // for desired pull-up or pull-down. + if (config.Mode & 0xf) == PinInputModePullUpDown { + var pullup uint32 + if config.Mode == PinInputPullup { + pullup = 1 + } + port.ODR.ReplaceBits(pullup, 0x1, pin) + } } func (p Pin) getPort() *stm32.GPIO_Type { @@ -215,6 +230,47 @@ func enableAltFuncClock(bus unsafe.Pointer) { } } +func (p Pin) registerInterrupt() interrupt.Interrupt { + pin := uint8(p) % 16 + + switch pin { + case 0: + return interrupt.New(stm32.IRQ_EXTI0, func(interrupt.Interrupt) { handlePinInterrupt(0) }) + case 1: + return interrupt.New(stm32.IRQ_EXTI1, func(interrupt.Interrupt) { handlePinInterrupt(1) }) + case 2: + return interrupt.New(stm32.IRQ_EXTI2, func(interrupt.Interrupt) { handlePinInterrupt(2) }) + case 3: + return interrupt.New(stm32.IRQ_EXTI3, func(interrupt.Interrupt) { handlePinInterrupt(3) }) + case 4: + return interrupt.New(stm32.IRQ_EXTI4, func(interrupt.Interrupt) { handlePinInterrupt(4) }) + case 5: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(5) }) + case 6: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(6) }) + case 7: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(7) }) + case 8: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(8) }) + case 9: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(9) }) + case 10: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(10) }) + case 11: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(11) }) + case 12: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(12) }) + case 13: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(13) }) + case 14: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(14) }) + case 15: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(15) }) + } + + return interrupt.Interrupt{} +} + //---------- UART related code // Configure the TX and RX pins From 5f9e339cf3cff0d3bdf600cc64fc2245a41a8db1 Mon Sep 17 00:00:00 2001 From: Kenneth Bell Date: Mon, 31 May 2021 17:45:49 -0700 Subject: [PATCH 24/70] stm32: support pin input interrupts --- src/examples/pininterrupt/stm32.go | 10 +++ src/machine/board_bluepill.go | 6 ++ src/machine/board_nucleol031k6.go | 6 ++ src/machine/board_nucleol432kc.go | 6 ++ src/machine/board_stm32f4disco.go | 4 ++ src/machine/machine_stm32.go | 7 ++ src/machine/machine_stm32_exti_afio.go | 27 +++++++ src/machine/machine_stm32_exti_exti.go | 26 +++++++ src/machine/machine_stm32_exti_syscfg.go | 27 +++++++ src/machine/machine_stm32_gpio_reva.go | 92 ++++++++++++++++++++++++ src/machine/machine_stm32_gpio_revb.go | 79 ++++++++++++++++++++ src/machine/machine_stm32f4.go | 41 +++++++++++ src/machine/machine_stm32f7.go | 41 +++++++++++ src/machine/machine_stm32l0.go | 42 +++++++++++ src/machine/machine_stm32l4.go | 54 ++++++++++++++ src/machine/machine_stm32l5.go | 59 +++++++++++++++ 16 files changed, 527 insertions(+) create mode 100644 src/examples/pininterrupt/stm32.go create mode 100644 src/machine/machine_stm32_exti_afio.go create mode 100644 src/machine/machine_stm32_exti_exti.go create mode 100644 src/machine/machine_stm32_exti_syscfg.go create mode 100644 src/machine/machine_stm32_gpio_reva.go create mode 100644 src/machine/machine_stm32_gpio_revb.go diff --git a/src/examples/pininterrupt/stm32.go b/src/examples/pininterrupt/stm32.go new file mode 100644 index 000000000..30a86313b --- /dev/null +++ b/src/examples/pininterrupt/stm32.go @@ -0,0 +1,10 @@ +// +build stm32 + +package main + +import "machine" + +const ( + buttonMode = machine.PinInputPulldown + buttonPinChange = machine.PinRising | machine.PinFalling +) diff --git a/src/machine/board_bluepill.go b/src/machine/board_bluepill.go index 6d0e4dd91..ce925025f 100644 --- a/src/machine/board_bluepill.go +++ b/src/machine/board_bluepill.go @@ -11,6 +11,12 @@ const ( LED = PC13 ) +const ( + // This board does not have a user button, so + // use first GPIO pin by default + BUTTON = PA0 +) + var Serial = UART1 // UART pins diff --git a/src/machine/board_nucleol031k6.go b/src/machine/board_nucleol031k6.go index 695dd8b0e..a758ba932 100644 --- a/src/machine/board_nucleol031k6.go +++ b/src/machine/board_nucleol031k6.go @@ -13,6 +13,12 @@ const ( LED_GREEN = PB3 ) +const ( + // This board does not have a user button, so + // use first GPIO pin by default + BUTTON = PA0 +) + const ( // Arduino Pins A0 = PA0 // ADC_IN0 diff --git a/src/machine/board_nucleol432kc.go b/src/machine/board_nucleol432kc.go index 8f32c46a1..d44e0f4fd 100644 --- a/src/machine/board_nucleol432kc.go +++ b/src/machine/board_nucleol432kc.go @@ -13,6 +13,12 @@ const ( LED_GREEN = PB3 ) +const ( + // This board does not have a user button, so + // use first GPIO pin by default + BUTTON = PA0 +) + const ( // Arduino Pins A0 = PA0 diff --git a/src/machine/board_stm32f4disco.go b/src/machine/board_stm32f4disco.go index 7a0616079..49f5650fd 100644 --- a/src/machine/board_stm32f4disco.go +++ b/src/machine/board_stm32f4disco.go @@ -20,6 +20,10 @@ const ( LED_BLUE = PD15 ) +const ( + BUTTON = PA0 +) + // UART pins const ( UART_TX_PIN = PA2 diff --git a/src/machine/machine_stm32.go b/src/machine/machine_stm32.go index 363036ca2..0ba9ef8d6 100644 --- a/src/machine/machine_stm32.go +++ b/src/machine/machine_stm32.go @@ -31,6 +31,13 @@ const ( // Also, the stm32f1xx series handles things differently from the stm32f0/2/3/4 // ---------- General pin operations ---------- +type PinChange uint8 + +const ( + PinRising PinChange = 1 << iota + PinFalling + PinToggle = PinRising | PinFalling +) // Set the pin to high or low. // Warning: only use this on an output pin! diff --git a/src/machine/machine_stm32_exti_afio.go b/src/machine/machine_stm32_exti_afio.go new file mode 100644 index 000000000..aee4936ac --- /dev/null +++ b/src/machine/machine_stm32_exti_afio.go @@ -0,0 +1,27 @@ +// +build stm32f1 + +package machine + +import ( + "device/stm32" + "runtime/volatile" +) + +func getEXTIConfigRegister(pin uint8) *volatile.Register32 { + switch (pin & 0xf) / 4 { + case 0: + return &stm32.AFIO.EXTICR1 + case 1: + return &stm32.AFIO.EXTICR2 + case 2: + return &stm32.AFIO.EXTICR3 + case 3: + return &stm32.AFIO.EXTICR4 + } + return nil +} + +func enableEXTIConfigRegisters() { + // Enable AFIO + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_AFIOEN) +} diff --git a/src/machine/machine_stm32_exti_exti.go b/src/machine/machine_stm32_exti_exti.go new file mode 100644 index 000000000..73db5e1e4 --- /dev/null +++ b/src/machine/machine_stm32_exti_exti.go @@ -0,0 +1,26 @@ +// +build stm32l5 + +package machine + +import ( + "device/stm32" + "runtime/volatile" +) + +func getEXTIConfigRegister(pin uint8) *volatile.Register32 { + switch (pin & 0xf) / 4 { + case 0: + return &stm32.EXTI.EXTICR1 + case 1: + return &stm32.EXTI.EXTICR2 + case 2: + return &stm32.EXTI.EXTICR3 + case 3: + return &stm32.EXTI.EXTICR4 + } + return nil +} + +func enableEXTIConfigRegisters() { + // No-op +} diff --git a/src/machine/machine_stm32_exti_syscfg.go b/src/machine/machine_stm32_exti_syscfg.go new file mode 100644 index 000000000..34d111fbe --- /dev/null +++ b/src/machine/machine_stm32_exti_syscfg.go @@ -0,0 +1,27 @@ +// +build stm32,!stm32f1,!stm32l5 + +package machine + +import ( + "device/stm32" + "runtime/volatile" +) + +func getEXTIConfigRegister(pin uint8) *volatile.Register32 { + switch (pin & 0xf) / 4 { + case 0: + return &stm32.SYSCFG.EXTICR1 + case 1: + return &stm32.SYSCFG.EXTICR2 + case 2: + return &stm32.SYSCFG.EXTICR3 + case 3: + return &stm32.SYSCFG.EXTICR4 + } + return nil +} + +func enableEXTIConfigRegisters() { + // Enable SYSCFG + stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN) +} diff --git a/src/machine/machine_stm32_gpio_reva.go b/src/machine/machine_stm32_gpio_reva.go new file mode 100644 index 000000000..3c0d4fe02 --- /dev/null +++ b/src/machine/machine_stm32_gpio_reva.go @@ -0,0 +1,92 @@ +// +build stm32,!stm32l4,!stm32l5 + +package machine + +import ( + "device/stm32" +) + +// This variant of the GPIO input interrupt logic is for +// chips with a smaller number of interrupt channels +// (that fits in a single register). + +// +// STM32 allows one interrupt source per pin number, with +// the same pin number in different ports sharing a single +// interrupt source (so PA0, PB0, PC0 all share). Only a +// single physical pin can be connected to each interrupt +// line. +// +// To call interrupt callbacks, we record here for each +// pin number the callback and the actual associated pin. +// + +// Callbacks for pin interrupt events +var pinCallbacks [16]func(Pin) + +// The pin currently associated with interrupt callback +// for a given slot. +var interruptPins [16]Pin + +// SetInterrupt sets an interrupt to be executed when a particular pin changes +// state. The pin should already be configured as an input, including a pull up +// or down if no external pull is provided. +// +// This call will replace a previously set callback on this pin. You can pass a +// nil func to unset the pin change interrupt. If you do so, the change +// parameter is ignored and can be set to any value (such as 0). +func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error { + port := uint32(uint8(p) / 16) + pin := uint8(p) % 16 + + enableEXTIConfigRegisters() + + if callback == nil { + stm32.EXTI.IMR.ClearBits(1 << pin) + pinCallbacks[pin] = nil + return nil + } + + if pinCallbacks[pin] != nil { + // The pin was already configured. + // To properly re-configure a pin, unset it first and set a new + // configuration. + return ErrNoPinChangeChannel + } + + // Set the callback now (before the interrupt is enabled) to avoid + // possible race condition + pinCallbacks[pin] = callback + interruptPins[pin] = p + + crReg := getEXTIConfigRegister(pin) + shift := (pin & 0x3) * 4 + crReg.ReplaceBits(port, 0xf, shift) + + if (change & PinRising) != 0 { + stm32.EXTI.RTSR.SetBits(1 << pin) + } + if (change & PinFalling) != 0 { + stm32.EXTI.FTSR.SetBits(1 << pin) + } + stm32.EXTI.IMR.SetBits(1 << pin) + + intr := p.registerInterrupt() + intr.SetPriority(0) + intr.Enable() + + return nil +} + +func handlePinInterrupt(pin uint8) { + if stm32.EXTI.PR.HasBits(1 << pin) { + // Writing 1 to the pending register clears the + // pending flag for that bit + stm32.EXTI.PR.Set(1 << pin) + + callback := pinCallbacks[pin] + if callback != nil { + callback(interruptPins[pin]) + } + } +} diff --git a/src/machine/machine_stm32_gpio_revb.go b/src/machine/machine_stm32_gpio_revb.go new file mode 100644 index 000000000..1a0e35378 --- /dev/null +++ b/src/machine/machine_stm32_gpio_revb.go @@ -0,0 +1,79 @@ +// +build stm32l4 stm32l5 + +package machine + +import ( + "device/stm32" +) + +// This variant of the GPIO input interrupt logic is for +// chips with a larger number of interrupt channels (more +// than fits in a single register). + +// +// STM32 allows one interrupt source per pin number, with +// the same pin number in different ports sharing a single +// interrupt source (so PA0, PB0, PC0 all share). Only a +// single physical pin can be connected to each interrupt +// line. +// +// To call interrupt callbacks, we record here for each +// pin number the callback and the actual associated pin. +// + +// Callbacks for pin interrupt events +var pinCallbacks [16]func(Pin) + +// The pin currently associated with interrupt callback +// for a given slot. +var interruptPins [16]Pin + +// SetInterrupt sets an interrupt to be executed when a particular pin changes +// state. The pin should already be configured as an input, including a pull up +// or down if no external pull is provided. +// +// This call will replace a previously set callback on this pin. You can pass a +// nil func to unset the pin change interrupt. If you do so, the change +// parameter is ignored and can be set to any value (such as 0). +func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error { + port := uint32(uint8(p) / 16) + pin := uint8(p) % 16 + + enableEXTIConfigRegisters() + + if callback == nil { + stm32.EXTI.IMR1.ClearBits(1 << pin) + pinCallbacks[pin] = nil + return nil + } + + if pinCallbacks[pin] != nil { + // The pin was already configured. + // To properly re-configure a pin, unset it first and set a new + // configuration. + return ErrNoPinChangeChannel + } + + // Set the callback now (before the interrupt is enabled) to avoid + // possible race condition + pinCallbacks[pin] = callback + interruptPins[pin] = p + + crReg := getEXTIConfigRegister(pin) + shift := (pin & 0x3) * 4 + crReg.ReplaceBits(port, 0xf, shift) + + if (change & PinRising) != 0 { + stm32.EXTI.RTSR1.SetBits(1 << pin) + } + if (change & PinFalling) != 0 { + stm32.EXTI.FTSR1.SetBits(1 << pin) + } + stm32.EXTI.IMR1.SetBits(1 << pin) + + intr := p.registerInterrupt() + intr.SetPriority(0) + intr.Enable() + + return nil +} diff --git a/src/machine/machine_stm32f4.go b/src/machine/machine_stm32f4.go index 3e6156606..652757a3b 100644 --- a/src/machine/machine_stm32f4.go +++ b/src/machine/machine_stm32f4.go @@ -200,6 +200,47 @@ func (p Pin) enableClock() { } } +func (p Pin) registerInterrupt() interrupt.Interrupt { + pin := uint8(p) % 16 + + switch pin { + case 0: + return interrupt.New(stm32.IRQ_EXTI0, func(interrupt.Interrupt) { handlePinInterrupt(0) }) + case 1: + return interrupt.New(stm32.IRQ_EXTI1, func(interrupt.Interrupt) { handlePinInterrupt(1) }) + case 2: + return interrupt.New(stm32.IRQ_EXTI2, func(interrupt.Interrupt) { handlePinInterrupt(2) }) + case 3: + return interrupt.New(stm32.IRQ_EXTI3, func(interrupt.Interrupt) { handlePinInterrupt(3) }) + case 4: + return interrupt.New(stm32.IRQ_EXTI4, func(interrupt.Interrupt) { handlePinInterrupt(4) }) + case 5: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(5) }) + case 6: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(6) }) + case 7: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(7) }) + case 8: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(8) }) + case 9: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(9) }) + case 10: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(10) }) + case 11: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(11) }) + case 12: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(12) }) + case 13: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(13) }) + case 14: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(14) }) + case 15: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(15) }) + } + + return interrupt.Interrupt{} +} + // Enable peripheral clock func enableAltFuncClock(bus unsafe.Pointer) { switch bus { diff --git a/src/machine/machine_stm32f7.go b/src/machine/machine_stm32f7.go index 51235709e..76f1007ac 100644 --- a/src/machine/machine_stm32f7.go +++ b/src/machine/machine_stm32f7.go @@ -309,6 +309,47 @@ func enableAltFuncClock(bus unsafe.Pointer) { } } +func (p Pin) registerInterrupt() interrupt.Interrupt { + pin := uint8(p) % 16 + + switch pin { + case 0: + return interrupt.New(stm32.IRQ_EXTI0, func(interrupt.Interrupt) { handlePinInterrupt(0) }) + case 1: + return interrupt.New(stm32.IRQ_EXTI1, func(interrupt.Interrupt) { handlePinInterrupt(1) }) + case 2: + return interrupt.New(stm32.IRQ_EXTI2, func(interrupt.Interrupt) { handlePinInterrupt(2) }) + case 3: + return interrupt.New(stm32.IRQ_EXTI3, func(interrupt.Interrupt) { handlePinInterrupt(3) }) + case 4: + return interrupt.New(stm32.IRQ_EXTI4, func(interrupt.Interrupt) { handlePinInterrupt(4) }) + case 5: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(5) }) + case 6: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(6) }) + case 7: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(7) }) + case 8: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(8) }) + case 9: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(9) }) + case 10: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(10) }) + case 11: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(11) }) + case 12: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(12) }) + case 13: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(13) }) + case 14: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(14) }) + case 15: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(15) }) + } + + return interrupt.Interrupt{} +} + //---------- Timer related code var ( diff --git a/src/machine/machine_stm32l0.go b/src/machine/machine_stm32l0.go index a25d6c9f2..f2a95e8d3 100644 --- a/src/machine/machine_stm32l0.go +++ b/src/machine/machine_stm32l0.go @@ -6,6 +6,7 @@ package machine import ( "device/stm32" + "runtime/interrupt" ) func CPUFrequency() uint32 { @@ -147,6 +148,47 @@ func (p Pin) enableClock() { } } +func (p Pin) registerInterrupt() interrupt.Interrupt { + pin := uint8(p) % 16 + + switch pin { + case 0: + return interrupt.New(stm32.IRQ_EXTI0_1, func(interrupt.Interrupt) { handlePinInterrupt(0) }) + case 1: + return interrupt.New(stm32.IRQ_EXTI0_1, func(interrupt.Interrupt) { handlePinInterrupt(1) }) + case 2: + return interrupt.New(stm32.IRQ_EXTI2_3, func(interrupt.Interrupt) { handlePinInterrupt(2) }) + case 3: + return interrupt.New(stm32.IRQ_EXTI2_3, func(interrupt.Interrupt) { handlePinInterrupt(3) }) + case 4: + return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(4) }) + case 5: + return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(5) }) + case 6: + return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(6) }) + case 7: + return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(7) }) + case 8: + return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(8) }) + case 9: + return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(9) }) + case 10: + return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(10) }) + case 11: + return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(11) }) + case 12: + return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(12) }) + case 13: + return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(13) }) + case 14: + return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(14) }) + case 15: + return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(15) }) + } + + return interrupt.Interrupt{} +} + //---------- UART related types and code // Configure the UART. diff --git a/src/machine/machine_stm32l4.go b/src/machine/machine_stm32l4.go index c464fa702..be58e9b5f 100644 --- a/src/machine/machine_stm32l4.go +++ b/src/machine/machine_stm32l4.go @@ -203,6 +203,60 @@ func enableAltFuncClock(bus unsafe.Pointer) { } } +func handlePinInterrupt(pin uint8) { + if stm32.EXTI.PR1.HasBits(1 << pin) { + // Writing 1 to the pending register clears the + // pending flag for that bit + stm32.EXTI.PR1.Set(1 << pin) + + callback := pinCallbacks[pin] + if callback != nil { + callback(interruptPins[pin]) + } + } +} + +func (p Pin) registerInterrupt() interrupt.Interrupt { + pin := uint8(p) % 16 + + switch pin { + case 0: + return interrupt.New(stm32.IRQ_EXTI0, func(interrupt.Interrupt) { handlePinInterrupt(0) }) + case 1: + return interrupt.New(stm32.IRQ_EXTI1, func(interrupt.Interrupt) { handlePinInterrupt(1) }) + case 2: + return interrupt.New(stm32.IRQ_EXTI2, func(interrupt.Interrupt) { handlePinInterrupt(2) }) + case 3: + return interrupt.New(stm32.IRQ_EXTI3, func(interrupt.Interrupt) { handlePinInterrupt(3) }) + case 4: + return interrupt.New(stm32.IRQ_EXTI4, func(interrupt.Interrupt) { handlePinInterrupt(4) }) + case 5: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(5) }) + case 6: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(6) }) + case 7: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(7) }) + case 8: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(8) }) + case 9: + return interrupt.New(stm32.IRQ_EXTI9_5, func(interrupt.Interrupt) { handlePinInterrupt(9) }) + case 10: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(10) }) + case 11: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(11) }) + case 12: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(12) }) + case 13: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(13) }) + case 14: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(14) }) + case 15: + return interrupt.New(stm32.IRQ_EXTI15_10, func(interrupt.Interrupt) { handlePinInterrupt(15) }) + } + + return interrupt.Interrupt{} +} + //---------- SPI related types and code // SPI on the STM32Fxxx using MODER / alternate function pins diff --git a/src/machine/machine_stm32l5.go b/src/machine/machine_stm32l5.go index 23aba7a07..3f2935cc2 100644 --- a/src/machine/machine_stm32l5.go +++ b/src/machine/machine_stm32l5.go @@ -271,6 +271,65 @@ func enableAltFuncClock(bus unsafe.Pointer) { } } +func (p Pin) registerInterrupt() interrupt.Interrupt { + pin := uint8(p) % 16 + + switch pin { + case 0: + return interrupt.New(stm32.IRQ_EXTI0, func(interrupt.Interrupt) { handlePinInterrupt(0) }) + case 1: + return interrupt.New(stm32.IRQ_EXTI1, func(interrupt.Interrupt) { handlePinInterrupt(1) }) + case 2: + return interrupt.New(stm32.IRQ_EXTI2, func(interrupt.Interrupt) { handlePinInterrupt(2) }) + case 3: + return interrupt.New(stm32.IRQ_EXTI3, func(interrupt.Interrupt) { handlePinInterrupt(3) }) + case 4: + return interrupt.New(stm32.IRQ_EXTI4, func(interrupt.Interrupt) { handlePinInterrupt(4) }) + case 5: + return interrupt.New(stm32.IRQ_EXTI5, func(interrupt.Interrupt) { handlePinInterrupt(5) }) + case 6: + return interrupt.New(stm32.IRQ_EXTI6, func(interrupt.Interrupt) { handlePinInterrupt(6) }) + case 7: + return interrupt.New(stm32.IRQ_EXTI7, func(interrupt.Interrupt) { handlePinInterrupt(7) }) + case 8: + return interrupt.New(stm32.IRQ_EXTI8, func(interrupt.Interrupt) { handlePinInterrupt(8) }) + case 9: + return interrupt.New(stm32.IRQ_EXTI9, func(interrupt.Interrupt) { handlePinInterrupt(9) }) + case 10: + return interrupt.New(stm32.IRQ_EXTI10, func(interrupt.Interrupt) { handlePinInterrupt(10) }) + case 11: + return interrupt.New(stm32.IRQ_EXTI11, func(interrupt.Interrupt) { handlePinInterrupt(11) }) + case 12: + return interrupt.New(stm32.IRQ_EXTI12, func(interrupt.Interrupt) { handlePinInterrupt(12) }) + case 13: + return interrupt.New(stm32.IRQ_EXTI13, func(interrupt.Interrupt) { handlePinInterrupt(13) }) + case 14: + return interrupt.New(stm32.IRQ_EXTI14, func(interrupt.Interrupt) { handlePinInterrupt(14) }) + case 15: + return interrupt.New(stm32.IRQ_EXTI15, func(interrupt.Interrupt) { handlePinInterrupt(15) }) + } + + return interrupt.Interrupt{} +} + +func handlePinInterrupt(pin uint8) { + // The pin abstraction doesn't differentiate pull-up + // events from pull-down events, so combine them to + // a single call here. + + if stm32.EXTI.RPR1.HasBits(1< Date: Thu, 10 Jun 2021 09:59:33 +0200 Subject: [PATCH 25/70] board/nano-33-ble: pins, blinking leds and serial --- src/machine/board_nano-33-ble.go | 100 +++++++++++++++++++++++++++++++ targets/nano-33-ble.json | 7 +++ targets/nano-33-ble.ld | 14 +++++ 3 files changed, 121 insertions(+) create mode 100644 src/machine/board_nano-33-ble.go create mode 100644 targets/nano-33-ble.json create mode 100644 targets/nano-33-ble.ld diff --git a/src/machine/board_nano-33-ble.go b/src/machine/board_nano-33-ble.go new file mode 100644 index 000000000..b8d68b0bd --- /dev/null +++ b/src/machine/board_nano-33-ble.go @@ -0,0 +1,100 @@ +// +build nano_33_ble + +// This contains the pin mappings for the Arduino Nano 33 BLE [Sense] boards. +// +// Flashing the board requires special version of bossac. +// +// This executable can be obtained two ways: +// 1) In Arduino IDE, install support for the board ("Arduino Mbed OS Nano Boards") +// Search for "tools/bossac/1.9.1-arduino2/bossac" in Arduino IDEs directory +// 2) Download https://downloads.arduino.cc/packages/package_index.json +// Search for "bossac-1.9.1-arduino2" in that file +// Download tarball for your OS and unpack it +// +// Once you have the executable, make it accessible in your PATH as "bossac_arduino2". +// +// It is possible to replace original bossac with this new one (this only adds support for nrf chip). +// In that case make "bossac_arduino2" symlink on it, for the board target to be able to find it. +// +// For more information, see: +// - https://store.arduino.cc/arduino-nano-33-ble +// - https://store.arduino.cc/arduino-nano-33-ble-sense +// +package machine + +const HasLowFrequencyCrystal = true + +// Digital Pins +const ( + D2 Pin = P1_11 + D3 Pin = P1_12 + D4 Pin = P1_15 + D5 Pin = P1_13 + D6 Pin = P1_14 + D7 Pin = P0_23 + D8 Pin = P0_21 + D9 Pin = P0_27 + D10 Pin = P1_02 + D11 Pin = P1_01 + D12 Pin = P1_08 + D13 Pin = P0_13 +) + +// Analog pins +const ( + A0 Pin = P0_04 + A1 Pin = P0_05 + A2 Pin = P0_30 + A3 Pin = P0_29 + A4 Pin = P0_31 + A5 Pin = P0_02 + A6 Pin = P0_28 + A7 Pin = P0_03 +) + +// Onboard LEDs +const ( + LED = LED_BUILTIN + LED1 = LED_RED + LED2 = LED_GREEN + LED3 = LED_BLUE + LED_BUILTIN = P0_13 + LED_RED = P0_24 + LED_GREEN = P0_16 + LED_BLUE = P0_06 +) + +// UART0 pins +const ( + UART_RX_PIN = P1_10 + UART_TX_PIN = P1_03 +) + +// Serial is the USB device +var ( + Serial = USB +) + +// I2C pins +const ( + SDA_PIN = P0_31 + SCL_PIN = P0_02 +) + +// SPI pins +const ( + SPI0_SCK_PIN = P0_13 + SPI0_SDO_PIN = P1_01 + SPI0_SDI_PIN = P1_08 +) + +// USB CDC identifiers +const ( + usb_STRING_PRODUCT = "Nano 33 BLE" + usb_STRING_MANUFACTURER = "Arduino" +) + +var ( + usb_VID uint16 = 0x2341 + usb_PID uint16 = 0x805a +) diff --git a/targets/nano-33-ble.json b/targets/nano-33-ble.json new file mode 100644 index 000000000..53b4fcac7 --- /dev/null +++ b/targets/nano-33-ble.json @@ -0,0 +1,7 @@ +{ + "inherits": ["nrf52840"], + "build-tags": ["nano_33_ble"], + "flash-command": "bossac_arduino2 -d -i -e -w -v -R --port={port} {bin}", + "flash-1200-bps-reset": "true", + "linkerscript": "targets/nano-33-ble.ld" +} diff --git a/targets/nano-33-ble.ld b/targets/nano-33-ble.ld new file mode 100644 index 000000000..cc533ea90 --- /dev/null +++ b/targets/nano-33-ble.ld @@ -0,0 +1,14 @@ + +/* + See also + https://github.com/arduino/ArduinoCore-mbed/blob/master/variants/ARDUINO_NANO33BLE/linker_script.ld +*/ +MEMORY +{ + FLASH_TEXT (rw) : ORIGIN = 0x10000, LENGTH = 0xf0000 + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x40000 +} + +_stack_size = 4K; + +INCLUDE "targets/arm.ld" From 3a458ec75ca03bbe13c36afded6f59af3e772fbf Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 2 Jun 2021 13:59:38 +0200 Subject: [PATCH 26/70] main: make flash-command portable and safer to use Previously, flash-command would assume it could execute a command straight via /bin/sh, at least on non-Windows systems. Otherwise it would just split the command using `strings.Split`. This is all a bit hacky, so I've replaced it with a proper solution: splitting the command _before_ substituting various paths using a real shell splitter (shlex.Split, from Google). This solves a few things: * It guards against special characters in path names. This can be an issue on Windows where the temporary path may contain spaces (this is uncommon on POSIX systems). * It is more portable, by disallowing the use of a shell. That way, it doesn't differentiate between Windows and non-Windows anymore. --- main.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/main.go b/main.go index c85feb697..c3cca64f7 100644 --- a/main.go +++ b/main.go @@ -314,8 +314,10 @@ func Flash(pkgName, port string, options *compileopts.Options) error { case "", "command": // Create the command. flashCmd := config.Target.FlashCommand - fileToken := "{" + fileExt[1:] + "}" - flashCmd = strings.ReplaceAll(flashCmd, fileToken, result.Binary) + flashCmdList, err := shlex.Split(flashCmd) + if err != nil { + return fmt.Errorf("could not parse flash command %#v: %w", flashCmd, err) + } if strings.Contains(flashCmd, "{port}") { var err error @@ -325,25 +327,23 @@ func Flash(pkgName, port string, options *compileopts.Options) error { } } - flashCmd = strings.ReplaceAll(flashCmd, "{port}", port) - - // Execute the command. - var cmd *exec.Cmd - switch runtime.GOOS { - case "windows": - command := strings.Split(flashCmd, " ") - if len(command) < 2 { - return errors.New("invalid flash command") - } - cmd = executeCommand(config.Options, command[0], command[1:]...) - default: - cmd = executeCommand(config.Options, "/bin/sh", "-c", flashCmd) + // Fill in fields in the command template. + fileToken := "{" + fileExt[1:] + "}" + for i, arg := range flashCmdList { + arg = strings.ReplaceAll(arg, fileToken, result.Binary) + arg = strings.ReplaceAll(arg, "{port}", port) + flashCmdList[i] = arg } + // Execute the command. + if len(flashCmdList) < 2 { + return fmt.Errorf("invalid flash command: %#v", flashCmd) + } + cmd := executeCommand(config.Options, flashCmdList[0], flashCmdList[1:]...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Dir = goenv.Get("TINYGOROOT") - err := cmd.Run() + err = cmd.Run() if err != nil { return &commandError{"failed to flash", result.Binary, err} } From b406b814162462efb7f8dffc69a8dce42b43dd1a Mon Sep 17 00:00:00 2001 From: sago35 Date: Mon, 7 Jun 2021 22:07:15 +0900 Subject: [PATCH 27/70] machine: add definition for ws2812 --- src/machine/board_circuitplay_bluefruit.go | 1 + src/machine/board_circuitplay_express.go | 1 + src/machine/board_clue_alpha.go | 1 + src/machine/board_feather-m4-can.go | 1 + src/machine/board_feather-m4.go | 3 ++- src/machine/board_feather-nrf52840.go | 1 + src/machine/board_feather-stm32f405.go | 1 + src/machine/board_grandcentral-m4.go | 1 + src/machine/board_matrixportal-m4.go | 1 + src/machine/board_metro-m4-airlift.go | 3 ++- src/machine/board_pybadge.go | 1 + src/machine/board_pygamer.go | 1 + src/machine/board_pyportal.go | 1 + src/machine/board_qtpy.go | 1 + 14 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/machine/board_circuitplay_bluefruit.go b/src/machine/board_circuitplay_bluefruit.go index ccb2e3c17..967672495 100644 --- a/src/machine/board_circuitplay_bluefruit.go +++ b/src/machine/board_circuitplay_bluefruit.go @@ -38,6 +38,7 @@ const ( const ( LED = D13 NEOPIXELS = D8 + WS2812 = D8 BUTTONA = D4 BUTTONB = D5 diff --git a/src/machine/board_circuitplay_express.go b/src/machine/board_circuitplay_express.go index be9904711..ad49c24bb 100644 --- a/src/machine/board_circuitplay_express.go +++ b/src/machine/board_circuitplay_express.go @@ -41,6 +41,7 @@ const ( const ( LED = D13 NEOPIXELS = D8 + WS2812 = D8 BUTTONA = D4 BUTTONB = D5 diff --git a/src/machine/board_clue_alpha.go b/src/machine/board_clue_alpha.go index 89f82056c..b421dccce 100644 --- a/src/machine/board_clue_alpha.go +++ b/src/machine/board_clue_alpha.go @@ -72,6 +72,7 @@ const ( LED1 = LED LED2 = D43 NEOPIXEL = D18 + WS2812 = D18 BUTTON_LEFT = D5 BUTTON_RIGHT = D11 diff --git a/src/machine/board_feather-m4-can.go b/src/machine/board_feather-m4-can.go index d5750f961..7911f5561 100644 --- a/src/machine/board_feather-m4-can.go +++ b/src/machine/board_feather-m4-can.go @@ -44,6 +44,7 @@ const ( const ( LED = D13 NEOPIXELS = D8 + WS2812 = D8 ) var Serial = USB diff --git a/src/machine/board_feather-m4.go b/src/machine/board_feather-m4.go index 251da7215..394191366 100644 --- a/src/machine/board_feather-m4.go +++ b/src/machine/board_feather-m4.go @@ -36,7 +36,8 @@ const ( ) const ( - LED = D13 + LED = D13 + WS2812 = D8 ) var Serial = USB diff --git a/src/machine/board_feather-nrf52840.go b/src/machine/board_feather-nrf52840.go index 2c6e84658..641a9de81 100644 --- a/src/machine/board_feather-nrf52840.go +++ b/src/machine/board_feather-nrf52840.go @@ -59,6 +59,7 @@ const ( LED1 = LED LED2 = D4 NEOPIXEL = D8 + WS2812 = D8 BUTTON = D7 QSPI_SCK = D27 diff --git a/src/machine/board_feather-stm32f405.go b/src/machine/board_feather-stm32f405.go index e5e8d4b7f..4bf1744b9 100644 --- a/src/machine/board_feather-stm32f405.go +++ b/src/machine/board_feather-stm32f405.go @@ -84,6 +84,7 @@ const ( LED_NEOPIXEL = D8 LED_BUILTIN = LED_RED LED = LED_BUILTIN + WS2812 = D8 ) func initLED() {} diff --git a/src/machine/board_grandcentral-m4.go b/src/machine/board_grandcentral-m4.go index 6d42034d6..014eda801 100644 --- a/src/machine/board_grandcentral-m4.go +++ b/src/machine/board_grandcentral-m4.go @@ -139,6 +139,7 @@ const ( LED_RX = UART_RX_LED_PIN LED_TX = UART_TX_LED_PIN NEOPIXEL = NEOPIXEL_PIN + WS2812 = NEOPIXEL_PIN ) var Serial = USB diff --git a/src/machine/board_matrixportal-m4.go b/src/machine/board_matrixportal-m4.go index 14d0cda53..9236e62dd 100644 --- a/src/machine/board_matrixportal-m4.go +++ b/src/machine/board_matrixportal-m4.go @@ -75,6 +75,7 @@ const ( const ( LED = D13 NEOPIXEL = D4 + WS2812 = D4 ) // Button pins diff --git a/src/machine/board_metro-m4-airlift.go b/src/machine/board_metro-m4-airlift.go index 5665a0813..276defa92 100644 --- a/src/machine/board_metro-m4-airlift.go +++ b/src/machine/board_metro-m4-airlift.go @@ -37,7 +37,8 @@ const ( ) const ( - LED = D13 + LED = D13 + WS2812 = D40 ) var Serial = USB diff --git a/src/machine/board_pybadge.go b/src/machine/board_pybadge.go index 687d933b7..3ff24a160 100644 --- a/src/machine/board_pybadge.go +++ b/src/machine/board_pybadge.go @@ -40,6 +40,7 @@ const ( const ( LED = D13 NEOPIXELS = D8 + WS2812 = D8 LIGHTSENSOR = A7 diff --git a/src/machine/board_pygamer.go b/src/machine/board_pygamer.go index e12eed9e4..890c3d3d9 100644 --- a/src/machine/board_pygamer.go +++ b/src/machine/board_pygamer.go @@ -42,6 +42,7 @@ const ( const ( LED = D13 NEOPIXELS = D8 + WS2812 = D8 SD_CS = D7 diff --git a/src/machine/board_pyportal.go b/src/machine/board_pyportal.go index c2ffb386e..7e28de44a 100644 --- a/src/machine/board_pyportal.go +++ b/src/machine/board_pyportal.go @@ -68,6 +68,7 @@ const ( TFT_WR = D26 NEOPIXEL = D2 + WS2812 = D2 SPK_SD = D50 ) diff --git a/src/machine/board_qtpy.go b/src/machine/board_qtpy.go index b17e8070e..5f58b8b0e 100644 --- a/src/machine/board_qtpy.go +++ b/src/machine/board_qtpy.go @@ -43,6 +43,7 @@ const ( const ( NEOPIXELS = D11 + WS2812 = D11 NEOPIXELS_POWER = D12 ) From 87e48c105791187ae1bbd096ae55029de2b072a3 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Wed, 9 Jun 2021 19:11:42 +0200 Subject: [PATCH 28/70] machine/rp2040: implement UART0/UART1, can be used on all rp2040 boards Signed-off-by: deadprogram --- src/machine/board_nano-rp2040.go | 6 -- src/machine/machine_rp2040.go | 34 +++++++- src/machine/machine_rp2040_gpio.go | 3 + src/machine/machine_rp2040_uart.go | 133 +++++++++++++++++++++++++++++ src/machine/uart.go | 19 ++++- src/runtime/runtime_rp2040.go | 5 ++ 6 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 src/machine/machine_rp2040_uart.go diff --git a/src/machine/board_nano-rp2040.go b/src/machine/board_nano-rp2040.go index 2863f672a..ca85596a7 100644 --- a/src/machine/board_nano-rp2040.go +++ b/src/machine/board_nano-rp2040.go @@ -47,12 +47,6 @@ const ( LED = GPIO6 ) -// UART1 pins -const ( - UART_TX_PIN Pin = GPIO0 - UART_RX_PIN Pin = GPIO1 -) - // I2C pins const ( SDA_PIN Pin = GPIO12 diff --git a/src/machine/machine_rp2040.go b/src/machine/machine_rp2040.go index 7f0a71c43..f7580967a 100644 --- a/src/machine/machine_rp2040.go +++ b/src/machine/machine_rp2040.go @@ -4,7 +4,7 @@ package machine import ( "device/rp" - _ "unsafe" + "runtime/interrupt" ) const ( @@ -82,3 +82,35 @@ func machineInit() { func ticks() uint64 { return timer.timeElapsed() } + +// UART pins +const ( + UART_TX_PIN = UART0_TX_PIN + UART_RX_PIN = UART0_RX_PIN + UART0_TX_PIN = GPIO0 + UART0_RX_PIN = GPIO1 + UART1_TX_PIN = GPIO8 + UART1_RX_PIN = GPIO9 +) + +// UART on the RP2040 +var ( + UART0 = &_UART0 + _UART0 = UART{ + Buffer: NewRingBuffer(), + Bus: rp.UART0, + } + + UART1 = &_UART1 + _UART1 = UART{ + Buffer: NewRingBuffer(), + Bus: rp.UART1, + } +) + +var Serial = UART0 + +func init() { + UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt) + UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt) +} diff --git a/src/machine/machine_rp2040_gpio.go b/src/machine/machine_rp2040_gpio.go index 62c6c3350..b994988e4 100644 --- a/src/machine/machine_rp2040_gpio.go +++ b/src/machine/machine_rp2040_gpio.go @@ -67,6 +67,7 @@ const ( PinInputPulldown PinInputPullup PinAnalog + PinUART ) // set drives the pin high @@ -152,6 +153,8 @@ func (p Pin) Configure(config PinConfig) { case PinAnalog: p.setFunc(fnNULL) p.pulloff() + case PinUART: + p.setFunc(fnUART) } } diff --git a/src/machine/machine_rp2040_uart.go b/src/machine/machine_rp2040_uart.go new file mode 100644 index 000000000..e2b5f7337 --- /dev/null +++ b/src/machine/machine_rp2040_uart.go @@ -0,0 +1,133 @@ +// +build rp2040 + +package machine + +import ( + "device/rp" + "runtime/interrupt" +) + +// UART on the RP2040. +type UART struct { + Buffer *RingBuffer + Bus *rp.UART0_Type + Interrupt interrupt.Interrupt +} + +// Configure the UART. +func (uart *UART) Configure(config UARTConfig) error { + initUART(uart) + + // Default baud rate to 115200. + if config.BaudRate == 0 { + config.BaudRate = 115200 + } + + // Use default pins if pins are not set. + if config.TX == 0 && config.RX == 0 { + // use default pins + config.TX = UART_TX_PIN + config.RX = UART_RX_PIN + } + + uart.SetBaudRate(config.BaudRate) + + // default to 8-1-N + uart.SetFormat(8, 1, ParityNone) + + // Enable the UART, both TX and RX + uart.Bus.UARTCR.SetBits(rp.UART0_UARTCR_UARTEN | + rp.UART0_UARTCR_RXE | + rp.UART0_UARTCR_TXE) + + // set GPIO mux to UART for the pins + config.TX.Configure(PinConfig{Mode: PinUART}) + config.RX.Configure(PinConfig{Mode: PinUART}) + + // Enable RX IRQ. + uart.Interrupt.SetPriority(0x80) + uart.Interrupt.Enable() + + // setup interrupt on receive + uart.Bus.UARTIMSC.Set(rp.UART0_UARTIMSC_RXIM) + + return nil +} + +// SetBaudRate sets the baudrate to be used for the UART. +func (uart *UART) SetBaudRate(br uint32) { + div := 8 * 125 * MHz / br + + ibrd := div >> 7 + var fbrd uint32 + + switch { + case ibrd == 0: + ibrd = 1 + fbrd = 0 + case ibrd >= 65535: + ibrd = 65535 + fbrd = 0 + default: + fbrd = ((div & 0x7f) + 1) / 2 + } + + // set PL011 baud divisor registers + uart.Bus.UARTIBRD.Set(ibrd) + uart.Bus.UARTFBRD.Set(fbrd) + + // PL011 needs a (dummy) line control register write. + // See https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_uart/uart.c#L93-L95 + uart.Bus.UARTLCR_H.SetBits(0) +} + +// WriteByte writes a byte of data to the UART. +func (uart *UART) WriteByte(c byte) error { + // wait until buffer is not full + for uart.Bus.UARTFR.HasBits(rp.UART0_UARTFR_TXFF) { + } + + // write data + uart.Bus.UARTDR.Set(uint32(c)) + return nil +} + +// SetFormat for number of data bits, stop bits, and parity for the UART. +func (uart *UART) SetFormat(databits, stopbits uint8, parity UARTParity) error { + var pen, pev uint8 + if parity != ParityNone { + pen = rp.UART0_UARTLCR_H_PEN + } + if parity == ParityEven { + pev = rp.UART0_UARTLCR_H_EPS + } + uart.Bus.UARTLCR_H.SetBits(uint32((databits-5)< Date: Sun, 6 Jun 2021 15:42:21 -0700 Subject: [PATCH 29/70] rp2040: patch elf to checksum 2nd stage boot --- builder/build.go | 84 ++++--- builder/elfpatch.go | 57 +++++ compileopts/config.go | 9 + compileopts/target.go | 1 + targets/pico.ld | 29 +-- targets/pico_boot_stage2.S | 433 +++++++++++++++++++++++++++++++++++-- targets/rp2040.json | 1 + targets/rp2040.ld | 30 ++- 8 files changed, 564 insertions(+), 80 deletions(-) create mode 100644 builder/elfpatch.go diff --git a/builder/build.go b/builder/build.go index 5ef7b3e51..8a433ca24 100644 --- a/builder/build.go +++ b/builder/build.go @@ -12,7 +12,9 @@ import ( "errors" "fmt" "go/types" + "hash/crc32" "io/ioutil" + "math/bits" "os" "path/filepath" "runtime" @@ -566,6 +568,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil return err } } + + // Apply ELF patches if config.AutomaticStackSize() { // Modify the .tinygo_stacksizes section that contains a stack size // for each goroutine. @@ -574,6 +578,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil return fmt.Errorf("could not modify stack sizes: %w", err) } } + if config.RP2040BootPatch() { + // Patch the second stage bootloader CRC into the .boot2 section + err = patchRP2040BootCRC(executable) + if err != nil { + return fmt.Errorf("could not patch RP2040 second stage boot loader: %w", err) + } + } if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" { sizes, err := loadProgramSize(executable) @@ -920,30 +931,7 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri // stack size information. Before this modification, all stack sizes in the // section assume the default stack size (which is relatively big). func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map[string]functionStackSize) error { - fp, err := os.OpenFile(executable, os.O_RDWR, 0) - if err != nil { - return err - } - defer fp.Close() - - elfFile, err := elf.NewFile(fp) - if err != nil { - return err - } - - section := elfFile.Section(".tinygo_stacksizes") - if section == nil { - return errors.New("could not find .tinygo_stacksizes section") - } - - if section.Size != section.FileSize { - // Sanity check. - return fmt.Errorf("expected .tinygo_stacksizes to have identical size and file size, got %d and %d", section.Size, section.FileSize) - } - - // Read all goroutine stack sizes. - data := make([]byte, section.Size) - _, err = fp.ReadAt(data, int64(section.Offset)) + data, fileHeader, err := getElfSectionData(executable, ".tinygo_stacksizes") if err != nil { return err } @@ -972,7 +960,7 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map stackSize += 4 // Add stack size used by interrupts. - switch elfFile.Machine { + switch fileHeader.Machine { case elf.EM_ARM: // On Cortex-M (assumed here), this stack size is 8 words or 32 // bytes. This is only to store the registers that the interrupt @@ -988,13 +976,7 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map } } - // Write back the modified stack sizes. - _, err = fp.WriteAt(data, int64(section.Offset)) - if err != nil { - return err - } - - return nil + return replaceElfSection(executable, ".tinygo_stacksizes", data) } // printStacks prints the maximum stack depth for functions that are started as @@ -1026,3 +1008,41 @@ func printStacks(calculatedStacks []string, stackSizes map[string]functionStackS } } } + +// RP2040 second stage bootloader CRC32 calculation +// +// Spec: https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf +// Section: 2.8.1.3.1. Checksum +func patchRP2040BootCRC(executable string) error { + bytes, _, err := getElfSectionData(executable, ".boot2") + if err != nil { + return err + } + + if len(bytes) != 256 { + return fmt.Errorf("rp2040 .boot2 section must be exactly 256 bytes") + } + + // From the 'official' RP2040 checksum script: + // + // Our bootrom CRC32 is slightly bass-ackward but it's + // best to work around for now (FIXME) + // 100% worth it to save two Thumb instructions + revBytes := make([]byte, len(bytes)) + for i := range bytes { + revBytes[i] = bits.Reverse8(bytes[i]) + } + + // crc32.Update does an initial negate and negates the + // result, so to meet RP2040 spec, pass 0x0 as initial + // hash and negate returned value. + // + // Note: checksum is over 252 bytes (256 - 4) + hash := bits.Reverse32(crc32.Update(0x0, crc32.IEEETable, revBytes[:252]) ^ 0xFFFFFFFF) + + // Write the CRC to the end of the bootloader. + binary.LittleEndian.PutUint32(bytes[252:], hash) + + // Update the .boot2 section to included the CRC + return replaceElfSection(executable, ".boot2", bytes) +} diff --git a/builder/elfpatch.go b/builder/elfpatch.go new file mode 100644 index 000000000..6a407db6f --- /dev/null +++ b/builder/elfpatch.go @@ -0,0 +1,57 @@ +package builder + +import ( + "debug/elf" + "fmt" + "os" +) + +func getElfSectionData(executable string, sectionName string) ([]byte, elf.FileHeader, error) { + elfFile, err := elf.Open(executable) + if err != nil { + return nil, elf.FileHeader{}, err + } + defer elfFile.Close() + + section := elfFile.Section(sectionName) + if section == nil { + return nil, elf.FileHeader{}, fmt.Errorf("could not find %s section", sectionName) + } + + data, err := section.Data() + + return data, elfFile.FileHeader, err +} + +func replaceElfSection(executable string, sectionName string, data []byte) error { + fp, err := os.OpenFile(executable, os.O_RDWR, 0) + if err != nil { + return err + } + defer fp.Close() + + elfFile, err := elf.Open(executable) + if err != nil { + return err + } + defer elfFile.Close() + + section := elfFile.Section(sectionName) + if section == nil { + return fmt.Errorf("could not find %s section", sectionName) + } + + // Implicitly check for compressed sections + if section.Size != section.FileSize { + return fmt.Errorf("expected section %s to have identical size and file size, got %d and %d", sectionName, section.Size, section.FileSize) + } + + // Only permit complete replacement of section + if section.Size != uint64(len(data)) { + return fmt.Errorf("expected section %s to have size %d, was actually %d", sectionName, len(data), section.Size) + } + + // Write the replacement section data + _, err = fp.WriteAt(data, int64(section.Offset)) + return err +} diff --git a/compileopts/config.go b/compileopts/config.go index d29616f0c..0bb839ff7 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -176,6 +176,15 @@ func (c *Config) AutomaticStackSize() bool { return false } +// RP2040BootPatch returns whether the RP2040 boot patch should be applied that +// calculates and patches in the checksum for the 2nd stage bootloader. +func (c *Config) RP2040BootPatch() bool { + if c.Target.RP2040BootPatch != nil { + return *c.Target.RP2040BootPatch + } + return false +} + // CFlags returns the flags to pass to the C compiler. This is necessary for CGo // preprocessing. func (c *Config) CFlags() []string { diff --git a/compileopts/target.go b/compileopts/target.go index 81de4ed07..24cf4f394 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -40,6 +40,7 @@ type TargetSpec struct { LDFlags []string `json:"ldflags"` LinkerScript string `json:"linkerscript"` ExtraFiles []string `json:"extra-files"` + RP2040BootPatch *bool `json:"rp2040-boot-patch"` // Patch RP2040 2nd stage bootloader checksum Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append FlashCommand string `json:"flash-command"` GDB []string `json:"gdb"` diff --git a/targets/pico.ld b/targets/pico.ld index 267fbc4c9..6ef32a3cb 100644 --- a/targets/pico.ld +++ b/targets/pico.ld @@ -1,31 +1,10 @@ MEMORY { - FLASH_TEXT (rx) : ORIGIN = 0x10000000, LENGTH = 2048k -} - -SECTIONS -{ - /* Second stage bootloader is prepended to the image. It must be 256 bytes big - and checksummed. It is usually built by the boot_stage2 target - in the Raspberry Pi Pico SDK - */ - - .boot2 : { - __boot2_start__ = .; - KEEP (*(.boot2)) - __boot2_end__ = .; - } > FLASH_TEXT - - ASSERT(__boot2_end__ - __boot2_start__ == 256, - "ERROR: Pico second stage bootloader must be 256 bytes in size") - - /* The second stage will always enter the image at the start of .text. - The debugger will use the ELF entry point, which is the _entry_point - symbol if present, otherwise defaults to start of .text. - This can be used to transfer control back to the bootrom on debugger - launches only, to perform proper flash setup. - */ + /* Reserve exactly 256 bytes at start of flash for second stage bootloader */ + BOOT2_TEXT (rx) : ORIGIN = 0x10000000, LENGTH = 256 + FLASH_TEXT (rx) : ORIGIN = 0x10000000 + 256, LENGTH = 2048K - 256 + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256k } INCLUDE "targets/rp2040.ld" diff --git a/targets/pico_boot_stage2.S b/targets/pico_boot_stage2.S index 4902d3bbb..274845b1a 100644 --- a/targets/pico_boot_stage2.S +++ b/targets/pico_boot_stage2.S @@ -1,23 +1,420 @@ -// Padded and checksummed version of: /home/rkanchan/src/pico-sdk/build/src/rp2_common/boot_stage2/bs2_default.bin +// +// Implementation of Pico stage 2 boot loader. This code is for the Winbond W25Q080 +// (as found in the Pico) from the official Pico SDK. +// +// This implementation has been made 'stand-alone' by including necessary code / +// symbols from the included files in the reference implementation directly into +// the source. Care has been taken to preserve ordering and it has been verified +// the generated binary is byte-for-byte identical to the reference code binary. +// +// Note: the stage 2 boot loader must be 256 bytes in length and have a checksum +// present. In TinyGo, the linker script is responsible for allocating 256 bytes +// for the .boot2 section and the build logic patches the checksum into the +// binary after linking, controlled by the '.json' flag 'rp2040-boot-patch'. +// +// The stage 2 bootstrap section can be inspected in an elf file using this command: +// objdump -s -j .boot2 .elf +// +// Original Source: +// https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/boot_stage2/boot2_w25q080.S +// +// Board Parameters +#define PICO_FLASH_SPI_CLKDIV 2 + + + +// ---------------------------------------------------------------------------- +// Second stage boot code +// Copyright (c) 2019-2021 Raspberry Pi (Trading) Ltd. +// SPDX-License-Identifier: BSD-3-Clause +// +// Device: Winbond W25Q080 +// Also supports W25Q16JV (which has some different SR instructions) +// Also supports AT25SF081 +// Also supports S25FL132K0 +// +// Description: Configures W25Q080 to run in Quad I/O continuous read XIP mode +// +// Details: * Check status register 2 to determine if QSPI mode is enabled, +// and perform an SR2 programming cycle if necessary. +// * Use SSI to perform a dummy 0xEB read command, with the mode +// continuation bits set, so that the flash will not require +// 0xEB instruction prefix on subsequent reads. +// * Configure SSI to write address, mode bits, but no instruction. +// SSI + flash are now jointly in a state where continuous reads +// can take place. +// * Jump to exit pointer passed in via lr. Bootrom passes null, +// in which case this code uses a default 256 byte flash offset +// +// Building: * This code must be position-independent, and use stack only +// * The code will be padded to a size of 256 bytes, including a +// 4-byte checksum. Therefore code size cannot exceed 252 bytes. +// ---------------------------------------------------------------------------- + + +// +// Expanded include files +// +#define CMD_WRITE_ENABLE 0x06 +#define CMD_READ_STATUS 0x05 +#define CMD_READ_STATUS2 0x35 +#define CMD_WRITE_STATUS 0x01 +#define SREG_DATA 0x02 // Enable quad-SPI mode + +#define XIP_BASE 0x10000000 +#define XIP_SSI_BASE 0x18000000 +#define PADS_QSPI_BASE 0x40020000 +#define PPB_BASE 0xe0000000 + +#define M0PLUS_VTOR_OFFSET 0x0000ed08 + +#define PADS_QSPI_GPIO_QSPI_SCLK_DRIVE_LSB 4 +#define PADS_QSPI_GPIO_QSPI_SCLK_SLEWFAST_BITS 0x00000001 +#define PADS_QSPI_GPIO_QSPI_SCLK_OFFSET 0x00000004 +#define PADS_QSPI_GPIO_QSPI_SD0_OFFSET 0x00000008 +#define PADS_QSPI_GPIO_QSPI_SD0_SCHMITT_BITS 0x00000002 +#define PADS_QSPI_GPIO_QSPI_SD1_OFFSET 0x0000000c +#define PADS_QSPI_GPIO_QSPI_SD2_OFFSET 0x00000010 +#define PADS_QSPI_GPIO_QSPI_SD3_OFFSET 0x00000014 + +#define SSI_CTRLR0_OFFSET 0x00000000 +#define SSI_CTRLR1_OFFSET 0x00000004 +#define SSI_SSIENR_OFFSET 0x00000008 +#define SSI_BAUDR_OFFSET 0x00000014 +#define SSI_SR_OFFSET 0x00000028 +#define SSI_DR0_OFFSET 0x00000060 +#define SSI_RX_SAMPLE_DLY_OFFSET 0x000000f0 + +#define SSI_CTRLR0_DFS_32_LSB 16 + +#define SSI_CTRLR0_SPI_FRF_VALUE_QUAD 0x2 +#define SSI_CTRLR0_SPI_FRF_LSB 21 + +#define SSI_CTRLR0_TMOD_VALUE_TX_AND_RX 0x0 +#define SSI_CTRLR0_TMOD_VALUE_EEPROM_READ 0x3 +#define SSI_CTRLR0_TMOD_LSB 8 + +#define SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_1C2A 0x1 +#define SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_2C2A 0x2 + +#define SSI_SPI_CTRLR0_OFFSET 0x000000f4 + +#define SSI_SPI_CTRLR0_INST_L_VALUE_NONE 0x0 +#define SSI_SPI_CTRLR0_INST_L_VALUE_8B 0x2 + +#define SSI_SPI_CTRLR0_TRANS_TYPE_LSB 0 +#define SSI_SPI_CTRLR0_ADDR_L_LSB 2 +#define SSI_SPI_CTRLR0_INST_L_LSB 8 +#define SSI_SPI_CTRLR0_WAIT_CYCLES_LSB 11 +#define SSI_SPI_CTRLR0_XIP_CMD_LSB 24 + +#define SSI_SR_BUSY_BITS 0x00000001 +#define SSI_SR_TFE_BITS 0x00000004 + + +// ---------------------------------------------------------------------------- +// Config section +// ---------------------------------------------------------------------------- +// It should be possible to support most flash devices by modifying this section + +// The serial flash interface will run at clk_sys/PICO_FLASH_SPI_CLKDIV. +// This must be a positive, even integer. +// The bootrom is very conservative with SPI frequency, but here we should be +// as aggressive as possible. + +#ifndef PICO_FLASH_SPI_CLKDIV +#define PICO_FLASH_SPI_CLKDIV 4 +#endif +#if PICO_FLASH_SPI_CLKDIV & 1 +#error PICO_FLASH_SPI_CLKDIV must be even +#endif + +// Define interface width: single/dual/quad IO +#define FRAME_FORMAT SSI_CTRLR0_SPI_FRF_VALUE_QUAD + +// For W25Q080 this is the "Read data fast quad IO" instruction: +#define CMD_READ 0xeb + +// "Mode bits" are 8 special bits sent immediately after +// the address bits in a "Read Data Fast Quad I/O" command sequence. +// On W25Q080, the four LSBs are don't care, and if MSBs == 0xa, the +// next read does not require the 0xeb instruction prefix. +#define MODE_CONTINUOUS_READ 0xa0 + +// The number of address + mode bits, divided by 4 (always 4, not function of +// interface width). +#define ADDR_L 8 + +// How many clocks of Hi-Z following the mode bits. For W25Q080, 4 dummy cycles +// are required. +#define WAIT_CYCLES 4 + +// If defined, we will read status reg, compare to SREG_DATA, and overwrite +// with our value if the SR doesn't match. +// We do a two-byte write to SR1 (01h cmd) rather than a one-byte write to +// SR2 (31h cmd) as the latter command isn't supported by WX25Q080. +// This isn't great because it will remove block protections. +// A better solution is to use a volatile SR write if your device supports it. +#define PROGRAM_STATUS_REG + +.syntax unified .cpu cortex-m0plus .thumb - .section .boot2, "ax" -.byte 0x00, 0xb5, 0x32, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, 0x98, 0x60 -.byte 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2e, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x02, 0x21, 0x59, 0x61 -.byte 0x01, 0x21, 0xf0, 0x22, 0x99, 0x50, 0x2b, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20 -.byte 0x00, 0xf0, 0x44, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x14, 0xd0, 0x06, 0x21, 0x19, 0x66, 0x00, 0xf0 -.byte 0x34, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66, 0x00, 0x20, 0x18, 0x66, 0x1a, 0x66, 0x00, 0xf0 -.byte 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e, 0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21 -.byte 0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60 -.byte 0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21, 0x19, 0x66, 0xa0, 0x21 -.byte 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21, 0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60 -.byte 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49 -.byte 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a, 0x04, 0x20 -.byte 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66 -.byte 0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40 -.byte 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00 -.byte 0xf4, 0x00, 0x00, 0x18, 0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0 -.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xb2, 0x4e, 0x7a +// The exit point is passed in lr. If entered from bootrom, this will be the +// flash address immediately following this second stage (0x10000100). +// Otherwise it will be a return address -- second stage being called as a +// function by user code, after copying out of XIP region. r3 holds SSI base, +// r0...2 used as temporaries. Other GPRs not used. +.global _stage2_boot +.type _stage2_boot,%function +.thumb_func +_stage2_boot: + push {lr} + + // Set pad configuration: + // - SCLK 8mA drive, no slew limiting + // - SDx disable input Schmitt to reduce delay + + ldr r3, =PADS_QSPI_BASE + movs r0, #(2 << PADS_QSPI_GPIO_QSPI_SCLK_DRIVE_LSB | PADS_QSPI_GPIO_QSPI_SCLK_SLEWFAST_BITS) + str r0, [r3, #PADS_QSPI_GPIO_QSPI_SCLK_OFFSET] + ldr r0, [r3, #PADS_QSPI_GPIO_QSPI_SD0_OFFSET] + movs r1, #PADS_QSPI_GPIO_QSPI_SD0_SCHMITT_BITS + bics r0, r1 + str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD0_OFFSET] + str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD1_OFFSET] + str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD2_OFFSET] + str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD3_OFFSET] + + ldr r3, =XIP_SSI_BASE + + // Disable SSI to allow further config + movs r1, #0 + str r1, [r3, #SSI_SSIENR_OFFSET] + + // Set baud rate + movs r1, #PICO_FLASH_SPI_CLKDIV + str r1, [r3, #SSI_BAUDR_OFFSET] + + // Set 1-cycle sample delay. If PICO_FLASH_SPI_CLKDIV == 2 then this means, + // if the flash launches data on SCLK posedge, we capture it at the time that + // the next SCLK posedge is launched. This is shortly before that posedge + // arrives at the flash, so data hold time should be ok. For + // PICO_FLASH_SPI_CLKDIV > 2 this pretty much has no effect. + + movs r1, #1 + movs r2, #SSI_RX_SAMPLE_DLY_OFFSET // == 0xf0 so need 8 bits of offset significance + str r1, [r3, r2] + +// On QSPI parts we usually need a 01h SR-write command to enable QSPI mode +// (i.e. turn WPn and HOLDn into IO2/IO3) +#ifdef PROGRAM_STATUS_REG +program_sregs: +#define CTRL0_SPI_TXRX \ + (7 << SSI_CTRLR0_DFS_32_LSB) | /* 8 bits per data frame */ \ + (SSI_CTRLR0_TMOD_VALUE_TX_AND_RX << SSI_CTRLR0_TMOD_LSB) + + ldr r1, =(CTRL0_SPI_TXRX) + str r1, [r3, #SSI_CTRLR0_OFFSET] + + // Enable SSI and select slave 0 + movs r1, #1 + str r1, [r3, #SSI_SSIENR_OFFSET] + + // Check whether SR needs updating + movs r0, #CMD_READ_STATUS2 + bl read_flash_sreg + movs r2, #SREG_DATA + cmp r0, r2 + beq skip_sreg_programming + + // Send write enable command + movs r1, #CMD_WRITE_ENABLE + str r1, [r3, #SSI_DR0_OFFSET] + + // Poll for completion and discard RX + bl wait_ssi_ready + ldr r1, [r3, #SSI_DR0_OFFSET] + + // Send status write command followed by data bytes + movs r1, #CMD_WRITE_STATUS + str r1, [r3, #SSI_DR0_OFFSET] + movs r0, #0 + str r0, [r3, #SSI_DR0_OFFSET] + str r2, [r3, #SSI_DR0_OFFSET] + + bl wait_ssi_ready + ldr r1, [r3, #SSI_DR0_OFFSET] + ldr r1, [r3, #SSI_DR0_OFFSET] + ldr r1, [r3, #SSI_DR0_OFFSET] + + // Poll status register for write completion +1: + movs r0, #CMD_READ_STATUS + bl read_flash_sreg + movs r1, #1 + tst r0, r1 + bne 1b + +skip_sreg_programming: + + // Disable SSI again so that it can be reconfigured + movs r1, #0 + str r1, [r3, #SSI_SSIENR_OFFSET] +#endif + +// Currently the flash expects an 8 bit serial command prefix on every +// transfer, which is a waste of cycles. Perform a dummy Fast Read Quad I/O +// command, with mode bits set such that the flash will not expect a serial +// command prefix on *subsequent* transfers. We don't care about the results +// of the read, the important part is the mode bits. + +dummy_read: +#define CTRLR0_ENTER_XIP \ + (FRAME_FORMAT /* Quad I/O mode */ \ + << SSI_CTRLR0_SPI_FRF_LSB) | \ + (31 << SSI_CTRLR0_DFS_32_LSB) | /* 32 data bits */ \ + (SSI_CTRLR0_TMOD_VALUE_EEPROM_READ /* Send INST/ADDR, Receive Data */ \ + << SSI_CTRLR0_TMOD_LSB) + + ldr r1, =(CTRLR0_ENTER_XIP) + str r1, [r3, #SSI_CTRLR0_OFFSET] + + movs r1, #0x0 // NDF=0 (single 32b read) + str r1, [r3, #SSI_CTRLR1_OFFSET] + +#define SPI_CTRLR0_ENTER_XIP \ + (ADDR_L << SSI_SPI_CTRLR0_ADDR_L_LSB) | /* Address + mode bits */ \ + (WAIT_CYCLES << SSI_SPI_CTRLR0_WAIT_CYCLES_LSB) | /* Hi-Z dummy clocks following address + mode */ \ + (SSI_SPI_CTRLR0_INST_L_VALUE_8B \ + << SSI_SPI_CTRLR0_INST_L_LSB) | /* 8-bit instruction */ \ + (SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_1C2A /* Send Command in serial mode then address in Quad I/O mode */ \ + << SSI_SPI_CTRLR0_TRANS_TYPE_LSB) + + ldr r1, =(SPI_CTRLR0_ENTER_XIP) + ldr r0, =(XIP_SSI_BASE + SSI_SPI_CTRLR0_OFFSET) // SPI_CTRL0 Register + str r1, [r0] + + movs r1, #1 // Re-enable SSI + str r1, [r3, #SSI_SSIENR_OFFSET] + + movs r1, #CMD_READ + str r1, [r3, #SSI_DR0_OFFSET] // Push SPI command into TX FIFO + movs r1, #MODE_CONTINUOUS_READ // 32-bit: 24 address bits (we don't care, so 0) and M[7:4]=1010 + str r1, [r3, #SSI_DR0_OFFSET] // Push Address into TX FIFO - this will trigger the transaction + + // Poll for completion + bl wait_ssi_ready + +// The flash is in a state where we can blast addresses in parallel, and get +// parallel data back. Now configure the SSI to translate XIP bus accesses +// into QSPI transfers of this form. + + movs r1, #0 + str r1, [r3, #SSI_SSIENR_OFFSET] // Disable SSI (and clear FIFO) to allow further config + +// Note that the INST_L field is used to select what XIP data gets pushed into +// the TX FIFO: +// INST_L_0_BITS {ADDR[23:0],XIP_CMD[7:0]} Load "mode bits" into XIP_CMD +// Anything else {XIP_CMD[7:0],ADDR[23:0]} Load SPI command into XIP_CMD +configure_ssi: +#define SPI_CTRLR0_XIP \ + (MODE_CONTINUOUS_READ /* Mode bits to keep flash in continuous read mode */ \ + << SSI_SPI_CTRLR0_XIP_CMD_LSB) | \ + (ADDR_L << SSI_SPI_CTRLR0_ADDR_L_LSB) | /* Total number of address + mode bits */ \ + (WAIT_CYCLES << SSI_SPI_CTRLR0_WAIT_CYCLES_LSB) | /* Hi-Z dummy clocks following address + mode */ \ + (SSI_SPI_CTRLR0_INST_L_VALUE_NONE /* Do not send a command, instead send XIP_CMD as mode bits after address */ \ + << SSI_SPI_CTRLR0_INST_L_LSB) | \ + (SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_2C2A /* Send Address in Quad I/O mode (and Command but that is zero bits long) */ \ + << SSI_SPI_CTRLR0_TRANS_TYPE_LSB) + + ldr r1, =(SPI_CTRLR0_XIP) + + ldr r0, =(XIP_SSI_BASE + SSI_SPI_CTRLR0_OFFSET) + str r1, [r0] + + movs r1, #1 + str r1, [r3, #SSI_SSIENR_OFFSET] // Re-enable SSI + +// Bus accesses to the XIP window will now be transparently serviced by the +// external flash on cache miss. We are ready to run code from flash. + + +// +// Helper Includes +// + +// +// #include "boot2_helpers/exit_from_boot2.S" +// + +// If entered from the bootrom, lr (which we earlier pushed) will be 0, +// and we vector through the table at the start of the main flash image. +// Any regular function call will have a nonzero value for lr. +check_return: + pop {r0} + cmp r0, #0 + beq vector_into_flash + bx r0 +vector_into_flash: + ldr r0, =(XIP_BASE + 0x100) + ldr r1, =(PPB_BASE + M0PLUS_VTOR_OFFSET) + str r0, [r1] + ldmia r0, {r0, r1} + msr msp, r0 + bx r1 + +// +// #include "boot2_helpers/wait_ssi_ready.S" +// +wait_ssi_ready: + push {r0, r1, lr} + + // Command is complete when there is nothing left to send + // (TX FIFO empty) and SSI is no longer busy (CSn deasserted) +1: + ldr r1, [r3, #SSI_SR_OFFSET] + movs r0, #SSI_SR_TFE_BITS + tst r1, r0 + beq 1b + movs r0, #SSI_SR_BUSY_BITS + tst r1, r0 + bne 1b + + pop {r0, r1, pc} + + +#ifdef PROGRAM_STATUS_REG + +// +// #include "boot2_helpers/read_flash_sreg.S" +// + +// Pass status read cmd into r0. +// Returns status value in r0. +.global read_flash_sreg +.type read_flash_sreg,%function +.thumb_func +read_flash_sreg: + push {r1, lr} + str r0, [r3, #SSI_DR0_OFFSET] + // Dummy byte: + str r0, [r3, #SSI_DR0_OFFSET] + + bl wait_ssi_ready + // Discard first byte and combine the next two + ldr r0, [r3, #SSI_DR0_OFFSET] + ldr r0, [r3, #SSI_DR0_OFFSET] + + pop {r1, pc} + +#endif + +.global literals +literals: +.ltorg + +.end diff --git a/targets/rp2040.json b/targets/rp2040.json index ce49ba356..c679758e6 100644 --- a/targets/rp2040.json +++ b/targets/rp2040.json @@ -6,6 +6,7 @@ "msd-firmware-name": "firmware.uf2", "binary-format": "uf2", "uf2-family-id": "0xe48bff56", + "rp2040-boot-patch": true, "extra-files": [ "src/device/rp/rp2040.s" ] diff --git a/targets/rp2040.ld b/targets/rp2040.ld index 186db68f6..5aa57ce69 100644 --- a/targets/rp2040.ld +++ b/targets/rp2040.ld @@ -1,9 +1,29 @@ -MEMORY -{ - RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256k -} - _stack_size = 2K; +SECTIONS +{ + /* Second stage bootloader is prepended to the image. It must be 256 bytes + and checksummed. The gap to the checksum is zero-padded. + */ + .boot2 : { + __boot2_start__ = .; + KEEP (*(.boot2)); + + /* Explicitly allocate space for CRC32 checksum at end of second stage + bootloader + */ + . = __boot2_start__ + 256 - 4; + LONG(0) + } > BOOT2_TEXT = 0x0 + + /* The second stage will always enter the image at the start of .text. + The debugger will use the ELF entry point, which is the _entry_point + symbol if present, otherwise defaults to start of .text. + This can be used to transfer control back to the bootrom on debugger + launches only, to perform proper flash setup. + */ +} + + INCLUDE "targets/arm.ld" From f2e8d7112cbe9a05d411c23a98cb99edfcf45369 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 4 Jun 2021 14:43:25 +0200 Subject: [PATCH 30/70] compiler: refactor method names This commit includes two changes: * It makes unexported interface methods package-private, so that it's not possible to type-assert on an unexported method in a different package. * It makes the globals used to identify interface methods defined globals, so that they can (eventually) be left in the program for an eventual non-LTO build mode. --- compiler/compiler.go | 2 +- compiler/interface.go | 15 +++++++++++++-- compiler/testdata/interface.ll | 12 ++++++------ transform/interface-lowering.go | 10 +++++++--- transform/rtcalls.go | 2 +- transform/testdata/interface.ll | 12 ++++++------ transform/testdata/reflect-implements.ll | 14 +++++++------- transform/testdata/reflect-implements.out.ll | 12 ++++++------ 8 files changed, 47 insertions(+), 32 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index f25832acf..925b61bf8 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -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 = 10 // last change: context parameter in go wrapper +const Version = 11 // last change: change method name globals func init() { llvm.InitializeAllTargets() diff --git a/compiler/interface.go b/compiler/interface.go index eda7e3653..5d8895d71 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -311,10 +311,21 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value { // used during the interface lowering pass. func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value { signature := methodSignature(method) - signatureGlobal := c.mod.NamedGlobal("func " + signature) + var globalName string + if token.IsExported(method.Name()) { + globalName = "reflect/methods." + signature + } else { + globalName = method.Type().(*types.Signature).Recv().Pkg().Path() + ".$methods." + signature + } + signatureGlobal := c.mod.NamedGlobal(globalName) if signatureGlobal.IsNil() { - signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), "func "+signature) + // TODO: put something useful in these globals, such as the method + // signature. Useful to one day implement reflect.Value.Method(n). + signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), globalName) + signatureGlobal.SetInitializer(llvm.ConstInt(c.ctx.Int8Type(), 0, false)) + signatureGlobal.SetLinkage(llvm.LinkOnceODRLinkage) signatureGlobal.SetGlobalConstant(true) + signatureGlobal.SetAlignment(1) } return signatureGlobal } diff --git a/compiler/testdata/interface.ll b/compiler/testdata/interface.ll index eb217bdc2..be9e229a1 100644 --- a/compiler/testdata/interface.ll +++ b/compiler/testdata/interface.ll @@ -13,15 +13,15 @@ target triple = "wasm32--wasi" @"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null } @"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:named:error" } @"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" } -@"func Error() string" = external constant i8 -@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"] +@"reflect/methods.Error() string" = linkonce_odr constant i8 0, align 1 +@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"] @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null } @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null } @"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" } -@"func String() string" = external constant i8 -@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"] +@"reflect/methods.String() string" = linkonce_odr constant i8 0, align 1 +@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.String() string"] @"reflect/types.typeid:basic:int" = external constant i8 -@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"] +@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"] declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) @@ -92,7 +92,7 @@ typeassert.next: ; preds = %typeassert.ok, %ent define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr { entry: - %invoke.func = call i32 @runtime.interfaceMethod(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* nonnull @"func Error() string", i8* undef, i8* null) + %invoke.func = call i32 @runtime.interfaceMethod(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* nonnull @"reflect/methods.Error() string", i8* undef, i8* null) %invoke.func.cast = inttoptr i32 %invoke.func to %runtime._string (i8*, i8*, i8*)* %0 = call %runtime._string %invoke.func.cast(i8* %itf.value, i8* undef, i8* undef) ret %runtime._string %0 diff --git a/transform/interface-lowering.go b/transform/interface-lowering.go index 79b3361af..57c8c76d2 100644 --- a/transform/interface-lowering.go +++ b/transform/interface-lowering.go @@ -54,10 +54,14 @@ type signatureInfo struct { // methodName takes a method name like "func String()" and returns only the // name, which is "String" in this case. func (s *signatureInfo) methodName() string { - if !strings.HasPrefix(s.name, "func ") { - panic("signature must start with \"func \"") + var methodName string + if strings.HasPrefix(s.name, "reflect/methods.") { + methodName = s.name[len("reflect/methods."):] + } else if idx := strings.LastIndex(s.name, ".$methods."); idx >= 0 { + methodName = s.name[idx+len(".$methods."):] + } else { + panic("could not find method name") } - methodName := s.name[len("func "):] if openingParen := strings.IndexByte(methodName, '('); openingParen < 0 { panic("no opening paren in signature name") } else { diff --git a/transform/rtcalls.go b/transform/rtcalls.go index 0ced0bfea..c10b3c285 100644 --- a/transform/rtcalls.go +++ b/transform/rtcalls.go @@ -105,7 +105,7 @@ func OptimizeStringEqual(mod llvm.Module) { // As of this writing, the (reflect.Type).Interface method has not yet been // implemented so this optimization is critical for the encoding/json package. func OptimizeReflectImplements(mod llvm.Module) { - implementsSignature := mod.NamedGlobal("func Implements(reflect.Type) bool") + implementsSignature := mod.NamedGlobal("reflect/methods.Implements(reflect.Type) bool") if implementsSignature.IsNil() { return } diff --git a/transform/testdata/interface.ll b/transform/testdata/interface.ll index c67595a2e..2101725a3 100644 --- a/transform/testdata/interface.ll +++ b/transform/testdata/interface.ll @@ -8,11 +8,11 @@ target triple = "armv7m-none-eabi" @"reflect/types.typeid:basic:uint8" = external constant i8 @"reflect/types.typeid:basic:int16" = external constant i8 @"reflect/types.type:basic:int" = private constant %runtime.typecodeID zeroinitializer -@"func NeverImplementedMethod()" = external constant i8 -@"Unmatched$interface" = private constant [1 x i8*] [i8* @"func NeverImplementedMethod()"] -@"func Double() int" = external constant i8 -@"Doubler$interface" = private constant [1 x i8*] [i8* @"func Double() int"] -@"Number$methodset" = private constant [1 x %runtime.interfaceMethodInfo] [%runtime.interfaceMethodInfo { i8* @"func Double() int", i32 ptrtoint (i32 (i8*, i8*)* @"(Number).Double$invoke" to i32) }] +@"reflect/methods.NeverImplementedMethod()" = linkonce_odr constant i8 0 +@"Unmatched$interface" = private constant [1 x i8*] [i8* @"reflect/methods.NeverImplementedMethod()"] +@"reflect/methods.Double() int" = linkonce_odr constant i8 0 +@"Doubler$interface" = private constant [1 x i8*] [i8* @"reflect/methods.Double() int"] +@"Number$methodset" = private constant [1 x %runtime.interfaceMethodInfo] [%runtime.interfaceMethodInfo { i8* @"reflect/methods.Double() int", i32 ptrtoint (i32 (i8*, i8*)* @"(Number).Double$invoke" to i32) }] @"reflect/types.type:named:Number" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* getelementptr inbounds ([1 x %runtime.interfaceMethodInfo], [1 x %runtime.interfaceMethodInfo]* @"Number$methodset", i32 0, i32 0) } declare i1 @runtime.interfaceImplements(i32, i8**) @@ -48,7 +48,7 @@ typeswitch.notUnmatched: br i1 %isDoubler, label %typeswitch.Doubler, label %typeswitch.notDoubler typeswitch.Doubler: - %doubler.func = call i32 @runtime.interfaceMethod(i32 %typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"Doubler$interface", i32 0, i32 0), i8* nonnull @"func Double() int") + %doubler.func = call i32 @runtime.interfaceMethod(i32 %typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"Doubler$interface", i32 0, i32 0), i8* nonnull @"reflect/methods.Double() int") %doubler.func.cast = inttoptr i32 %doubler.func to i32 (i8*, i8*)* %doubler.result = call i32 %doubler.func.cast(i8* %value, i8* null) call void @runtime.printint32(i32 %doubler.result) diff --git a/transform/testdata/reflect-implements.ll b/transform/testdata/reflect-implements.ll index 22c76485d..29f382432 100644 --- a/transform/testdata/reflect-implements.ll +++ b/transform/testdata/reflect-implements.ll @@ -6,11 +6,11 @@ target triple = "i686--linux" @"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null } @"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null } -@"func Error() string" = external constant i8 -@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"] -@"func Align() int" = external constant i8 -@"func Implements(reflect.Type) bool" = external constant i8 -@"reflect.Type$interface" = linkonce_odr constant [2 x i8*] [i8* @"func Align() int", i8* @"func Implements(reflect.Type) bool"] +@"reflect/methods.Error() string" = linkonce_odr constant i8 0 +@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"] +@"reflect/methods.Align() int" = linkonce_odr constant i8 0 +@"reflect/methods.Implements(reflect.Type) bool" = linkonce_odr constant i8 0 +@"reflect.Type$interface" = linkonce_odr constant [2 x i8*] [i8* @"reflect/methods.Align() int", i8* @"reflect/methods.Implements(reflect.Type) bool"] @"reflect/types.type:named:reflect.rawType" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:uintptr", i32 0, %runtime.interfaceMethodInfo* getelementptr inbounds ([20 x %runtime.interfaceMethodInfo], [20 x %runtime.interfaceMethodInfo]* @"reflect.rawType$methodset", i32 0, i32 0) } @"reflect.rawType$methodset" = linkonce_odr constant [20 x %runtime.interfaceMethodInfo] zeroinitializer @"reflect/types.type:basic:uintptr" = linkonce_odr constant %runtime.typecodeID zeroinitializer @@ -28,7 +28,7 @@ declare i32 @runtime.interfaceMethod(i32, i8**, i8*, i8*, i8*) ; known at compile time (after the interp pass has run). define i1 @main.isError(i32 %typ.typecode, i8* %typ.value, i8* %context, i8* %parentHandle) { entry: - %invoke.func = call i32 @runtime.interfaceMethod(i32 %typ.typecode, i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"reflect.Type$interface", i32 0, i32 0), i8* nonnull @"func Implements(reflect.Type) bool", i8* undef, i8* null) + %invoke.func = call i32 @runtime.interfaceMethod(i32 %typ.typecode, i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"reflect.Type$interface", i32 0, i32 0), i8* nonnull @"reflect/methods.Implements(reflect.Type) bool", i8* undef, i8* null) %invoke.func.cast = inttoptr i32 %invoke.func to i1 (i8*, i32, i8*, i8*, i8*)* %result = call i1 %invoke.func.cast(i8* %typ.value, i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:reflect.rawType" to i32), i8* bitcast (%runtime.typecodeID* @"reflect/types.type:named:error" to i8*), i8* undef, i8* undef) ret i1 %result @@ -41,7 +41,7 @@ entry: ; } define i1 @main.isUnknown(i32 %typ.typecode, i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) { entry: - %invoke.func = call i32 @runtime.interfaceMethod(i32 %typ.typecode, i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"reflect.Type$interface", i32 0, i32 0), i8* nonnull @"func Implements(reflect.Type) bool", i8* undef, i8* null) + %invoke.func = call i32 @runtime.interfaceMethod(i32 %typ.typecode, i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"reflect.Type$interface", i32 0, i32 0), i8* nonnull @"reflect/methods.Implements(reflect.Type) bool", i8* undef, i8* null) %invoke.func.cast = inttoptr i32 %invoke.func to i1 (i8*, i32, i8*, i8*, i8*)* %result = call i1 %invoke.func.cast(i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* undef, i8* undef) ret i1 %result diff --git a/transform/testdata/reflect-implements.out.ll b/transform/testdata/reflect-implements.out.ll index 03d977699..f1bc92c11 100644 --- a/transform/testdata/reflect-implements.out.ll +++ b/transform/testdata/reflect-implements.out.ll @@ -6,11 +6,11 @@ target triple = "i686--linux" @"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null } @"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null } -@"func Error() string" = external constant i8 -@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"] -@"func Align() int" = external constant i8 -@"func Implements(reflect.Type) bool" = external constant i8 -@"reflect.Type$interface" = linkonce_odr constant [2 x i8*] [i8* @"func Align() int", i8* @"func Implements(reflect.Type) bool"] +@"reflect/methods.Error() string" = linkonce_odr constant i8 0 +@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"] +@"reflect/methods.Align() int" = linkonce_odr constant i8 0 +@"reflect/methods.Implements(reflect.Type) bool" = linkonce_odr constant i8 0 +@"reflect.Type$interface" = linkonce_odr constant [2 x i8*] [i8* @"reflect/methods.Align() int", i8* @"reflect/methods.Implements(reflect.Type) bool"] @"reflect/types.type:named:reflect.rawType" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:uintptr", i32 0, %runtime.interfaceMethodInfo* getelementptr inbounds ([20 x %runtime.interfaceMethodInfo], [20 x %runtime.interfaceMethodInfo]* @"reflect.rawType$methodset", i32 0, i32 0) } @"reflect.rawType$methodset" = linkonce_odr constant [20 x %runtime.interfaceMethodInfo] zeroinitializer @"reflect/types.type:basic:uintptr" = linkonce_odr constant %runtime.typecodeID zeroinitializer @@ -28,7 +28,7 @@ entry: define i1 @main.isUnknown(i32 %typ.typecode, i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) { entry: - %invoke.func = call i32 @runtime.interfaceMethod(i32 %typ.typecode, i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"reflect.Type$interface", i32 0, i32 0), i8* nonnull @"func Implements(reflect.Type) bool", i8* undef, i8* null) + %invoke.func = call i32 @runtime.interfaceMethod(i32 %typ.typecode, i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"reflect.Type$interface", i32 0, i32 0), i8* nonnull @"reflect/methods.Implements(reflect.Type) bool", i8* undef, i8* null) %invoke.func.cast = inttoptr i32 %invoke.func to i1 (i8*, i32, i8*, i8*, i8*)* %result = call i1 %invoke.func.cast(i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* undef, i8* undef) ret i1 %result From 28a2ad47575f74159cd4baf662d6c2849e1af78d Mon Sep 17 00:00:00 2001 From: Yurii Soldak Date: Wed, 19 May 2021 23:06:59 +0200 Subject: [PATCH 31/70] gdb: use "extended-remote" instead of "remote", allows connect from another client My gdb complains bare "remote" command is deprecated On top of that "extended-remote" allows "disconnect" command that enables attaching from another debug client, like an IDE See https://sourceware.org/gdb/current/onlinedocs/gdb/Connecting.html --- main.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index c3cca64f7..992affa5c 100644 --- a/main.go +++ b/main.go @@ -434,7 +434,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro case "native": // Run GDB directly. case "openocd": - gdbCommands = append(gdbCommands, "target remote :3333", "monitor halt", "load", "monitor reset halt") + gdbCommands = append(gdbCommands, "target extended-remote :3333", "monitor halt", "load", "monitor reset halt") // We need a separate debugging daemon for on-chip debugging. args, err := config.OpenOCDConfiguration() @@ -453,7 +453,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro daemon.Stderr = w } case "jlink": - gdbCommands = append(gdbCommands, "target remote :2331", "load", "monitor reset halt") + gdbCommands = append(gdbCommands, "target extended-remote :2331", "load", "monitor reset halt") // We need a separate debugging daemon for on-chip debugging. daemon = executeCommand(config.Options, "JLinkGDBServer", "-device", config.Target.JLinkDevice) @@ -468,7 +468,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro daemon.Stderr = w } case "qemu": - gdbCommands = append(gdbCommands, "target remote :1234") + gdbCommands = append(gdbCommands, "target extended-remote :1234") // Run in an emulator. args := append(config.Target.Emulator[1:], result.Binary, "-s", "-S") @@ -476,7 +476,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro daemon.Stdout = os.Stdout daemon.Stderr = os.Stderr case "qemu-user": - gdbCommands = append(gdbCommands, "target remote :1234") + gdbCommands = append(gdbCommands, "target extended-remote :1234") // Run in an emulator. args := append(config.Target.Emulator[1:], "-g", "1234", result.Binary) @@ -484,7 +484,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro daemon.Stdout = os.Stdout daemon.Stderr = os.Stderr case "mgba": - gdbCommands = append(gdbCommands, "target remote :2345") + gdbCommands = append(gdbCommands, "target extended-remote :2345") // Run in an emulator. args := append(config.Target.Emulator[1:], result.Binary, "-g") @@ -492,7 +492,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro daemon.Stdout = os.Stdout daemon.Stderr = os.Stderr case "simavr": - gdbCommands = append(gdbCommands, "target remote :1234") + gdbCommands = append(gdbCommands, "target extended-remote :1234") // Run in an emulator. args := append(config.Target.Emulator[1:], "-g", result.Binary) From d1f445735cae8f8e118b5deec577734139464716 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 17 Jun 2021 23:56:11 +0200 Subject: [PATCH 32/70] syscall: fix int type in libc version Int in Go and C are two different types (hence why CGo has C.int). The code in syscall assumed they were of the same type, which led to a bug: https://github.com/tinygo-org/tinygo/issues/1957 While the C standard makes no guarantees on the size of int, in most modern operating systems it is 32-bits so Go int32 would be the correct choice. --- src/syscall/syscall_libc.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index 2612938de..80675f191 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -13,7 +13,7 @@ type sliceHeader struct { } func Close(fd int) (err error) { - if libc_close(fd) < 0 { + if libc_close(int32(fd)) < 0 { err = getErrno() } return @@ -21,7 +21,7 @@ func Close(fd int) (err error) { func Write(fd int, p []byte) (n int, err error) { buf, count := splitSlice(p) - n = libc_write(fd, buf, uint(count)) + n = libc_write(int32(fd), buf, uint(count)) if n < 0 { err = getErrno() } @@ -30,7 +30,7 @@ func Write(fd int, p []byte) (n int, err error) { func Read(fd int, p []byte) (n int, err error) { buf, count := splitSlice(p) - n = libc_read(fd, buf, uint(count)) + n = libc_read(int32(fd), buf, uint(count)) if n < 0 { err = getErrno() } @@ -43,7 +43,7 @@ func Seek(fd int, offset int64, whence int) (off int64, err error) { func Open(path string, flag int, mode uint32) (fd int, err error) { data := append([]byte(path), 0) - fd = libc_open(&data[0], flag, mode) + fd = int(libc_open(&data[0], int32(flag), mode)) if fd < 0 { err = getErrno() } @@ -91,7 +91,7 @@ func splitSlice(p []byte) (buf *byte, len uintptr) { // ssize_t write(int fd, const void *buf, size_t count) //export write -func libc_write(fd int, buf *byte, count uint) int +func libc_write(fd int32, buf *byte, count uint) int // char *getenv(const char *name); //export getenv @@ -99,12 +99,12 @@ func libc_getenv(name *byte) *byte // ssize_t read(int fd, void *buf, size_t count); //export read -func libc_read(fd int, buf *byte, count uint) int +func libc_read(fd int32, buf *byte, count uint) int // int open(const char *pathname, int flags, mode_t mode); //export open -func libc_open(pathname *byte, flags int, mode uint32) int +func libc_open(pathname *byte, flags int32, mode uint32) int32 // int close(int fd) //export close -func libc_close(fd int) int +func libc_close(fd int32) int32 From cd628bcde6d86cac40be31993bac4338120d75ff Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 18 Jun 2021 09:29:09 +0200 Subject: [PATCH 33/70] nrf52840: add support for flashing with the BOSSA tool This only works with a custom bossac build from Arduino, not with the upstream version. It avoids needing the manual "double tap" to enter bootloader mode before flashing firmware. --- .../machine_nrf52840_usb_reset_bossa.go | 26 +++++++++++++++++++ .../machine_nrf52840_usb_reset_none.go | 2 +- targets/nano-33-ble.json | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 src/machine/machine_nrf52840_usb_reset_bossa.go diff --git a/src/machine/machine_nrf52840_usb_reset_bossa.go b/src/machine/machine_nrf52840_usb_reset_bossa.go new file mode 100644 index 000000000..42c6ad000 --- /dev/null +++ b/src/machine/machine_nrf52840_usb_reset_bossa.go @@ -0,0 +1,26 @@ +// +build nrf52840,nrf52840_reset_bossa + +package machine + +import ( + "device/arm" + "device/nrf" +) + +const DFU_MAGIC_SERIAL_ONLY_RESET = 0xb0 + +// checkShouldReset is called by the USB-CDC implementation to check whether to +// reset into the bootloader/OTA and if so, resets the chip appropriately. +func checkShouldReset() { + if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 { + EnterSerialBootloader() + } +} + +// EnterSerialBootloader resets the chip into the serial bootloader. After +// reset, it can be flashed using serial/nrfutil. +func EnterSerialBootloader() { + arm.DisableInterrupts() + nrf.POWER.GPREGRET.Set(DFU_MAGIC_SERIAL_ONLY_RESET) + arm.SystemReset() +} diff --git a/src/machine/machine_nrf52840_usb_reset_none.go b/src/machine/machine_nrf52840_usb_reset_none.go index 08c615eb7..796a021e7 100644 --- a/src/machine/machine_nrf52840_usb_reset_none.go +++ b/src/machine/machine_nrf52840_usb_reset_none.go @@ -1,4 +1,4 @@ -// +build nrf52840,!nrf52840_reset_uf2 +// +build nrf52840,!nrf52840_reset_uf2,!nrf52840_reset_bossa package machine diff --git a/targets/nano-33-ble.json b/targets/nano-33-ble.json index 53b4fcac7..df22221fd 100644 --- a/targets/nano-33-ble.json +++ b/targets/nano-33-ble.json @@ -1,6 +1,6 @@ { "inherits": ["nrf52840"], - "build-tags": ["nano_33_ble"], + "build-tags": ["nano_33_ble", "nrf52840_reset_bossa"], "flash-command": "bossac_arduino2 -d -i -e -w -v -R --port={port} {bin}", "flash-1200-bps-reset": "true", "linkerscript": "targets/nano-33-ble.ld" From 1913cb76a5c6996c2d90107bad0a8bd0a4512327 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 18 Jun 2021 09:39:12 +0200 Subject: [PATCH 34/70] cortexm: bump default stack size to 2048 bytes Previously it was 1024 bytes, which occasionally ran into a stack overflow. I hope that 2048 bytes will be enough for most purposes. I've also removed some 2048-byte stack size settings in JSON files, which are unnecessary now that the parent (cortex-m.json) sets them. --- targets/cortex-m.json | 2 +- targets/feather-stm32f405.json | 1 - targets/grandcentral-m4.json | 3 +-- targets/p1am-100.json | 3 +-- targets/pygamer.json | 3 +-- targets/pyportal.json | 3 +-- targets/wioterminal.json | 3 +-- 7 files changed, 6 insertions(+), 12 deletions(-) diff --git a/targets/cortex-m.json b/targets/cortex-m.json index eed685c78..0021d03cb 100644 --- a/targets/cortex-m.json +++ b/targets/cortex-m.json @@ -8,7 +8,7 @@ "rtlib": "compiler-rt", "libc": "picolibc", "automatic-stack-size": true, - "default-stack-size": 1024, + "default-stack-size": 2048, "cflags": [ "-Oz", "-mthumb", diff --git a/targets/feather-stm32f405.json b/targets/feather-stm32f405.json index 0e86cd4c5..d3bb1dc9c 100644 --- a/targets/feather-stm32f405.json +++ b/targets/feather-stm32f405.json @@ -2,7 +2,6 @@ "inherits": ["cortex-m4"], "build-tags": ["feather_stm32f405", "stm32f405", "stm32f4", "stm32"], "automatic-stack-size": false, - "default-stack-size": 1024, "linkerscript": "targets/stm32f405.ld", "extra-files": [ "src/device/stm32/stm32f405.s" diff --git a/targets/grandcentral-m4.json b/targets/grandcentral-m4.json index 6229d4565..4545dfd9f 100644 --- a/targets/grandcentral-m4.json +++ b/targets/grandcentral-m4.json @@ -5,6 +5,5 @@ "flash-method": "msd", "msd-volume-name": "GCM4BOOT", "msd-firmware-name": "firmware.uf2", - "openocd-interface": "jlink", - "default-stack-size": 2048 + "openocd-interface": "jlink" } diff --git a/targets/p1am-100.json b/targets/p1am-100.json index be9b0e446..fcfaff1ac 100644 --- a/targets/p1am-100.json +++ b/targets/p1am-100.json @@ -2,6 +2,5 @@ "inherits": ["atsamd21g18a"], "build-tags": ["sam", "atsamd21g18a", "p1am_100"], "flash-command": "bossac -d -i -e -w -v -R --port={port} --offset=0x2000 {bin}", - "flash-1200-bps-reset": "true", - "default-stack-size": 2048 + "flash-1200-bps-reset": "true" } diff --git a/targets/pygamer.json b/targets/pygamer.json index 71244afe7..63dc34a18 100644 --- a/targets/pygamer.json +++ b/targets/pygamer.json @@ -4,6 +4,5 @@ "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "PYGAMERBOOT", - "msd-firmware-name": "arcade.uf2", - "default-stack-size": 2048 + "msd-firmware-name": "arcade.uf2" } diff --git a/targets/pyportal.json b/targets/pyportal.json index 14b95ef8e..e03efcb6d 100644 --- a/targets/pyportal.json +++ b/targets/pyportal.json @@ -4,6 +4,5 @@ "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "PORTALBOOT", - "msd-firmware-name": "firmware.uf2", - "default-stack-size": 2048 + "msd-firmware-name": "firmware.uf2" } diff --git a/targets/wioterminal.json b/targets/wioterminal.json index 8595b7c93..093947e16 100644 --- a/targets/wioterminal.json +++ b/targets/wioterminal.json @@ -4,6 +4,5 @@ "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "Arduino", - "msd-firmware-name": "firmware.uf2", - "default-stack-size": 2048 + "msd-firmware-name": "firmware.uf2" } From 8e33f1c9ebd267ba1bcc0d05ee55aab1ff268b78 Mon Sep 17 00:00:00 2001 From: Kenneth Bell Date: Mon, 14 Jun 2021 18:50:28 -0700 Subject: [PATCH 35/70] rp2040: support Adafruit Feather RP2040 --- Makefile | 2 + README.md | 3 +- src/machine/board_feather_rp2040.go | 10 ++ targets/feather-rp2040-boot-stage2.S | 17 +++ targets/feather-rp2040.json | 10 ++ targets/feather-rp2040.ld | 10 ++ targets/pico-boot-stage2.S | 17 +++ targets/pico.json | 2 +- ...ico_boot_stage2.S => rp2040-boot-stage2.S} | 101 +++++++++++++----- 9 files changed, 145 insertions(+), 27 deletions(-) create mode 100644 src/machine/board_feather_rp2040.go create mode 100644 targets/feather-rp2040-boot-stage2.S create mode 100644 targets/feather-rp2040.json create mode 100644 targets/feather-rp2040.ld create mode 100644 targets/pico-boot-stage2.S rename targets/{pico_boot_stage2.S => rp2040-boot-stage2.S} (82%) diff --git a/Makefile b/Makefile index 09977ccd0..e2981789b 100644 --- a/Makefile +++ b/Makefile @@ -358,6 +358,8 @@ smoketest: @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=pico examples/blinky1 @$(MD5SUM) test.hex + $(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/blinky1 + @$(MD5SUM) test.hex # test pwm $(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm @$(MD5SUM) test.hex diff --git a/README.md b/README.md index 21941e96b..ae3a8b526 100644 --- a/README.md +++ b/README.md @@ -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 62 microcontroller boards are currently supported: +The following 63 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 62 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 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) * [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727) diff --git a/src/machine/board_feather_rp2040.go b/src/machine/board_feather_rp2040.go new file mode 100644 index 000000000..7894c5cff --- /dev/null +++ b/src/machine/board_feather_rp2040.go @@ -0,0 +1,10 @@ +// +build feather_rp2040 + +package machine + +const ( + LED = GPIO13 + + // Onboard crystal oscillator frequency, in MHz. + xoscFreq = 12 // MHz +) diff --git a/targets/feather-rp2040-boot-stage2.S b/targets/feather-rp2040-boot-stage2.S new file mode 100644 index 000000000..e6e694a4c --- /dev/null +++ b/targets/feather-rp2040-boot-stage2.S @@ -0,0 +1,17 @@ +// Adafruit Feather RP2040 Stage 2 Bootloader + +// +// This file defines the parameters specific to the flash-chip found +// on the Adafruit Feather RP2040. The generic implementation is in +// rp2040-boot-stage2.S +// + +#define BOARD_PICO_FLASH_SPI_CLKDIV 2 +#define BOARD_CMD_READ 0xe7 +#define BOARD_QUAD_OK 1 +#define BOARD_QUAD_ENABLE_STATUS_BYTE 2 +#define BOARD_QUAD_ENABLE_BIT_MASK 2 +#define BOARD_SPLIT_STATUS_WRITE 1 +#define BOARD_WAIT_CYCLES 2 + +#include "rp2040-boot-stage2.S" \ No newline at end of file diff --git a/targets/feather-rp2040.json b/targets/feather-rp2040.json new file mode 100644 index 000000000..45f6b60b6 --- /dev/null +++ b/targets/feather-rp2040.json @@ -0,0 +1,10 @@ +{ + "inherits": [ + "rp2040" + ], + "build-tags": ["feather_rp2040"], + "linkerscript": "targets/feather-rp2040.ld", + "extra-files": [ + "targets/feather-rp2040-boot-stage2.S" + ] +} diff --git a/targets/feather-rp2040.ld b/targets/feather-rp2040.ld new file mode 100644 index 000000000..d97942f5c --- /dev/null +++ b/targets/feather-rp2040.ld @@ -0,0 +1,10 @@ + +MEMORY +{ + /* Reserve exactly 256 bytes at start of flash for second stage bootloader */ + BOOT2_TEXT (rx) : ORIGIN = 0x10000000, LENGTH = 256 + FLASH_TEXT (rx) : ORIGIN = 0x10000000 + 256, LENGTH = 8192K - 256 + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256k +} + +INCLUDE "targets/rp2040.ld" \ No newline at end of file diff --git a/targets/pico-boot-stage2.S b/targets/pico-boot-stage2.S new file mode 100644 index 000000000..88dd55176 --- /dev/null +++ b/targets/pico-boot-stage2.S @@ -0,0 +1,17 @@ +// Raspberry Pi Pico Stage 2 Bootloader + +// +// This file defines the parameters specific to the flash-chip found +// on the official Pico boards. The generic implementation is in +// rp2040-boot-stage2.S +// + +#define BOARD_PICO_FLASH_SPI_CLKDIV 2 +#define BOARD_CMD_READ 0xeb +#define BOARD_QUAD_OK 1 +#define BOARD_QUAD_ENABLE_STATUS_BYTE 2 +#define BOARD_QUAD_ENABLE_BIT_MASK 2 +#define BOARD_SPLIT_STATUS_WRITE 0 +#define BOARD_WAIT_CYCLES 4 + +#include "rp2040-boot-stage2.S" \ No newline at end of file diff --git a/targets/pico.json b/targets/pico.json index e8e5a06b1..011d5110f 100644 --- a/targets/pico.json +++ b/targets/pico.json @@ -5,6 +5,6 @@ "build-tags": ["pico"], "linkerscript": "targets/pico.ld", "extra-files": [ - "targets/pico_boot_stage2.S" + "targets/pico-boot-stage2.S" ] } diff --git a/targets/pico_boot_stage2.S b/targets/rp2040-boot-stage2.S similarity index 82% rename from targets/pico_boot_stage2.S rename to targets/rp2040-boot-stage2.S index 274845b1a..8a16b6448 100644 --- a/targets/pico_boot_stage2.S +++ b/targets/rp2040-boot-stage2.S @@ -1,11 +1,21 @@ // -// Implementation of Pico stage 2 boot loader. This code is for the Winbond W25Q080 -// (as found in the Pico) from the official Pico SDK. +// Implementation of RP2040 stage 2 boot loader. This code is derived from the +// Winbond W25Q080 implementation (as found in the Pico) in the official Pico SDK. // // This implementation has been made 'stand-alone' by including necessary code / // symbols from the included files in the reference implementation directly into -// the source. Care has been taken to preserve ordering and it has been verified -// the generated binary is byte-for-byte identical to the reference code binary. +// the source. It has also been modified to include the conditional logic from +// the CircuitPython implementation that supports additional flash chips. The +// CiruitPython source is here: +// https://github.com/adafruit/circuitpython/blob/main/ports/raspberrypi/stage2.c.jinja +// +// This file cannot be assembled directly, instead assemble the board-specific file +// (such as pico-boot-stage2.S) which defines the parameters specific to the flash +// chip included on that board. +// +// Care has been taken to preserve ordering and it has been verified the generated +// binary is byte-for-byte identical to the reference code binary when assembled for +// the Pico. // // Note: the stage 2 boot loader must be 256 bytes in length and have a checksum // present. In TinyGo, the linker script is responsible for allocating 256 bytes @@ -19,10 +29,6 @@ // https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/boot_stage2/boot2_w25q080.S // -// Board Parameters -#define PICO_FLASH_SPI_CLKDIV 2 - - // ---------------------------------------------------------------------------- // Second stage boot code @@ -59,7 +65,8 @@ #define CMD_WRITE_ENABLE 0x06 #define CMD_READ_STATUS 0x05 #define CMD_READ_STATUS2 0x35 -#define CMD_WRITE_STATUS 0x01 +#define CMD_WRITE_STATUS1 0x01 +#define CMD_WRITE_STATUS2 0x31 #define SREG_DATA 0x02 // Enable quad-SPI mode #define XIP_BASE 0x10000000 @@ -123,18 +130,32 @@ // The bootrom is very conservative with SPI frequency, but here we should be // as aggressive as possible. -#ifndef PICO_FLASH_SPI_CLKDIV -#define PICO_FLASH_SPI_CLKDIV 4 -#endif +#define PICO_FLASH_SPI_CLKDIV BOARD_PICO_FLASH_SPI_CLKDIV #if PICO_FLASH_SPI_CLKDIV & 1 #error PICO_FLASH_SPI_CLKDIV must be even #endif +#if BOARD_QUAD_OK==1 // Define interface width: single/dual/quad IO -#define FRAME_FORMAT SSI_CTRLR0_SPI_FRF_VALUE_QUAD +#define FRAME_FORMAT SSI_CTRLR0_SPI_FRF_VALUE_QUAD +#define TRANSACTION_TYPE SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_2C2A +// Note that the INST_L field is used to select what XIP data gets pushed into +// the TX FIFO: +// INST_L_0_BITS {ADDR[23:0],XIP_CMD[7:0]} Load "mode bits" into XIP_CMD +// Anything else {XIP_CMD[7:0],ADDR[23:0]} Load SPI command into XIP_CMD +#define INSTRUCTION_LENGTH SSI_SPI_CTRLR0_INST_L_VALUE_NONE +#define READ_INSTRUCTION MODE_CONTINUOUS_READ +#define ADDR_L 8 // 6 for address, 2 for mode +#else +#define FRAME_FORMAT SSI_CTRLR0_SPI_FRF_VALUE_STD +#define TRANSACTION_TYPE SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_1C1A +#define INSTRUCTION_LENGTH SSI_SPI_CTRLR0_INST_L_VALUE_8B +#define READ_INSTRUCTION BOARD_CMD_READ +#define ADDR_L 6 // * 4 = 24 +#endif -// For W25Q080 this is the "Read data fast quad IO" instruction: -#define CMD_READ 0xeb +// The flash-chip specific read isntruction +#define CMD_READ BOARD_CMD_READ // "Mode bits" are 8 special bits sent immediately after // the address bits in a "Read Data Fast Quad I/O" command sequence. @@ -142,13 +163,10 @@ // next read does not require the 0xeb instruction prefix. #define MODE_CONTINUOUS_READ 0xa0 -// The number of address + mode bits, divided by 4 (always 4, not function of -// interface width). -#define ADDR_L 8 - // How many clocks of Hi-Z following the mode bits. For W25Q080, 4 dummy cycles // are required. -#define WAIT_CYCLES 4 +#define WAIT_CYCLES BOARD_WAIT_CYCLES + // If defined, we will read status reg, compare to SREG_DATA, and overwrite // with our value if the SR doesn't match. @@ -184,10 +202,14 @@ _stage2_boot: ldr r0, [r3, #PADS_QSPI_GPIO_QSPI_SD0_OFFSET] movs r1, #PADS_QSPI_GPIO_QSPI_SD0_SCHMITT_BITS bics r0, r1 +#if BOARD_QUAD_OK==1 str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD0_OFFSET] +#endif str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD1_OFFSET] +#if BOARD_QUAD_OK==1 str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD2_OFFSET] str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD3_OFFSET] +#endif ldr r3, =XIP_SSI_BASE @@ -225,9 +247,15 @@ program_sregs: str r1, [r3, #SSI_SSIENR_OFFSET] // Check whether SR needs updating +#if BOARD_QUAD_OK==1 +# if BOARD_QUAD_ENABLE_STATUS_BYTE==1 + movs r0, #CMD_READ_STATUS1 +# elif BOARD_QUAD_ENABLE_STATUS_BYTE==2 movs r0, #CMD_READ_STATUS2 +# endif + bl read_flash_sreg - movs r2, #SREG_DATA + movs r2, #BOARD_QUAD_ENABLE_BIT_MASK cmp r0, r2 beq skip_sreg_programming @@ -240,17 +268,37 @@ program_sregs: ldr r1, [r3, #SSI_DR0_OFFSET] // Send status write command followed by data bytes - movs r1, #CMD_WRITE_STATUS +# if BOARD_SPLIT_STATUS_WRITE==1 +# if BOARD_QUAD_ENABLE_STATUS_BYTE==1 + movs r1, #CMD_WRITE_STATUS1 +# elif BOARD_QUAD_ENABLE_STATUS_BYTE==2 + movs r1, #CMD_WRITE_STATUS2 +# endif str r1, [r3, #SSI_DR0_OFFSET] + str r2, [r3, #SSI_DR0_OFFSET] + + bl wait_ssi_ready + //ldr r1, [r3, #SSI_DR0_OFFSET] + ldr r1, [r3, #SSI_DR0_OFFSET] + ldr r1, [r3, #SSI_DR0_OFFSET] + +# else + movs r1, #CMD_WRITE_STATUS1 + str r1, [r3, #SSI_DR0_OFFSET] +# if BOARD_QUAD_ENABLE_STATUS_BYTE==2 movs r0, #0 str r0, [r3, #SSI_DR0_OFFSET] +# endif str r2, [r3, #SSI_DR0_OFFSET] bl wait_ssi_ready ldr r1, [r3, #SSI_DR0_OFFSET] ldr r1, [r3, #SSI_DR0_OFFSET] +# if BOARD_QUAD_ENABLE_STATUS_BYTE==2 ldr r1, [r3, #SSI_DR0_OFFSET] +# endif +# endif // Poll status register for write completion 1: movs r0, #CMD_READ_STATUS @@ -258,6 +306,7 @@ program_sregs: movs r1, #1 tst r0, r1 bne 1b +#endif skip_sreg_programming: @@ -286,6 +335,7 @@ dummy_read: movs r1, #0x0 // NDF=0 (single 32b read) str r1, [r3, #SSI_CTRLR1_OFFSET] +#if BOARD_QUAD_OK==1 #define SPI_CTRLR0_ENTER_XIP \ (ADDR_L << SSI_SPI_CTRLR0_ADDR_L_LSB) | /* Address + mode bits */ \ (WAIT_CYCLES << SSI_SPI_CTRLR0_WAIT_CYCLES_LSB) | /* Hi-Z dummy clocks following address + mode */ \ @@ -315,6 +365,7 @@ dummy_read: movs r1, #0 str r1, [r3, #SSI_SSIENR_OFFSET] // Disable SSI (and clear FIFO) to allow further config +#endif // Note that the INST_L field is used to select what XIP data gets pushed into // the TX FIFO: @@ -322,13 +373,13 @@ dummy_read: // Anything else {XIP_CMD[7:0],ADDR[23:0]} Load SPI command into XIP_CMD configure_ssi: #define SPI_CTRLR0_XIP \ - (MODE_CONTINUOUS_READ /* Mode bits to keep flash in continuous read mode */ \ + (READ_INSTRUCTION /* Mode bits to keep flash in continuous read mode */ \ << SSI_SPI_CTRLR0_XIP_CMD_LSB) | \ (ADDR_L << SSI_SPI_CTRLR0_ADDR_L_LSB) | /* Total number of address + mode bits */ \ (WAIT_CYCLES << SSI_SPI_CTRLR0_WAIT_CYCLES_LSB) | /* Hi-Z dummy clocks following address + mode */ \ - (SSI_SPI_CTRLR0_INST_L_VALUE_NONE /* Do not send a command, instead send XIP_CMD as mode bits after address */ \ + (INSTRUCTION_LENGTH /* Do not send a command, instead send XIP_CMD as mode bits after address */ \ << SSI_SPI_CTRLR0_INST_L_LSB) | \ - (SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_2C2A /* Send Address in Quad I/O mode (and Command but that is zero bits long) */ \ + (TRANSACTION_TYPE /* Send Address in Quad I/O mode (and Command but that is zero bits long) */ \ << SSI_SPI_CTRLR0_TRANS_TYPE_LSB) ldr r1, =(SPI_CTRLR0_XIP) From e107efa63f5369ded1134466abf2631e4fd579f6 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 17 Jun 2021 17:12:05 +0200 Subject: [PATCH 36/70] main: detect specific serial port IDs based on USB vid/pid This makes it possible to flash a board even when there are multiple different kinds of boards attached, e.g. an Arduino Uno and a Circuit Playground Express. You can find the VID/PID pair in several ways: 1. By running `lsusb` before and after attaching the board and looking at the new USB device. 2. By grepping for `usb_PID` and `usb_VID` in the TinyGo source code. 3. By checking the Arduino IDE boards.txt from the vendor. Note that one board may have multiple VID/PID pairs: * The bootloader and main program may have a different PID, so far I've seen that the main program generally has the bootloader PID with 0x8000 added. * The software running on the board may have an erroneous PID, for example from a different board. I've seen this happen a few times. * A single board may have had some revisions which changed the PID. This is particularly true for the Arduino Uno. As a fallback, if the given VID/PID pair isn't found, the whole set of serial ports will be used. There are many boards which I haven't included yet simply because I couldn't test them. --- compileopts/target.go | 1 + main.go | 73 +++++++++++++++++++++++++++--- targets/arduino-nano33.json | 1 + targets/arduino.json | 1 + targets/circuitplay-bluefruit.json | 1 + targets/circuitplay-express.json | 1 + targets/itsybitsy-m4.json | 1 + targets/nano-33-ble.json | 1 + targets/pybadge.json | 1 + targets/pyportal.json | 1 + 10 files changed, 75 insertions(+), 7 deletions(-) diff --git a/compileopts/target.go b/compileopts/target.go index 24cf4f394..4fb62fce3 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -45,6 +45,7 @@ type TargetSpec struct { FlashCommand string `json:"flash-command"` GDB []string `json:"gdb"` PortReset string `json:"flash-1200-bps-reset"` + SerialPort []string `json:"serial-port"` // serial port IDs in the form "acm:vid:pid" or "usb:vid:pid" FlashMethod string `json:"flash-method"` FlashVolume string `json:"msd-volume-name"` FlashFilename string `json:"msd-firmware-name"` diff --git a/main.go b/main.go index 992affa5c..24d943624 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,7 @@ import ( "path/filepath" "regexp" "runtime" + "strconv" "strings" "sync/atomic" "time" @@ -296,7 +297,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error { return builder.Build(pkgName, fileExt, config, func(result builder.BuildResult) error { // do we need port reset to put MCU into bootloader mode? if config.Target.PortReset == "true" && flashMethod != "openocd" { - port, err := getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' })) + port, err := getDefaultPort(port, config.Target.SerialPort) if err != nil { return err } @@ -321,7 +322,7 @@ func Flash(pkgName, port string, options *compileopts.Options) error { if strings.Contains(flashCmd, "{port}") { var err error - port, err = getDefaultPort(strings.FieldsFunc(port, func(c rune) bool { return c == ',' })) + port, err = getDefaultPort(port, config.Target.SerialPort) if err != nil { return err } @@ -718,7 +719,8 @@ func windowsFindUSBDrive(volume string, options *compileopts.Options) (string, e } // getDefaultPort returns the default serial port depending on the operating system. -func getDefaultPort(portCandidates []string) (port string, err error) { +func getDefaultPort(portFlag string, usbInterfaces []string) (port string, err error) { + portCandidates := strings.FieldsFunc(portFlag, func(c rune) bool { return c == ',' }) if len(portCandidates) == 1 { return portCandidates[0], nil } @@ -734,13 +736,70 @@ func getDefaultPort(portCandidates []string) (port string, err error) { return "", err } - for _, p := range portsList { - if p.IsUSB { - ports = append(ports, p.Name) + var preferredPortIDs [][2]uint16 + for _, s := range usbInterfaces { + parts := strings.Split(s, ":") + if len(parts) != 3 || (parts[0] != "acm" && parts[0] == "usb") { + // acm and usb are the two types of serial ports recognized + // under Linux (ttyACM*, ttyUSB*). Other operating systems don't + // generally make this distinction. If this is not one of the + // given USB devices, don't try to parse the USB IDs. + continue } + vid, err := strconv.ParseUint(parts[1], 16, 16) + if err != nil { + return "", fmt.Errorf("could not parse USB vendor ID %q: %w", parts[1], err) + } + pid, err := strconv.ParseUint(parts[2], 16, 16) + if err != nil { + return "", fmt.Errorf("could not parse USB product ID %q: %w", parts[1], err) + } + preferredPortIDs = append(preferredPortIDs, [2]uint16{uint16(vid), uint16(pid)}) } - if ports == nil || len(ports) == 0 { + var primaryPorts []string // ports picked from preferred USB VID/PID + var secondaryPorts []string // other ports (as a fallback) + for _, p := range portsList { + if !p.IsUSB { + continue + } + if p.VID != "" && p.PID != "" { + foundPort := false + vid, vidErr := strconv.ParseUint(p.VID, 16, 16) + pid, pidErr := strconv.ParseUint(p.PID, 16, 16) + if vidErr == nil && pidErr == nil { + for _, id := range preferredPortIDs { + if uint16(vid) == id[0] && uint16(pid) == id[1] { + primaryPorts = append(primaryPorts, p.Name) + foundPort = true + continue + } + } + } + if foundPort { + continue + } + } + + secondaryPorts = append(secondaryPorts, p.Name) + } + if len(primaryPorts) == 1 { + // There is exactly one match in the set of preferred ports. Use + // this port, even if there may be others available. This allows + // flashing a specific board even if there are multiple available. + return primaryPorts[0], nil + } else if len(primaryPorts) > 1 { + // There are multiple preferred ports, probably because more than + // one device of the same type are connected (e.g. two Arduino + // Unos). + ports = primaryPorts + } else { + // No preferred ports found. Fall back to other serial ports + // available in the system. + ports = secondaryPorts + } + + if len(ports) == 0 { // fallback switch runtime.GOOS { case "darwin": diff --git a/targets/arduino-nano33.json b/targets/arduino-nano33.json index 45ca81e84..f1797b797 100644 --- a/targets/arduino-nano33.json +++ b/targets/arduino-nano33.json @@ -2,5 +2,6 @@ "inherits": ["atsamd21g18a"], "build-tags": ["arduino_nano33"], "flash-command": "bossac -i -e -w -v -R -U --port={port} --offset=0x2000 {bin}", + "serial-port": ["acm:2341:8057", "acm:2341:0057"], "flash-1200-bps-reset": "true" } diff --git a/targets/arduino.json b/targets/arduino.json index ebe8af51f..91dc49b89 100644 --- a/targets/arduino.json +++ b/targets/arduino.json @@ -6,5 +6,6 @@ "-Wl,--defsym=_stack_size=512" ], "flash-command": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}:i", + "serial-port": ["acm:2341:0043", "acm:2341:0001", "acm:2a03:0043", "acm:2341:0243"], "emulator": ["simavr", "-m", "atmega328p", "-f", "16000000"] } diff --git a/targets/circuitplay-bluefruit.json b/targets/circuitplay-bluefruit.json index 6a57e1dac..24bf5577f 100644 --- a/targets/circuitplay-bluefruit.json +++ b/targets/circuitplay-bluefruit.json @@ -3,6 +3,7 @@ "build-tags": ["circuitplay_bluefruit","nrf52840_reset_uf2", "softdevice", "s140v6"], "flash-1200-bps-reset": "true", "flash-method": "msd", + "serial-port": ["acm:239a:8045", "acm:239a:45"], "msd-volume-name": "CPLAYBTBOOT", "msd-firmware-name": "firmware.uf2", "uf2-family-id": "0xADA52840", diff --git a/targets/circuitplay-express.json b/targets/circuitplay-express.json index a2b4143c8..e70dfb93a 100644 --- a/targets/circuitplay-express.json +++ b/targets/circuitplay-express.json @@ -3,6 +3,7 @@ "build-tags": ["circuitplay_express"], "flash-1200-bps-reset": "true", "flash-method": "msd", + "serial-port": ["acm:239a:8018", "acm:239a:18"], "msd-volume-name": "CPLAYBOOT", "msd-firmware-name": "firmware.uf2" } diff --git a/targets/itsybitsy-m4.json b/targets/itsybitsy-m4.json index 2d6a88eda..9c7a634ba 100644 --- a/targets/itsybitsy-m4.json +++ b/targets/itsybitsy-m4.json @@ -3,6 +3,7 @@ "build-tags": ["itsybitsy_m4"], "flash-1200-bps-reset": "true", "flash-method": "msd", + "serial-port": ["acm:239a:802b", "acm:239a:002b"], "msd-volume-name": "ITSYM4BOOT", "msd-firmware-name": "firmware.uf2" } diff --git a/targets/nano-33-ble.json b/targets/nano-33-ble.json index df22221fd..c5381e9f4 100644 --- a/targets/nano-33-ble.json +++ b/targets/nano-33-ble.json @@ -2,6 +2,7 @@ "inherits": ["nrf52840"], "build-tags": ["nano_33_ble", "nrf52840_reset_bossa"], "flash-command": "bossac_arduino2 -d -i -e -w -v -R --port={port} {bin}", + "serial-port": ["acm:2341:805a", "acm:2341:005a"], "flash-1200-bps-reset": "true", "linkerscript": "targets/nano-33-ble.ld" } diff --git a/targets/pybadge.json b/targets/pybadge.json index 0514f5372..b76b32469 100644 --- a/targets/pybadge.json +++ b/targets/pybadge.json @@ -3,6 +3,7 @@ "build-tags": ["pybadge"], "flash-1200-bps-reset": "true", "flash-method": "msd", + "serial-port": ["acm:239a:8033", "acm:239a:33"], "msd-volume-name": "PYBADGEBOOT", "msd-firmware-name": "arcade.uf2" } diff --git a/targets/pyportal.json b/targets/pyportal.json index e03efcb6d..a26d82f08 100644 --- a/targets/pyportal.json +++ b/targets/pyportal.json @@ -3,6 +3,7 @@ "build-tags": ["pyportal"], "flash-1200-bps-reset": "true", "flash-method": "msd", + "serial-port": ["acm:239a:8035", "acm:239a:35", "acm:239a:8036"], "msd-volume-name": "PORTALBOOT", "msd-firmware-name": "firmware.uf2" } From 64058c3efb2ca7a5352203a728cfd59b6c0a3cc4 Mon Sep 17 00:00:00 2001 From: "Federico G. Schwindt" Date: Mon, 21 Jun 2021 15:21:44 +0100 Subject: [PATCH 37/70] net: os: add more stubs for 1.15 Fix importing net/http. --- src/net/dial.go | 1 + src/os/file_go_other.go | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/net/dial.go b/src/net/dial.go index 763096d9e..a1cb75d87 100644 --- a/src/net/dial.go +++ b/src/net/dial.go @@ -8,6 +8,7 @@ import ( type Dialer struct { Timeout time.Duration Deadline time.Time + DualStack bool KeepAlive time.Duration } diff --git a/src/os/file_go_other.go b/src/os/file_go_other.go index d8d680ffe..351de7acc 100644 --- a/src/os/file_go_other.go +++ b/src/os/file_go_other.go @@ -44,3 +44,8 @@ const ( func (m FileMode) IsDir() bool { return false } + +// IsRegular is a stub, always returning false +func (m FileMode) IsRegular() bool { + return false +} From d8ac7ccaae01986d60935469941eaf8f0003d43b Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 19 Jun 2021 20:22:39 +0200 Subject: [PATCH 38/70] interp: fix a bug in pointer cast workaround This was triggered by the following code: var smallPrimesProduct = new(big.Int).SetUint64(16294579238595022365) It is part of the new TinyGo version of the crypto/rand package. --- interp/interp.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/interp/interp.go b/interp/interp.go index 58be2c86b..8da27705f 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -129,7 +129,7 @@ func Run(mod llvm.Module, debug bool) error { // Update all global variables in the LLVM module. mem := memoryView{r: r} - for _, obj := range r.objects { + for i, obj := range r.objects { if obj.llvmGlobal.IsNil() { continue } @@ -159,6 +159,12 @@ func Run(mod llvm.Module, debug bool) error { name := obj.llvmGlobal.Name() obj.llvmGlobal.EraseFromParentAsGlobal() newGlobal.SetName(name) + + // Update interp-internal references. + delete(r.globals, obj.llvmGlobal) + obj.llvmGlobal = newGlobal + r.globals[newGlobal] = i + r.objects[i] = obj continue } if err != nil { From d94f42f6e284e459f0141f986a2232f9867b1c4e Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 19 Jun 2021 16:19:41 +0200 Subject: [PATCH 39/70] crypto/rand: replace this package with a TinyGo version This package provides access to an operating system resource (cryptographic numbers) and so needs to be replaced with a TinyGo version that does this in a different way. I've made the following choices while adding this feature: - I'm using the getentropy call whenever possible (most POSIX like systems), because it is easier to use and more reliable. Linux is the exception: it only added getentropy relatively recently. - I've left bare-metal implementations to a future patch. This because it's hard to reliably get cryptographically secure random numbers on embedded devices: most devices do not have a hardware PRNG for this purpose. --- loader/goroot.go | 51 +++++++--- src/crypto/rand/rand.go | 19 ++++ src/crypto/rand/rand_getentropy.go | 38 ++++++++ src/crypto/rand/rand_urandom.go | 38 ++++++++ src/crypto/rand/util.go | 143 +++++++++++++++++++++++++++++ testdata/env.go | 23 +++++ testdata/env.txt | 1 + 7 files changed, 298 insertions(+), 15 deletions(-) create mode 100644 src/crypto/rand/rand.go create mode 100644 src/crypto/rand/rand_getentropy.go create mode 100644 src/crypto/rand/rand_urandom.go create mode 100644 src/crypto/rand/util.go diff --git a/loader/goroot.go b/loader/goroot.go index 03c8919e7..6770d0458 100644 --- a/loader/goroot.go +++ b/loader/goroot.go @@ -2,6 +2,14 @@ package loader // This file constructs a new temporary GOROOT directory by merging both the // standard Go GOROOT and the GOROOT from TinyGo using symlinks. +// +// The goal is to replace specific packages from Go with a TinyGo version. It's +// never a partial replacement, either a package is fully replaced or it is not. +// This is important because if we did allow to merge packages (e.g. by adding +// files to a package), it would lead to a dependency on implementation details +// with all the maintenance burden that results in. Only allowing to replace +// packages as a whole avoids this as packages are already designed to have a +// public (backwards-compatible) API. import ( "crypto/sha512" @@ -139,6 +147,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides if err != nil { return err } + hasTinyGoFiles := false for _, e := range tinygoEntries { if e.IsDir() { // A directory, so merge this thing. @@ -154,6 +163,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides if err != nil { return err } + hasTinyGoFiles = true } } @@ -164,21 +174,30 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides return err } for _, e := range gorootEntries { - if !e.IsDir() { - // Don't merge in files from Go. Otherwise we'd end up with a - // weird syscall package with files from both roots. - continue - } - if _, ok := overrides[path.Join(importPath, e.Name())+"/"]; ok { - // Already included above, so don't bother trying to create this - // symlink. - continue - } - newname := filepath.Join(tmpgoroot, "src", importPath, e.Name()) - oldname := filepath.Join(goroot, "src", importPath, e.Name()) - err := symlink(oldname, newname) - if err != nil { - return err + if e.IsDir() { + if _, ok := overrides[path.Join(importPath, e.Name())+"/"]; ok { + // Already included above, so don't bother trying to create this + // symlink. + continue + } + newname := filepath.Join(tmpgoroot, "src", importPath, e.Name()) + oldname := filepath.Join(goroot, "src", importPath, e.Name()) + err := symlink(oldname, newname) + if err != nil { + return err + } + } else { + // Only merge files from Go if TinyGo does not have any files. + // Otherwise we'd end up with a weird mix from both Go + // implementations. + if !hasTinyGoFiles { + newname := filepath.Join(tmpgoroot, "src", importPath, e.Name()) + oldname := filepath.Join(goroot, "src", importPath, e.Name()) + err := symlink(oldname, newname) + if err != nil { + return err + } + } } } } @@ -201,6 +220,8 @@ func needsSyscallPackage(buildTags []string) bool { func pathsToOverride(needsSyscallPackage bool) map[string]bool { paths := map[string]bool{ "/": true, + "crypto/": true, + "crypto/rand/": false, "device/": false, "examples/": false, "internal/": true, diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go new file mode 100644 index 000000000..e09acefe4 --- /dev/null +++ b/src/crypto/rand/rand.go @@ -0,0 +1,19 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package rand implements a cryptographically secure +// random number generator. +package rand + +import "io" + +// Reader is a global, shared instance of a cryptographically +// secure random number generator. +var Reader io.Reader + +// Read is a helper function that calls Reader.Read using io.ReadFull. +// On return, n == len(b) if and only if err == nil. +func Read(b []byte) (n int, err error) { + return io.ReadFull(Reader, b) +} diff --git a/src/crypto/rand/rand_getentropy.go b/src/crypto/rand/rand_getentropy.go new file mode 100644 index 000000000..661132fb7 --- /dev/null +++ b/src/crypto/rand/rand_getentropy.go @@ -0,0 +1,38 @@ +// +build darwin freebsd wasi + +// This implementation of crypto/rand uses the getentropy system call (available +// on both MacOS and WASI) to generate random numbers. + +package rand + +import ( + "errors" + "unsafe" +) + +var errReadFailed = errors.New("rand: could not read random bytes") + +func init() { + Reader = &reader{} +} + +type reader struct { +} + +func (r *reader) Read(b []byte) (n int, err error) { + if len(b) != 0 { + if len(b) > 256 { + b = b[:256] + } + result := libc_getentropy(unsafe.Pointer(&b[0]), len(b)) + if result < 0 { + // Maybe we should return a syscall.Errno here? + return 0, errReadFailed + } + } + return len(b), nil +} + +// int getentropy(void *buf, size_t buflen); +//export getentropy +func libc_getentropy(buf unsafe.Pointer, buflen int) int diff --git a/src/crypto/rand/rand_urandom.go b/src/crypto/rand/rand_urandom.go new file mode 100644 index 000000000..64388de7c --- /dev/null +++ b/src/crypto/rand/rand_urandom.go @@ -0,0 +1,38 @@ +// +build linux,!baremetal,!wasi + +// This implementation of crypto/rand uses the /dev/urandom pseudo-file to +// generate random numbers. +// TODO: convert to the getentropy or getrandom libc function on Linux once it +// is more widely supported. + +package rand + +import ( + "syscall" +) + +func init() { + Reader = &reader{} +} + +type reader struct { + fd int +} + +func (r *reader) Read(b []byte) (n int, err error) { + if len(b) == 0 { + return + } + + // Open /dev/urandom first if needed. + if r.fd == 0 { + fd, err := syscall.Open("/dev/urandom", syscall.O_RDONLY, 0) + if err != nil { + return 0, err + } + r.fd = fd + } + + // Read from the file. + return syscall.Read(r.fd, b) +} diff --git a/src/crypto/rand/util.go b/src/crypto/rand/util.go new file mode 100644 index 000000000..4dd171120 --- /dev/null +++ b/src/crypto/rand/util.go @@ -0,0 +1,143 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +import ( + "errors" + "io" + "math/big" +) + +// smallPrimes is a list of small, prime numbers that allows us to rapidly +// exclude some fraction of composite candidates when searching for a random +// prime. This list is truncated at the point where smallPrimesProduct exceeds +// a uint64. It does not include two because we ensure that the candidates are +// odd by construction. +var smallPrimes = []uint8{ + 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, +} + +// smallPrimesProduct is the product of the values in smallPrimes and allows us +// to reduce a candidate prime by this number and then determine whether it's +// coprime to all the elements of smallPrimes without further big.Int +// operations. +var smallPrimesProduct = new(big.Int).SetUint64(16294579238595022365) + +// Prime returns a number, p, of the given size, such that p is prime +// with high probability. +// Prime will return error for any error returned by rand.Read or if bits < 2. +func Prime(rand io.Reader, bits int) (p *big.Int, err error) { + if bits < 2 { + err = errors.New("crypto/rand: prime size must be at least 2-bit") + return + } + + b := uint(bits % 8) + if b == 0 { + b = 8 + } + + bytes := make([]byte, (bits+7)/8) + p = new(big.Int) + + bigMod := new(big.Int) + + for { + _, err = io.ReadFull(rand, bytes) + if err != nil { + return nil, err + } + + // Clear bits in the first byte to make sure the candidate has a size <= bits. + bytes[0] &= uint8(int(1<= 2 { + bytes[0] |= 3 << (b - 2) + } else { + // Here b==1, because b cannot be zero. + bytes[0] |= 1 + if len(bytes) > 1 { + bytes[1] |= 0x80 + } + } + // Make the value odd since an even number this large certainly isn't prime. + bytes[len(bytes)-1] |= 1 + + p.SetBytes(bytes) + + // Calculate the value mod the product of smallPrimes. If it's + // a multiple of any of these primes we add two until it isn't. + // The probability of overflowing is minimal and can be ignored + // because we still perform Miller-Rabin tests on the result. + bigMod.Mod(p, smallPrimesProduct) + mod := bigMod.Uint64() + + NextDelta: + for delta := uint64(0); delta < 1<<20; delta += 2 { + m := mod + delta + for _, prime := range smallPrimes { + if m%uint64(prime) == 0 && (bits > 6 || m != uint64(prime)) { + continue NextDelta + } + } + + if delta > 0 { + bigMod.SetUint64(delta) + p.Add(p, bigMod) + } + break + } + + // There is a tiny possibility that, by adding delta, we caused + // the number to be one bit too long. Thus we check BitLen + // here. + if p.ProbablyPrime(20) && p.BitLen() == bits { + return + } + } +} + +// Int returns a uniform random value in [0, max). It panics if max <= 0. +func Int(rand io.Reader, max *big.Int) (n *big.Int, err error) { + if max.Sign() <= 0 { + panic("crypto/rand: argument to Int is <= 0") + } + n = new(big.Int) + n.Sub(max, n.SetUint64(1)) + // bitLen is the maximum bit length needed to encode a value < max. + bitLen := n.BitLen() + if bitLen == 0 { + // the only valid result is 0 + return + } + // k is the maximum byte length needed to encode a value < max. + k := (bitLen + 7) / 8 + // b is the number of bits in the most significant byte of max-1. + b := uint(bitLen % 8) + if b == 0 { + b = 8 + } + + bytes := make([]byte, k) + + for { + _, err = io.ReadFull(rand, bytes) + if err != nil { + return nil, err + } + + // Clear bits in the first byte to increase the probability + // that the candidate is < max. + bytes[0] &= uint8(int(1< 159*len(buf) { + println("random numbers don't seem that random, the average byte is", sum/len(buf)) + } else { + println("random number check was successful") + } } diff --git a/testdata/env.txt b/testdata/env.txt index 8ba50a7fd..e392cd5e9 100644 --- a/testdata/env.txt +++ b/testdata/env.txt @@ -3,3 +3,4 @@ ENV2: VALUE2 arg: first arg: second +random number check was successful From c3032660c9e2e1fd5a188c6ac54902637a77b975 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Mon, 21 Jun 2021 14:50:24 +0200 Subject: [PATCH 40/70] wasi: remove wasm build tag The wasm build tag together with GOARCH=arm was causing problems in the internal/cpu package. In general, I think having two architecture build tag will only cause problems (in this case, wasm and arm) so I've removed the wasm build tag and replaced it with tinygo.wasm. This is similar to the tinygo.riscv build tag, which is used for older Go versions that don't yet have RISC-V support in the standard library (and therefore pretend to be GOARCH=arm instead). --- compileopts/config.go | 2 +- src/runtime/arch_arm.go | 2 +- src/runtime/{arch_wasm.go => arch_tinygowasm.go} | 2 +- src/runtime/gc_globals_conservative.go | 2 +- src/runtime/gc_globals_precise.go | 2 +- src/runtime/gc_stack_portable.go | 2 +- src/runtime/gc_stack_raw.go | 2 +- src/runtime/{runtime_wasm.go => runtime_tinygowasm.go} | 2 +- src/runtime/runtime_wasm_wasi.go | 2 +- targets/wasi.json | 2 +- targets/wasm.json | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) rename src/runtime/{arch_wasm.go => arch_tinygowasm.go} (98%) rename src/runtime/{runtime_wasm.go => runtime_tinygowasm.go} (98%) diff --git a/compileopts/config.go b/compileopts/config.go index 0bb839ff7..821aa8313 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -89,7 +89,7 @@ func (c *Config) NeedsStackObjects() bool { switch c.GC() { case "conservative", "extalloc": for _, tag := range c.BuildTags() { - if tag == "wasm" { + if tag == "tinygo.wasm" { return true } } diff --git a/src/runtime/arch_arm.go b/src/runtime/arch_arm.go index e2dc4b5bf..19caa619b 100644 --- a/src/runtime/arch_arm.go +++ b/src/runtime/arch_arm.go @@ -1,4 +1,4 @@ -// +build arm,!baremetal,!wasm arm,arm7tdmi +// +build arm,!baremetal,!tinygo.wasm arm,arm7tdmi package runtime diff --git a/src/runtime/arch_wasm.go b/src/runtime/arch_tinygowasm.go similarity index 98% rename from src/runtime/arch_wasm.go rename to src/runtime/arch_tinygowasm.go index 00dd0deb5..0ee3afd3b 100644 --- a/src/runtime/arch_wasm.go +++ b/src/runtime/arch_tinygowasm.go @@ -1,4 +1,4 @@ -// +build wasm +// +build tinygo.wasm package runtime diff --git a/src/runtime/gc_globals_conservative.go b/src/runtime/gc_globals_conservative.go index bee55a88f..564765515 100644 --- a/src/runtime/gc_globals_conservative.go +++ b/src/runtime/gc_globals_conservative.go @@ -1,5 +1,5 @@ // +build gc.conservative gc.extalloc -// +build baremetal wasm +// +build baremetal tinygo.wasm package runtime diff --git a/src/runtime/gc_globals_precise.go b/src/runtime/gc_globals_precise.go index 2d6c8c14b..f79b71fdc 100644 --- a/src/runtime/gc_globals_precise.go +++ b/src/runtime/gc_globals_precise.go @@ -1,5 +1,5 @@ // +build gc.conservative gc.extalloc -// +build !baremetal,!wasm +// +build !baremetal,!tinygo.wasm package runtime diff --git a/src/runtime/gc_stack_portable.go b/src/runtime/gc_stack_portable.go index d48b24972..d4a046374 100644 --- a/src/runtime/gc_stack_portable.go +++ b/src/runtime/gc_stack_portable.go @@ -1,5 +1,5 @@ // +build gc.conservative gc.extalloc -// +build wasm +// +build tinygo.wasm package runtime diff --git a/src/runtime/gc_stack_raw.go b/src/runtime/gc_stack_raw.go index 74be7fe82..01b07d9bb 100644 --- a/src/runtime/gc_stack_raw.go +++ b/src/runtime/gc_stack_raw.go @@ -1,5 +1,5 @@ // +build gc.conservative gc.extalloc -// +build !wasm +// +build !tinygo.wasm package runtime diff --git a/src/runtime/runtime_wasm.go b/src/runtime/runtime_tinygowasm.go similarity index 98% rename from src/runtime/runtime_wasm.go rename to src/runtime/runtime_tinygowasm.go index 1146f5781..989fbb803 100644 --- a/src/runtime/runtime_wasm.go +++ b/src/runtime/runtime_tinygowasm.go @@ -1,4 +1,4 @@ -// +build wasm +// +build tinygo.wasm package runtime diff --git a/src/runtime/runtime_wasm_wasi.go b/src/runtime/runtime_wasm_wasi.go index 76d0a4520..4f0432f7a 100644 --- a/src/runtime/runtime_wasm_wasi.go +++ b/src/runtime/runtime_wasm_wasi.go @@ -1,4 +1,4 @@ -// +build wasm,wasi +// +build tinygo.wasm,wasi package runtime diff --git a/targets/wasi.json b/targets/wasi.json index c24ed8f10..b80b77d1a 100644 --- a/targets/wasi.json +++ b/targets/wasi.json @@ -1,6 +1,6 @@ { "llvm-target": "wasm32--wasi", - "build-tags": ["wasm", "wasi"], + "build-tags": ["tinygo.wasm", "wasi"], "goos": "linux", "goarch": "arm", "linker": "wasm-ld", diff --git a/targets/wasm.json b/targets/wasm.json index 6208eb27f..eb03eb8c8 100644 --- a/targets/wasm.json +++ b/targets/wasm.json @@ -1,6 +1,6 @@ { "llvm-target": "wasm32--wasi", - "build-tags": ["js", "wasm"], + "build-tags": ["tinygo.wasm"], "goos": "js", "goarch": "wasm", "linker": "wasm-ld", From 293f4ea7bc9858c219c75bd3c2bc88dea1d42b99 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 30 Mar 2021 13:41:29 +0200 Subject: [PATCH 41/70] compiler: add tests for pragmas These pragmas weren't really tested anywhere, except that some code might break if they are not properly applied. These tests make it easy to see they work correctly and also provide a logical place to add new pragma tests. I've also made a slight change to how functions and globals are created: with the change they're also created in the IR even if they're not referenced. This makes testing easier. --- compiler/compiler.go | 6 +++--- compiler/compiler_test.go | 1 + compiler/testdata/pragma.go | 41 +++++++++++++++++++++++++++++++++++ compiler/testdata/pragma.ll | 43 +++++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 compiler/testdata/pragma.go create mode 100644 compiler/testdata/pragma.ll diff --git a/compiler/compiler.go b/compiler/compiler.go index 925b61bf8..a34d98483 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -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,8 +758,8 @@ 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) } diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index 4149e1491..45d5b8c3a 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -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"}, } diff --git a/compiler/testdata/pragma.go b/compiler/testdata/pragma.go new file mode 100644 index 000000000..505e78a9f --- /dev/null +++ b/compiler/testdata/pragma.go @@ -0,0 +1,41 @@ +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() { +} diff --git a/compiler/testdata/pragma.ll b/compiler/testdata/pragma.ll new file mode 100644 index 000000000..0fdc753e5 --- /dev/null +++ b/compiler/testdata/pragma.ll @@ -0,0 +1,43 @@ +; 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 + +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 +} + +attributes #0 = { "wasm-export-name"="extern_func" } +attributes #1 = { inlinehint } +attributes #2 = { noinline } From 2bb70812a8cc0562204622689652132938567a81 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 30 Mar 2021 14:09:23 +0200 Subject: [PATCH 42/70] compiler: add function and global section pragmas This patch adds a new pragma for functions and globals to set the section name. This can be useful to place a function or global in a special device specific section, for example: * Functions may be placed in RAM to make them run faster, or in flash (if RAM is the default) to not let them take up RAM. * DMA memory may only be placed in a special memory area. * Some RAM may be faster than other RAM, and some globals may be performance critical thus placing them in this special RAM area can help. * Some (large) global variables may need to be placed in external RAM, which can be done by placing them in a special section. To use it, you have to place a function or global in a special section, for example: //go:section .externalram var externalRAMBuffer [1024]byte This can then be placed in a special section of the linker script, for example something like this: .bss.extram (NOLOAD) : { *(.externalram) } > ERAM --- compiler/compiler.go | 6 ++++++ compiler/symbol.go | 10 ++++++++++ compiler/testdata/pragma.go | 25 +++++++++++++++++++++++++ compiler/testdata/pragma.ll | 16 ++++++++++++++++ transform/globals.go | 2 +- 5 files changed, 58 insertions(+), 1 deletion(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index a34d98483..cc696d5b0 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -762,6 +762,9 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package if !info.extern { 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. diff --git a/compiler/symbol.go b/compiler/symbol.go index 6eec892dd..49ccfa20d 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -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] + } } } } diff --git a/compiler/testdata/pragma.go b/compiler/testdata/pragma.go index 505e78a9f..b6ebc83b2 100644 --- a/compiler/testdata/pragma.go +++ b/compiler/testdata/pragma.go @@ -39,3 +39,28 @@ func inlineFunc() { //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 diff --git a/compiler/testdata/pragma.ll b/compiler/testdata/pragma.ll index 0fdc753e5..0515098c5 100644 --- a/compiler/testdata/pragma.ll +++ b/compiler/testdata/pragma.ll @@ -6,6 +6,9 @@ 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*) @@ -38,6 +41,19 @@ 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" } diff --git a/transform/globals.go b/transform/globals.go index 2d0349e9f..d147062bf 100644 --- a/transform/globals.go +++ b/transform/globals.go @@ -11,7 +11,7 @@ import "tinygo.org/x/go-llvm" func ApplyFunctionSections(mod llvm.Module) { llvmFn := mod.FirstFunction() for !llvmFn.IsNil() { - if !llvmFn.IsDeclaration() { + if !llvmFn.IsDeclaration() && llvmFn.Section() == "" { name := llvmFn.Name() llvmFn.SetSection(".text." + name) } From bfe3f6864741da9259b27c6ba372ded4cbdaa562 Mon Sep 17 00:00:00 2001 From: Yurii Soldak Date: Mon, 14 Jun 2021 00:50:46 +0200 Subject: [PATCH 43/70] smoke&readme: add missing boards --- Makefile | 6 +++++- README.md | 8 ++++++-- targets/nano-33-ble-sense.json | 3 +++ 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 targets/nano-33-ble-sense.json diff --git a/Makefile b/Makefile index e2981789b..6abdc261d 100644 --- a/Makefile +++ b/Makefile @@ -356,7 +356,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 diff --git a/README.md b/README.md index ae3a8b526..f28f4eb8f 100644 --- a/README.md +++ b/README.md @@ -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 67 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) @@ -69,7 +69,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 +99,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) diff --git a/targets/nano-33-ble-sense.json b/targets/nano-33-ble-sense.json new file mode 100644 index 000000000..943829d2d --- /dev/null +++ b/targets/nano-33-ble-sense.json @@ -0,0 +1,3 @@ +{ + "inherits": ["nano-33-ble"] +} From e02f308d43ff43fa649f4129c15e0f3809420801 Mon Sep 17 00:00:00 2001 From: Yurii Soldak Date: Wed, 23 Jun 2021 15:29:15 +0200 Subject: [PATCH 44/70] rp2040: fix for nano-rp2040 board --- targets/nano-rp2040.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/nano-rp2040.json b/targets/nano-rp2040.json index fb74a2e74..a0222c970 100644 --- a/targets/nano-rp2040.json +++ b/targets/nano-rp2040.json @@ -5,6 +5,6 @@ "build-tags": ["nano_rp2040"], "linkerscript": "targets/pico.ld", "extra-files": [ - "targets/pico_boot_stage2.S" + "targets/pico-boot-stage2.S" ] } From e65592599cb6c3a0ffd73d5a021b9aa4e00959ae Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 25 Jun 2021 15:00:32 +0200 Subject: [PATCH 45/70] compiler: implement syscall.rawSyscallNoError in inline assembly This makes it possible to call syscall.Getpid() on Linux, for example. These syscalls never return an error so don't need any error checking. --- compiler/compiler.go | 4 +++- compiler/syscall.go | 38 ++++++++++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index cc696d5b0..7841cd656 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -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() @@ -1310,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"): diff --git a/compiler/syscall.go b/compiler/syscall.go index 5b93e9ec1..6a0bd3287 100644 --- a/compiler/syscall.go +++ b/compiler/syscall.go @@ -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 +} From 75298bb84bcb966998ca00f3cc5506a9757a5038 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 25 Jun 2021 15:06:17 +0200 Subject: [PATCH 46/70] os: implement process related functions This commit implements various process related functions like os.Getuid() and os.Getpid(). It also implements or improves this support in the syscall package if it isn't available yet. --- src/os/exec.go | 12 +++++++++++ src/os/file.go | 5 ----- src/os/proc.go | 28 ++++++++++++++++++++++++ src/syscall/proc_emulated.go | 13 +++++++++++ src/syscall/proc_hosted.go | 37 ++++++++++++++++++++++++++++++++ src/syscall/syscall_baremetal.go | 6 ------ src/syscall/syscall_libc.go | 4 ---- testdata/stdlib.go | 9 ++++++++ 8 files changed, 99 insertions(+), 15 deletions(-) create mode 100644 src/syscall/proc_emulated.go create mode 100644 src/syscall/proc_hosted.go diff --git a/src/os/exec.go b/src/os/exec.go index 66cc79999..8bc544ba2 100644 --- a/src/os/exec.go +++ b/src/os/exec.go @@ -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() +} diff --git a/src/os/file.go b/src/os/file.go index a7aa40430..4d90bdde5 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -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 -} diff --git a/src/os/proc.go b/src/os/proc.go index d3bfb1270..fe2245f36 100644 --- a/src/os/proc.go +++ b/src/os/proc.go @@ -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() +} diff --git a/src/syscall/proc_emulated.go b/src/syscall/proc_emulated.go new file mode 100644 index 000000000..89b22e818 --- /dev/null +++ b/src/syscall/proc_emulated.go @@ -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 } diff --git a/src/syscall/proc_hosted.go b/src/syscall/proc_hosted.go new file mode 100644 index 000000000..5f52a4ca8 --- /dev/null +++ b/src/syscall/proc_hosted.go @@ -0,0 +1,37 @@ +// +build !baremetal,!wasi,!wasm + +// This file assumes there is a libc available that runs on a real operating +// system. + +package syscall + +func Getuid() int { return int(libc_getuid()) } +func Geteuid() int { return int(libc_geteuid()) } +func Getgid() int { return int(libc_getgid()) } +func Getegid() int { return int(libc_getegid()) } +func Getpid() int { return int(libc_getpid()) } +func Getppid() int { return int(libc_getppid()) } + +// uid_t getuid(void) +//export getuid +func libc_getuid() int32 + +// gid_t getgid(void) +//export getgid +func libc_getgid() int32 + +// uid_t geteuid(void) +//export geteuid +func libc_geteuid() int32 + +// gid_t getegid(void) +//export getegid +func libc_getegid() int32 + +// gid_t getpid(void) +//export getpid +func libc_getpid() int32 + +// gid_t getppid(void) +//export getppid +func libc_getppid() int32 diff --git a/src/syscall/syscall_baremetal.go b/src/syscall/syscall_baremetal.go index 28fa39ba8..4f0c6e530 100644 --- a/src/syscall/syscall_baremetal.go +++ b/src/syscall/syscall_baremetal.go @@ -98,14 +98,8 @@ type ProcAttr struct { type SysProcAttr struct { } -func Getegid() int { return 1 } -func Geteuid() int { return 1 } -func Getgid() int { return 1 } func Getgroups() ([]int, error) { return []int{1}, nil } -func Getppid() int { return 2 } -func Getpid() int { return 3 } func Gettimeofday(tv *Timeval) error { return ENOSYS } -func Getuid() int { return 1 } func Kill(pid int, signum Signal) error { return ENOSYS } func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { return 0, ENOSYS diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index 80675f191..bc6359630 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -62,10 +62,6 @@ func Kill(pid int, sig Signal) (err error) { return ENOSYS // TODO } -func Getpid() (pid int) { - panic("unimplemented: getpid") // TODO -} - func Getenv(key string) (value string, found bool) { data := append([]byte(key), 0) raw := libc_getenv(&data[0]) diff --git a/testdata/stdlib.go b/testdata/stdlib.go index e6d6677b6..55237ff68 100644 --- a/testdata/stdlib.go +++ b/testdata/stdlib.go @@ -5,6 +5,7 @@ import ( "math/rand" "os" "strings" + "syscall" ) func main() { @@ -13,6 +14,14 @@ func main() { fmt.Println("stdout:", os.Stdout.Name()) fmt.Println("stderr:", os.Stderr.Name()) + // Package syscall, this mostly checks whether the calls don't trigger an error. + syscall.Getuid() + syscall.Geteuid() + syscall.Getgid() + syscall.Getegid() + syscall.Getpid() + syscall.Getppid() + // package math/rand fmt.Println("pseudorandom number:", rand.Int31()) From 96e863f0f37cef0e8f36cb0d5f1c94fe062bc03d Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 1 Jun 2021 13:27:58 +0200 Subject: [PATCH 47/70] all: add a flag to the command line to select the serial implementation This can be very useful for some purposes: * It makes it possible to disable the UART in cases where it is not needed or needs to be disabled to conserve power. * It makes it possible to disable the serial output to reduce code size, which may be important for some chips. Sometimes, a few kB can be saved this way. * It makes it possible to override the default, for example you might want to use an actual UART to debug the USB-CDC implementation. It also lowers the dependency on having machine.Serial defined, which is often not defined when targeting a chip. Eventually, we might want to make it possible to write `-target=nrf52` or `-target=atmega328p` for example to target the chip itself with no board specific assumptions. The defaults don't change. I checked this by running `make smoketest` before and after and comparing the results. --- Makefile | 2 + compileopts/config.go | 14 +++++- compileopts/options.go | 11 +++++ compileopts/target.go | 1 + main.go | 2 + src/machine/board_arduino_mkr1000.go | 2 - src/machine/board_arduino_zero.go | 2 - src/machine/board_atsamd21.go | 2 - src/machine/board_atsame54-xpro.go | 2 - src/machine/board_bluepill.go | 2 +- src/machine/board_circuitplay_bluefruit.go | 2 - src/machine/board_clue_alpha.go | 5 --- src/machine/board_esp32-coreboard-v2.go | 2 - src/machine/board_feather-m4-can.go | 2 - src/machine/board_feather-m4.go | 2 - src/machine/board_feather-nrf52840.go | 5 --- src/machine/board_feather-stm32f405.go | 2 +- src/machine/board_grandcentral-m4.go | 2 - src/machine/board_hifive1b.go | 2 +- src/machine/board_itsybitsy-m4.go | 2 - src/machine/board_itsybitsy-nrf52840.go | 5 --- src/machine/board_lgt92.go | 2 +- src/machine/board_maixbit.go | 2 +- src/machine/board_metro-m4-airlift.go | 2 - src/machine/board_microbit-v2.go | 2 +- src/machine/board_microbit.go | 2 +- src/machine/board_nicenano.go | 5 --- src/machine/board_nodemcu.go | 2 - src/machine/board_nrf52840-mdk-usb-dongle.go | 3 -- src/machine/board_nrf52840-mdk.go | 3 -- src/machine/board_nucleof103rb.go | 2 +- src/machine/board_nucleof722ze.go | 2 +- src/machine/board_nucleol031k6.go | 2 +- src/machine/board_nucleol432kc.go | 2 +- src/machine/board_nucleol552ze.go | 2 +- src/machine/board_particle_argon.go | 2 +- src/machine/board_particle_boron.go | 2 +- src/machine/board_particle_xenon.go | 2 +- src/machine/board_pca10031.go | 2 +- src/machine/board_pca10040.go | 2 +- src/machine/board_pca10056.go | 2 +- src/machine/board_pca10059.go | 5 --- src/machine/board_pinetime-devkit0.go | 2 +- src/machine/board_pybadge.go | 2 - src/machine/board_pygamer.go | 2 - src/machine/board_pyportal.go | 2 - src/machine/board_reelboard.go | 2 +- src/machine/board_stm32f4disco.go | 2 +- src/machine/board_teensy36.go | 2 + src/machine/board_teensy40.go | 6 +-- src/machine/board_wioterminal.go | 2 - src/machine/board_x9pro.go | 2 +- src/machine/machine_atmega.go | 2 +- src/machine/machine_esp32.go | 2 + src/machine/machine_esp8266.go | 2 + src/machine/machine_generic.go | 12 ++--- src/machine/machine_rp2040.go | 2 +- src/machine/serial-none.go | 6 +++ src/machine/serial-uart.go | 6 +++ src/machine/serial-usb.go | 6 +++ src/machine/serial.go | 46 ++++++++++++++++++++ src/machine/uart.go | 6 --- targets/arduino-mkr1000.json | 1 + targets/arduino-zero.json | 1 + targets/atmega1280.json | 1 + targets/atmega1284p.json | 1 + targets/atmega2560.json | 1 + targets/atmega328p.json | 1 + targets/atsamd21e18a.json | 1 + targets/atsamd21g18a.json | 1 + targets/atsame54-xpro.json | 1 + targets/bluepill.json | 1 + targets/circuitplay-bluefruit.json | 1 + targets/clue-alpha.json | 1 + targets/esp32.json | 1 + targets/feather-m4-can.json | 1 + targets/feather-m4.json | 1 + targets/feather-nrf52840.json | 1 + targets/feather-stm32f405.json | 1 + targets/grandcentral-m4.json | 1 + targets/hifive1-qemu.json | 1 + targets/hifive1b.json | 1 + targets/itsybitsy-m4.json | 1 + targets/itsybitsy-nrf52840.json | 1 + targets/lgt92.json | 1 + targets/maixbit.json | 1 + targets/metro-m4-airlift.json | 1 + targets/microbit-v2.json | 1 + targets/microbit.json | 1 + targets/nicenano.json | 1 + targets/nodemcu.json | 3 +- targets/nrf52840-mdk-usb-dongle.json | 1 + targets/nrf52840-mdk.json | 1 + targets/nucleo-f103rb.json | 1 + targets/nucleo-f722ze.json | 1 + targets/nucleo-l031k6.json | 1 + targets/nucleo-l432kc.json | 1 + targets/nucleo-l552ze.json | 1 + targets/particle-3rd-gen.json | 1 + targets/pca10031.json | 1 + targets/pca10040.json | 1 + targets/pca10056.json | 1 + targets/pca10059.json | 1 + targets/pico.json | 1 + targets/pinetime-devkit0.json | 1 + targets/pybadge.json | 1 + targets/pygamer.json | 1 + targets/pyportal.json | 1 + targets/reelboard.json | 1 + targets/stm32f4disco.json | 1 + targets/teensy36.json | 1 + targets/teensy40.json | 1 + targets/wioterminal.json | 1 + targets/x9pro.json | 1 + 114 files changed, 185 insertions(+), 104 deletions(-) create mode 100644 src/machine/serial-none.go create mode 100644 src/machine/serial-uart.go create mode 100644 src/machine/serial-usb.go create mode 100644 src/machine/serial.go diff --git a/Makefile b/Makefile index 6abdc261d..1d6cac87f 100644 --- a/Makefile +++ b/Makefile @@ -436,6 +436,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 diff --git a/compileopts/config.go b/compileopts/config.go index 821aa8313..a60f778e0 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -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", "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) { diff --git a/compileopts/options.go b/compileopts/options.go index 10c143b80..7e7bfcafc 100644 --- a/compileopts/options.go +++ b/compileopts/options.go @@ -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 { diff --git a/compileopts/target.go b/compileopts/target.go index 4fb62fce3..ba5f73826 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -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"` diff --git a/main.go b/main.go index 24d943624..7c3ee96df 100644 --- a/main.go +++ b/main.go @@ -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") @@ -1081,6 +1082,7 @@ func main() { GC: *gc, PanicStrategy: *panicStrategy, Scheduler: *scheduler, + Serial: *serial, PrintIR: *printIR, DumpSSA: *dumpSSA, VerifyIR: *verifyIR, diff --git a/src/machine/board_arduino_mkr1000.go b/src/machine/board_arduino_mkr1000.go index 863faeeb6..30ad48e90 100644 --- a/src/machine/board_arduino_mkr1000.go +++ b/src/machine/board_arduino_mkr1000.go @@ -47,8 +47,6 @@ const ( LED = D6 ) -var Serial = USB - // USBCDC pins const ( USBCDC_DM_PIN Pin = PA24 diff --git a/src/machine/board_arduino_zero.go b/src/machine/board_arduino_zero.go index 773515c99..651eea821 100644 --- a/src/machine/board_arduino_zero.go +++ b/src/machine/board_arduino_zero.go @@ -35,8 +35,6 @@ const ( LED3 Pin = PB03 // RX LED ) -var Serial = USB - // ADC pins const ( AREF Pin = PA03 diff --git a/src/machine/board_atsamd21.go b/src/machine/board_atsamd21.go index 800c4cb30..3b11b452a 100644 --- a/src/machine/board_atsamd21.go +++ b/src/machine/board_atsamd21.go @@ -74,5 +74,3 @@ const ( PB30 Pin = 62 PB31 Pin = 63 ) - -var Serial = USB diff --git a/src/machine/board_atsame54-xpro.go b/src/machine/board_atsame54-xpro.go index 01ae282c1..c61324b64 100644 --- a/src/machine/board_atsame54-xpro.go +++ b/src/machine/board_atsame54-xpro.go @@ -15,8 +15,6 @@ const ( BUTTON = PB31 ) -var Serial = USB - const ( // https://ww1.microchip.com/downloads/en/DeviceDoc/70005321A.pdf diff --git a/src/machine/board_bluepill.go b/src/machine/board_bluepill.go index ce925025f..bbfdac8bb 100644 --- a/src/machine/board_bluepill.go +++ b/src/machine/board_bluepill.go @@ -17,7 +17,7 @@ const ( BUTTON = PA0 ) -var Serial = UART1 +var DefaultUART = UART1 // UART pins const ( diff --git a/src/machine/board_circuitplay_bluefruit.go b/src/machine/board_circuitplay_bluefruit.go index 967672495..a65861b27 100644 --- a/src/machine/board_circuitplay_bluefruit.go +++ b/src/machine/board_circuitplay_bluefruit.go @@ -57,8 +57,6 @@ const ( UART_RX_PIN = P0_30 // PORTB ) -var Serial = USB - // I2C pins const ( SDA_PIN = P0_05 // I2C0 external diff --git a/src/machine/board_clue_alpha.go b/src/machine/board_clue_alpha.go index b421dccce..67c2ad7ef 100644 --- a/src/machine/board_clue_alpha.go +++ b/src/machine/board_clue_alpha.go @@ -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 diff --git a/src/machine/board_esp32-coreboard-v2.go b/src/machine/board_esp32-coreboard-v2.go index b1d61d87e..0145fe4ea 100644 --- a/src/machine/board_esp32-coreboard-v2.go +++ b/src/machine/board_esp32-coreboard-v2.go @@ -68,8 +68,6 @@ const ( ADC3 Pin = IO39 ) -var Serial = UART0 - // UART0 pins const ( UART_TX_PIN = IO1 diff --git a/src/machine/board_feather-m4-can.go b/src/machine/board_feather-m4-can.go index 7911f5561..a954ce9cb 100644 --- a/src/machine/board_feather-m4-can.go +++ b/src/machine/board_feather-m4-can.go @@ -47,8 +47,6 @@ const ( WS2812 = D8 ) -var Serial = USB - // USBCDC pins const ( USBCDC_DM_PIN = PA24 diff --git a/src/machine/board_feather-m4.go b/src/machine/board_feather-m4.go index 394191366..86158ed64 100644 --- a/src/machine/board_feather-m4.go +++ b/src/machine/board_feather-m4.go @@ -40,8 +40,6 @@ const ( WS2812 = D8 ) -var Serial = USB - // USBCDC pins const ( USBCDC_DM_PIN = PA24 diff --git a/src/machine/board_feather-nrf52840.go b/src/machine/board_feather-nrf52840.go index 641a9de81..488d9608b 100644 --- a/src/machine/board_feather-nrf52840.go +++ b/src/machine/board_feather-nrf52840.go @@ -76,11 +76,6 @@ const ( UART_TX_PIN = D1 ) -// Serial is the USB device -var ( - Serial = USB -) - // I2C pins const ( SDA_PIN = D22 // I2C0 external diff --git a/src/machine/board_feather-stm32f405.go b/src/machine/board_feather-stm32f405.go index 4bf1744b9..ebba9edc8 100644 --- a/src/machine/board_feather-stm32f405.go +++ b/src/machine/board_feather-stm32f405.go @@ -141,7 +141,7 @@ var ( TxAltFuncSelector: AF7_USART1_2_3, RxAltFuncSelector: AF7_USART1_2_3, } - Serial = UART1 + DefaultUART = UART1 ) func initUART() { diff --git a/src/machine/board_grandcentral-m4.go b/src/machine/board_grandcentral-m4.go index 014eda801..c1b629c9e 100644 --- a/src/machine/board_grandcentral-m4.go +++ b/src/machine/board_grandcentral-m4.go @@ -142,8 +142,6 @@ const ( WS2812 = NEOPIXEL_PIN ) -var Serial = USB - // UART pins const ( UART1_RX_PIN = D0 // (PB25) diff --git a/src/machine/board_hifive1b.go b/src/machine/board_hifive1b.go index 7ce225ccf..d16225a68 100644 --- a/src/machine/board_hifive1b.go +++ b/src/machine/board_hifive1b.go @@ -35,7 +35,7 @@ const ( LED_BLUE = P21 ) -var Serial = UART0 +var DefaultUART = UART0 const ( // TODO: figure out the pin numbers for these. diff --git a/src/machine/board_itsybitsy-m4.go b/src/machine/board_itsybitsy-m4.go index 282f9087a..099ceed46 100644 --- a/src/machine/board_itsybitsy-m4.go +++ b/src/machine/board_itsybitsy-m4.go @@ -37,8 +37,6 @@ const ( LED = D13 ) -var Serial = USB - // USBCDC pins const ( USBCDC_DM_PIN = PA24 diff --git a/src/machine/board_itsybitsy-nrf52840.go b/src/machine/board_itsybitsy-nrf52840.go index 64aeed506..fc9b23f25 100644 --- a/src/machine/board_itsybitsy-nrf52840.go +++ b/src/machine/board_itsybitsy-nrf52840.go @@ -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 diff --git a/src/machine/board_lgt92.go b/src/machine/board_lgt92.go index 97e1fa2ab..e043dcd77 100644 --- a/src/machine/board_lgt92.go +++ b/src/machine/board_lgt92.go @@ -54,7 +54,7 @@ const ( I2C0_SDA_PIN = PA10 ) -var Serial = UART0 +var DefaultUART = UART0 var ( diff --git a/src/machine/board_maixbit.go b/src/machine/board_maixbit.go index 766054d0d..e2c551b0e 100644 --- a/src/machine/board_maixbit.go +++ b/src/machine/board_maixbit.go @@ -52,7 +52,7 @@ const ( LED_BLUE = D14 ) -var Serial = UART0 +var DefaultUART = UART0 // Default pins for UARTHS. const ( diff --git a/src/machine/board_metro-m4-airlift.go b/src/machine/board_metro-m4-airlift.go index 276defa92..4aa248150 100644 --- a/src/machine/board_metro-m4-airlift.go +++ b/src/machine/board_metro-m4-airlift.go @@ -41,8 +41,6 @@ const ( WS2812 = D40 ) -var Serial = USB - // USBCDC pins const ( USBCDC_DM_PIN = PA24 diff --git a/src/machine/board_microbit-v2.go b/src/machine/board_microbit-v2.go index a034e2ef8..38a9dad50 100644 --- a/src/machine/board_microbit-v2.go +++ b/src/machine/board_microbit-v2.go @@ -12,7 +12,7 @@ const ( BUTTONB Pin = P11 ) -var Serial = UART0 +var DefaultUART = UART0 // UART pins const ( diff --git a/src/machine/board_microbit.go b/src/machine/board_microbit.go index 5b0ac2bff..b4af49f8d 100644 --- a/src/machine/board_microbit.go +++ b/src/machine/board_microbit.go @@ -12,7 +12,7 @@ const ( BUTTONB Pin = 26 ) -var Serial = UART0 +var DefaultUART = UART0 // UART pins const ( diff --git a/src/machine/board_nicenano.go b/src/machine/board_nicenano.go index 213fdb339..94511bf9f 100644 --- a/src/machine/board_nicenano.go +++ b/src/machine/board_nicenano.go @@ -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 diff --git a/src/machine/board_nodemcu.go b/src/machine/board_nodemcu.go index 4177f80c4..f9f1af4f9 100644 --- a/src/machine/board_nodemcu.go +++ b/src/machine/board_nodemcu.go @@ -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 diff --git a/src/machine/board_nrf52840-mdk-usb-dongle.go b/src/machine/board_nrf52840-mdk-usb-dongle.go index e4923f4e9..57a3d1976 100644 --- a/src/machine/board_nrf52840-mdk-usb-dongle.go +++ b/src/machine/board_nrf52840-mdk-usb-dongle.go @@ -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 diff --git a/src/machine/board_nrf52840-mdk.go b/src/machine/board_nrf52840-mdk.go index d9dc41305..fbc42861b 100644 --- a/src/machine/board_nrf52840-mdk.go +++ b/src/machine/board_nrf52840-mdk.go @@ -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 diff --git a/src/machine/board_nucleof103rb.go b/src/machine/board_nucleof103rb.go index 6fa0c27c7..22580e20b 100644 --- a/src/machine/board_nucleof103rb.go +++ b/src/machine/board_nucleof103rb.go @@ -34,7 +34,7 @@ var ( Buffer: NewRingBuffer(), Bus: stm32.USART2, } - Serial = UART2 + DefaultUART = UART2 ) func init() { diff --git a/src/machine/board_nucleof722ze.go b/src/machine/board_nucleof722ze.go index 003e8fc75..41d3f2849 100644 --- a/src/machine/board_nucleof722ze.go +++ b/src/machine/board_nucleof722ze.go @@ -38,7 +38,7 @@ var ( TxAltFuncSelector: UART_ALT_FN, RxAltFuncSelector: UART_ALT_FN, } - Serial = UART1 + DefaultUART = UART1 ) func init() { diff --git a/src/machine/board_nucleol031k6.go b/src/machine/board_nucleol031k6.go index a758ba932..fe85f276b 100644 --- a/src/machine/board_nucleol031k6.go +++ b/src/machine/board_nucleol031k6.go @@ -76,7 +76,7 @@ var ( TxAltFuncSelector: 4, RxAltFuncSelector: 4, } - Serial = UART1 + DefaultUART = UART1 // I2C1 is documented, alias to I2C0 as well I2C1 = &I2C{ diff --git a/src/machine/board_nucleol432kc.go b/src/machine/board_nucleol432kc.go index d44e0f4fd..bfebd1eed 100644 --- a/src/machine/board_nucleol432kc.go +++ b/src/machine/board_nucleol432kc.go @@ -78,7 +78,7 @@ var ( TxAltFuncSelector: 7, RxAltFuncSelector: 3, } - Serial = UART1 + DefaultUART = UART1 // I2C1 is documented, alias to I2C0 as well I2C1 = &I2C{ diff --git a/src/machine/board_nucleol552ze.go b/src/machine/board_nucleol552ze.go index 98e2d5c1f..a0d3ee522 100644 --- a/src/machine/board_nucleol552ze.go +++ b/src/machine/board_nucleol552ze.go @@ -38,7 +38,7 @@ var ( TxAltFuncSelector: UART_ALT_FN, RxAltFuncSelector: UART_ALT_FN, } - Serial = UART1 + DefaultUART = UART1 ) const ( diff --git a/src/machine/board_particle_argon.go b/src/machine/board_particle_argon.go index 1130eaf82..9427029f4 100644 --- a/src/machine/board_particle_argon.go +++ b/src/machine/board_particle_argon.go @@ -41,7 +41,7 @@ const ( // UART var ( - Serial = UART0 + DefaultUART = UART0 ) const ( diff --git a/src/machine/board_particle_boron.go b/src/machine/board_particle_boron.go index 6c2f70a80..b6a2c3e54 100644 --- a/src/machine/board_particle_boron.go +++ b/src/machine/board_particle_boron.go @@ -41,7 +41,7 @@ const ( // UART var ( - Serial = UART0 + DefaultUART = UART0 ) const ( diff --git a/src/machine/board_particle_xenon.go b/src/machine/board_particle_xenon.go index a6e48ae0a..0b25f373b 100644 --- a/src/machine/board_particle_xenon.go +++ b/src/machine/board_particle_xenon.go @@ -41,7 +41,7 @@ const ( // UART var ( - Serial = UART0 + DefaultUART = UART0 ) const ( diff --git a/src/machine/board_pca10031.go b/src/machine/board_pca10031.go index fbddfb232..122c00d20 100644 --- a/src/machine/board_pca10031.go +++ b/src/machine/board_pca10031.go @@ -19,7 +19,7 @@ const ( LED_BLUE Pin = 23 ) -var Serial = UART0 +var DefaultUART = UART0 // UART pins const ( diff --git a/src/machine/board_pca10040.go b/src/machine/board_pca10040.go index 10e2ab2ed..f425ca37c 100644 --- a/src/machine/board_pca10040.go +++ b/src/machine/board_pca10040.go @@ -23,7 +23,7 @@ const ( BUTTON4 Pin = 16 ) -var Serial = UART0 +var DefaultUART = UART0 // UART pins for NRF52840-DK const ( diff --git a/src/machine/board_pca10056.go b/src/machine/board_pca10056.go index 824bd3f01..783629465 100644 --- a/src/machine/board_pca10056.go +++ b/src/machine/board_pca10056.go @@ -22,7 +22,7 @@ const ( BUTTON4 Pin = 25 ) -var Serial = UART0 +var DefaultUART = UART0 // UART pins const ( diff --git a/src/machine/board_pca10059.go b/src/machine/board_pca10059.go index 5079534aa..7f6167913 100644 --- a/src/machine/board_pca10059.go +++ b/src/machine/board_pca10059.go @@ -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 diff --git a/src/machine/board_pinetime-devkit0.go b/src/machine/board_pinetime-devkit0.go index 5222010bd..b9669c84d 100644 --- a/src/machine/board_pinetime-devkit0.go +++ b/src/machine/board_pinetime-devkit0.go @@ -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: diff --git a/src/machine/board_pybadge.go b/src/machine/board_pybadge.go index 3ff24a160..e175da683 100644 --- a/src/machine/board_pybadge.go +++ b/src/machine/board_pybadge.go @@ -67,8 +67,6 @@ const ( BUTTON_B_MASK = 128 ) -var Serial = USB - // USBCDC pins const ( USBCDC_DM_PIN = PA24 diff --git a/src/machine/board_pygamer.go b/src/machine/board_pygamer.go index 890c3d3d9..f537f8529 100644 --- a/src/machine/board_pygamer.go +++ b/src/machine/board_pygamer.go @@ -70,8 +70,6 @@ const ( BUTTON_B_MASK = 128 ) -var Serial = USB - // USBCDC pins const ( USBCDC_DM_PIN = PA24 diff --git a/src/machine/board_pyportal.go b/src/machine/board_pyportal.go index 7e28de44a..ff2fcb7fe 100644 --- a/src/machine/board_pyportal.go +++ b/src/machine/board_pyportal.go @@ -95,8 +95,6 @@ const ( LED = D13 ) -var Serial = USB - // USBCDC pins const ( USBCDC_DM_PIN = PA24 diff --git a/src/machine/board_reelboard.go b/src/machine/board_reelboard.go index 70eb70539..7fd715bb7 100644 --- a/src/machine/board_reelboard.go +++ b/src/machine/board_reelboard.go @@ -29,7 +29,7 @@ const ( BUTTON Pin = 7 ) -var Serial = UART0 +var DefaultUART = UART0 // UART pins const ( diff --git a/src/machine/board_stm32f4disco.go b/src/machine/board_stm32f4disco.go index 49f5650fd..cc3711a19 100644 --- a/src/machine/board_stm32f4disco.go +++ b/src/machine/board_stm32f4disco.go @@ -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 diff --git a/src/machine/board_teensy36.go b/src/machine/board_teensy36.go index 0808b4b35..8fa71f6e9 100644 --- a/src/machine/board_teensy36.go +++ b/src/machine/board_teensy36.go @@ -87,6 +87,8 @@ var ( TeensyUART5 = UART4 ) +var DefaultUART = UART0 + const ( defaultUART0RX = D00 defaultUART0TX = D01 diff --git a/src/machine/board_teensy40.go b/src/machine/board_teensy40.go index 54ae4e6b4..289edadd8 100644 --- a/src/machine/board_teensy40.go +++ b/src/machine/board_teensy40.go @@ -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(), diff --git a/src/machine/board_wioterminal.go b/src/machine/board_wioterminal.go index abbe55500..57bbff1b5 100644 --- a/src/machine/board_wioterminal.go +++ b/src/machine/board_wioterminal.go @@ -325,8 +325,6 @@ const ( OUTPUT_CTR_3V3 = PC15 ) -var Serial = USB - // USBCDC pins const ( USBCDC_DM_PIN = PIN_USB_DM diff --git a/src/machine/board_x9pro.go b/src/machine/board_x9pro.go index 63d7257a5..111dcf5fd 100644 --- a/src/machine/board_x9pro.go +++ b/src/machine/board_x9pro.go @@ -27,4 +27,4 @@ const ( const HasLowFrequencyCrystal = true -var Serial = UART0 +var DefaultUART = UART0 diff --git a/src/machine/machine_atmega.go b/src/machine/machine_atmega.go index fe6536e7f..6e2b59cf1 100644 --- a/src/machine/machine_atmega.go +++ b/src/machine/machine_atmega.go @@ -122,7 +122,7 @@ func (i2c *I2C) readByte() byte { } // Always use UART0 as the serial output. -var Serial = UART0 +var DefaultUART = UART0 // UART var ( diff --git a/src/machine/machine_esp32.go b/src/machine/machine_esp32.go index ee3f61de1..b5f801b49 100644 --- a/src/machine/machine_esp32.go +++ b/src/machine/machine_esp32.go @@ -251,6 +251,8 @@ func (p Pin) mux() *volatile.Register32 { } } +var DefaultUART = UART0 + var ( UART0 = &_UART0 _UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()} diff --git a/src/machine/machine_esp8266.go b/src/machine/machine_esp8266.go index e8a9ecf64..72c672066 100644 --- a/src/machine/machine_esp8266.go +++ b/src/machine/machine_esp8266.go @@ -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()} diff --git a/src/machine/machine_generic.go b/src/machine/machine_generic.go index 98dca2df8..70551e27a 100644 --- a/src/machine/machine_generic.go +++ b/src/machine/machine_generic.go @@ -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) diff --git a/src/machine/machine_rp2040.go b/src/machine/machine_rp2040.go index f7580967a..297c92e3b 100644 --- a/src/machine/machine_rp2040.go +++ b/src/machine/machine_rp2040.go @@ -108,7 +108,7 @@ var ( } ) -var Serial = UART0 +var DefaultUART = UART0 func init() { UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt) diff --git a/src/machine/serial-none.go b/src/machine/serial-none.go new file mode 100644 index 000000000..22e94cc90 --- /dev/null +++ b/src/machine/serial-none.go @@ -0,0 +1,6 @@ +// +build baremetal,serial.none + +package machine + +// Serial is a null device: writes to it are ignored. +var Serial = NullSerial{} diff --git a/src/machine/serial-uart.go b/src/machine/serial-uart.go new file mode 100644 index 000000000..d3edf832f --- /dev/null +++ b/src/machine/serial-uart.go @@ -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 diff --git a/src/machine/serial-usb.go b/src/machine/serial-usb.go new file mode 100644 index 000000000..476d4b0cf --- /dev/null +++ b/src/machine/serial-usb.go @@ -0,0 +1,6 @@ +// +build baremetal,serial.usb + +package machine + +// Serial is implemented via USB (USB-CDC). +var Serial = USB diff --git a/src/machine/serial.go b/src/machine/serial.go new file mode 100644 index 000000000..fd02d6ca0 --- /dev/null +++ b/src/machine/serial.go @@ -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 +} diff --git a/src/machine/uart.go b/src/machine/uart.go index 701fb9797..8fbac8711 100644 --- a/src/machine/uart.go +++ b/src/machine/uart.go @@ -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 { diff --git a/targets/arduino-mkr1000.json b/targets/arduino-mkr1000.json index a5abfca52..89d7bfa69 100644 --- a/targets/arduino-mkr1000.json +++ b/targets/arduino-mkr1000.json @@ -1,6 +1,7 @@ { "inherits": ["atsamd21g18a"], "build-tags": ["arduino_mkr1000"], + "serial": "usb", "flash-command": "bossac -i -e -w -v -R -U --port={port} --offset=0x2000 {bin}", "flash-1200-bps-reset": "true" } diff --git a/targets/arduino-zero.json b/targets/arduino-zero.json index 50483512e..045bb8c05 100644 --- a/targets/arduino-zero.json +++ b/targets/arduino-zero.json @@ -1,6 +1,7 @@ { "inherits": ["atsamd21g18a"], "build-tags": ["arduino_zero"], + "serial": "usb", "flash-command": "bossac -i -e -w -v -R -U --port={port} --offset=0x2000 {bin}", "flash-1200-bps-reset": "true" } diff --git a/targets/atmega1280.json b/targets/atmega1280.json index 21324a36d..2b0bfde7b 100644 --- a/targets/atmega1280.json +++ b/targets/atmega1280.json @@ -2,6 +2,7 @@ "inherits": ["avr"], "cpu": "atmega1280", "build-tags": ["atmega1280", "atmega"], + "serial": "uart", "cflags": [ "-mmcu=atmega1280" ], diff --git a/targets/atmega1284p.json b/targets/atmega1284p.json index 2c2d29e18..3fbecd4b7 100644 --- a/targets/atmega1284p.json +++ b/targets/atmega1284p.json @@ -2,6 +2,7 @@ "inherits": ["avr"], "cpu": "atmega1284p", "build-tags": ["atmega1284p", "atmega"], + "serial": "uart", "cflags": [ "-mmcu=atmega1284p" ], diff --git a/targets/atmega2560.json b/targets/atmega2560.json index f460c9c89..9caa088cf 100644 --- a/targets/atmega2560.json +++ b/targets/atmega2560.json @@ -2,6 +2,7 @@ "inherits": ["avr"], "cpu": "atmega2560", "build-tags": ["atmega2560", "atmega"], + "serial": "uart", "cflags": [ "-mmcu=atmega2560" ], diff --git a/targets/atmega328p.json b/targets/atmega328p.json index 7a10eceba..24a272147 100644 --- a/targets/atmega328p.json +++ b/targets/atmega328p.json @@ -2,6 +2,7 @@ "inherits": ["avr"], "cpu": "atmega328p", "build-tags": ["atmega328p", "atmega", "avr5"], + "serial": "uart", "cflags": [ "-mmcu=atmega328p" ], diff --git a/targets/atsamd21e18a.json b/targets/atsamd21e18a.json index 0fcf50675..92b671c9f 100644 --- a/targets/atsamd21e18a.json +++ b/targets/atsamd21e18a.json @@ -1,6 +1,7 @@ { "inherits": ["cortex-m0plus"], "build-tags": ["atsamd21e18a", "atsamd21e18", "atsamd21", "sam"], + "serial": "usb", "linkerscript": "targets/atsamd21.ld", "extra-files": [ "src/device/sam/atsamd21e18a.s" diff --git a/targets/atsamd21g18a.json b/targets/atsamd21g18a.json index 956afba54..db46c9628 100644 --- a/targets/atsamd21g18a.json +++ b/targets/atsamd21g18a.json @@ -1,6 +1,7 @@ { "inherits": ["cortex-m0plus"], "build-tags": ["atsamd21g18a", "atsamd21g18", "atsamd21", "sam"], + "serial": "usb", "linkerscript": "targets/atsamd21.ld", "extra-files": [ "src/device/sam/atsamd21g18a.s" diff --git a/targets/atsame54-xpro.json b/targets/atsame54-xpro.json index 631df8cfb..1ab7c4eea 100644 --- a/targets/atsame54-xpro.json +++ b/targets/atsame54-xpro.json @@ -1,6 +1,7 @@ { "inherits": ["atsame54p20a"], "build-tags": ["atsame54_xpro"], + "serial": "usb", "flash-method": "openocd", "openocd-interface": "cmsis-dap", "default-stack-size": 4096 diff --git a/targets/bluepill.json b/targets/bluepill.json index 9f1e4b004..752261f4d 100644 --- a/targets/bluepill.json +++ b/targets/bluepill.json @@ -1,6 +1,7 @@ { "inherits": ["cortex-m3"], "build-tags": ["bluepill", "stm32f103", "stm32f1", "stm32"], + "serial": "uart", "linkerscript": "targets/stm32.ld", "extra-files": [ "src/device/stm32/stm32f103.s" diff --git a/targets/circuitplay-bluefruit.json b/targets/circuitplay-bluefruit.json index 24bf5577f..0c1f6ae3b 100644 --- a/targets/circuitplay-bluefruit.json +++ b/targets/circuitplay-bluefruit.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52840"], "build-tags": ["circuitplay_bluefruit","nrf52840_reset_uf2", "softdevice", "s140v6"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "serial-port": ["acm:239a:8045", "acm:239a:45"], diff --git a/targets/clue-alpha.json b/targets/clue-alpha.json index e2594ea7d..ebe83f79d 100644 --- a/targets/clue-alpha.json +++ b/targets/clue-alpha.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52840"], "build-tags": ["clue_alpha","nrf52840_reset_uf2", "softdevice", "s140v6"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "CLUEBOOT", diff --git a/targets/esp32.json b/targets/esp32.json index 3559e8920..ded861624 100644 --- a/targets/esp32.json +++ b/targets/esp32.json @@ -3,6 +3,7 @@ "cpu": "esp32", "build-tags": ["esp32", "esp"], "scheduler": "tasks", + "serial": "uart", "linker": "xtensa-esp32-elf-ld", "default-stack-size": 2048, "cflags": [ diff --git a/targets/feather-m4-can.json b/targets/feather-m4-can.json index 190d4d8f9..22222622f 100644 --- a/targets/feather-m4-can.json +++ b/targets/feather-m4-can.json @@ -1,6 +1,7 @@ { "inherits": ["atsame51j19a"], "build-tags": ["feather_m4_can"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "FTHRCANBOOT", diff --git a/targets/feather-m4.json b/targets/feather-m4.json index ccf06ec54..408946297 100644 --- a/targets/feather-m4.json +++ b/targets/feather-m4.json @@ -1,6 +1,7 @@ { "inherits": ["atsamd51j19a"], "build-tags": ["feather_m4"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "FEATHERBOOT", diff --git a/targets/feather-nrf52840.json b/targets/feather-nrf52840.json index f0b706186..c334f7065 100644 --- a/targets/feather-nrf52840.json +++ b/targets/feather-nrf52840.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52840"], "build-tags": ["feather_nrf52840","nrf52840_reset_uf2", "softdevice", "s140v6"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "FTHR840BOOT", diff --git a/targets/feather-stm32f405.json b/targets/feather-stm32f405.json index d3bb1dc9c..3d824d1ed 100644 --- a/targets/feather-stm32f405.json +++ b/targets/feather-stm32f405.json @@ -1,6 +1,7 @@ { "inherits": ["cortex-m4"], "build-tags": ["feather_stm32f405", "stm32f405", "stm32f4", "stm32"], + "serial": "uart", "automatic-stack-size": false, "linkerscript": "targets/stm32f405.ld", "extra-files": [ diff --git a/targets/grandcentral-m4.json b/targets/grandcentral-m4.json index 4545dfd9f..7b374c98e 100644 --- a/targets/grandcentral-m4.json +++ b/targets/grandcentral-m4.json @@ -1,6 +1,7 @@ { "inherits": ["atsamd51p20a"], "build-tags": ["grandcentral_m4"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "GCM4BOOT", diff --git a/targets/hifive1-qemu.json b/targets/hifive1-qemu.json index 9a50cee64..60fe27a4d 100644 --- a/targets/hifive1-qemu.json +++ b/targets/hifive1-qemu.json @@ -1,6 +1,7 @@ { "inherits": ["fe310"], "build-tags": ["hifive1b", "qemu"], + "serial": "uart", "linkerscript": "targets/hifive1-qemu.ld", "emulator": ["qemu-system-riscv32", "-machine", "sifive_e", "-nographic", "-kernel"] } diff --git a/targets/hifive1b.json b/targets/hifive1b.json index bd561ae0b..1084531b3 100644 --- a/targets/hifive1b.json +++ b/targets/hifive1b.json @@ -1,6 +1,7 @@ { "inherits": ["fe310"], "build-tags": ["hifive1b"], + "serial": "uart", "linkerscript": "targets/hifive1b.ld", "flash-method": "msd", "msd-volume-name": "HiFive", diff --git a/targets/itsybitsy-m4.json b/targets/itsybitsy-m4.json index 9c7a634ba..af4027a4a 100644 --- a/targets/itsybitsy-m4.json +++ b/targets/itsybitsy-m4.json @@ -1,6 +1,7 @@ { "inherits": ["atsamd51g19a"], "build-tags": ["itsybitsy_m4"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "serial-port": ["acm:239a:802b", "acm:239a:002b"], diff --git a/targets/itsybitsy-nrf52840.json b/targets/itsybitsy-nrf52840.json index b8637fd00..aab873c18 100644 --- a/targets/itsybitsy-nrf52840.json +++ b/targets/itsybitsy-nrf52840.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52840"], "build-tags": ["itsybitsy_nrf52840","nrf52840_reset_uf2", "softdevice", "s140v6"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "ITSY840BOOT", diff --git a/targets/lgt92.json b/targets/lgt92.json index 82c603a9b..23bc84dd2 100644 --- a/targets/lgt92.json +++ b/targets/lgt92.json @@ -5,6 +5,7 @@ "build-tags": [ "lgt92" ], + "serial": "uart", "linkerscript": "targets/stm32l072czt6.ld", "flash-method": "openocd", "openocd-interface": "stlink-v2", diff --git a/targets/maixbit.json b/targets/maixbit.json index 21322072a..8f6216162 100644 --- a/targets/maixbit.json +++ b/targets/maixbit.json @@ -1,6 +1,7 @@ { "inherits": ["k210"], "build-tags": ["maixbit"], + "serial": "uart", "linkerscript": "targets/maixbit.ld", "flash-command": "kflash -p {port} --noansi --verbose {bin}" } diff --git a/targets/metro-m4-airlift.json b/targets/metro-m4-airlift.json index a282ac55a..5783a4336 100644 --- a/targets/metro-m4-airlift.json +++ b/targets/metro-m4-airlift.json @@ -1,6 +1,7 @@ { "inherits": ["atsamd51j19a"], "build-tags": ["metro_m4_airlift"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "METROM4BOOT", diff --git a/targets/microbit-v2.json b/targets/microbit-v2.json index 33a9392eb..c8c7461c3 100644 --- a/targets/microbit-v2.json +++ b/targets/microbit-v2.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52833"], "build-tags": ["microbit_v2"], + "serial": "uart", "flash-method": "msd", "openocd-interface": "cmsis-dap", "msd-volume-name": "MICROBIT", diff --git a/targets/microbit.json b/targets/microbit.json index 1b677c5d3..a890100da 100644 --- a/targets/microbit.json +++ b/targets/microbit.json @@ -1,6 +1,7 @@ { "inherits": ["nrf51"], "build-tags": ["microbit"], + "serial": "uart", "flash-method": "msd", "openocd-interface": "cmsis-dap", "msd-volume-name": "MICROBIT", diff --git a/targets/nicenano.json b/targets/nicenano.json index 3a406ea5c..b18a0c1ca 100644 --- a/targets/nicenano.json +++ b/targets/nicenano.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52840"], "build-tags": ["nicenano","nrf52840_reset_uf2", "softdevice", "s140v6"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "NICENANO", diff --git a/targets/nodemcu.json b/targets/nodemcu.json index c1fdbec06..c8c6c4039 100644 --- a/targets/nodemcu.json +++ b/targets/nodemcu.json @@ -1,4 +1,5 @@ { "inherits": ["esp8266"], - "build-tags": ["nodemcu"] + "build-tags": ["nodemcu"], + "serial": "uart" } diff --git a/targets/nrf52840-mdk-usb-dongle.json b/targets/nrf52840-mdk-usb-dongle.json index fa4c72256..34e76b779 100644 --- a/targets/nrf52840-mdk-usb-dongle.json +++ b/targets/nrf52840-mdk-usb-dongle.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52840"], "build-tags": ["nrf52840_mdk_usb_dongle", "nrf52840_reset_uf2", "softdevice", "s140v6"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "MDK-DONGLE", diff --git a/targets/nrf52840-mdk.json b/targets/nrf52840-mdk.json index 244b3d96d..03528e501 100644 --- a/targets/nrf52840-mdk.json +++ b/targets/nrf52840-mdk.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52840"], "build-tags": ["nrf52840_mdk"], + "serial": "usb", "flash-method": "openocd", "openocd-interface": "cmsis-dap" } diff --git a/targets/nucleo-f103rb.json b/targets/nucleo-f103rb.json index 1c12af847..723c5eef9 100644 --- a/targets/nucleo-f103rb.json +++ b/targets/nucleo-f103rb.json @@ -1,6 +1,7 @@ { "inherits": ["cortex-m3"], "build-tags": ["nucleof103rb", "stm32f103", "stm32f1","stm32"], + "serial": "uart", "linkerscript": "targets/stm32f103rb.ld", "extra-files": [ "src/device/stm32/stm32f103.s" diff --git a/targets/nucleo-f722ze.json b/targets/nucleo-f722ze.json index 25a388b9e..f426b332d 100644 --- a/targets/nucleo-f722ze.json +++ b/targets/nucleo-f722ze.json @@ -1,6 +1,7 @@ { "inherits": ["cortex-m7"], "build-tags": ["nucleof722ze", "stm32f7x2", "stm32f7", "stm32"], + "serial": "uart", "linkerscript": "targets/stm32f7x2zetx.ld", "extra-files": [ "src/device/stm32/stm32f7x2.s" diff --git a/targets/nucleo-l031k6.json b/targets/nucleo-l031k6.json index 928dd43dc..eed8f38f0 100644 --- a/targets/nucleo-l031k6.json +++ b/targets/nucleo-l031k6.json @@ -1,6 +1,7 @@ { "inherits": ["cortex-m0"], "build-tags": ["nucleol031k6", "stm32l031", "stm32l0x1", "stm32l0", "stm32"], + "serial": "uart", "linkerscript": "targets/stm32l031k6.ld", "extra-files": [ "src/device/stm32/stm32l0x1.s" diff --git a/targets/nucleo-l432kc.json b/targets/nucleo-l432kc.json index f13c8bde4..028b034b0 100644 --- a/targets/nucleo-l432kc.json +++ b/targets/nucleo-l432kc.json @@ -1,6 +1,7 @@ { "inherits": ["cortex-m4"], "build-tags": ["nucleol432kc", "stm32l432", "stm32l4x2", "stm32l4", "stm32"], + "serial": "uart", "linkerscript": "targets/stm32l4x2.ld", "extra-files": [ "src/device/stm32/stm32l4x2.s" diff --git a/targets/nucleo-l552ze.json b/targets/nucleo-l552ze.json index 56e286ab5..044b58371 100644 --- a/targets/nucleo-l552ze.json +++ b/targets/nucleo-l552ze.json @@ -1,6 +1,7 @@ { "inherits": ["cortex-m33"], "build-tags": ["nucleol552ze", "stm32l552", "stm32l5x2", "stm32l5", "stm32"], + "serial": "uart", "linkerscript": "targets/stm32l5x2xe.ld", "extra-files": [ "src/device/stm32/stm32l552.s" diff --git a/targets/particle-3rd-gen.json b/targets/particle-3rd-gen.json index 2229319e7..ef39a7340 100644 --- a/targets/particle-3rd-gen.json +++ b/targets/particle-3rd-gen.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52840"], "build-tags": ["particle_3rd_gen"], + "serial": "uart", "flash-method": "openocd", "openocd-interface": "cmsis-dap" } diff --git a/targets/pca10031.json b/targets/pca10031.json index cccae828a..8af365cc8 100644 --- a/targets/pca10031.json +++ b/targets/pca10031.json @@ -1,6 +1,7 @@ { "inherits": ["nrf51"], "build-tags": ["pca10031"], + "serial": "uart", "flash-command": "nrfjprog -f nrf51 --sectorerase --program {hex} --reset", "openocd-interface": "cmsis-dap" } diff --git a/targets/pca10040.json b/targets/pca10040.json index 7685be427..b751b3bd6 100644 --- a/targets/pca10040.json +++ b/targets/pca10040.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52"], "build-tags": ["pca10040"], + "serial": "uart", "flash-method": "openocd", "flash-command": "nrfjprog -f nrf52 --sectorerase --program {hex} --reset", "openocd-interface": "jlink", diff --git a/targets/pca10056.json b/targets/pca10056.json index fc0acaa0a..b604624cf 100644 --- a/targets/pca10056.json +++ b/targets/pca10056.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52840"], "build-tags": ["pca10056"], + "serial": "uart", "flash-method": "command", "flash-command": "nrfjprog -f nrf52 --sectorerase --program {hex} --reset", "msd-volume-name": "JLINK", diff --git a/targets/pca10059.json b/targets/pca10059.json index 5a8eb8265..b5286e989 100644 --- a/targets/pca10059.json +++ b/targets/pca10059.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52840"], "build-tags": ["pca10059"], + "serial": "usb", "linkerscript": "targets/pca10059.ld", "binary-format": "nrf-dfu", "flash-command": "nrfutil dfu usb-serial -pkg {zip} -p {port} -b 115200" diff --git a/targets/pico.json b/targets/pico.json index 011d5110f..36e3ba30e 100644 --- a/targets/pico.json +++ b/targets/pico.json @@ -3,6 +3,7 @@ "rp2040" ], "build-tags": ["pico"], + "serial": "uart", "linkerscript": "targets/pico.ld", "extra-files": [ "targets/pico-boot-stage2.S" diff --git a/targets/pinetime-devkit0.json b/targets/pinetime-devkit0.json index adbe874ae..8655769ee 100644 --- a/targets/pinetime-devkit0.json +++ b/targets/pinetime-devkit0.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52"], "build-tags": ["pinetime_devkit0"], + "serial": "uart", "flash-method": "openocd", "flash-command": "nrfjprog -f nrf52 --sectorerase --program {hex} --reset", "openocd-interface": "jlink", diff --git a/targets/pybadge.json b/targets/pybadge.json index b76b32469..15af8f8a9 100644 --- a/targets/pybadge.json +++ b/targets/pybadge.json @@ -1,6 +1,7 @@ { "inherits": ["atsamd51j19a"], "build-tags": ["pybadge"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "serial-port": ["acm:239a:8033", "acm:239a:33"], diff --git a/targets/pygamer.json b/targets/pygamer.json index 63dc34a18..03147e08a 100644 --- a/targets/pygamer.json +++ b/targets/pygamer.json @@ -1,6 +1,7 @@ { "inherits": ["atsamd51j19a"], "build-tags": ["pygamer"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "PYGAMERBOOT", diff --git a/targets/pyportal.json b/targets/pyportal.json index a26d82f08..abda3ab18 100644 --- a/targets/pyportal.json +++ b/targets/pyportal.json @@ -1,6 +1,7 @@ { "inherits": ["atsamd51j20a"], "build-tags": ["pyportal"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "serial-port": ["acm:239a:8035", "acm:239a:35", "acm:239a:8036"], diff --git a/targets/reelboard.json b/targets/reelboard.json index 60aeb1cfd..b6da0a449 100644 --- a/targets/reelboard.json +++ b/targets/reelboard.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52840"], "build-tags": ["reelboard"], + "serial": "uart", "flash-method": "msd", "msd-volume-name": "reel-board", "msd-firmware-name": "firmware.hex", diff --git a/targets/stm32f4disco.json b/targets/stm32f4disco.json index 622c5b0d6..4485c41d3 100644 --- a/targets/stm32f4disco.json +++ b/targets/stm32f4disco.json @@ -1,6 +1,7 @@ { "inherits": ["cortex-m4"], "build-tags": ["stm32f4disco", "stm32f407", "stm32f4", "stm32"], + "serial": "uart", "linkerscript": "targets/stm32f407.ld", "extra-files": [ "src/device/stm32/stm32f407.s" diff --git a/targets/teensy36.json b/targets/teensy36.json index c341511ec..253e39704 100644 --- a/targets/teensy36.json +++ b/targets/teensy36.json @@ -1,6 +1,7 @@ { "inherits": ["cortex-m4"], "build-tags": ["teensy36", "teensy", "mk66f18", "nxp"], + "serial": "uart", "linkerscript": "targets/nxpmk66f18.ld", "extra-files": [ "src/device/nxp/mk66f18.s", diff --git a/targets/teensy40.json b/targets/teensy40.json index d08b8d1dd..c530003b3 100644 --- a/targets/teensy40.json +++ b/targets/teensy40.json @@ -1,6 +1,7 @@ { "inherits": ["cortex-m7"], "build-tags": ["teensy40", "teensy", "mimxrt1062", "nxp"], + "serial": "uart", "automatic-stack-size": false, "default-stack-size": 4096, "linkerscript": "targets/mimxrt1062-teensy40.ld", diff --git a/targets/wioterminal.json b/targets/wioterminal.json index 093947e16..1f81b928f 100644 --- a/targets/wioterminal.json +++ b/targets/wioterminal.json @@ -1,6 +1,7 @@ { "inherits": ["atsamd51p19a"], "build-tags": ["wioterminal"], + "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "Arduino", diff --git a/targets/x9pro.json b/targets/x9pro.json index a9ecc0c83..9b966b4b9 100644 --- a/targets/x9pro.json +++ b/targets/x9pro.json @@ -1,6 +1,7 @@ { "inherits": ["nrf52"], "build-tags": ["x9pro"], + "serial": "uart", "flash-method": "openocd", "flash-command": "nrfjprog -f nrf52 --sectorerase --program {hex} --reset", "openocd-interface": "jlink", From 0e267dd2304cb45c42d51d54a57107e3f00ee6d8 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Fri, 25 Jun 2021 18:20:44 +0200 Subject: [PATCH 48/70] targets: add serial key to JSON files for newly added rp2040 boards, and also nano-33-ble board Signed-off-by: deadprogram --- src/machine/board_nano-33-ble.go | 5 ----- targets/feather-rp2040.json | 1 + targets/nano-33-ble.json | 1 + targets/nano-rp2040.json | 1 + 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/machine/board_nano-33-ble.go b/src/machine/board_nano-33-ble.go index b8d68b0bd..4483f811d 100644 --- a/src/machine/board_nano-33-ble.go +++ b/src/machine/board_nano-33-ble.go @@ -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 diff --git a/targets/feather-rp2040.json b/targets/feather-rp2040.json index 45f6b60b6..955c2a887 100644 --- a/targets/feather-rp2040.json +++ b/targets/feather-rp2040.json @@ -2,6 +2,7 @@ "inherits": [ "rp2040" ], + "serial": "uart", "build-tags": ["feather_rp2040"], "linkerscript": "targets/feather-rp2040.ld", "extra-files": [ diff --git a/targets/nano-33-ble.json b/targets/nano-33-ble.json index c5381e9f4..bbf80da52 100644 --- a/targets/nano-33-ble.json +++ b/targets/nano-33-ble.json @@ -3,6 +3,7 @@ "build-tags": ["nano_33_ble", "nrf52840_reset_bossa"], "flash-command": "bossac_arduino2 -d -i -e -w -v -R --port={port} {bin}", "serial-port": ["acm:2341:805a", "acm:2341:005a"], + "serial": "usb", "flash-1200-bps-reset": "true", "linkerscript": "targets/nano-33-ble.ld" } diff --git a/targets/nano-rp2040.json b/targets/nano-rp2040.json index a0222c970..461e0746b 100644 --- a/targets/nano-rp2040.json +++ b/targets/nano-rp2040.json @@ -2,6 +2,7 @@ "inherits": [ "rp2040" ], + "serial": "uart", "build-tags": ["nano_rp2040"], "linkerscript": "targets/pico.ld", "extra-files": [ From e5453ebe27ff102f7319653162b4661e41ceace0 Mon Sep 17 00:00:00 2001 From: sago35 Date: Sat, 26 Jun 2021 12:09:40 +0900 Subject: [PATCH 49/70] machine/feather-nrf52840-sense: add board definition for Adafruit Feather nRF52840 Sense --- Makefile | 2 + README.md | 3 +- src/machine/board_feather-nrf52840-sense.go | 101 ++++++++++++++++++++ targets/feather-nrf52840-sense.json | 11 +++ 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 src/machine/board_feather-nrf52840-sense.go create mode 100644 targets/feather-nrf52840-sense.json diff --git a/Makefile b/Makefile index 1d6cac87f..b62956a56 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index f28f4eb8f..8f233d6ba 100644 --- a/README.md +++ b/README.md @@ -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 67 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 67 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) diff --git a/src/machine/board_feather-nrf52840-sense.go b/src/machine/board_feather-nrf52840-sense.go new file mode 100644 index 000000000..4d8804f73 --- /dev/null +++ b/src/machine/board_feather-nrf52840-sense.go @@ -0,0 +1,101 @@ +// +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 = D0 + UART_TX_PIN = D1 +) + +// 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 +) diff --git a/targets/feather-nrf52840-sense.json b/targets/feather-nrf52840-sense.json new file mode 100644 index 000000000..d8210063f --- /dev/null +++ b/targets/feather-nrf52840-sense.json @@ -0,0 +1,11 @@ +{ + "inherits": ["nrf52840"], + "build-tags": ["feather_nrf52840_sense","nrf52840_reset_uf2", "softdevice", "s140v6"], + "serial": "usb", + "flash-1200-bps-reset": "true", + "flash-method": "msd", + "msd-volume-name": "FTHR840BOOT", + "msd-firmware-name": "firmware.uf2", + "uf2-family-id": "0xADA52840", + "linkerscript": "targets/circuitplay-bluefruit.ld" +} From 025f2fe7e44dcccdb6ea852eb7b3a6bc1e18de00 Mon Sep 17 00:00:00 2001 From: ardnew Date: Sat, 26 Jun 2021 18:21:57 -0500 Subject: [PATCH 50/70] add Serial var to Adafruit Matrix Portal M4 --- src/machine/board_matrixportal-m4_baremetal.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/machine/board_matrixportal-m4_baremetal.go b/src/machine/board_matrixportal-m4_baremetal.go index 8e0cd8543..5c57fbd35 100644 --- a/src/machine/board_matrixportal-m4_baremetal.go +++ b/src/machine/board_matrixportal-m4_baremetal.go @@ -9,6 +9,7 @@ import ( // UART on the MatrixPortal M4 var ( + Serial = UART1 UART1 = &_UART1 _UART1 = UART{ Buffer: NewRingBuffer(), From e127ceac6761eb28579cf2b7278c2cd298451aac Mon Sep 17 00:00:00 2001 From: sago35 Date: Mon, 28 Jun 2021 15:18:00 +0900 Subject: [PATCH 51/70] machine/feather-nrf52840-sense: fix msd-volume-name --- targets/feather-nrf52840-sense.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/feather-nrf52840-sense.json b/targets/feather-nrf52840-sense.json index d8210063f..a6735333f 100644 --- a/targets/feather-nrf52840-sense.json +++ b/targets/feather-nrf52840-sense.json @@ -4,7 +4,7 @@ "serial": "usb", "flash-1200-bps-reset": "true", "flash-method": "msd", - "msd-volume-name": "FTHR840BOOT", + "msd-volume-name": "FTHRSNSBOOT", "msd-firmware-name": "firmware.uf2", "uf2-family-id": "0xADA52840", "linkerscript": "targets/circuitplay-bluefruit.ld" From b00cfc001e626fc9fb665e72d4d9fbb556776d84 Mon Sep 17 00:00:00 2001 From: sago35 Date: Sat, 26 Jun 2021 13:41:44 +0900 Subject: [PATCH 52/70] targets: add serial and serial-port key to JSON files for adafruit boards --- targets/clue-alpha.json | 1 + targets/feather-m0.json | 2 ++ targets/feather-m4-can.json | 1 + targets/feather-m4.json | 1 + targets/feather-nrf52840-sense.json | 1 + targets/feather-nrf52840.json | 1 + targets/grandcentral-m4.json | 1 + targets/itsybitsy-m0.json | 2 ++ targets/itsybitsy-nrf52840.json | 1 + targets/matrixportal-m4.json | 2 ++ targets/metro-m4-airlift.json | 1 + targets/pygamer.json | 1 + targets/qtpy.json | 2 ++ targets/trinket-m0.json | 2 ++ 14 files changed, 19 insertions(+) diff --git a/targets/clue-alpha.json b/targets/clue-alpha.json index ebe83f79d..26088d531 100644 --- a/targets/clue-alpha.json +++ b/targets/clue-alpha.json @@ -2,6 +2,7 @@ "inherits": ["nrf52840"], "build-tags": ["clue_alpha","nrf52840_reset_uf2", "softdevice", "s140v6"], "serial": "usb", + "serial-port": ["acm:239a:8072", "acm:239a:0072", "acm:239a:0071", "acm:239a:8071"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "CLUEBOOT", diff --git a/targets/feather-m0.json b/targets/feather-m0.json index 9262833b3..f8070fb10 100644 --- a/targets/feather-m0.json +++ b/targets/feather-m0.json @@ -1,6 +1,8 @@ { "inherits": ["atsamd21g18a"], "build-tags": ["feather_m0"], + "serial": "usb", + "serial-port": ["acm:239a:801b", "acm:239a:001b", "acm:239a:800b", "acm:239a:000b", "acm:239a:0015"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "FEATHERBOOT", diff --git a/targets/feather-m4-can.json b/targets/feather-m4-can.json index 22222622f..1c5b67e86 100644 --- a/targets/feather-m4-can.json +++ b/targets/feather-m4-can.json @@ -2,6 +2,7 @@ "inherits": ["atsame51j19a"], "build-tags": ["feather_m4_can"], "serial": "usb", + "serial-port": ["acm:239a:80cd", "acm:239a:00cd"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "FTHRCANBOOT", diff --git a/targets/feather-m4.json b/targets/feather-m4.json index 408946297..4c33ddd8f 100644 --- a/targets/feather-m4.json +++ b/targets/feather-m4.json @@ -2,6 +2,7 @@ "inherits": ["atsamd51j19a"], "build-tags": ["feather_m4"], "serial": "usb", + "serial-port": ["acm:239a:8022", "acm:239a:0022"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "FEATHERBOOT", diff --git a/targets/feather-nrf52840-sense.json b/targets/feather-nrf52840-sense.json index a6735333f..b39c82105 100644 --- a/targets/feather-nrf52840-sense.json +++ b/targets/feather-nrf52840-sense.json @@ -2,6 +2,7 @@ "inherits": ["nrf52840"], "build-tags": ["feather_nrf52840_sense","nrf52840_reset_uf2", "softdevice", "s140v6"], "serial": "usb", + "serial-port": ["acm:239a:8087", "acm:239a:0087", "acm:239a:0088", "acm:239a:8088"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "FTHRSNSBOOT", diff --git a/targets/feather-nrf52840.json b/targets/feather-nrf52840.json index c334f7065..a4c491956 100644 --- a/targets/feather-nrf52840.json +++ b/targets/feather-nrf52840.json @@ -2,6 +2,7 @@ "inherits": ["nrf52840"], "build-tags": ["feather_nrf52840","nrf52840_reset_uf2", "softdevice", "s140v6"], "serial": "usb", + "serial-port": ["acm:239a:8029", "acm:239a:0029", "acm:239a:002a", "acm:239a:802a"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "FTHR840BOOT", diff --git a/targets/grandcentral-m4.json b/targets/grandcentral-m4.json index 7b374c98e..392b55502 100644 --- a/targets/grandcentral-m4.json +++ b/targets/grandcentral-m4.json @@ -2,6 +2,7 @@ "inherits": ["atsamd51p20a"], "build-tags": ["grandcentral_m4"], "serial": "usb", + "serial-port": ["acm:239a:8031", "acm:239a:0031", "acm:239a:0032"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "GCM4BOOT", diff --git a/targets/itsybitsy-m0.json b/targets/itsybitsy-m0.json index db9dd2cf9..0482dc6cb 100644 --- a/targets/itsybitsy-m0.json +++ b/targets/itsybitsy-m0.json @@ -1,6 +1,8 @@ { "inherits": ["atsamd21g18a"], "build-tags": ["itsybitsy_m0"], + "serial": "usb", + "serial-port": ["acm:239a:800f", "acm:239a:000f", "acm:239a:8012"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "ITSYBOOT", diff --git a/targets/itsybitsy-nrf52840.json b/targets/itsybitsy-nrf52840.json index aab873c18..d657b8a96 100644 --- a/targets/itsybitsy-nrf52840.json +++ b/targets/itsybitsy-nrf52840.json @@ -2,6 +2,7 @@ "inherits": ["nrf52840"], "build-tags": ["itsybitsy_nrf52840","nrf52840_reset_uf2", "softdevice", "s140v6"], "serial": "usb", + "serial-port": ["acm:239A:8052", "acm:239A:0052", "acm:239A:0051", "acm:239A:8051"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "ITSY840BOOT", diff --git a/targets/matrixportal-m4.json b/targets/matrixportal-m4.json index cca54a7e5..e9125f0e4 100644 --- a/targets/matrixportal-m4.json +++ b/targets/matrixportal-m4.json @@ -1,6 +1,8 @@ { "inherits": ["atsamd51j19a"], "build-tags": ["atsamd51j19a", "matrixportal_m4"], + "serial": "usb", + "serial-port": ["acm:239a:80c9", "acm:239a:00c9", "acm:239a:80ca"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "MATRIXBOOT", diff --git a/targets/metro-m4-airlift.json b/targets/metro-m4-airlift.json index 5783a4336..33ffd61cf 100644 --- a/targets/metro-m4-airlift.json +++ b/targets/metro-m4-airlift.json @@ -2,6 +2,7 @@ "inherits": ["atsamd51j19a"], "build-tags": ["metro_m4_airlift"], "serial": "usb", + "serial-port": ["acm:239A:8037", "acm:239A:0037"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "METROM4BOOT", diff --git a/targets/pygamer.json b/targets/pygamer.json index 03147e08a..4f4ab6e33 100644 --- a/targets/pygamer.json +++ b/targets/pygamer.json @@ -2,6 +2,7 @@ "inherits": ["atsamd51j19a"], "build-tags": ["pygamer"], "serial": "usb", + "serial-port": ["acm:239a:803d", "acm:239a:003d", "acm:239a:803e"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "PYGAMERBOOT", diff --git a/targets/qtpy.json b/targets/qtpy.json index 98821876a..53f1221b1 100644 --- a/targets/qtpy.json +++ b/targets/qtpy.json @@ -1,6 +1,8 @@ { "inherits": ["atsamd21e18a"], "build-tags": ["qtpy"], + "serial": "usb", + "serial-port": ["acm:239a:80cb", "acm:239a:00cb", "acm:239a:00cc"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "QTPY_BOOT", diff --git a/targets/trinket-m0.json b/targets/trinket-m0.json index af0e450ef..61bfc0a19 100644 --- a/targets/trinket-m0.json +++ b/targets/trinket-m0.json @@ -1,6 +1,8 @@ { "inherits": ["atsamd21e18a"], "build-tags": ["trinket_m0"], + "serial": "usb", + "serial-port": ["acm:239a:801e", "acm:239a:001e"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "TRINKETBOOT", From c8e231bc0b9aa276b2fb50050f4c1b412cfe77e5 Mon Sep 17 00:00:00 2001 From: sago35 Date: Sat, 26 Jun 2021 14:46:54 +0900 Subject: [PATCH 53/70] targets: add serial and serial-port key to JSON files for seeed boards --- targets/wioterminal.json | 1 + targets/xiao.json | 2 ++ 2 files changed, 3 insertions(+) diff --git a/targets/wioterminal.json b/targets/wioterminal.json index 1f81b928f..5564d0761 100644 --- a/targets/wioterminal.json +++ b/targets/wioterminal.json @@ -2,6 +2,7 @@ "inherits": ["atsamd51p19a"], "build-tags": ["wioterminal"], "serial": "usb", + "serial-port": ["acm:2886:002d", "acm:2886:802d"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "Arduino", diff --git a/targets/xiao.json b/targets/xiao.json index 50dee8f2a..80cc99d38 100644 --- a/targets/xiao.json +++ b/targets/xiao.json @@ -1,6 +1,8 @@ { "inherits": ["atsamd21g18a"], "build-tags": ["xiao"], + "serial": "usb", + "serial-port": ["acm:2886:802f", "acm:2886:002f"], "flash-1200-bps-reset": "true", "flash-method": "msd", "msd-volume-name": "Arduino", From 64d048c47c95bc0a70b4cff109afc1f9039705a4 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 24 Jun 2021 01:18:00 +0200 Subject: [PATCH 54/70] main: release version 0.19.0 --- CHANGELOG.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ goenv/version.go | 2 +- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fe0f0e30..9c427d9e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 --- diff --git a/goenv/version.go b/goenv/version.go index 909f330f4..c257e845e 100644 --- a/goenv/version.go +++ b/goenv/version.go @@ -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.19.0" // GetGorootVersion returns the major and minor version for a given GOROOT path. // If the goroot cannot be determined, (0, 0) is returned. From 2d633e3a28b11254fc5bf643c1730bd5759c69c6 Mon Sep 17 00:00:00 2001 From: sago35 Date: Thu, 1 Jul 2021 18:44:15 +0900 Subject: [PATCH 55/70] version: update TinyGo version to 0.20.0-dev --- goenv/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/goenv/version.go b/goenv/version.go index c257e845e..e0fe0949c 100644 --- a/goenv/version.go +++ b/goenv/version.go @@ -12,7 +12,7 @@ import ( // Version of TinyGo. // Update this value before release of new version of software. -const Version = "0.19.0" +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. From 42785e08e88dcbcfbbb36e6b12f081de16c86093 Mon Sep 17 00:00:00 2001 From: Patricio Whittingslow Date: Tue, 29 Jun 2021 22:16:21 -0300 Subject: [PATCH 56/70] add MAC address implementation to net --- src/net/mac.go | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/net/mac.go b/src/net/mac.go index 13f505192..ea3b921f0 100644 --- a/src/net/mac.go +++ b/src/net/mac.go @@ -7,3 +7,93 @@ 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} +} + +// 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 +} From 444dded92cfaf870be3a590e13419af49f1192b0 Mon Sep 17 00:00:00 2001 From: soypat Date: Wed, 30 Jun 2021 08:49:39 -0300 Subject: [PATCH 57/70] move xtoi2 to parse.go --- src/net/mac.go | 12 ------------ src/net/parse.go | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/net/mac.go b/src/net/mac.go index ea3b921f0..815b2738f 100644 --- a/src/net/mac.go +++ b/src/net/mac.go @@ -85,15 +85,3 @@ func ParseMAC(s string) (hw HardwareAddr, err error) { err: return nil, &AddrError{Err: "invalid MAC address", Addr: s} } - -// 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 -} diff --git a/src/net/parse.go b/src/net/parse.go index 1255f918f..2a840c854 100644 --- a/src/net/parse.go +++ b/src/net/parse.go @@ -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 From 0565b7c0e050f0cccbccdcbc1a9cee43462603d9 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Mon, 5 Jul 2021 01:50:52 +0200 Subject: [PATCH 58/70] cortexm: fix stack overflow because of unaligned stacks On ARM, the stack has to be aligned to 8 bytes on function calls, but not necessarily within a function. Leaf functions can take advantage of this by not keeping the stack aligned so they can avoid pushing one register. However, because regular functions might expect an aligned stack, the interrupt controller will forcibly re-align the stack when an interrupt happens in such a leaf function (controlled by the STKALIGN flag, defaults to on). This means that stack size calculation (as used in TinyGo) needs to make sure this extra space for stack re-alignment is available. This commit fixes this by aligning the stack size that will be used for new goroutines. Additionally, it increases the stack canary size from 4 to 8 bytes, to keep the stack aligned. This is not strictly necessary but is required by the AAPCS so let's do it anyway just to be sure. --- builder/build.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/builder/build.go b/builder/build.go index 8a433ca24..f17d26c72 100644 --- a/builder/build.go +++ b/builder/build.go @@ -953,15 +953,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 +973,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. From cdba4fa8ccde75839dcbbbac30ebfd727aeff909 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Thu, 8 Jul 2021 23:52:40 +0200 Subject: [PATCH 59/70] interp: don't ignore array indices for untyped objects This fixes https://github.com/tinygo-org/tinygo/issues/1884. My original plan to fix this was much more complicated, but then I realized that the output type doesn't matter anyway and I can simply cast the type to an *i8 and perform a GEP on that pointer. --- interp/interp.go | 2 +- interp/memory.go | 11 +++++++++++ testdata/init.go | 15 +++++++++++++++ testdata/init.txt | 2 ++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/interp/interp.go b/interp/interp.go index 8da27705f..d3976ef7f 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -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. diff --git a/interp/memory.go b/interp/memory.go index ccd98c8b0..53b798a3e 100644 --- a/interp/memory.go +++ b/interp/memory.go @@ -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 } diff --git a/testdata/init.go b/testdata/init.go index 6e7d9e7ba..5cb7f2d28 100644 --- a/testdata/init.go +++ b/testdata/init.go @@ -13,6 +13,8 @@ func main() { println("v5:", len(v5), v5 == nil) println("v6:", v6) println("v7:", cap(v7), string(v7)) + println("v8:", v8) + println("v9:", len(v9), v9[0], v9[1], v9[2]) println(uint8SliceSrc[0]) println(uint8SliceDst[0]) @@ -35,6 +37,8 @@ var ( v5 = map[string]int{} v6 = float64(v1) < 2.6 v7 = []byte("foo") + v8 string + v9 []int uint8SliceSrc = []uint8{3, 100} uint8SliceDst []uint8 @@ -48,4 +52,15 @@ func init() { intSliceDst = make([]int16, len(intSliceSrc)) copy(intSliceDst, intSliceSrc) + + v8 = sliceString("foobarbaz", 3, 8) + v9 = sliceSlice([]int{0, 1, 2, 3, 4, 5}, 2, 5) +} + +func sliceString(s string, start, end int) string { + return s[start:end] +} + +func sliceSlice(s []int, start, end int) []int { + return s[start:end] } diff --git a/testdata/init.txt b/testdata/init.txt index c421c1151..a6b9736c5 100644 --- a/testdata/init.txt +++ b/testdata/init.txt @@ -7,6 +7,8 @@ v4: 0 true v5: 0 false v6: false v7: 3 foo +v8: barba +v9: 3 2 3 4 3 3 5 From 8cc7c6d57202575e2ac4fc5024830333308efee9 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 14 Jul 2021 15:44:00 +0200 Subject: [PATCH 60/70] interp: populate Inst field in interp.Error It is used in the main package but wasn't actually set anywhere. --- interp/errors.go | 1 + 1 file changed, 1 insertion(+) diff --git a/interp/errors.go b/interp/errors.go index 48cf85df0..c90de3329 100644 --- a/interp/errors.go +++ b/interp/errors.go @@ -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}}, From 607d8242111560233ffd54eda461a43cc9c4760b Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 14 Jul 2021 15:45:11 +0200 Subject: [PATCH 61/70] interp: keep reverted package initializers in order Previously, a package initializer that could not be reverted correctly would be called at runtime. But the initializer would be called in the wrong order: after later packages are initialized. This commit fixes this oversight and adds a test to verify the new behavior. --- interp/interp.go | 4 +++- interp/interp_test.go | 1 + interp/testdata/revert.ll | 21 +++++++++++++++++++++ interp/testdata/revert.out.ll | 15 +++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 interp/testdata/revert.ll create mode 100644 interp/testdata/revert.out.ll diff --git a/interp/interp.go b/interp/interp.go index d3976ef7f..574fe01ab 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -110,17 +110,19 @@ 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()) } mem.revert() + i8undef := llvm.Undef(r.i8ptrType) + r.builder.CreateCall(fn, []llvm.Value{i8undef, i8undef}, "") continue } return callErr } - call.EraseFromParentAsInstruction() for index, obj := range mem.objects { r.objects[index] = obj } diff --git a/interp/interp_test.go b/interp/interp_test.go index 50af8af60..9702cb837 100644 --- a/interp/interp_test.go +++ b/interp/interp_test.go @@ -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) { diff --git a/interp/testdata/revert.ll b/interp/testdata/revert.ll new file mode 100644 index 000000000..49354ce9c --- /dev/null +++ b/interp/testdata/revert.ll @@ -0,0 +1,21 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64--linux" + +declare void @externalCall(i64) + +define void @runtime.initAll() unnamed_addr { +entry: + call void @foo.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 { + unreachable ; this triggers a revert of @foo.init. +} + +define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr { +entry: + call void @externalCall(i64 3) + ret void +} diff --git a/interp/testdata/revert.out.ll b/interp/testdata/revert.out.ll new file mode 100644 index 000000000..7309439f2 --- /dev/null +++ b/interp/testdata/revert.out.ll @@ -0,0 +1,15 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64--linux" + +declare void @externalCall(i64) local_unnamed_addr + +define void @runtime.initAll() unnamed_addr { +entry: + call fastcc void @foo.init(i8* undef, i8* undef) + call void @externalCall(i64 3) + ret void +} + +define internal fastcc void @foo.init(i8* %context, i8* %parentHandle) unnamed_addr { + unreachable +} From efa0410075ea492a1113224236a1210be74e1650 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 14 Jul 2021 20:10:37 +0200 Subject: [PATCH 62/70] interp: fix bug in compiler-time/run-time package initializers Make sure that if a package initializer cannot be run, later package initializers won't try to access any global variables touched by the uninterpretable package initializer. --- interp/interp.go | 23 +++++++++++++++++++++++ interp/testdata/revert.ll | 11 +++++++++++ interp/testdata/revert.out.ll | 6 ++++++ 3 files changed, 40 insertions(+) diff --git a/interp/interp.go b/interp/interp.go index 574fe01ab..cab9d9e35 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -116,9 +116,16 @@ func Run(mod llvm.Module, debug bool) error { 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 @@ -272,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 + } + } +} diff --git a/interp/testdata/revert.ll b/interp/testdata/revert.ll index 49354ce9c..41fb6a81e 100644 --- a/interp/testdata/revert.ll +++ b/interp/testdata/revert.ll @@ -3,17 +3,28 @@ 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) diff --git a/interp/testdata/revert.out.ll b/interp/testdata/revert.out.ll index 7309439f2..4f38e4c41 100644 --- a/interp/testdata/revert.out.ll +++ b/interp/testdata/revert.out.ll @@ -1,15 +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 } From 00ea0b1d57a7ae775df81d0913af7905f7d76e32 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 10 Jul 2021 23:15:58 +0200 Subject: [PATCH 63/70] build: list libraries at the end of the linker command Static libraries should be added at the end of the linker command, after all object files. If that isn't done, that's _usually_ not a problem, unless there are duplicate symbols. In that case, weird dependency issues can arise. To solve that, object files (that may include symbols to override symbols in the library) should be listed first on the command line and then the static libraries should be listed. This fixes an issue with overriding some symbols in wasi-libc. --- builder/build.go | 50 +++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/builder/build.go b/builder/build.go index f17d26c72..5c38433bd 100644 --- a/builder/build.go +++ b/builder/build.go @@ -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,31 @@ 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) + } + // 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{ From b40703e9860ef5872bc57fff0cbb91ee6c964208 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 10 Jul 2021 12:17:43 +0200 Subject: [PATCH 64/70] wasm: override dlmalloc heap implementation from wasi-libc These two heaps conflict with each other, so that if any function uses the dlmalloc heap implementation it will eventually result in memory corruption. This commit fixes this by implementing all heap-related functions. This overrides the functions that are implemented in wasi-libc. That's why all of them are implemented (even if they just panic): to make sure no program accidentally uses the wrong one. --- main_test.go | 29 ++++++++++---------- src/runtime/arch_tinygowasm.go | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 15 deletions(-) diff --git a/main_test.go b/main_test.go index 51fd21fd2..9ed0c6c54 100644 --- a/main_test.go +++ b/main_test.go @@ -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() diff --git a/src/runtime/arch_tinygowasm.go b/src/runtime/arch_tinygowasm.go index 0ee3afd3b..753591e9c 100644 --- a/src/runtime/arch_tinygowasm.go +++ b/src/runtime/arch_tinygowasm.go @@ -55,3 +55,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 +} From 73cf187552c4a104f986a209c6813a5eb58ac5f7 Mon Sep 17 00:00:00 2001 From: sago35 Date: Mon, 28 Jun 2021 16:09:02 +0900 Subject: [PATCH 65/70] machine/feather-nrf52: fix pin definition of uart --- src/machine/board_feather-nrf52840-sense.go | 8 ++++++-- src/machine/board_feather-nrf52840.go | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/machine/board_feather-nrf52840-sense.go b/src/machine/board_feather-nrf52840-sense.go index 4d8804f73..8a4f71446 100644 --- a/src/machine/board_feather-nrf52840-sense.go +++ b/src/machine/board_feather-nrf52840-sense.go @@ -72,8 +72,8 @@ const ( // UART0 pins (logical UART1) const ( - UART_RX_PIN = D0 - UART_TX_PIN = D1 + UART_RX_PIN = D1 + UART_TX_PIN = D0 ) // I2C pins @@ -99,3 +99,7 @@ var ( usb_VID uint16 = 0x239A usb_PID uint16 = 0x8088 ) + +var ( + DefaultUART = UART0 +) diff --git a/src/machine/board_feather-nrf52840.go b/src/machine/board_feather-nrf52840.go index 488d9608b..e433e1037 100644 --- a/src/machine/board_feather-nrf52840.go +++ b/src/machine/board_feather-nrf52840.go @@ -72,8 +72,8 @@ const ( // UART0 pins (logical UART1) const ( - UART_RX_PIN = D0 - UART_TX_PIN = D1 + UART_RX_PIN = D1 + UART_TX_PIN = D0 ) // I2C pins @@ -99,3 +99,7 @@ var ( usb_VID uint16 = 0x239A usb_PID uint16 = 0x802A ) + +var ( + DefaultUART = UART0 +) From 03481789b0bbc83b9381b3aec32c87965f8158c2 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 20 Jul 2021 13:21:25 +0200 Subject: [PATCH 66/70] runtime: fix time base for time.Now() This function previously returned the atomic time, that isn't affected by system time changes but also has a time base at some arbitrary time in the past. This makes sense for baremetal platforms (which typically don't know the wall time) but it gives surprising results on Linux and macOS: time.Now() usually returns a time somewhere near the start of 1970. This commit fixes this by obtaining both time values: the monotonic time and the wall clock time. This is also how the Go runtime implements the time.now function. --- src/runtime/baremetal.go | 21 +++++++++++++++++++++ src/runtime/os_darwin.go | 6 ++++++ src/runtime/os_linux.go | 6 ++++++ src/runtime/runtime.go | 21 --------------------- src/runtime/runtime_tinygowasm.go | 8 ++++++++ src/runtime/runtime_unix.go | 24 +++++++++++++++++------- 6 files changed, 58 insertions(+), 28 deletions(-) diff --git a/src/runtime/baremetal.go b/src/runtime/baremetal.go index ce69e338d..5abd13710 100644 --- a/src/runtime/baremetal.go +++ b/src/runtime/baremetal.go @@ -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 +} diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go index ac31b67da..be8789098 100644 --- a/src/runtime/os_darwin.go +++ b/src/runtime/os_darwin.go @@ -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 +) diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go index 7613134e3..aa056173b 100644 --- a/src/runtime/os_linux.go +++ b/src/runtime/os_linux.go @@ -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 +) diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index 7b1b4f5ae..55102411b 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -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() { diff --git a/src/runtime/runtime_tinygowasm.go b/src/runtime/runtime_tinygowasm.go index 989fbb803..80eaa8d3e 100644 --- a/src/runtime/runtime_tinygowasm.go +++ b/src/runtime/runtime_tinygowasm.go @@ -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() diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go index c185c4ab7..464d8dfe5 100644 --- a/src/runtime/runtime_unix.go +++ b/src/runtime/runtime_unix.go @@ -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) From e834d7887143fc8da542d3d29907e8db7a5b9bb2 Mon Sep 17 00:00:00 2001 From: "Federico G. Schwindt" Date: Thu, 15 Jul 2021 19:14:49 +0100 Subject: [PATCH 67/70] Fix undefined symbols error Currently TinyGo does not process SFiles (assembly files), which are needed by math/big. Add math_big_pure_go to the build tags to unbreak it. --- compileopts/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compileopts/config.go b/compileopts/config.go index a60f778e0..36782506d 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -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(), "serial." + c.Serial()}...) + 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)) } From 65c1978965bc2f00d07765978f977617ba40b28f Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 25 Jul 2021 14:30:16 +0200 Subject: [PATCH 68/70] wasm: align heap to 16 bytes This commit fixes two things: * It changes the alignment to 16 bytes (from 4), to match max_align_t in C. * It manually aligns heapStart on WebAssembly, to work around a bug in wasm-ld with --stack-first (see https://reviews.llvm.org/D106499). --- src/runtime/arch_tinygowasm.go | 6 ++++-- src/runtime/gc_conservative.go | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/runtime/arch_tinygowasm.go b/src/runtime/arch_tinygowasm.go index 753591e9c..a19a14487 100644 --- a/src/runtime/arch_tinygowasm.go +++ b/src/runtime/arch_tinygowasm.go @@ -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 diff --git a/src/runtime/gc_conservative.go b/src/runtime/gc_conservative.go index e96825922..67fbfdb2c 100644 --- a/src/runtime/gc_conservative.go +++ b/src/runtime/gc_conservative.go @@ -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. From 7434e5a2c7110e512baa93d747ce47d5b4798446 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 13 Jul 2021 16:59:43 +0200 Subject: [PATCH 69/70] main: strip debug information at link time instead of at compile time Stripping debug information at link time also allows relocation compression (aka linker relaxations). Keeping debug information at compile time and optionally stripping it at link time has some advantages: * Automatic stack sizes on Cortex-M rely on the presence of debug information. * Some parts of the compiler now rely on the presence of debug information for proper diagnostics. * It works better with the cache: there is no distinction between debug and no-debug builds. * It makes it easier (or possible at all) to enable debug information in the wasi-libc library without big downsides. --- builder/build.go | 35 ++++++++++++++++++++++++++++++++++- compileopts/config.go | 10 +++++----- main.go | 2 +- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/builder/build.go b/builder/build.go index 5c38433bd..c1fd78e3f 100644 --- a/builder/build.go +++ b/builder/build.go @@ -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(), } @@ -539,6 +539,39 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil 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{ diff --git a/compileopts/config.go b/compileopts/config.go index 36782506d..f2ba28578 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -209,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 } @@ -250,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 } diff --git a/main.go b/main.go index 7c3ee96df..c9fd0c042 100644 --- a/main.go +++ b/main.go @@ -1019,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)") From 98e70c9b196742e37f7da3ca0e78b4f63738da8e Mon Sep 17 00:00:00 2001 From: soypat Date: Fri, 9 Jul 2021 14:40:21 -0300 Subject: [PATCH 70/70] machine/rp2040: add SPI support spi working with loopback SPI working apply @deadprogram's suggestions consolidate SPI board pin naming fix up SPI configuration add feather-rp2040 SPI pins add arduino connect SPI pins add SPI handle variables --- src/machine/board_feather_rp2040.go | 17 ++ src/machine/board_nano-rp2040.go | 7 +- src/machine/board_pico.go | 17 ++ src/machine/machine_rp2040_gpio.go | 3 + src/machine/machine_rp2040_spi.go | 361 ++++++++++++++++++++++++++++ 5 files changed, 404 insertions(+), 1 deletion(-) create mode 100644 src/machine/machine_rp2040_spi.go diff --git a/src/machine/board_feather_rp2040.go b/src/machine/board_feather_rp2040.go index 7894c5cff..4be1b4938 100644 --- a/src/machine/board_feather_rp2040.go +++ b/src/machine/board_feather_rp2040.go @@ -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 +) diff --git a/src/machine/board_nano-rp2040.go b/src/machine/board_nano-rp2040.go index ca85596a7..3db965a79 100644 --- a/src/machine/board_nano-rp2040.go +++ b/src/machine/board_nano-rp2040.go @@ -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 diff --git a/src/machine/board_pico.go b/src/machine/board_pico.go index 3a4d67b45..961c923cb 100644 --- a/src/machine/board_pico.go +++ b/src/machine/board_pico.go @@ -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 +) diff --git a/src/machine/machine_rp2040_gpio.go b/src/machine/machine_rp2040_gpio.go index b994988e4..e5394685d 100644 --- a/src/machine/machine_rp2040_gpio.go +++ b/src/machine/machine_rp2040_gpio.go @@ -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) } } diff --git a/src/machine/machine_rp2040_spi.go b/src/machine/machine_rp2040_spi.go new file mode 100644 index 000000000..ac2bcabe6 --- /dev/null +++ b/src/machine/machine_rp2040_spi.go @@ -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) + 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< 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 +}