Compare commits

...

98 Commits

Author SHA1 Message Date
Ayke van Laethem ce0abac5a3 avr: use compiler-rt 2020-06-27 01:22:31 +02:00
Ayke van Laethem 43d7f48de4 main: support gdb debugging with AVR
Be able to run `tinygo gdb -target=arduino examples/serial` and debug a
program with the power of a real debugger.

Note that this only works on LLVM 11 because older versions have a bug
in the AVR backend that cause it to produce invalid debug information:
https://reviews.llvm.org/D74213.
2020-06-27 01:22:31 +02:00
Ayke van Laethem f7f2082979 main: add initial support for (in-development) LLVM 11
This can be useful to test improvements in LLVM master and to make it
possible to support LLVM 11 for the most part already before the next
release. That also allows catching LLVM bugs early to fix them upstream.

Note that tests do not yet pass for this LLVM version, but the TinyGo
compiler can be built with the binaries from apt.llvm.org (at the time
of making this commit).
2020-06-27 01:19:21 +02:00
Yannis Huber 9b1a19f184 gen-device-svd: fix lowercase in register spaced array 2020-06-26 13:16:34 +02:00
deadprogram 1ad6953858 build: clean tinygo cache before running tests
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-06-23 20:10:30 +02:00
sago35 3e8cdb62c9 main: use ToSlash() to specify pkgName
This change allows windows users to specify the package with backslash
as a path separator
2020-06-23 19:20:18 +02:00
Cornel 720a54a0fe extend stdlib to allow import of more packages (#1099)
* stdlib: extend stdlib to allow import of more packages
2020-06-23 11:56:28 +02:00
deadprogram de45da5df4 docker: try installing lld in initial stage to avoid cache problem with deb package
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-06-22 19:50:56 +02:00
Hiroki Noda c9fc95f6f3 wasm32: Add --no-demangle option
LLD supports only C++ demangle, so disable it.
2020-06-22 11:42:17 +02:00
sago35 2721ab146f Seeed XIAO support (#1170)
* machine/xiao: add support for Seeedstudio XIAO board
2020-06-22 09:01:13 +02:00
Jaden Weiss 81c723db1a device/arm: do not mask fault handlers in critical sections 2020-06-22 00:08:14 +02:00
sago35 9a20af5b59 Add adc settings 2020-06-19 15:07:57 +02:00
APDevice 4e8e3f348f Added Adafruit PyGamer Target (#1173)
* machine/PyGamer: add board support
2020-06-19 14:35:42 +02:00
deadprogram 496452b676 machine/hifive1b: add definitions for UART0 pins
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-06-17 16:28:37 +02:00
Ayke van Laethem e86ca2080d runtime/interrupt: add cross-chip disable/restore interrupt support
This should cover all supported baremetal targets.
2020-06-14 14:44:22 -04:00
Ayke van Laethem e2bf7bbb49 device: add new cross-arch Asm and AsmFull functions
This is necessary to avoid a circular dependency between the device/avr
and runtime/interrupts package in the next commit.

It may be worth replacing existing calls like device/arm.Asm to
device.Asm, to have a single place where these are defined.
2020-06-14 14:44:22 -04:00
Ayke van Laethem 4d1d0c2d3b main: go mod tidy
Clean up the go.mod and go.sum which have gotten a bit messy, and add an
extra line to go.sum that keeps reappearing locally for some reason (so
it seems important).
2020-06-11 08:14:48 +02:00
deadprogram 2281b6a3f5 machine/hifive1b: remove extra println left in by mistake
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-06-09 13:30:22 +02:00
Jaden Weiss 4321923641 compiler/runtime: move the channel blocked list onto the stack
Previously, chansend and chanrecv allocated a heap object before blocking on a channel.
This object was used to implement a linked list of goroutines blocked on the channel.
The chansend and chanrecv now instead accept a buffer to store this object in as an argument.
The compiler now creates a stack allocation for this object and passes it in.
2020-06-08 19:59:57 +02:00
Ayke van Laethem 4e4b5955db nrf: support debugging the PCA10056
This change adds support for `tinygo gdb` on the PCA10056.

The board is normally flashed with the MSD programmer. Debugging needs a
real debugger interface however, which is relatively simple by just
adding the right OpenOCD configuration.
2020-06-08 19:54:41 +02:00
Ayke van Laethem 169d5f17b8 nrf: fix bug in SPI.Tx
There was what appears to be a race condition in the Tx function. While
it would work fine in many cases, when there were interrupts (such as
when using BLE), the function would just hang waiting for `EVENTS_READY`
to arrive.

I think what was happening was that the `spi.Bus.RXD.Get()` would start
the next transfer, which would complete (and generate an event) before
`EVENTS_READY` was reset to 0. The fix is easy: clear `EVENTS_READY`
before doing something that can trigger an event.

I believe I've seen this bug before on the PineTime but I couldn't find
the issue back then.
2020-06-08 19:54:41 +02:00
Ayke van Laethem 9ed5eae6a9 cgo: use scanner.Error in libclang
Previously it would return a `*scanner.Error`, which is not supported in
the error printer of the main package. This can easily be fixed by
making it a regular object (instead of a pointer).
2020-06-08 19:54:41 +02:00
Yannis Huber e2c55e3d26 gen-device-svd: fix lowercase cluster name 2020-06-08 16:59:13 +02:00
Yannis Huber d3f5b51cd8 compiler: add support for custom code model 2020-06-08 16:50:39 +02:00
Yannis Huber 2396c22658 risc-v: add support for 64-bit RISC-V CPUs 2020-06-08 16:47:39 +02:00
Yannis Huber d61d5d7ab1 Zero PLIC threshold value at startup
The PLIC threshold value must be zeroed at startup to permit
all interrupt priorities. Fixes #1128.
2020-06-07 11:45:26 +02:00
sago35 c5a896771d Seeed WioTerminal support (#1124)
* machine/wioterminal: add support for wioterminal board
2020-06-06 12:00:26 +02:00
sago35 0c880ec44c Standardize SAMD51 UART settings (#1155)
* machine/samd51: standardize samd51 uart settings
2020-06-05 08:14:31 +02:00
sago35 64d51b215f Extend SAMD51 pinPadMapping 2020-06-03 19:37:16 +02:00
sago35 f103e910d7 Add SAMD51 pin change interrupt settings 2020-06-03 19:37:16 +02:00
sago35 40afeea569 Add SAMD51 ADC settings 2020-06-03 19:37:16 +02:00
sago35 97122972fb Add SAMD51 pins 2020-06-03 19:37:16 +02:00
sago35 72064e12db WIP flash: fix touchSerialPortAt1200bps on windows 2020-06-03 15:26:03 +02:00
Ayke van Laethem 3c31a3110f builder: use newer version of gohex
This version adds error handling for the DumpIntelHex method, and thus
fixes a TODO comment.
2020-06-01 19:03:15 +02:00
Ayke van Laethem 0e73790d67 Dockerfile: avoid duplicate LLVM apt line
This line causes problems when installing software: apt-get complains
that there are duplicate lines.
2020-06-01 11:47:29 +02:00
deadprogram bcbc241d81 main: improve/simplify auto-retry to locate MSD for UF2 and HEX flashing
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-05-31 13:23:19 +02:00
deadprogram 5e2a8024a3 main: use auto-retry (up to 10 seconds) to locate MSD for UF2 and HEX flashing
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-05-31 13:23:19 +02:00
sago35 b92fad8da6 sam: fix register access for interrupts pins in samd51 (#1141)
* machine/samd51: fix register access for interrupts pins in samd51
2020-05-30 18:03:46 +02:00
sago35 5c8d4e54d6 sam: add support for pin change interrupts (samd5x) 2020-05-29 11:36:22 +02:00
Ayke van Laethem fed433c046 compiler: add support for atomic operations
This also implements DisableInterrupts/EnableInterrupts for RISC-V, as
those operations were needed to implement a few libcalls.
2020-05-28 15:11:46 +02:00
Ayke van Laethem 734613c20e transform: introduce check for method calls on nil interfaces
I ran into an issue where I did a method call on a nil interface and it
resulted in a HardFault. Luckily I quickly realized what was going on so
I could fix it, but I think undefined behavior is definitely the wrong
behavior in this case. This commit therefore changes such calls to cause
a nil panic instead of introducing undefined behavior.

This does have a code size impact. It's relatively minor, much lower
than I expected. When comparing the before and after of the drivers
smoke tests (probably the most representative sample available), I found
that most did not change at all and those that did change, normally not
more than 100 bytes (16 or 32 byte changes are typical).

Right now the pattern is the following:

    switch typecode {
    case 1:
        call method 1
    case 2:
        call method 2
    default:
        nil panic
    }

I also tried the following (in the hope that it would be easier to
optimize), but it didn't really result in a code size reduction:

    switch typecode {
    case 1:
        call method 1
    case 2:
        call method 2
    case 0:
        nil panic
    default:
        unreachable
    }

Some code got smaller, while other code (the majority) got bigger. Maybe
this can be improved once range[1] is finally allowed[2] on function
parameters, but it'll probably take a while before that is implemented.

[1]: https://llvm.org/docs/LangRef.html#range-metadata
[2]: https://github.com/rust-lang/rust/issues/50156
2020-05-28 13:42:36 +02:00
Ayke van Laethem 1570adac1c transform: do not special-case zero or one implementations of a method call
This is a common case, but it also complicates the code. Removing this
special case does have a negative effect on code size in rare cases, but
I don't think it's worth keeping around (and possibly causing bugs) for
such uncommon cases.

This should not result in functional changes, although the output (as
stated above) sometimes changes a little bit.
2020-05-28 13:42:36 +02:00
Ayke van Laethem c72f9eb08c sam: add support for pin change interrupts 2020-05-28 11:20:33 +02:00
Ayke van Laethem 19c7965fc5 nrf: add support for pin change interrupts 2020-05-28 11:20:33 +02:00
Ayke van Laethem c248418dbe compiler: fix a few crashes due to named types
There were a few cases left where a named type would cause a crash in
the compiler. While going through enough code would have found them
eventually, I specifically looked for the `Type().(` pattern: a Type()
call that is then used in a type assert. Most of those were indeed bugs,
although for some I couldn't come up with a reproducer so I left them
as-is.
2020-05-27 16:14:41 +02:00
Ayke van Laethem 4ca2d3f0cf loader: load packages using Go modules
This commit replaces the existing ad-hoc package loader with a package
loader that uses the x/tools/go/packages package to find all
to-be-loaded packages.
2020-05-27 13:08:17 +02:00
Ayke van Laethem 35015a7918 loader: merge roots from both Go and TinyGo in a cached directory
This commit changes the way that packages are looked up. Instead of
working around the loader package by modifying the GOROOT variable for
specific packages, create a new GOROOT using symlinks. This GOROOT is
cached for the specified configuration (Go version, underlying GOROOT
path, TinyGo path, whether to override the syscall package).

This will also enable go module support in the future.

Windows is a bit harder to support, because it only allows the creation
of symlinks when developer mode is enabled. This is worked around by
using symlinks and if that fails, using directory junctions or hardlinks
instead. This should work in the vast majority of cases. The only case
it doesn't work, is if developer mode is disabled and TinyGo, the Go
toolchain, and the cache directory are not all on the same filesystem.
If this is a problem, it is still possible to improve the code by using
file copies instead.

As a side effect, this also makes diagnostics use a relative file path
only when the file is not in GOROOT or in TINYGOROOT.
2020-05-27 13:08:17 +02:00
Ayke van Laethem bde73fc214 main: fix test subcommand
It was broken for quite some time without anybody noticing...
2020-05-27 13:08:17 +02:00
Ayke van Laethem ab2a81cc52 main: move TinyGo version to goenv
This is needed to make it available to more packages, for caching
purposes.

For caching, the version itself may not be enough during development.
But for regular releases, the version provides some protection against
accidentally using a cache entry that is invalid in a newer version.
2020-05-27 13:08:17 +02:00
Ayke van Laethem 2a98433c8e builder: move Go version code to goenv package
This is necessary to avoid a circular dependency in the loader (which
soon will need to read the Go version) and because it seems like a
better place anyway.
2020-05-27 13:08:17 +02:00
Brad Peabody 4918395f88 added test for wasm log output
callback case for log test
2020-05-27 08:43:29 +02:00
Ayke van Laethem 67ac4fdd8e nrf: add microbit-s110v8 target
This makes it possible to use Bluetooth on the BBC micro:bit.

Note that you need to use -programmer=cmsis-dap otherwise the SoftDevice
will be erased while flashing something that uses Bluetooth.
2020-05-26 18:50:33 +02:00
Ayke van Laethem e69131c0d3 nrf: expose the RAM base address
The RAM base address is needed during SoftDevice initialization. So far,
the same magic value has been used in aykevl/go-bluetooth and in TinyGo,
but this should be configured in only one place.

This will have additional benefits in the future:

  * It is currently set to 0x39c0, which is around 14.5kB. Most nrf51822
    chips have only 16kB of RAM, so this is way too much for those
    chips.
  * LLD in LLVM 11 allows expressions in the MEMORY part of linker
    scripts, which will allow overriding the SoftDevice RAM area with a
    linker flag, which might come in handy.
2020-05-26 18:49:44 +02:00
Ayke van Laethem 9f4459cee1 arm: make FPU configuraton consistent
Eventually we might want to start using the FPU, but the easy option
right now is to simply disable it everywhere. Previously, it depended on
whether Clang was built as part of TinyGo or it was an external binary.
By setting the floating point mode explicitly, such inconsistencies are
avoided.

This commit creates a new cortex-m4 target which can be the central
place for setting FPU-related settings across all Cortex-M4 chips.
2020-05-26 16:39:14 +02:00
Ayke van Laethem 3c55689566 runtime: refactor time handling
This commit refactors both determining the current time and sleeping for
a given time. It also improves precision for many chips.

  * The nrf chips had a long-standing TODO comment about a slightly
    inaccurate clock. This should now be fixed.
  * The SAM D2x/D5x chips may have a slightly more accurate clock,
    although probably within the error margin of the RTC. Also, by
    working with RTC ticks and converting in the least number of places,
    code size is often slightly reduced (usually just a few bytes, up to
    around 1kB in some cases).
  * I believe the HiFive1 rev B timer was slightly wrong (32768Hz vs
    30517.6Hz). Because the datasheet says the clock runs at 32768Hz,
    I've used the same conversion code here as in the nrf and sam cases.
  * I couldn't test both stm32 timers, so I kept them as they currently
    are. It may be possible to make them more efficient by using the
    native tick frequency instead of using microseconds everywhere.
2020-05-25 22:08:28 +02:00
Brad Peabody 95f509b109 wasm test suite (#1116)
* wasm: add test suite using headlless chrome
2020-05-23 14:12:01 +02:00
Ayke van Laethem dda576e80b avr: add support for PinInputPullup 2020-05-22 13:17:04 +02:00
Ayke van Laethem da505a6b17 avr: unify GPIO pin/port code
All the AVRs that I've looked at had the same pin/port structure, with
the possible states being input/floating, input/pullup, low, and high
(with the same PORT/DDR registers). The main difference is the number of
available ports and pins. To reduce the amount of code and avoid
duplication (and thus errors) I decided to centralize this, following
the design used by the atmega2560 but while using a trick to save
tracking a few registers.

In the process, I noticed that the Pin.Get() function was incorrect on
the atmega2560 implementation. It is now fixed in the unified code.
2020-05-22 13:17:04 +02:00
deadprogram 424d775bf4 build: remove CircleCI orb, now using different integration
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-05-22 00:07:03 +02:00
Lucas Teske 726d735ad3 cgo: Add LDFlags support 2020-05-21 00:57:19 +02:00
deadprogram b9fd6cee6f build: add webhook notifier orb for circleci
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-05-20 00:15:23 +02:00
cebernardi 76fb3bd177 compileopts: improve error reporting of unsupported flags 2020-05-16 23:29:47 +02:00
Brad Peabody 29ca1147f5 call scheduler from resume 2020-05-16 23:16:53 +02:00
Jaden Weiss 473644d918 internal/bytealg: reimplement bytealg in pure Go
Previously, we implemented individual bytealg functions via linknaming, and had to update them every once in a while when we hit linker errors.
Instead, this change reimplements the bytealg package in pure Go.
If something is missing, it will cause a compiler error rather than a linker error.
This is easier to test and maintain.
2020-05-16 14:56:05 +02:00
Ayke van Laethem 38fc340802 sam: return an error when an incorrect PWM pin is used
Previously it would trigger a nil pointer panic.
2020-05-13 19:19:45 +02:00
Ayke van Laethem b5f028e1f7 ci: build .deb files along with .tar.gz files for Debian 2020-05-13 08:39:12 +02:00
Ayke van Laethem aa40ddc48b ci: do not install the SiFive toolchain
We don't need it anymore since we use lld to link RISC-V binaries.
2020-05-13 08:29:40 +02:00
Ayke van Laethem 6bcb40fe01 os: implement virtual filesystem support
This allows applications to mount filesystems in the os package. This is
useful for mounting external flash filesystems, for example.
2020-05-13 08:08:57 +02:00
cornelk e907db1481 os: add Args and stub it with mock data 2020-05-12 16:53:07 +02:00
Ayke van Laethem 6b8940421e avr: use standard pin numbering
This commit changes pin numbering for atmega328 based boards (Uno, Nano)
to use the standard format, where pin number is determined by the
pin/port. Previously, pin numbers were based on what the Uno uses, which
does not seem to have a clear pattern.

One difference is that counting starts at port B, as there is no port A.
So PB0 is 0, PB1 is 1… PC0 is 8.

This commit also moves PWM code to the atmega328 file, as it may not be
generic to all ATmega chips.
2020-05-12 08:16:34 +02:00
cornelk 2c71f08922 reflect: add Cap and Len support for map and chan 2020-05-12 01:17:27 +02:00
cornelk 7e64bc8f77 runtime: add cap and len support for chans 2020-05-12 01:17:27 +02:00
cornelk 1461563e3f testdata: fix formatting 2020-05-12 01:17:27 +02:00
cornelk acdaa72365 runtime: fix compilation errors when using gc.extalloc 2020-05-12 01:11:46 +02:00
deadprogram 01f5c1d455 machine/arduino-nano33: remove (d)ebug flag to reduce console noise when flashing
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-05-09 13:49:40 +02:00
deadprogram 00f3a65903 machine/arduino-nano33: use (U)SB flag to ensure that device can be found when not on default port
Signed-off-by: deadprogram <ron@hybridgroup.com>
2020-05-09 13:49:40 +02:00
Jaden Weiss b4815192a6 testdata, sync: add sync.Mutex test to testdata/coroutines.go 2020-05-09 01:08:56 +02:00
Jaden Weiss c54e1cc955 sync: modify sync.Cond 2020-05-09 01:08:56 +02:00
Jaden Weiss 7801921cc0 testdata: replace fake waitgroup in channel.go with sync.WaitGroup 2020-05-09 01:08:56 +02:00
Jaden Weiss afc6bd5cdd sync: add WaitGroup 2020-05-09 01:08:56 +02:00
Jaden Weiss ae2cbbf851 internal/task: fix nil panic in (*internal/task.Stack).Pop
While adding some code to clear the Next field when popping from a task stack for safety reasons, the clear was placed outside of a nil pointer check.
As a result, (*internal/task.Stack).Pop panicked when the Stack is empty.
2020-05-09 01:08:56 +02:00
Jaden Weiss ccd79ee289 add sync.Cond 2020-05-09 01:08:56 +02:00
Jaden Weiss 171d0fe123 implement mutex blocking 2020-05-09 01:08:56 +02:00
sago35 5ed0f67e1c sam: fix ROM / RAM size on atsamd51j20 2020-05-07 23:04:10 +02:00
sago35 8ce3cfad40 flash: fix getDefaultPort() fails on Windows locales such as Japan 2020-05-07 22:47:45 +02:00
Ayke van Laethem 904fa852f6 transform: fix debug information in func lowering pass
This commit fixes errors like the following:

    inlinable function call in a function with debug info must have a !dbg location
      call void @runtime.nilPanic(i8* undef, i8* null)
    inlinable function call in a function with debug info must have a !dbg location
      %24 = call fastcc %runtime._interface @"(*github.com/vugu/vugu/domrender.JSRenderer).render$1"(%"github.com/vugu/vugu.VGNode"** %19, i32 %22, i32 %23, i8* %15, i8* undef)
    error: optimizations caused a verification failure

Not all instructions had a debug location, which apparently caused
issues for the inliner.
2020-05-07 08:24:14 +02:00
Ayke van Laethem d0b2585e82 main: update go-llvm to fix macOS build problem 2020-05-05 08:11:20 +02:00
Ayke van Laethem 23e88bfb15 arm: allow nesting in DisableInterrupts and EnableInterrupts
This finally fixes a TODO left in the code.
2020-04-29 18:25:16 +02:00
Ayke van Laethem 6389e45d99 all: replace ReadRegister with AsmFull inline assembly
This makes AsmFull more powerful (by supporting return values) and
avoids a compiler builtin.
2020-04-29 18:25:16 +02:00
Ayke van Laethem 9342e73ae1 builder: fix picolibc include path
Previously we used --sysroot to set the sysroot explicitly.
Unfortunately, this flag is not used directly by Clang to set the
include path (<sysroot>/include) but is instead interpreted by the
toolchain code. This means that even when the toolchain is explicitly
set (using the --sysroot parameter), it may still decide to use a
different include path such as <sysroot>/usr/include (such as on
baremetal aarch64).

This commit uses the Clang-internal -internal-isystem flag which sets
the include directory directly (as a system include path). This should
be more robust.

The reason the --sysroot parameter has so far worked is that all
existing targets happened to add <sysroot>/include as an include path.

The relevant Clang code is here:
https://github.com/llvm/llvm-project/blob/release/9.x/clang/lib/Driver/Driver.cpp#L4693-L4739
So far, RISC-V is handled by RISCVToolchain, Cortex-M targets by
BareMetal (which seems to be specific to ARM unlike what the name says)
and aarch64 fell back to Generic_ELF.
2020-04-29 15:41:08 +02:00
Ayke van Laethem fc4857e98c runtime: avoid recursion in printuint64 function
This function is called from runtime.printitf, which is called from
runtime._panic, and is therefore the leaf function of many call paths.
This makes analyzing stack usage very difficult.

Also forwarding printuint32 to printuint64 as it reduces code size in
the few examples I've tested. Printing numbers is not often done so it
doesn't matter if it's a bit slow (the serial connection is probably
slower anyway).
2020-04-26 17:19:07 +02:00
Yannis Huber f66492a338 Fix return address in scheduler 2020-04-26 16:58:02 +02:00
Jaden Weiss 445fd37bef main: update version for beginning of v0.14 development cycle 2020-04-26 16:57:24 +02:00
Ayke van Laethem a9ba6ebad9 main: version 0.13.1
This release fixes a few bugs introduced in the previous 0.13.0 release.
2020-04-21 17:02:43 +02:00
Ayke van Laethem 565ff99c31 gba: always use ARM mode instead of Thumb mode
This results in bigger code size, but it works around a bug in the
linker.

The issue starts with the problem that libraries (picolibc, compiler-rt)
were compiled as ARM and the rest as Thumb. This causes some blx
instructions to be inserted by the linker to call into these libraries.

Ideally we should fix the libraries to use Thumb mode instead, but that
requires some more extensive changes (including fixes to compiler-rt)
and it's just way easier to use ARM mode everywhere.
2020-04-21 15:40:52 +02:00
Jaden Weiss ceeba528e7 runtime: copy stack scan assembly for GBA
The GC stack scanning code was implemented in the Cortex-M assembly, which meant that it was not available on the GBA which is pre-cortex.
This change adds a copy of the relevant code into a new asembly file which is used on the GBA.
2020-04-21 10:28:42 +02:00
Ayke van Laethem 16c2d84c49 compiler: add parameter names to IR
This makes viewing the IR easier because parameters have readable names.

This also makes it easier to write compiler tests (still a work in
progress), that work in LLVM 9 and LLVM 10, as LLVM 10 started printing
value names for unnamed parameters.
2020-04-21 08:54:39 +02:00
Ayke van Laethem f00bb63330 runtime: do not put scheduler and GC code in the same section
This allows dead code elimination and avoids linker errors with
-scheduler=leaking.
2020-04-20 21:32:29 +02:00
214 changed files with 6226 additions and 1551 deletions
+19 -6
View File
@@ -37,6 +37,13 @@ commands:
sudo tar -C /usr/local -xf node-v10.15.1-linux-x64.tar.xz
sudo ln -s /usr/local/node-v10.15.1-linux-x64/bin/node /usr/bin/node
rm node-v10.15.1-linux-x64.tar.xz
install-chrome:
steps:
- run:
name: "Install Chrome"
command: |
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb
llvm-source-linux:
steps:
- restore_cache:
@@ -71,6 +78,7 @@ commands:
- apt-dependencies:
llvm: "<<parameters.llvm>>"
- install-node
- install-chrome
- restore_cache:
keys:
- go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }}
@@ -85,9 +93,11 @@ commands:
key: wasi-libc-sysroot-systemclang-v1
paths:
- lib/wasi-libc/sysroot
- run: tinygo clean
- run: go test -v -tags=llvm<<parameters.llvm>> ./cgo ./compileopts ./interp ./transform .
- run: make gen-device -j4
- run: make smoketest
- run: make wasmtest
- save_cache:
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
@@ -204,13 +214,21 @@ commands:
- run:
name: "Test TinyGo"
command: make test
- run:
name: "Install fpm"
command: |
sudo apt-get install ruby ruby-dev
sudo gem install --no-document fpm
- run:
name: "Build TinyGo release"
command: |
make release -j3
make release deb -j3
cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz
cp -p build/release.deb /tmp/tinygo_amd64.deb
- store_artifacts:
path: /tmp/tinygo.linux-amd64.tar.gz
- store_artifacts:
path: /tmp/tinygo_amd64.deb
- save_cache:
key: go-cache-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
paths:
@@ -294,11 +312,6 @@ commands:
tar -C /usr/local/opt -xf /tmp/tinygo.darwin-amd64.tar.gz
ln -s /usr/local/opt/tinygo/bin/tinygo /usr/local/bin/tinygo
tinygo version
- run:
name: "Download SiFive GNU toolchain"
command: |
curl -O https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-apple-darwin.tar.gz
sudo tar -C /usr/local --strip-components=1 -xf riscv64-unknown-elf-gcc-8.2.0-2019.05.3-x86_64-apple-darwin.tar.gz
- run: make smoketest AVR=0
- save_cache:
key: go-cache-macos-v2-{{ checksum "go.mod" }}-{{ .Environment.CIRCLE_BUILD_NUM }}
+9
View File
@@ -1,3 +1,12 @@
0.13.1
---
* **standard library**
- `runtime`: do not put scheduler and GC code in the same section
- `runtime`: copy stack scan assembly for GBA
* **boards**
- `gameboy-advance`: always use ARM mode instead of Thumb mode
0.13.0
---
* **command line**
+2 -5
View File
@@ -4,7 +4,7 @@ FROM golang:1.14 AS tinygo-base
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-10 main" >> /etc/apt/sources.list && \
apt-get update && \
apt-get install -y llvm-10-dev libclang-10-dev git
apt-get install -y llvm-10-dev libclang-10-dev lld-10 git
COPY . /tinygo
@@ -27,10 +27,7 @@ COPY --from=tinygo-base /go/bin/tinygo /go/bin/tinygo
COPY --from=tinygo-base /tinygo/src /tinygo/src
COPY --from=tinygo-base /tinygo/targets /tinygo/targets
RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-10 main" >> /etc/apt/sources.list && \
apt-get update && \
apt-get install -y libllvm10 lld-10
RUN apt-get install -y libllvm10 lld-10
# tinygo-avr stage installs the needed dependencies to compile TinyGo programs for AVR microcontrollers.
FROM tinygo-base AS tinygo-avr
+24 -3
View File
@@ -182,7 +182,7 @@ tinygo-test:
.PHONY: smoketest
smoketest:
$(TINYGO) version
# test all examples
# test all examples (except pwm)
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/adc
@@ -203,7 +203,7 @@ smoketest:
@$(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/pwm
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
@$(MD5SUM) test.hex
@@ -231,6 +231,8 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=microbit-s110v8 examples/echo
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nrf52840-mdk examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1
@@ -291,11 +293,18 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=reelboard-s140v7 examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pygamer examples/blinky1
$(TINYGO) build -size short -o test.hex -target=xiao examples/blinky1
@$(MD5SUM) test.hex
ifneq ($(AVR), 0)
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino examples/pwm
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-nano examples/blinky1
@@ -310,7 +319,10 @@ endif
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/export
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/main
release: tinygo gen-device wasi-libc
wasmtest:
$(GO) test ./tests/wasm
build/release: tinygo gen-device wasi-libc
@mkdir -p build/release/tinygo/bin
@mkdir -p build/release/tinygo/lib/clang/include
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
@@ -345,4 +357,13 @@ release: tinygo gen-device wasi-libc
./build/tinygo build-library -target=armv6m-none-eabi -o build/release/tinygo/pkg/armv6m-none-eabi/picolibc.a picolibc
./build/tinygo build-library -target=armv7m-none-eabi -o build/release/tinygo/pkg/armv7m-none-eabi/picolibc.a picolibc
./build/tinygo build-library -target=armv7em-none-eabi -o build/release/tinygo/pkg/armv7em-none-eabi/picolibc.a picolibc
release: build/release
tar -czf build/release.tar.gz -C build/release tinygo
deb: build/release
@mkdir -p build/release-deb/usr/local/bin
@mkdir -p build/release-deb/usr/local/lib
cp -ar build/release/tinygo build/release-deb/usr/local/lib/tinygo
ln -sf ../lib/tinygo/bin/tinygo build/release-deb/usr/local/bin/tinygo
fpm -f -s dir -t deb -n tinygo -v $(shell grep "const Version = " goenv/version.go | awk '{print $$NF}') -m '@tinygo-org' --description='TinyGo is a Go compiler for small places.' --license='BSD 3-Clause' --url=https://tinygo.org/ --deb-changelog CHANGELOG.md -p build/release.deb -C ./build/release-deb
+3 -1
View File
@@ -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 32 microcontroller boards are currently supported:
The following 34 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)
@@ -72,6 +72,8 @@ The following 32 microcontroller boards are currently supported:
* [Particle Xenon](https://docs.particle.io/datasheets/discontinued/xenon-datasheet/)
* [Phytec reel board](https://www.phytec.eu/product-eu/internet-of-things/reelboard/)
* [PineTime DevKit](https://www.pine64.org/pinetime/)
* [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)
* [SiFIve HiFive1](https://www.sifive.com/boards/hifive1)
* [ST Micro "Nucleo F103RB"](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html)
* [ST Micro STM32F103XX "Bluepill"](http://wiki.stm32duino.com/index.php?title=Blue_Pill)
+1 -1
View File
@@ -69,7 +69,7 @@ jobs:
script: |
export PATH="$PATH:./llvm-build/bin:/c/Program Files/qemu"
unset GOROOT
make release -j4
make build/release -j4
- publish: $(System.DefaultWorkingDirectory)/build/release/tinygo
displayName: Publish zip as artifact
artifact: tinygo
+5 -1
View File
@@ -32,7 +32,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
if err != nil {
return err
}
mod, extraFiles, errs := compiler.Compile(pkgName, machine, config)
mod, extraFiles, extraLDFlags, errs := compiler.Compile(pkgName, machine, config)
if errs != nil {
return newMultiError(errs)
}
@@ -187,6 +187,10 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
ldflags = append(ldflags, outpath)
}
if len(extraLDFlags) > 0 {
ldflags = append(ldflags, extraLDFlags...)
}
// Link the object files together.
err = link(config.Target.Linker, ldflags...)
if err != nil {
+8 -2
View File
@@ -16,8 +16,10 @@ var genericBuiltins = []string{
"addvsi3.c",
"addvti3.c",
"apple_versioning.c",
"ashlsi3.c",
"ashldi3.c",
"ashlti3.c",
"ashrsi3.c",
"ashrdi3.c",
"ashrti3.c",
"bswapdi2.c",
@@ -72,7 +74,9 @@ var genericBuiltins = []string{
"floatunsisf.c",
"floatuntidf.c",
"floatuntisf.c",
//"int_util.c",
"fp_mode.c",
"int_util.c",
"lshrsi3.c",
"lshrdi3.c",
"lshrti3.c",
"moddi3.c",
@@ -86,6 +90,7 @@ var genericBuiltins = []string{
"muloti4.c",
"mulsc3.c",
"mulsf3.c",
"mulsi3.c",
"multi3.c",
"multf3.c",
"mulvdi3.c",
@@ -108,6 +113,7 @@ var genericBuiltins = []string{
"powidf2.c",
"powisf2.c",
"powitf2.c",
"powixf2.c",
"subdf3.c",
"subsf3.c",
"subvdi3.c",
@@ -159,7 +165,7 @@ var aeabiBuiltins = []string{
var CompilerRT = Library{
name: "compiler-rt",
cflags: func() []string { return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"} },
sourceDir: "lib/compiler-rt/lib/builtins",
sourceDir: "llvm-project.master/compiler-rt/lib/builtins",
sources: func(target string) []string {
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
+1 -1
View File
@@ -21,7 +21,7 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if goroot == "" {
return nil, errors.New("cannot locate $GOROOT, please set it manually")
}
major, minor, err := getGorootVersion(goroot)
major, minor, err := goenv.GetGorootVersion(goroot)
if err != nil {
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
}
-58
View File
@@ -1,71 +1,13 @@
package builder
import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"sort"
"strings"
)
// getGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned.
func getGorootVersion(goroot string) (major, minor int, err error) {
s, err := GorootVersionString(goroot)
if err != nil {
return 0, 0, err
}
if s == "" || s[:2] != "go" {
return 0, 0, errors.New("could not parse Go version: version does not start with 'go' prefix")
}
parts := strings.Split(s[2:], ".")
if len(parts) < 2 {
return 0, 0, errors.New("could not parse Go version: version has less than two parts")
}
// Ignore the errors, we don't really handle errors here anyway.
var trailing string
n, err := fmt.Sscanf(s, "go%d.%d%s", &major, &minor, &trailing)
if n == 2 && err == io.EOF {
// Means there were no trailing characters (i.e., not an alpha/beta)
err = nil
}
if err != nil {
return 0, 0, fmt.Errorf("failed to parse version: %s", err)
}
return
}
// GorootVersionString returns the version string as reported by the Go
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
// can have some variations (for beta releases, for example).
func GorootVersionString(goroot string) (string, error) {
if data, err := ioutil.ReadFile(filepath.Join(
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
r := regexp.MustCompile("const TheVersion = `(.*)`")
matches := r.FindSubmatch(data)
if len(matches) != 2 {
return "", errors.New("Invalid go version output:\n" + string(data))
}
return string(matches[1]), nil
} else if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
return string(data), nil
} else {
return "", err
}
}
// getClangHeaderPath returns the path to the built-in Clang headers. It tries
// multiple locations, which should make it find the directory when installed in
// various ways.
+4 -1
View File
@@ -69,11 +69,14 @@ func (l *Library) Load(target string) (path string, err error) {
// Precalculate the flags to the compiler invocation.
args := append(l.cflags(), "-c", "-Oz", "-g", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
args = append(args, "-fshort-enums", "-fomit-frame-pointer")
args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft")
}
if strings.HasPrefix(target, "riscv32-") {
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
}
if strings.HasPrefix(target, "avr-") {
args = append(args, "-mdouble=64", "-mmcu=atmega1284p")
}
// Compile all sources.
var objs []string
+1 -2
View File
@@ -123,8 +123,7 @@ func objcopy(infile, outfile string) error {
if err != nil {
return objcopyError{"failed to create .hex file", err}
}
mem.DumpIntelHex(f, 16) // TODO: handle error
return nil
return mem.DumpIntelHex(f, 16)
default:
panic("unreachable")
}
+1 -1
View File
@@ -12,7 +12,7 @@ var Picolibc = Library{
name: "picolibc",
cflags: func() []string {
picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc")
return []string{"-Werror", "-Wall", "-std=gnu11", "-D_COMPILING_NEWLIB", "--sysroot=" + picolibcDir, "-I" + picolibcDir + "/tinystdio", "-I" + goenv.Get("TINYGOROOT") + "/lib/picolibc-include"}
return []string{"-Werror", "-Wall", "-std=gnu11", "-D_COMPILING_NEWLIB", "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", picolibcDir + "/include", "-I" + picolibcDir + "/tinystdio", "-I" + goenv.Get("TINYGOROOT") + "/lib/picolibc-include"}
},
sourceDir: "lib/picolibc/newlib/libc",
sources: func(target string) []string {
+17 -3
View File
@@ -41,6 +41,7 @@ type cgoPackage struct {
elaboratedTypes map[string]*elaboratedTypeInfo
enums map[string]enumInfo
anonStructNum int
ldflags []string
}
// constantInfo stores some information about a CGo constant found by libclang
@@ -156,7 +157,7 @@ typedef unsigned long long _Cgo_ulonglong;
// newly created *ast.File that should be added to the list of to-be-parsed
// files. If there is one or more error, it returns these in the []error slice
// but still modifies the AST.
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []error) {
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []error) {
p := &cgoPackage{
dir: dir,
fset: fset,
@@ -183,7 +184,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
// Find the absolute path for this package.
packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
if err != nil {
return nil, []error{
return nil, nil, []error{
scanner.Error{
Pos: fset.Position(files[0].Pos()),
Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error
@@ -359,6 +360,19 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
}
makePathsAbsolute(flags, packagePath)
cflags = append(cflags, flags...)
case "LDFLAGS":
flags, err := shlex.Split(value)
if err != nil {
// TODO: find the exact location where the error happened.
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+colon+1], "failed to parse flags in #cgo line: "+err.Error())
continue
}
if err := checkLinkerFlags(name, flags); err != nil {
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+colon+1], err.Error())
continue
}
makePathsAbsolute(flags, packagePath)
p.ldflags = append(p.ldflags, flags...)
default:
startPos := strings.LastIndex(line[4:colon], name) + 4
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+startPos], "invalid #cgo line: "+name)
@@ -412,7 +426,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
// Print the newly generated in-memory AST, for debugging.
//ast.Print(fset, p.generated)
return p.generated, p.errors
return p.generated, p.ldflags, p.errors
}
// makePathsAbsolute converts some common path compiler flags (-I, -L) from
+1 -1
View File
@@ -50,7 +50,7 @@ func TestCGo(t *testing.T) {
}
// Process the AST with CGo.
cgoAST, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
cgoAST, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
// Check the AST for type errors.
var typecheckErrors []error
+3 -3
View File
@@ -246,9 +246,9 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
}
value := source[len(name):]
// Try to convert this #define into a Go constant expression.
expr, err := parseConst(pos+token.Pos(len(name)), p.fset, value)
if err != nil {
p.errors = append(p.errors, err)
expr, scannerError := parseConst(pos+token.Pos(len(name)), p.fset, value)
if scannerError != nil {
p.errors = append(p.errors, *scannerError)
}
if expr != nil {
// Parsing was successful.
+1 -1
View File
@@ -1,5 +1,5 @@
// +build !byollvm
// +build !llvm9
// +build !llvm9,!llvm11
package cgo
+14
View File
@@ -0,0 +1,14 @@
// +build !byollvm
// +build llvm11
package cgo
/*
#cgo linux CFLAGS: -I/usr/lib/llvm-11/include
#cgo darwin CFLAGS: -I/usr/local/opt/llvm@11/include
#cgo freebsd CFLAGS: -I/usr/local/llvm11/include
#cgo linux LDFLAGS: -L/usr/lib/llvm-11/lib -lclang
#cgo darwin LDFLAGS: -L/usr/local/opt/llvm@11/lib -lclang -lffi
#cgo freebsd LDFLAGS: -L/usr/local/llvm11/lib -lclang
*/
import "C"
+7
View File
@@ -21,6 +21,13 @@ package main
#if defined(NOTDEFINED)
#warning flag must not be defined
#endif
// Check Compiler flags
#cgo LDFLAGS: -lc
// This flag is not valid ldflags
#cgo LDFLAGS: -does-not-exists
*/
import "C"
+1
View File
@@ -1,6 +1,7 @@
// CGo errors:
// testdata/flags.go:5:7: invalid #cgo line: NOFLAGS
// testdata/flags.go:8:13: invalid flag: -fdoes-not-exist
// testdata/flags.go:29:14: invalid flag: -does-not-exists
package main
+12 -3
View File
@@ -129,8 +129,8 @@ func (c *Config) NeedsStackObjects() bool {
}
}
// Scheduler returns the scheduler implementation. Valid values are "coroutines"
// and "tasks".
// Scheduler returns the scheduler implementation. Valid values are "none",
//"coroutines" and "tasks".
func (c *Config) Scheduler() string {
if c.Options.Scheduler != "" {
return c.Options.Scheduler
@@ -173,7 +173,7 @@ func (c *Config) CFlags() []string {
}
if c.Target.Libc == "picolibc" {
root := goenv.Get("TINYGOROOT")
cflags = append(cflags, "--sysroot="+filepath.Join(root, "lib", "picolibc", "newlib", "libc"))
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"))
}
return cflags
@@ -272,6 +272,15 @@ func (c *Config) OpenOCDConfiguration() (args []string, err error) {
return args, nil
}
// CodeModel returns the code model used on this platform.
func (c *Config) CodeModel() string {
if c.Target.CodeModel != "" {
return c.Target.CodeModel
}
return "default"
}
type TestConfig struct {
CompileTestBinary bool
// TODO: Filter the test functions to run, include verbose flag, etc
+62
View File
@@ -1,5 +1,17 @@
package compileopts
import (
"fmt"
"strings"
)
var (
validGCOptions = []string{"none", "leaking", "extalloc", "conservative"}
validSchedulerOptions = []string{"none", "tasks", "coroutines"}
validPrintSizeOptions = []string{"none", "short", "full"}
validPanicStrategyOptions = []string{"print", "trap"}
)
// Options contains extra options to give to the compiler. These options are
// usually passed from the command line.
type Options struct {
@@ -21,3 +33,53 @@ type Options struct {
TestConfig TestConfig
Programmer string
}
// Verify performs a validation on the given options, raising an error if options are not valid.
func (o *Options) Verify() error {
if o.GC != "" {
valid := isInArray(validGCOptions, o.GC)
if !valid {
return fmt.Errorf(`invalid gc option '%s': valid values are %s`,
o.GC,
strings.Join(validGCOptions, ", "))
}
}
if o.Scheduler != "" {
valid := isInArray(validSchedulerOptions, o.Scheduler)
if !valid {
return fmt.Errorf(`invalid scheduler option '%s': valid values are %s`,
o.Scheduler,
strings.Join(validSchedulerOptions, ", "))
}
}
if o.PrintSizes != "" {
valid := isInArray(validPrintSizeOptions, o.PrintSizes)
if !valid {
return fmt.Errorf(`invalid size option '%s': valid values are %s`,
o.PrintSizes,
strings.Join(validPrintSizeOptions, ", "))
}
}
if o.PanicStrategy != "" {
valid := isInArray(validPanicStrategyOptions, o.PanicStrategy)
if !valid {
return fmt.Errorf(`invalid panic option '%s': valid values are %s`,
o.PanicStrategy,
strings.Join(validPanicStrategyOptions, ", "))
}
}
return nil
}
func isInArray(arr []string, item string) bool {
for _, i := range arr {
if i == item {
return true
}
}
return false
}
+138
View File
@@ -0,0 +1,138 @@
package compileopts_test
import (
"errors"
"testing"
"github.com/tinygo-org/tinygo/compileopts"
)
func TestVerifyOptions(t *testing.T) {
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, extalloc, conservative`)
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, coroutines`)
expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
testCases := []struct {
name string
opts compileopts.Options
expectedError error
}{
{
name: "OptionsEmpty",
opts: compileopts.Options{},
},
{
name: "InvalidGCOption",
opts: compileopts.Options{
GC: "incorrect",
},
expectedError: expectedGCError,
},
{
name: "GCOptionNone",
opts: compileopts.Options{
GC: "none",
},
},
{
name: "GCOptionLeaking",
opts: compileopts.Options{
GC: "leaking",
},
},
{
name: "GCOptionExtalloc",
opts: compileopts.Options{
GC: "extalloc",
},
},
{
name: "GCOptionConservative",
opts: compileopts.Options{
GC: "conservative",
},
},
{
name: "InvalidSchedulerOption",
opts: compileopts.Options{
Scheduler: "incorrect",
},
expectedError: expectedSchedulerError,
},
{
name: "SchedulerOptionNone",
opts: compileopts.Options{
Scheduler: "none",
},
},
{
name: "SchedulerOptionTasks",
opts: compileopts.Options{
Scheduler: "tasks",
},
},
{
name: "SchedulerOptionCoroutines",
opts: compileopts.Options{
Scheduler: "coroutines",
},
},
{
name: "InvalidPrintSizeOption",
opts: compileopts.Options{
PrintSizes: "incorrect",
},
expectedError: expectedPrintSizeError,
},
{
name: "PrintSizeOptionNone",
opts: compileopts.Options{
PrintSizes: "none",
},
},
{
name: "PrintSizeOptionShort",
opts: compileopts.Options{
PrintSizes: "short",
},
},
{
name: "PrintSizeOptionFull",
opts: compileopts.Options{
PrintSizes: "full",
},
},
{
name: "InvalidPanicOption",
opts: compileopts.Options{
PanicStrategy: "incorrect",
},
expectedError: expectedPanicStrategyError,
},
{
name: "PanicOptionPrint",
opts: compileopts.Options{
PanicStrategy: "print",
},
},
{
name: "PanicOptionTrap",
opts: compileopts.Options{
PanicStrategy: "trap",
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := tc.opts.Verify()
if tc.expectedError != err {
if tc.expectedError.Error() != err.Error() {
t.Errorf("expected %v, got %v", tc.expectedError, err)
}
}
})
}
}
+4
View File
@@ -49,6 +49,7 @@ type TargetSpec struct {
OpenOCDTarget string `json:"openocd-target"`
OpenOCDTransport string `json:"openocd-transport"`
JLinkDevice string `json:"jlink-device"`
CodeModel string `json:"code-model"`
}
// copyProperties copies all properties that are set in spec2 into itself.
@@ -130,6 +131,9 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
if spec2.JLinkDevice != "" {
spec.JLinkDevice = spec2.JLinkDevice
}
if spec2.CodeModel != "" {
spec.CodeModel = spec2.CodeModel
}
}
// load reads a target specification from the JSON in the given io.Reader. It
+57
View File
@@ -0,0 +1,57 @@
package compiler
import (
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
// createAtomicOp lowers an atomic library call by lowering it as an LLVM atomic
// operation. It returns the result of the operation and true if the call could
// be lowered inline, and false otherwise.
func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
name := call.Value.(*ssa.Function).Name()
switch name {
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
ptr := b.getValue(call.Args[0])
val := b.getValue(call.Args[1])
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAdd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
// Return the new value, not the original value returned by atomicrmw.
return b.CreateAdd(oldVal, val, ""), true
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
ptr := b.getValue(call.Args[0])
val := b.getValue(call.Args[1])
isPointer := val.Type().TypeKind() == llvm.PointerTypeKind
if isPointer {
// atomicrmw only supports integers, so cast to an integer.
val = b.CreatePtrToInt(val, b.uintptrType, "")
ptr = b.CreateBitCast(ptr, llvm.PointerType(val.Type(), 0), "")
}
oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpXchg, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
if isPointer {
oldVal = b.CreateIntToPtr(oldVal, b.i8ptrType, "")
}
return oldVal, true
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
ptr := b.getValue(call.Args[0])
old := b.getValue(call.Args[1])
newVal := b.getValue(call.Args[2])
tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
swapped := b.CreateExtractValue(tuple, 1, "")
return swapped, true
case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer":
ptr := b.getValue(call.Args[0])
val := b.CreateLoad(ptr, "")
val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
val.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
return val, true
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
ptr := b.getValue(call.Args[0])
val := b.getValue(call.Args[1])
store := b.CreateStore(val, ptr)
store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required
return store, true
default:
return llvm.Value{}, false
}
}
+61 -22
View File
@@ -2,6 +2,7 @@ package compiler
import (
"go/types"
"strconv"
"tinygo.org/x/go-llvm"
)
@@ -13,6 +14,14 @@ import (
// a struct contains more fields, it is passed as a struct without expanding.
const maxFieldsPerParam = 3
// paramInfo contains some information collected about a function parameter,
// useful while declaring or defining a function.
type paramInfo struct {
llvmType llvm.Type
name string // name, possibly with suffixes for e.g. struct fields
flags paramFlags
}
// paramFlags identifies parameter attributes for flags. Most importantly, it
// determines which parameters are dereferenceable_or_null and which aren't.
type paramFlags uint8
@@ -48,19 +57,23 @@ func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm
// Expand an argument type to a list that can be used in a function call
// parameter list.
func expandFormalParamType(t llvm.Type, goType types.Type) ([]llvm.Type, []paramFlags) {
func expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
switch t.TypeKind() {
case llvm.StructTypeKind:
fields, fieldFlags := flattenAggregateType(t, goType)
if len(fields) <= maxFieldsPerParam {
return fields, fieldFlags
fieldInfos := flattenAggregateType(t, name, goType)
if len(fieldInfos) <= maxFieldsPerParam {
return fieldInfos
} else {
// failed to lower
return []llvm.Type{t}, []paramFlags{getTypeFlags(goType)}
}
default:
// TODO: split small arrays
return []llvm.Type{t}, []paramFlags{getTypeFlags(goType)}
}
// TODO: split small arrays
return []paramInfo{
{
llvmType: t,
name: name,
flags: getTypeFlags(goType),
},
}
}
@@ -91,10 +104,10 @@ func (b *builder) expandFormalParamOffsets(t llvm.Type) []uint64 {
func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
switch v.Type().TypeKind() {
case llvm.StructTypeKind:
fieldTypes, _ := flattenAggregateType(v.Type(), nil)
if len(fieldTypes) <= maxFieldsPerParam {
fieldInfos := flattenAggregateType(v.Type(), "", nil)
if len(fieldInfos) <= maxFieldsPerParam {
fields := b.flattenAggregate(v)
if len(fields) != len(fieldTypes) {
if len(fields) != len(fieldInfos) {
panic("type and value param lowering don't match")
}
return fields
@@ -110,23 +123,49 @@ func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
// Try to flatten a struct type to a list of types. Returns a 1-element slice
// with the passed in type if this is not possible.
func flattenAggregateType(t llvm.Type, goType types.Type) ([]llvm.Type, []paramFlags) {
func flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
typeFlags := getTypeFlags(goType)
switch t.TypeKind() {
case llvm.StructTypeKind:
fields := make([]llvm.Type, 0, t.StructElementTypesCount())
fieldFlags := make([]paramFlags, 0, cap(fields))
paramInfos := make([]paramInfo, 0, t.StructElementTypesCount())
for i, subfield := range t.StructElementTypes() {
subfields, subfieldFlags := flattenAggregateType(subfield, extractSubfield(goType, i))
for i := range subfieldFlags {
subfieldFlags[i] |= typeFlags
suffix := strconv.Itoa(i)
if goType != nil {
// Try to come up with a good suffix for this struct field,
// depending on which Go type it's based on.
switch goType := goType.Underlying().(type) {
case *types.Interface:
suffix = []string{"typecode", "value"}[i]
case *types.Slice:
suffix = []string{"data", "len", "cap"}[i]
case *types.Struct:
suffix = goType.Field(i).Name()
case *types.Basic:
switch goType.Kind() {
case types.Complex64, types.Complex128:
suffix = []string{"r", "i"}[i]
case types.String:
suffix = []string{"data", "len"}[i]
}
case *types.Signature:
suffix = []string{"context", "funcptr"}[i]
}
}
fields = append(fields, subfields...)
fieldFlags = append(fieldFlags, subfieldFlags...)
subInfos := flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
for i := range subInfos {
subInfos[i].flags |= typeFlags
}
paramInfos = append(paramInfos, subInfos...)
}
return fields, fieldFlags
return paramInfos
default:
return []llvm.Type{t}, []paramFlags{typeFlags}
return []paramInfo{
{
llvmType: t,
name: name,
flags: typeFlags,
},
}
}
}
@@ -226,7 +265,7 @@ func (b *builder) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Val
func (b *builder) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
switch t.TypeKind() {
case llvm.StructTypeKind:
flattened, _ := flattenAggregateType(t, nil)
flattened := flattenAggregateType(t, "", nil)
if len(flattened) <= maxFieldsPerParam {
value := llvm.ConstNull(t)
for i, subtyp := range t.StructElementTypes() {
+17 -7
View File
@@ -12,7 +12,7 @@ import (
)
func (b *builder) createMakeChan(expr *ssa.MakeChan) llvm.Value {
elementSize := b.targetData.TypeAllocSize(b.getLLVMType(expr.Type().(*types.Chan).Elem()))
elementSize := b.targetData.TypeAllocSize(b.getLLVMType(expr.Type().Underlying().(*types.Chan).Elem()))
elementSizeValue := llvm.ConstInt(b.uintptrType, elementSize, false)
bufSize := b.getValue(expr.Size)
b.createChanBoundsCheck(elementSize, bufSize, expr.Size.Type().Underlying().(*types.Basic), expr.Pos())
@@ -35,27 +35,37 @@ func (b *builder) createChanSend(instr *ssa.Send) {
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
b.CreateStore(chanValue, valueAlloca)
// Do the send.
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast}, "")
// Allocate blockedlist buffer.
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
// End the lifetime of the alloca.
// Do the send.
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
// End the lifetime of the allocas.
// This also works around a bug in CoroSplit, at least in LLVM 8:
// https://bugs.llvm.org/show_bug.cgi?id=41742
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
}
// createChanRecv emits a pseudo chan receive operation. It is lowered to the
// actual channel receive operation during goroutine lowering.
func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
valueType := b.getLLVMType(unop.X.Type().(*types.Chan).Elem())
valueType := b.getLLVMType(unop.X.Type().Underlying().(*types.Chan).Elem())
ch := b.getValue(unop.X)
// Allocate memory to receive into.
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
// Allocate blockedlist buffer.
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
// Do the receive.
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast}, "")
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
received := b.CreateLoad(valueAlloca, "chan.received")
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
if unop.CommaOk {
@@ -117,7 +127,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
switch state.Dir {
case types.RecvOnly:
// Make sure the receive buffer is big enough and has the correct alignment.
llvmType := b.getLLVMType(state.Chan.Type().(*types.Chan).Elem())
llvmType := b.getLLVMType(state.Chan.Type().Underlying().(*types.Chan).Elem())
if size := b.targetData.TypeAllocSize(llvmType); size > recvbufSize {
recvbufSize = size
}
+73 -101
View File
@@ -91,7 +91,25 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
return llvm.TargetMachine{}, err
}
features := strings.Join(config.Features(), ",")
machine := target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, llvm.RelocStatic, llvm.CodeModelDefault)
var codeModel llvm.CodeModel
switch config.CodeModel() {
case "default":
codeModel = llvm.CodeModelDefault
case "tiny":
codeModel = llvm.CodeModelTiny
case "small":
codeModel = llvm.CodeModelSmall
case "kernel":
codeModel = llvm.CodeModelKernel
case "medium":
codeModel = llvm.CodeModelMedium
case "large":
codeModel = llvm.CodeModelLarge
}
machine := target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, llvm.RelocStatic, codeModel)
return machine, nil
}
@@ -103,7 +121,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
// violation. Eventually, this Compile function should only compile a single
// package and not the whole program, and loading of the program (including CGo
// processing) should be moved outside the compiler package.
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []string, []error) {
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (mod llvm.Module, extrafiles []string, extraldflags []string, errors []error) {
c := &compilerContext{
Config: config,
difiles: make(map[string]llvm.Metadata),
@@ -137,64 +155,26 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
dummyFunc.EraseFromParentAsFunction()
// Prefix the GOPATH with the system GOROOT, as GOROOT is already set to
// the TinyGo root.
overlayGopath := goenv.Get("GOPATH")
if overlayGopath == "" {
overlayGopath = goenv.Get("GOROOT")
} else {
overlayGopath = goenv.Get("GOROOT") + string(filepath.ListSeparator) + overlayGopath
}
wd, err := os.Getwd()
if err != nil {
return c.mod, nil, []error{err}
return c.mod, nil, nil, []error{err}
}
goroot, err := loader.GetCachedGoroot(c.Config)
if err != nil {
return c.mod, nil, nil, []error{err}
}
lprogram := &loader.Program{
Build: &build.Context{
GOARCH: c.GOARCH(),
GOOS: c.GOOS(),
GOROOT: goenv.Get("GOROOT"),
GOROOT: goroot,
GOPATH: goenv.Get("GOPATH"),
CgoEnabled: c.CgoEnabled(),
UseAllFiles: false,
Compiler: "gc", // must be one of the recognized compilers
BuildTags: c.BuildTags(),
},
OverlayBuild: &build.Context{
GOARCH: c.GOARCH(),
GOOS: c.GOOS(),
GOROOT: goenv.Get("TINYGOROOT"),
GOPATH: overlayGopath,
CgoEnabled: c.CgoEnabled(),
UseAllFiles: false,
Compiler: "gc", // must be one of the recognized compilers
BuildTags: c.BuildTags(),
},
OverlayPath: func(path string) string {
// Return the (overlay) import path when it should be overlaid, and
// "" if it should not.
if strings.HasPrefix(path, tinygoPath+"/src/") {
// Avoid issues with packages that are imported twice, one from
// GOPATH and one from TINYGOPATH.
path = path[len(tinygoPath+"/src/"):]
}
switch path {
case "machine", "os", "reflect", "runtime", "runtime/interrupt", "runtime/volatile", "sync", "testing", "internal/reflectlite", "internal/task":
return path
default:
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
return path
} else if path == "syscall" {
for _, tag := range c.BuildTags() {
if tag == "baremetal" || tag == "darwin" {
return path
}
}
}
}
return ""
},
Tests: c.TestConfig.CompileTestBinary,
TypeChecker: types.Config{
Sizes: &stdSizes{
IntSize: int64(c.targetData.TypeAllocSize(c.intType)),
@@ -208,38 +188,22 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
ClangHeaders: c.ClangHeaders,
}
if strings.HasSuffix(pkgName, ".go") {
_, err = lprogram.ImportFile(pkgName)
if err != nil {
return c.mod, nil, []error{err}
}
} else {
_, err = lprogram.Import(pkgName, wd, token.Position{
Filename: "build command-line-arguments",
})
if err != nil {
return c.mod, nil, []error{err}
}
}
_, err = lprogram.Import("runtime", "", token.Position{
Filename: "build default import",
})
err = lprogram.Load(pkgName)
if err != nil {
return c.mod, nil, []error{err}
return c.mod, nil, nil, []error{err}
}
err = lprogram.Parse(c.TestConfig.CompileTestBinary)
err = lprogram.Parse()
if err != nil {
return c.mod, nil, []error{err}
return c.mod, nil, nil, []error{err}
}
c.ir = ir.NewProgram(lprogram, pkgName)
c.ir = ir.NewProgram(lprogram)
// Run a simple dead code elimination pass.
err = c.ir.SimpleDCE()
if err != nil {
return c.mod, nil, []error{err}
return c.mod, nil, nil, []error{err}
}
// Initialize debug information.
@@ -378,12 +342,15 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
// Gather the list of (C) file paths that should be included in the build.
var extraFiles []string
for _, pkg := range c.ir.LoaderProgram.Sorted() {
for _, file := range pkg.CFiles {
extraFiles = append(extraFiles, filepath.Join(pkg.Package.Dir, file))
for _, file := range pkg.OtherFiles {
switch strings.ToLower(filepath.Ext(file)) {
case ".c":
extraFiles = append(extraFiles, file)
}
}
}
return c.mod, extraFiles, c.diagnostics
return c.mod, extraFiles, lprogram.LDFlags, c.diagnostics
}
// getLLVMRuntimeType obtains a named type from the runtime package and returns
@@ -738,21 +705,23 @@ func (c *compilerContext) createFunctionDeclaration(f *ir.Function) {
retType = c.ctx.StructType(results, false)
}
var paramTypes []llvm.Type
var paramTypeVariants []paramFlags
var paramInfos []paramInfo
for _, param := range f.Params {
paramType := c.getLLVMType(param.Type())
paramTypeFragments, paramTypeFragmentVariants := expandFormalParamType(paramType, param.Type())
paramTypes = append(paramTypes, paramTypeFragments...)
paramTypeVariants = append(paramTypeVariants, paramTypeFragmentVariants...)
paramFragmentInfos := expandFormalParamType(paramType, param.Name(), param.Type())
paramInfos = append(paramInfos, paramFragmentInfos...)
}
// Add an extra parameter as the function context. This context is used in
// closures and bound methods, but should be optimized away when not used.
if !f.IsExported() {
paramTypes = append(paramTypes, c.i8ptrType) // context
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
paramTypeVariants = append(paramTypeVariants, 0, 0)
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", flags: 0})
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "parentHandle", flags: 0})
}
var paramTypes []llvm.Type
for _, info := range paramInfos {
paramTypes = append(paramTypes, info.llvmType)
}
fnType := llvm.FunctionType(retType, paramTypes, false)
@@ -764,12 +733,12 @@ func (c *compilerContext) createFunctionDeclaration(f *ir.Function) {
}
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
for i, typ := range paramTypes {
if paramTypeVariants[i]&paramIsDeferenceableOrNull == 0 {
for i, info := range paramInfos {
if info.flags&paramIsDeferenceableOrNull == 0 {
continue
}
if typ.TypeKind() == llvm.PointerTypeKind {
el := typ.ElementType()
if info.llvmType.TypeKind() == llvm.PointerTypeKind {
el := info.llvmType.ElementType()
size := c.targetData.TypeAllocSize(el)
if size == 0 {
// dereferenceable_or_null(0) appears to be illegal in LLVM.
@@ -911,9 +880,10 @@ func (b *builder) createFunctionDefinition() {
for _, param := range b.fn.Params {
llvmType := b.getLLVMType(param.Type())
fields := make([]llvm.Value, 0, 1)
fieldFragments, _ := expandFormalParamType(llvmType, nil)
for range fieldFragments {
fields = append(fields, b.fn.LLVMFn.Param(llvmParamIndex))
for _, info := range expandFormalParamType(llvmType, param.Name(), param.Type()) {
param := b.fn.LLVMFn.Param(llvmParamIndex)
param.SetName(info.name)
fields = append(fields, param)
llvmParamIndex++
}
b.locals[param] = b.collapseFormalParam(llvmType, fields)
@@ -1101,7 +1071,7 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
// goroutine:
// * The function context, for closures.
// * The function pointer (for tasks).
funcPtr, context := b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().(*types.Signature))
funcPtr, context := b.decodeFuncValue(b.getValue(instr.Call.Value), instr.Call.Value.Type().Underlying().(*types.Signature))
params = append(params, context) // context parameter
switch b.Scheduler() {
case "none", "coroutines":
@@ -1198,9 +1168,7 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
var llvmCap llvm.Value
switch args[0].Type().(type) {
case *types.Chan:
// Channel. Buffered channels haven't been implemented yet so always
// return 0.
llvmCap = llvm.ConstInt(b.intType, 0, false)
llvmCap = b.createRuntimeCall("chanCap", []llvm.Value{value}, "cap")
case *types.Slice:
llvmCap = b.CreateExtractValue(value, 2, "cap")
default:
@@ -1256,9 +1224,7 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
// string or slice
llvmLen = b.CreateExtractValue(value, 1, "len")
case *types.Chan:
// Channel. Buffered channels haven't been implemented yet so always
// return 0.
llvmLen = llvm.ConstInt(b.intType, 0, false)
llvmLen = b.createRuntimeCall("chanLen", []llvm.Value{value}, "len")
case *types.Map:
llvmLen = b.createRuntimeCall("hashmapLen", []llvm.Value{value}, "len")
default:
@@ -1361,11 +1327,9 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
return b.createMemoryCopyCall(fn, instr.Args)
case name == "runtime.memzero":
return b.createMemoryZeroCall(instr.Args)
case name == "device/arm.ReadRegister" || name == "device/riscv.ReadRegister":
return b.createReadRegister(name, instr.Args)
case name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
case name == "device.Asm" || name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
return b.createInlineAsm(instr.Args)
case name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
return b.createInlineAsmFull(instr)
case strings.HasPrefix(name, "device/arm.SVCall"):
return b.emitSVCall(instr.Args)
@@ -1377,6 +1341,14 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
return b.createVolatileLoad(instr)
case strings.HasPrefix(name, "runtime/volatile.Store"):
return b.createVolatileStore(instr)
case strings.HasPrefix(name, "sync/atomic."):
val, ok := b.createAtomicOp(instr)
if ok {
// This call could be lowered as an atomic operation.
return val, nil
}
// This call couldn't be lowered as an atomic operation, it's
// probably something else. Continue as usual.
case name == "runtime/interrupt.New":
return b.createInterruptGlobal(instr)
}
@@ -1570,7 +1542,7 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
index := b.getValue(expr.Index)
// Check bounds.
arrayLen := expr.X.Type().(*types.Array).Len()
arrayLen := expr.X.Type().Underlying().(*types.Array).Len()
arrayLenLLVM := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
b.createLookupBoundsCheck(arrayLenLLVM, index, expr.Index.Type())
@@ -1682,8 +1654,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
}
// Bounds checking.
lenType := expr.Len.Type().(*types.Basic)
capType := expr.Cap.Type().(*types.Basic)
lenType := expr.Len.Type().Underlying().(*types.Basic)
capType := expr.Cap.Type().Underlying().(*types.Basic)
b.createSliceBoundsCheck(maxSize, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
// Allocate the backing array.
+6 -4
View File
@@ -125,13 +125,15 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
// The receiver is not an interface, but a i8* type.
recv = c.i8ptrType
}
recvFragments, _ := expandFormalParamType(recv, nil)
paramTypes = append(paramTypes, recvFragments...)
for _, info := range expandFormalParamType(recv, "", nil) {
paramTypes = append(paramTypes, info.llvmType)
}
}
for i := 0; i < typ.Params().Len(); i++ {
subType := c.getLLVMType(typ.Params().At(i).Type())
paramTypeFragments, _ := expandFormalParamType(subType, nil)
paramTypes = append(paramTypes, paramTypeFragments...)
for _, info := range expandFormalParamType(subType, "", nil) {
paramTypes = append(paramTypes, info.llvmType)
}
}
// All functions take these parameters at the end.
paramTypes = append(paramTypes, c.i8ptrType) // context
+41 -41
View File
@@ -13,27 +13,6 @@ import (
"tinygo.org/x/go-llvm"
)
// This is a compiler builtin, which reads the given register by name:
//
// func ReadRegister(name string) uintptr
//
// The register name must be a constant, for example "sp".
func (b *builder) createReadRegister(name string, args []ssa.Value) (llvm.Value, error) {
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{}, false)
regname := constant.StringVal(args[0].(*ssa.Const).Value)
var asm string
switch name {
case "device/arm.ReadRegister":
asm = "mov $0, " + regname
case "device/riscv.ReadRegister":
asm = "mv $0, " + regname
default:
panic("unknown architecture")
}
target := llvm.InlineAsm(fnType, asm, "=r", false, false, 0)
return b.CreateCall(target, nil, ""), nil
}
// This is a compiler builtin, which emits a piece of inline assembly with no
// operands or return values. It is useful for trivial instructions, like wfi in
// ARM or sleep in AVR.
@@ -52,7 +31,7 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
// This is a compiler builtin, which allows assembly to be called in a flexible
// way.
//
// func AsmFull(asm string, regs map[string]interface{})
// func AsmFull(asm string, regs map[string]interface{}) uintptr
//
// The asm parameter must be a constant string. The regs parameter must be
// provided immediately. For example:
@@ -66,24 +45,24 @@ func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
registers := map[string]llvm.Value{}
registerMap := instr.Args[1].(*ssa.MakeMap)
for _, r := range *registerMap.Referrers() {
switch r := r.(type) {
case *ssa.DebugRef:
// ignore
case *ssa.MapUpdate:
if r.Block() != registerMap.Block() {
return llvm.Value{}, b.makeError(instr.Pos(), "register value map must be created in the same basic block")
if registerMap, ok := instr.Args[1].(*ssa.MakeMap); ok {
for _, r := range *registerMap.Referrers() {
switch r := r.(type) {
case *ssa.DebugRef:
// ignore
case *ssa.MapUpdate:
if r.Block() != registerMap.Block() {
return llvm.Value{}, b.makeError(instr.Pos(), "register value map must be created in the same basic block")
}
key := constant.StringVal(r.Key.(*ssa.Const).Value)
registers[key] = b.getValue(r.Value.(*ssa.MakeInterface).X)
case *ssa.Call:
if r.Common() == instr {
break
}
default:
return llvm.Value{}, b.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String())
}
key := constant.StringVal(r.Key.(*ssa.Const).Value)
//println("value:", r.Value.(*ssa.MakeInterface).X.String())
registers[key] = b.getValue(r.Value.(*ssa.MakeInterface).X)
case *ssa.Call:
if r.Common() == instr {
break
}
default:
return llvm.Value{}, b.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String())
}
}
// TODO: handle dollar signs in asm string
@@ -92,6 +71,15 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
argTypes := []llvm.Type{}
args := []llvm.Value{}
constraints := []string{}
hasOutput := false
asmString = regexp.MustCompile("\\{\\}").ReplaceAllStringFunc(asmString, func(s string) string {
hasOutput = true
return "$0"
})
if hasOutput {
constraints = append(constraints, "=&r")
registerNumbers[""] = 0
}
asmString = regexp.MustCompile("\\{[a-zA-Z]+\\}").ReplaceAllStringFunc(asmString, func(s string) string {
// TODO: skip strings like {r4} etc. that look like ARM push/pop
// instructions.
@@ -121,9 +109,21 @@ func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error)
if err != nil {
return llvm.Value{}, err
}
fnType := llvm.FunctionType(b.ctx.VoidType(), argTypes, false)
var outputType llvm.Type
if hasOutput {
outputType = b.uintptrType
} else {
outputType = b.ctx.VoidType()
}
fnType := llvm.FunctionType(outputType, argTypes, false)
target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0)
return b.CreateCall(target, args, ""), nil
result := b.CreateCall(target, args, "")
if hasOutput {
return result, nil
} else {
// Make sure we return something valid.
return llvm.ConstInt(b.uintptrType, 0, false), nil
}
}
// This is a compiler builtin which emits an inline SVCall instruction. It can
+4 -1
View File
@@ -446,7 +446,10 @@ func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
// Get the expanded receiver type.
receiverType := c.getLLVMType(f.Params[0].Type())
expandedReceiverType, _ := expandFormalParamType(receiverType, nil)
var expandedReceiverType []llvm.Type
for _, info := range expandFormalParamType(receiverType, "", nil) {
expandedReceiverType = append(expandedReceiverType, info.llvmType)
}
// Does this method even need any wrapping?
if len(expandedReceiverType) == 1 && receiverType.TypeKind() == llvm.PointerTypeKind {
+4 -3
View File
@@ -4,10 +4,11 @@ go 1.11
require (
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac
github.com/chromedp/chromedp v0.5.3
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892
go.bug.st/serial v1.0.0
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
google.golang.org/appengine v1.4.0 // indirect
tinygo.org/x/go-llvm v0.0.0-20200401165421-8d120882fc7a
tinygo.org/x/go-llvm v0.0.0-20200503224449-70c558526021
)
+20 -29
View File
@@ -1,13 +1,26 @@
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 h1:oMCHnXa6CCCafdPDbMh/lWRhRByN0VFLvv+g+ayx1SI=
github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac h1:T7V5BXqnYd55Hj/g5uhDYumg9Fp3rMTS6bykYtTIFX4=
github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g=
github.com/chromedp/chromedp v0.5.3 h1:F9LafxmYpsQhWQBdCs+6Sret1zzeeFyHS5LkRF//Ffg=
github.com/chromedp/chromedp v0.5.3/go.mod h1:YLdPtndaHQ4rCpSpBG+IPpy9JvX0VD+7aaLxYgYj28w=
github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0=
github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46 h1:wXG2bA8fO7Vv7lLk2PihFMTqmbT173Tje39oKzQ50Mo=
github.com/marcinbor85/gohex v0.0.0-20180128172054-7a43cd876e46/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M=
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08 h1:V0an7KRw92wmJysvFvtqtKMAPmvS5O0jtB0nYo6t+gs=
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08/go.mod h1:dFWs1zEqDjFtnBXsd1vPOZaLsESovai349994nHx3e0=
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892 h1:6J+qramlHVLmiBOgRiBOnQkno8uprqG6YFFQTt6uYIw=
github.com/marcinbor85/gohex v0.0.0-20200531091804-343a4b548892/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -17,43 +30,21 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef h1:ymc9FeDom3RIEA3coKokSllBB1hRcMT0tZ1W3Jf9Ids=
golang.org/x/tools v0.0.0-20190227180812-8dcc6e70cdef/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 h1:0sfSpGSa544Fwnbot3Oxq/U6SXqjty6Jy/3wRhVS7ig=
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
tinygo.org/x/go-llvm v0.0.0-20190224120431-7707ae5d1261 h1:rJS2Hga39YAnm7DE4qrPm6Dr/67EOojL0XPzvbEeBiw=
tinygo.org/x/go-llvm v0.0.0-20190224120431-7707ae5d1261/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20190818154551-95bc4ffe1add h1:dFjMH1sLhYADg8UQm7DB56B7e+TfvAmWmEZLhyv3r/w=
tinygo.org/x/go-llvm v0.0.0-20190818154551-95bc4ffe1add/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20191103182207-90b6e4bdc0b9 h1:d6rAX39a3C0pKrY5HcojEGyN8w9ocU0v7X28lC/TRKU=
tinygo.org/x/go-llvm v0.0.0-20191103182207-90b6e4bdc0b9/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20191103200204-37e93e3f04e2 h1:Q5Hv3e5cLMGkiYwYgZL1Zrv6nb/EY+DJpRWrdO6ws6o=
tinygo.org/x/go-llvm v0.0.0-20191103200204-37e93e3f04e2/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20191113125529-bad6d01809e8 h1:9Bfvso+tTVQg16UzOA614NaYA4x8vsRBNtd3eBrXwp0=
tinygo.org/x/go-llvm v0.0.0-20191113125529-bad6d01809e8/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257 h1:o8VDylrMN7gWemBMu8rEyuogKPhcLTdx5KrUAp9macc=
tinygo.org/x/go-llvm v0.0.0-20191124211856-b2db3df3f257/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20191215173731-ad71f3d24aae h1:s8J5EyxCkHxXB08UI3gk9W9IS/ekizRvSX+PfZxnAB0=
tinygo.org/x/go-llvm v0.0.0-20191215173731-ad71f3d24aae/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566 h1:a4y30bTf7U0zDA75v2PTL+XQ2OzJetj19gK8XwQpUNY=
tinygo.org/x/go-llvm v0.0.0-20200104190746-1ff21df33566/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20200226165415-53522ab6713d h1:mtgZh/e8a3wxneQFuLXoQYO//1mvlki02yZ1JCwMKp4=
tinygo.org/x/go-llvm v0.0.0-20200226165415-53522ab6713d/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20200401165421-8d120882fc7a h1:Ugje2Lxuv8CFncHzs5W+hWfJvPsM+W4K0zRvzFbLvoE=
tinygo.org/x/go-llvm v0.0.0-20200401165421-8d120882fc7a/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
tinygo.org/x/go-llvm v0.0.0-20200503224449-70c558526021 h1:d8T98WXGjrTgDmMXgxa6nb9EAYXGXwnzXygnJl6d+ac=
tinygo.org/x/go-llvm v0.0.0-20200503224449-70c558526021/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
+68
View File
@@ -0,0 +1,68 @@
package goenv
import (
"errors"
"fmt"
"io"
"io/ioutil"
"path/filepath"
"regexp"
"strings"
)
// Version of TinyGo.
// Update this value before release of new version of software.
const Version = "0.14.0-dev"
// GetGorootVersion returns the major and minor version for a given GOROOT path.
// If the goroot cannot be determined, (0, 0) is returned.
func GetGorootVersion(goroot string) (major, minor int, err error) {
s, err := GorootVersionString(goroot)
if err != nil {
return 0, 0, err
}
if s == "" || s[:2] != "go" {
return 0, 0, errors.New("could not parse Go version: version does not start with 'go' prefix")
}
parts := strings.Split(s[2:], ".")
if len(parts) < 2 {
return 0, 0, errors.New("could not parse Go version: version has less than two parts")
}
// Ignore the errors, we don't really handle errors here anyway.
var trailing string
n, err := fmt.Sscanf(s, "go%d.%d%s", &major, &minor, &trailing)
if n == 2 && err == io.EOF {
// Means there were no trailing characters (i.e., not an alpha/beta)
err = nil
}
if err != nil {
return 0, 0, fmt.Errorf("failed to parse version: %s", err)
}
return
}
// GorootVersionString returns the version string as reported by the Go
// toolchain for the given GOROOT path. It is usually of the form `go1.x.y` but
// can have some variations (for beta releases, for example).
func GorootVersionString(goroot string) (string, error) {
if data, err := ioutil.ReadFile(filepath.Join(
goroot, "src", "runtime", "internal", "sys", "zversion.go")); err == nil {
r := regexp.MustCompile("const TheVersion = `(.*)`")
matches := r.FindSubmatch(data)
if len(matches) != 2 {
return "", errors.New("Invalid go version output:\n" + string(data))
}
return string(matches[1]), nil
} else if data, err := ioutil.ReadFile(filepath.Join(goroot, "VERSION")); err == nil {
return string(data), nil
} else {
return "", err
}
}
+4 -63
View File
@@ -63,73 +63,14 @@ const (
)
// Create and initialize a new *Program from a *ssa.Program.
func NewProgram(lprogram *loader.Program, mainPath string) *Program {
func NewProgram(lprogram *loader.Program) *Program {
program := lprogram.LoadSSA()
program.Build()
// Find the main package, which is a bit difficult when running a .go file
// directly.
mainPkg := program.ImportedPackage(mainPath)
if mainPkg == nil {
for _, pkgInfo := range program.AllPackages() {
if pkgInfo.Pkg.Name() == "main" {
if mainPkg != nil {
panic("more than one main package found")
}
mainPkg = pkgInfo
}
}
}
mainPkg := program.ImportedPackage(lprogram.MainPkg.PkgPath)
if mainPkg == nil {
panic("could not find main package")
}
// Make a list of packages in import order.
packageList := []*ssa.Package{}
packageSet := map[string]struct{}{}
worklist := []string{"runtime", mainPath}
for len(worklist) != 0 {
pkgPath := worklist[0]
var pkg *ssa.Package
if pkgPath == mainPath {
pkg = mainPkg // necessary for compiling individual .go files
} else {
pkg = program.ImportedPackage(pkgPath)
}
if pkg == nil {
// Non-SSA package (e.g. cgo).
packageSet[pkgPath] = struct{}{}
worklist = worklist[1:]
continue
}
if _, ok := packageSet[pkgPath]; ok {
// Package already in the final package list.
worklist = worklist[1:]
continue
}
unsatisfiedImports := make([]string, 0)
imports := pkg.Pkg.Imports()
for _, pkg := range imports {
if _, ok := packageSet[pkg.Path()]; ok {
continue
}
unsatisfiedImports = append(unsatisfiedImports, pkg.Path())
}
if len(unsatisfiedImports) == 0 {
// All dependencies of this package are satisfied, so add this
// package to the list.
packageList = append(packageList, pkg)
packageSet[pkgPath] = struct{}{}
worklist = worklist[1:]
} else {
// Prepend all dependencies to the worklist and reconsider this
// package (by not removing it from the worklist). At that point, it
// must be possible to add it to packageList.
worklist = append(unsatisfiedImports, worklist...)
}
}
p := &Program{
Program: program,
LoaderProgram: lprogram,
@@ -137,8 +78,8 @@ func NewProgram(lprogram *loader.Program, mainPath string) *Program {
functionMap: make(map[*ssa.Function]*Function),
}
for _, pkg := range packageList {
p.AddPackage(pkg)
for _, pkg := range lprogram.Sorted() {
p.AddPackage(program.ImportedPackage(pkg.PkgPath))
}
return p
-27
View File
@@ -1,10 +1,5 @@
package loader
import (
"go/token"
"strings"
)
// Errors contains a list of parser errors or a list of typechecker errors for
// the given package.
type Errors struct {
@@ -15,25 +10,3 @@ type Errors struct {
func (e Errors) Error() string {
return "could not compile: " + e.Errs[0].Error()
}
// ImportCycleErrors is returned when encountering an import cycle. The list of
// packages is a list from the root package to the leaf package that imports one
// of the packages in the list.
type ImportCycleError struct {
Packages []string
ImportPositions []token.Position
}
func (e *ImportCycleError) Error() string {
var msg strings.Builder
msg.WriteString("import cycle:\n\t")
msg.WriteString(strings.Join(e.Packages, "\n\t"))
msg.WriteString("\n at ")
for i, pos := range e.ImportPositions {
if i > 0 {
msg.WriteString(", ")
}
msg.WriteString(pos.String())
}
return msg.String()
}
+240
View File
@@ -0,0 +1,240 @@
package loader
// This file constructs a new temporary GOROOT directory by merging both the
// standard Go GOROOT and the GOROOT from TinyGo using symlinks.
import (
"crypto/sha512"
"encoding/hex"
"errors"
"fmt"
"io/ioutil"
"math/rand"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strconv"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/goenv"
)
// GetCachedGoroot creates a new GOROOT by merging both the standard GOROOT and
// the GOROOT from TinyGo using lots of symbolic links.
func GetCachedGoroot(config *compileopts.Config) (string, error) {
goroot := goenv.Get("GOROOT")
if goroot == "" {
return "", errors.New("could not determine GOROOT")
}
tinygoroot := goenv.Get("TINYGOROOT")
if tinygoroot == "" {
return "", errors.New("could not determine TINYGOROOT")
}
// Determine the location of the cached GOROOT.
version, err := goenv.GorootVersionString(goroot)
if err != nil {
return "", err
}
// This hash is really a cache key, that contains (hopefully) enough
// information to make collisions unlikely during development.
// By including the Go version and TinyGo version, cache collisions should
// not happen outside of development.
hash := sha512.New512_256()
fmt.Fprintln(hash, goroot)
fmt.Fprintln(hash, version)
fmt.Fprintln(hash, goenv.Version)
fmt.Fprintln(hash, tinygoroot)
gorootsHash := hash.Sum(nil)
gorootsHashHex := hex.EncodeToString(gorootsHash[:])
cachedgoroot := filepath.Join(goenv.Get("GOCACHE"), "goroot-"+version+"-"+gorootsHashHex)
if needsSyscallPackage(config.BuildTags()) {
cachedgoroot += "-syscall"
}
if _, err := os.Stat(cachedgoroot); err == nil {
return cachedgoroot, nil
}
tmpgoroot := cachedgoroot + ".tmp" + strconv.Itoa(rand.Int())
err = os.MkdirAll(tmpgoroot, 0777)
if err != nil {
return "", err
}
// Remove the temporary directory if it wasn't moved to the right place
// (for example, when there was an error).
defer os.RemoveAll(tmpgoroot)
for _, name := range []string{"bin", "lib", "pkg"} {
err = symlink(filepath.Join(goroot, name), filepath.Join(tmpgoroot, name))
if err != nil {
return "", err
}
}
err = mergeDirectory(goroot, tinygoroot, tmpgoroot, "", pathsToOverride(needsSyscallPackage(config.BuildTags())))
if err != nil {
return "", err
}
err = os.Rename(tmpgoroot, cachedgoroot)
if err != nil {
if os.IsExist(err) {
// Another invocation of TinyGo also seems to have created a GOROOT.
// Use that one instead. Our new GOROOT will be automatically
// deleted by the defer above.
return cachedgoroot, nil
}
return "", err
}
return cachedgoroot, nil
}
// mergeDirectory merges two roots recursively. The tmpgoroot is the directory
// that will be created by this call by either symlinking the directory from
// goroot or tinygoroot, or by creating the directory and merging the contents.
func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides map[string]bool) error {
if mergeSubdirs, ok := overrides[importPath+"/"]; ok {
if !mergeSubdirs {
// This directory and all subdirectories should come from the TinyGo
// root, so simply make a symlink.
newname := filepath.Join(tmpgoroot, "src", importPath)
oldname := filepath.Join(tinygoroot, "src", importPath)
return symlink(oldname, newname)
}
// Merge subdirectories. Start by making the directory to merge.
err := os.Mkdir(filepath.Join(tmpgoroot, "src", importPath), 0777)
if err != nil {
return err
}
// Symlink all files from TinyGo, and symlink directories from TinyGo
// that need to be overridden.
tinygoEntries, err := ioutil.ReadDir(filepath.Join(tinygoroot, "src", importPath))
if err != nil {
return err
}
for _, e := range tinygoEntries {
if e.IsDir() {
// A directory, so merge this thing.
err := mergeDirectory(goroot, tinygoroot, tmpgoroot, path.Join(importPath, e.Name()), overrides)
if err != nil {
return err
}
} else {
// A file, so symlink this.
newname := filepath.Join(tmpgoroot, "src", importPath, e.Name())
oldname := filepath.Join(tinygoroot, "src", importPath, e.Name())
err := symlink(oldname, newname)
if err != nil {
return err
}
}
}
// Symlink all directories from $GOROOT that are not part of the TinyGo
// overrides.
gorootEntries, err := ioutil.ReadDir(filepath.Join(goroot, "src", importPath))
if err != nil {
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
}
}
}
return nil
}
// needsSyscallPackage returns whether the syscall package should be overriden
// with the TinyGo version. This is the case on some targets.
func needsSyscallPackage(buildTags []string) bool {
for _, tag := range buildTags {
if tag == "baremetal" || tag == "darwin" {
return true
}
}
return false
}
// The boolean indicates whether to merge the subdirs. True means merge, false
// means use the TinyGo version.
func pathsToOverride(needsSyscallPackage bool) map[string]bool {
paths := map[string]bool{
"/": true,
"device/": false,
"examples/": false,
"internal/": true,
"internal/bytealg/": false,
"internal/reflectlite/": false,
"internal/task/": false,
"machine/": false,
"os/": true,
"reflect/": false,
"runtime/": false,
"sync/": true,
"testing/": false,
}
if needsSyscallPackage {
paths["syscall/"] = true // include syscall/js
}
return paths
}
// symlink creates a symlink or something similar. On Unix-like systems, it
// always creates a symlink. On Windows, it tries to create a symlink and if
// that fails, creates a hardlink or directory junction instead.
//
// Note that while Windows 10 does support symlinks and allows them to be
// created using os.Symlink, it requires developer mode to be enabled.
// Therefore provide a fallback for when symlinking is not possible.
// Unfortunately this fallback only works when TinyGo is installed on the same
// filesystem as the TinyGo cache and the Go installation (which is usually the
// C drive).
func symlink(oldname, newname string) error {
symlinkErr := os.Symlink(oldname, newname)
if runtime.GOOS == "windows" && symlinkErr != nil {
// Fallback for when developer mode is disabled.
// Note that we return the symlink error even if something else fails
// later on. This is because symlinks are the easiest to support
// (they're also used on Linux and MacOS) and enabling them is easy:
// just enable developer mode.
st, err := os.Stat(oldname)
if err != nil {
return symlinkErr
}
if st.IsDir() {
// Make a directory junction. There may be a way to do this
// programmatically, but it involves a lot of magic. Use the mklink
// command built into cmd instead (mklink is a builtin, not an
// external command).
err := exec.Command("cmd", "/k", "mklink", "/J", newname, oldname).Run()
if err != nil {
return symlinkErr
}
} else {
// Make a hard link.
err := os.Link(oldname, newname)
if err != nil {
return symlinkErr
}
}
return nil // success
}
return symlinkErr
}
+168 -221
View File
@@ -3,6 +3,7 @@ package loader
import (
"bytes"
"errors"
"fmt"
"go/ast"
"go/build"
"go/parser"
@@ -12,114 +13,191 @@ import (
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"text/template"
"github.com/tinygo-org/tinygo/cgo"
"github.com/tinygo-org/tinygo/goenv"
"golang.org/x/tools/go/packages"
)
// Program holds all packages and some metadata about the program as a whole.
type Program struct {
mainPkg string
Build *build.Context
OverlayBuild *build.Context
OverlayPath func(path string) string
Tests bool
Packages map[string]*Package
MainPkg *Package
sorted []*Package
fset *token.FileSet
TypeChecker types.Config
Dir string // current working directory (for error reporting)
TINYGOROOT string // root of the TinyGo installation or root of the source code
CFlags []string
LDFlags []string
ClangHeaders string
}
// Package holds a loaded package, its imports, and its parsed files.
type Package struct {
*Program
*build.Package
Imports map[string]*Package
Importing bool
Files []*ast.File
Pkg *types.Package
*packages.Package
Files []*ast.File
Pkg *types.Package
types.Info
}
// Import loads the given package relative to srcDir (for the vendor directory).
// It only loads the current package without recursion.
func (p *Program) Import(path, srcDir string, pos token.Position) (*Package, error) {
// Load loads the given package with all dependencies (including the runtime
// package). Call .Parse() afterwards to parse all Go files (including CGo
// processing, if necessary).
func (p *Program) Load(importPath string) error {
if p.Packages == nil {
p.Packages = make(map[string]*Package)
}
// Load this package.
ctx := p.Build
if newPath := p.OverlayPath(path); newPath != "" {
ctx = p.OverlayBuild
path = newPath
}
buildPkg, err := ctx.Import(path, srcDir, build.ImportComment)
err := p.loadPackage(importPath)
if err != nil {
return nil, scanner.Error{
Pos: pos,
Msg: err.Error(), // TODO: define a new error type that will wrap the inner error
}
return err
}
if existingPkg, ok := p.Packages[buildPkg.ImportPath]; ok {
// Already imported, or at least started the import.
return existingPkg, nil
p.MainPkg = p.sorted[len(p.sorted)-1]
if _, ok := p.Packages["runtime"]; !ok {
// The runtime package wasn't loaded. Although `go list -deps` seems to
// return the full dependency list, there is no way to get those
// packages from the go/packages package. Therefore load the runtime
// manually and add it to the list of to-be-compiled packages
// (duplicates are already filtered).
return p.loadPackage("runtime")
}
p.sorted = nil // invalidate the sorted order of packages
pkg := p.newPackage(buildPkg)
p.Packages[buildPkg.ImportPath] = pkg
if p.mainPkg == "" {
p.mainPkg = buildPkg.ImportPath
}
return pkg, nil
return nil
}
// ImportFile loads and parses the import statements in the given path and
// creates a pseudo-package out of it.
func (p *Program) ImportFile(path string) (*Package, error) {
if p.Packages == nil {
p.Packages = make(map[string]*Package)
func (p *Program) loadPackage(importPath string) error {
cgoEnabled := "0"
if p.Build.CgoEnabled {
cgoEnabled = "1"
}
if _, ok := p.Packages[path]; ok {
// unlikely
return nil, errors.New("loader: cannot import file that is already imported as package: " + path)
}
file, err := p.parseFile(path, parser.ImportsOnly)
pkgs, err := packages.Load(&packages.Config{
Mode: packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps,
Env: append(os.Environ(), "GOROOT="+p.Build.GOROOT, "GOOS="+p.Build.GOOS, "GOARCH="+p.Build.GOARCH, "CGO_ENABLED="+cgoEnabled),
BuildFlags: []string{"-tags", strings.Join(p.Build.BuildTags, " ")},
Tests: p.Tests,
}, importPath)
if err != nil {
return nil, err
return err
}
buildPkg := &build.Package{
Dir: filepath.Dir(path),
ImportPath: path,
GoFiles: []string{filepath.Base(path)},
var pkg *packages.Package
if p.Tests {
// We need the second package. Quoting from the docs:
// > For example, when using the go command, loading "fmt" with Tests=true
// > returns four packages, with IDs "fmt" (the standard package),
// > "fmt [fmt.test]" (the package as compiled for the test),
// > "fmt_test" (the test functions from source files in package fmt_test),
// > and "fmt.test" (the test binary).
pkg = pkgs[1]
} else {
if len(pkgs) != 1 {
return fmt.Errorf("expected exactly one package while importing %s, got %d", importPath, len(pkgs))
}
pkg = pkgs[0]
}
for _, importSpec := range file.Imports {
buildPkg.Imports = append(buildPkg.Imports, importSpec.Path.Value[1:len(importSpec.Path.Value)-1])
}
p.sorted = nil // invalidate the sorted order of packages
pkg := p.newPackage(buildPkg)
p.Packages[buildPkg.ImportPath] = pkg
var importError *Errors
var addPackages func(pkg *packages.Package)
addPackages = func(pkg *packages.Package) {
if _, ok := p.Packages[pkg.PkgPath]; ok {
return
}
pkg2 := p.newPackage(pkg)
p.Packages[pkg.PkgPath] = pkg2
if len(pkg.Errors) != 0 {
if importError != nil {
// There was another error reported already. Do not report
// errors from multiple packages at once.
return
}
importError = &Errors{
Pkg: pkg2,
}
for _, err := range pkg.Errors {
pos := token.Position{}
fields := strings.Split(err.Pos, ":")
if len(fields) >= 2 {
// There is some file/line/column information.
if n, err := strconv.Atoi(fields[len(fields)-2]); err == nil {
// Format: filename.go:line:colum
pos.Filename = strings.Join(fields[:len(fields)-2], ":")
pos.Line = n
pos.Column, _ = strconv.Atoi(fields[len(fields)-1])
} else {
// Format: filename.go:line
pos.Filename = strings.Join(fields[:len(fields)-1], ":")
pos.Line, _ = strconv.Atoi(fields[len(fields)-1])
}
pos.Filename = p.getOriginalPath(pos.Filename)
}
importError.Errs = append(importError.Errs, scanner.Error{
Pos: pos,
Msg: err.Msg,
})
}
return
}
if p.mainPkg == "" {
p.mainPkg = buildPkg.ImportPath
}
// Get the list of imports (sorted alphabetically).
names := make([]string, 0, len(pkg.Imports))
for name := range pkg.Imports {
names = append(names, name)
}
sort.Strings(names)
return pkg, nil
// Add all the imports.
for _, name := range names {
addPackages(pkg.Imports[name])
}
p.sorted = append(p.sorted, pkg2)
}
addPackages(pkg)
if importError != nil {
return *importError
}
return nil
}
// getOriginalPath looks whether this path is in the generated GOROOT and if so,
// replaces the path with the original path (in GOROOT or TINYGOROOT). Otherwise
// the input path is returned.
func (p *Program) getOriginalPath(path string) string {
originalPath := path
if strings.HasPrefix(path, p.Build.GOROOT+string(filepath.Separator)) {
// If this file is part of the synthetic GOROOT, try to infer the
// original path.
relpath := path[len(filepath.Join(p.Build.GOROOT, "src"))+1:]
realgorootPath := filepath.Join(goenv.Get("GOROOT"), "src", relpath)
if _, err := os.Stat(realgorootPath); err == nil {
originalPath = realgorootPath
}
maybeInTinyGoRoot := false
for prefix := range pathsToOverride(needsSyscallPackage(p.Build.BuildTags)) {
if !strings.HasPrefix(relpath, prefix) {
continue
}
maybeInTinyGoRoot = true
}
if maybeInTinyGoRoot {
tinygoPath := filepath.Join(p.TINYGOROOT, "src", relpath)
if _, err := os.Stat(tinygoPath); err == nil {
originalPath = tinygoPath
}
}
}
return originalPath
}
// newPackage instantiates a new *Package object with initialized members.
func (p *Program) newPackage(pkg *build.Package) *Package {
func (p *Program) newPackage(pkg *packages.Package) *Package {
return &Package{
Program: p,
Package: pkg,
Imports: make(map[string]*Package, len(pkg.Imports)),
Info: types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
@@ -134,87 +212,25 @@ func (p *Program) newPackage(pkg *build.Package) *Package {
// Sorted returns a list of all packages, sorted in a way that no packages come
// before the packages they depend upon.
func (p *Program) Sorted() []*Package {
if p.sorted == nil {
p.sort()
}
return p.sorted
}
func (p *Program) sort() {
p.sorted = nil
packageList := make([]*Package, 0, len(p.Packages))
packageSet := make(map[string]struct{}, len(p.Packages))
worklist := make([]string, 0, len(p.Packages))
for path := range p.Packages {
worklist = append(worklist, path)
}
sort.Strings(worklist)
for len(worklist) != 0 {
pkgPath := worklist[0]
pkg := p.Packages[pkgPath]
if _, ok := packageSet[pkgPath]; ok {
// Package already in the final package list.
worklist = worklist[1:]
continue
}
unsatisfiedImports := make([]string, 0)
for _, pkg := range pkg.Imports {
if _, ok := packageSet[pkg.ImportPath]; ok {
continue
}
unsatisfiedImports = append(unsatisfiedImports, pkg.ImportPath)
}
sort.Strings(unsatisfiedImports)
if len(unsatisfiedImports) == 0 {
// All dependencies of this package are satisfied, so add this
// package to the list.
packageList = append(packageList, pkg)
packageSet[pkgPath] = struct{}{}
worklist = worklist[1:]
} else {
// Prepend all dependencies to the worklist and reconsider this
// package (by not removing it from the worklist). At that point, it
// must be possible to add it to packageList.
worklist = append(unsatisfiedImports, worklist...)
}
}
p.sorted = packageList
}
// Parse recursively imports all packages, parses them, and typechecks them.
// Parse parses all packages and typechecks them.
//
// The returned error may be an Errors error, which contains a list of errors.
//
// Idempotent.
func (p *Program) Parse(compileTestBinary bool) error {
includeTests := compileTestBinary
// Load all imports
for _, pkg := range p.Sorted() {
err := pkg.importRecursively(includeTests)
if err != nil {
if err, ok := err.(*ImportCycleError); ok {
if pkg.ImportPath != err.Packages[0] {
err.Packages = append([]string{pkg.ImportPath}, err.Packages...)
}
}
return err
}
}
func (p *Program) Parse() error {
// Parse all packages.
for _, pkg := range p.Sorted() {
err := pkg.Parse(includeTests)
err := pkg.Parse()
if err != nil {
return err
}
}
if compileTestBinary {
err := p.SwapTestMain()
if p.Tests {
err := p.swapTestMain()
if err != nil {
return err
}
@@ -231,7 +247,7 @@ func (p *Program) Parse(compileTestBinary bool) error {
return nil
}
func (p *Program) SwapTestMain() error {
func (p *Program) swapTestMain() error {
var tests []string
isTestFunc := func(f *ast.FuncDecl) bool {
@@ -241,8 +257,7 @@ func (p *Program) SwapTestMain() error {
}
return false
}
mainPkg := p.Packages[p.mainPkg]
for _, f := range mainPkg.Files {
for _, f := range p.MainPkg.Files {
for i, d := range f.Decls {
switch v := d.(type) {
case *ast.FuncDecl:
@@ -293,7 +308,7 @@ func main () {
if err != nil {
return err
}
path := filepath.Join(p.mainPkg, "$testmain.go")
path := filepath.Join(p.MainPkg.Dir, "$testmain.go")
if p.fset == nil {
p.fset = token.NewFileSet()
@@ -303,7 +318,7 @@ func main () {
if err != nil {
return err
}
mainPkg.Files = append(mainPkg.Files, newMain)
p.MainPkg.Files = append(p.MainPkg.Files, newMain)
return nil
}
@@ -319,34 +334,27 @@ func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
return nil, err
}
defer rd.Close()
relpath := path
if filepath.IsAbs(path) {
rp, err := filepath.Rel(p.Dir, path)
if err == nil {
relpath = rp
}
}
return parser.ParseFile(p.fset, relpath, rd, mode)
return parser.ParseFile(p.fset, p.getOriginalPath(path), rd, mode)
}
// Parse parses and typechecks this package.
//
// Idempotent.
func (p *Package) Parse(includeTests bool) error {
func (p *Package) Parse() error {
if len(p.Files) != 0 {
return nil
}
// Load the AST.
// TODO: do this in parallel.
if p.ImportPath == "unsafe" {
if p.PkgPath == "unsafe" {
// Special case for the unsafe package. Don't even bother loading
// the files.
p.Pkg = types.Unsafe
return nil
}
files, err := p.parseFiles(includeTests)
files, err := p.parseFiles()
if err != nil {
return err
}
@@ -373,7 +381,7 @@ func (p *Package) Check() error {
// Do typechecking of the package.
checker.Importer = p
typesPkg, err := checker.Check(p.ImportPath, p.fset, p.Files, &p.Info)
typesPkg, err := checker.Check(p.PkgPath, p.fset, p.Files, &p.Info)
if err != nil {
if err, ok := err.(Errors); ok {
return err
@@ -385,22 +393,14 @@ func (p *Package) Check() error {
}
// parseFiles parses the loaded list of files and returns this list.
func (p *Package) parseFiles(includeTests bool) ([]*ast.File, error) {
func (p *Package) parseFiles() ([]*ast.File, error) {
// TODO: do this concurrently.
var files []*ast.File
var fileErrs []error
var gofiles []string
if includeTests {
gofiles = make([]string, 0, len(p.GoFiles)+len(p.TestGoFiles))
gofiles = append(gofiles, p.GoFiles...)
gofiles = append(gofiles, p.TestGoFiles...)
} else {
gofiles = p.GoFiles
}
for _, file := range gofiles {
f, err := p.parseFile(filepath.Join(p.Package.Dir, file), parser.ParseComments)
var cgoFiles []*ast.File
for _, file := range p.GoFiles {
f, err := p.parseFile(file, parser.ParseComments)
if err != nil {
fileErrs = append(fileErrs, err)
continue
@@ -409,27 +409,24 @@ func (p *Package) parseFiles(includeTests bool) ([]*ast.File, error) {
fileErrs = append(fileErrs, err)
continue
}
for _, importSpec := range f.Imports {
if importSpec.Path.Value == `"C"` {
cgoFiles = append(cgoFiles, f)
}
}
files = append(files, f)
}
for _, file := range p.CgoFiles {
path := filepath.Join(p.Package.Dir, file)
f, err := p.parseFile(path, parser.ParseComments)
if err != nil {
fileErrs = append(fileErrs, err)
continue
}
files = append(files, f)
}
if len(p.CgoFiles) != 0 {
cflags := append(p.CFlags, "-I"+p.Package.Dir)
if len(cgoFiles) != 0 {
cflags := append(p.CFlags, "-I"+filepath.Dir(p.GoFiles[0]))
if p.ClangHeaders != "" {
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.ClangHeaders)
}
generated, errs := cgo.Process(files, p.Program.Dir, p.fset, cflags)
generated, ldflags, errs := cgo.Process(files, p.Program.Dir, p.fset, cflags)
if errs != nil {
fileErrs = append(fileErrs, errs...)
}
files = append(files, generated)
p.LDFlags = append(p.LDFlags, ldflags...)
}
if len(fileErrs) != 0 {
return nil, Errors{p, fileErrs}
@@ -445,58 +442,8 @@ func (p *Package) Import(to string) (*types.Package, error) {
return types.Unsafe, nil
}
if _, ok := p.Imports[to]; ok {
return p.Imports[to].Pkg, nil
return p.Packages[p.Imports[to].PkgPath].Pkg, nil
} else {
return nil, errors.New("package not imported: " + to)
}
}
// importRecursively calls Program.Import() on all imported packages, and calls
// importRecursively() on the imported packages as well.
//
// Idempotent.
func (p *Package) importRecursively(includeTests bool) error {
p.Importing = true
imports := p.Package.Imports
if includeTests {
imports = append(imports, p.Package.TestImports...)
}
for _, to := range imports {
if to == "C" {
// Do CGo processing in a later stage.
continue
}
if _, ok := p.Imports[to]; ok {
continue
}
// Find error location.
var pos token.Position
if len(p.Package.ImportPos[to]) > 0 {
pos = p.Package.ImportPos[to][0]
} else {
pos = token.Position{Filename: p.Package.ImportPath}
}
importedPkg, err := p.Program.Import(to, p.Package.Dir, pos)
if err != nil {
if err, ok := err.(*ImportCycleError); ok {
err.Packages = append([]string{p.ImportPath}, err.Packages...)
}
return err
}
if importedPkg.Importing {
return &ImportCycleError{[]string{p.ImportPath, importedPkg.ImportPath}, p.ImportPos[to]}
}
err = importedPkg.importRecursively(false)
if err != nil {
if err, ok := err.(*ImportCycleError); ok {
err.Packages = append([]string{p.ImportPath}, err.Packages...)
}
return err
}
p.Imports[to] = importedPkg
}
p.Importing = false
return nil
}
+156 -52
View File
@@ -124,6 +124,7 @@ func Build(pkgName, outpath string, options *compileopts.Options) error {
// Test runs the tests in the given package.
func Test(pkgName string, options *compileopts.Options) error {
options.TestConfig.CompileTestBinary = true
config, err := builder.NewConfig(options)
if err != nil {
return err
@@ -135,7 +136,6 @@ func Test(pkgName string, options *compileopts.Options) error {
// For details: https://github.com/golang/go/issues/21360
config.Target.BuildTags = append(config.Target.BuildTags, "test")
options.TestConfig.CompileTestBinary = true
return builder.Build(pkgName, ".elf", config, func(tmppath string) error {
cmd := exec.Command(tmppath)
cmd.Stdout = os.Stdout
@@ -310,10 +310,12 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
switch gdbInterface {
case "msd", "command", "":
if len(config.Target.Emulator) != 0 {
// Assume QEMU as an emulator.
if config.Target.Emulator[0] == "mgba" {
gdbInterface = "mgba"
} else if config.Target.Emulator[0] == "simavr" {
gdbInterface = "simavr"
} else {
// Assume QEMU as an emulator.
gdbInterface = "qemu"
}
} else if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
@@ -379,6 +381,14 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
daemon = exec.Command(config.Target.Emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "simavr":
gdbCommands = append(gdbCommands, "target remote :1234")
// Run in an emulator.
args := append(config.Target.Emulator[1:], "-g", tmppath)
daemon = exec.Command(config.Target.Emulator[0], args...)
daemon.Stdout = os.Stdout
daemon.Stderr = os.Stderr
case "msd":
return errors.New("gdb is not supported for drag-and-drop programmable devices")
default:
@@ -479,6 +489,13 @@ func touchSerialPortAt1200bps(port string) (err error) {
// Open port
p, e := serial.Open(port, &serial.Mode{BaudRate: 1200})
if e != nil {
if runtime.GOOS == `windows` {
se, ok := e.(*serial.PortError)
if ok && se.Code() == serial.InvalidSerialPort {
// InvalidSerialPort error occurs when transitioning to boot
return nil
}
}
time.Sleep(1 * time.Second)
err = e
continue
@@ -491,6 +508,8 @@ func touchSerialPortAt1200bps(port string) (err error) {
return fmt.Errorf("opening port: %s", err)
}
const maxMSDRetries = 10
func flashUF2UsingMSD(volume, tmppath string) error {
// find standard UF2 info path
var infoPath string
@@ -507,15 +526,12 @@ func flashUF2UsingMSD(volume, tmppath string) error {
infoPath = path + "/INFO_UF2.TXT"
}
d, err := filepath.Glob(infoPath)
d, err := locateDevice(volume, infoPath)
if err != nil {
return err
}
if d == nil {
return errors.New("unable to locate UF2 device: " + volume)
}
return moveFile(tmppath, filepath.Dir(d[0])+"/flash.uf2")
return moveFile(tmppath, filepath.Dir(d)+"/flash.uf2")
}
func flashHexUsingMSD(volume, tmppath string) error {
@@ -534,15 +550,31 @@ func flashHexUsingMSD(volume, tmppath string) error {
destPath = path + "/"
}
d, err := filepath.Glob(destPath)
d, err := locateDevice(volume, destPath)
if err != nil {
return err
}
if d == nil {
return errors.New("unable to locate device: " + volume)
}
return moveFile(tmppath, d[0]+"/flash.hex")
return moveFile(tmppath, d+"/flash.hex")
}
func locateDevice(volume, path string) (string, error) {
var d []string
var err error
for i := 0; i < maxMSDRetries; i++ {
d, err = filepath.Glob(path)
if err != nil {
return "", err
}
if d != nil {
break
}
time.Sleep(500 * time.Millisecond)
}
if d == nil {
return "", errors.New("unable to locate device: " + volume)
}
return d[0], nil
}
func windowsFindUSBDrive(volume string) (string, error) {
@@ -603,29 +635,18 @@ func getDefaultPort() (port string, err error) {
case "freebsd":
portPath = "/dev/cuaU*"
case "windows":
cmd := exec.Command("wmic",
"PATH", "Win32_SerialPort", "WHERE", "Caption LIKE 'USB Serial%'", "GET", "DeviceID")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
ports, err := serial.GetPortsList()
if err != nil {
return "", err
}
if out.String() == "No Instance(s) Available." {
if len(ports) == 0 {
return "", errors.New("no serial ports available")
} else if len(ports) > 1 {
return "", errors.New("multiple serial ports available - use -port flag")
}
for _, line := range strings.Split(out.String(), "\n") {
words := strings.Fields(line)
if len(words) == 1 {
if strings.Contains(words[0], "COM") {
return words[0], nil
}
}
}
return "", errors.New("unable to locate a serial port")
return ports[0], nil
default:
return "", errors.New("unable to search for a default USB device to be flashed on this OS")
}
@@ -641,9 +662,39 @@ func getDefaultPort() (port string, err error) {
return d[0], nil
}
// runGoList runs the `go list` command but using the configuration used for
// TinyGo.
func runGoList(config *compileopts.Config, flagJSON, flagDeps bool, pkgs []string) error {
goroot, err := loader.GetCachedGoroot(config)
if err != nil {
return err
}
args := []string{"list"}
if flagJSON {
args = append(args, "-json")
}
if flagDeps {
args = append(args, "-deps")
}
if len(config.BuildTags()) != 0 {
args = append(args, "-tags", strings.Join(config.BuildTags(), " "))
}
args = append(args, pkgs...)
cgoEnabled := "0"
if config.CgoEnabled() {
cgoEnabled = "1"
}
cmd := exec.Command("go", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = append(os.Environ(), "GOROOT="+goroot, "GOOS="+config.GOOS(), "GOARCH="+config.GOARCH(), "CGO_ENABLED="+cgoEnabled)
cmd.Run()
return nil
}
func usage() {
fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.")
fmt.Fprintln(os.Stderr, "version:", version)
fmt.Fprintln(os.Stderr, "version:", goenv.Version)
fmt.Fprintf(os.Stderr, "usage: %s command [-printir] [-target=<target>] -o <output> <input>\n", os.Args[0])
fmt.Fprintln(os.Stderr, "\ncommands:")
fmt.Fprintln(os.Stderr, " build: compile packages and dependencies")
@@ -652,12 +703,27 @@ func usage() {
fmt.Fprintln(os.Stderr, " flash: compile and flash to the device")
fmt.Fprintln(os.Stderr, " gdb: run/flash and immediately enter GDB")
fmt.Fprintln(os.Stderr, " env: list environment variables used during build")
fmt.Fprintln(os.Stderr, " list: run go list using the TinyGo root")
fmt.Fprintln(os.Stderr, " clean: empty cache directory ("+goenv.Get("GOCACHE")+")")
fmt.Fprintln(os.Stderr, " help: print this help text")
fmt.Fprintln(os.Stderr, "\nflags:")
flag.PrintDefaults()
}
// try to make the path relative to the current working directory. If any error
// occurs, this error is ignored and the absolute path is returned instead.
func tryToMakePathRelative(dir string) string {
wd, err := os.Getwd()
if err != nil {
return dir
}
relpath, err := filepath.Rel(wd, dir)
if err != nil {
return dir
}
return relpath
}
// printCompilerError prints compiler errors using the provided logger function
// (similar to fmt.Println).
//
@@ -665,8 +731,24 @@ func usage() {
// to limitations in the LLVM bindings.
func printCompilerError(logln func(...interface{}), err error) {
switch err := err.(type) {
case types.Error, scanner.Error:
case types.Error:
printCompilerError(logln, scanner.Error{
Pos: err.Fset.Position(err.Pos),
Msg: err.Msg,
})
case scanner.Error:
if !strings.HasPrefix(err.Pos.Filename, filepath.Join(goenv.Get("GOROOT"), "src")) && !strings.HasPrefix(err.Pos.Filename, filepath.Join(goenv.Get("TINYGOROOT"), "src")) {
// This file is not from the standard library (either the GOROOT or
// the TINYGOROOT). Make the path relative, for easier reading.
// Ignore any errors in the process (falling back to the absolute
// path).
err.Pos.Filename = tryToMakePathRelative(err.Pos.Filename)
}
logln(err)
case scanner.ErrorList:
for _, scannerErr := range err {
printCompilerError(logln, *scannerErr)
}
case *interp.Error:
logln("#", err.ImportPath)
logln(err.Error())
@@ -683,13 +765,13 @@ func printCompilerError(logln func(...interface{}), err error) {
}
}
case loader.Errors:
logln("#", err.Pkg.ImportPath)
logln("#", err.Pkg.PkgPath)
for _, err := range err.Errs {
logln(err)
printCompilerError(logln, err)
}
case *builder.MultiError:
for _, err := range err.Errs {
logln(err)
printCompilerError(logln, err)
}
default:
logln("error:", err)
@@ -706,11 +788,18 @@ func handleCompilerError(err error) {
}
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
usage()
os.Exit(1)
}
command := os.Args[1]
outpath := flag.String("o", "", "output filename")
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
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 (coroutines, tasks)")
scheduler := flag.String("scheduler", "", "which scheduler to use (none, coroutines, tasks)")
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")
@@ -726,12 +815,11 @@ func main() {
wasmAbi := flag.String("wasm-abi", "js", "WebAssembly ABI conventions: js (no i64 params) or generic")
heapSize := flag.String("heap-size", "1M", "default heap size in bytes (only supported by WebAssembly)")
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
usage()
os.Exit(1)
var flagJSON, flagDeps *bool
if command == "list" {
flagJSON = flag.Bool("json", false, "print data in JSON format")
flagDeps = flag.Bool("deps", false, "")
}
command := os.Args[1]
// Early command processing, before commands are interpreted by the Go flag
// library.
@@ -770,12 +858,6 @@ func main() {
options.LDFlags = strings.Split(*ldFlags, " ")
}
if *panicStrategy != "print" && *panicStrategy != "trap" {
fmt.Fprintln(os.Stderr, "Panic strategy must be either print or trap.")
usage()
os.Exit(1)
}
var err error
if options.HeapSize, err = parseSize(*heapSize); err != nil {
fmt.Fprintln(os.Stderr, "Could not read heap size:", *heapSize)
@@ -785,6 +867,13 @@ func main() {
os.Setenv("CC", "clang -target="+*target)
err = options.Verify()
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
usage()
os.Exit(1)
}
switch command {
case "build":
if *outpath == "" {
@@ -794,7 +883,7 @@ func main() {
}
pkgName := "."
if flag.NArg() == 1 {
pkgName = flag.Arg(0)
pkgName = filepath.ToSlash(flag.Arg(0))
} else if flag.NArg() > 1 {
fmt.Fprintln(os.Stderr, "build only accepts a single positional argument: package name, but multiple were specified")
usage()
@@ -803,6 +892,7 @@ func main() {
if options.Target == "" && filepath.Ext(*outpath) == ".wasm" {
options.Target = "wasm"
}
err := Build(pkgName, *outpath, options)
handleCompilerError(err)
case "build-library":
@@ -839,8 +929,9 @@ func main() {
usage()
os.Exit(1)
}
pkgName := filepath.ToSlash(flag.Arg(0))
if command == "flash" {
err := Flash(flag.Arg(0), *port, options)
err := Flash(pkgName, *port, options)
handleCompilerError(err)
} else {
if !options.Debug {
@@ -848,7 +939,7 @@ func main() {
usage()
os.Exit(1)
}
err := FlashGDB(flag.Arg(0), *ocdOutput, options)
err := FlashGDB(pkgName, *ocdOutput, options)
handleCompilerError(err)
}
case "run":
@@ -857,12 +948,13 @@ func main() {
usage()
os.Exit(1)
}
err := Run(flag.Arg(0), options)
pkgName := filepath.ToSlash(flag.Arg(0))
err := Run(pkgName, options)
handleCompilerError(err)
case "test":
pkgName := "."
if flag.NArg() == 1 {
pkgName = flag.Arg(0)
pkgName = filepath.ToSlash(flag.Arg(0))
} else if flag.NArg() > 1 {
fmt.Fprintln(os.Stderr, "test only accepts a single positional argument: package name, but multiple were specified")
usage()
@@ -895,6 +987,18 @@ func main() {
fmt.Printf("build tags: %s\n", strings.Join(config.BuildTags(), " "))
fmt.Printf("garbage collector: %s\n", config.GC())
fmt.Printf("scheduler: %s\n", config.Scheduler())
case "list":
config, err := builder.NewConfig(options)
if err != nil {
fmt.Fprintln(os.Stderr, err)
usage()
os.Exit(1)
}
err = runGoList(config, *flagJSON, *flagDeps, flag.Args())
if err != nil {
fmt.Fprintln(os.Stderr, "failed to run `go list`:", err)
os.Exit(1)
}
case "clean":
// remove cache directory
err := os.RemoveAll(goenv.Get("GOCACHE"))
@@ -906,10 +1010,10 @@ func main() {
usage()
case "version":
goversion := "<unknown>"
if s, err := builder.GorootVersionString(goenv.Get("GOROOT")); err == nil {
if s, err := goenv.GorootVersionString(goenv.Get("GOROOT")); err == nil {
goversion = s
}
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", version, runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
fmt.Printf("tinygo version %s %s/%s (using go version %s and LLVM version %s)\n", goenv.Version, runtime.GOOS, runtime.GOARCH, goversion, llvm.Version)
case "env":
if flag.NArg() == 0 {
// Show all environment variables.
+1 -1
View File
@@ -73,7 +73,7 @@ func TestCompiler(t *testing.T) {
t.Run("ARM64Linux", func(t *testing.T) {
runPlatTests("aarch64--linux-gnu", matches, t)
})
goVersion, err := builder.GorootVersionString(goenv.Get("GOROOT"))
goVersion, err := goenv.GorootVersionString(goenv.Get("GOROOT"))
if err != nil {
t.Error("could not get Go version:", err)
return
+13 -15
View File
@@ -52,11 +52,10 @@ func Asm(asm string)
// "value": 1
// "result": &dest,
// })
func AsmFull(asm string, regs map[string]interface{})
// ReadRegister returns the contents of the specified register. The register
// must be a processor register, reachable with the "mov" instruction.
func ReadRegister(name string) uintptr
//
// You can use {} in the asm string (which expands to a register) to set the
// return value.
func AsmFull(asm string, regs map[string]interface{}) uintptr
// Run the following system call (SVCall) with 0 arguments.
func SVCall0(num uintptr) uintptr
@@ -192,22 +191,21 @@ func SetPriority(irq uint32, priority uint32) {
NVIC.IPR[regnum].Set((uint32(NVIC.IPR[regnum].Get()) &^ mask) | priority)
}
// DisableInterrupts disables all interrupts, and returns the old state.
//
// TODO: it doesn't actually return the old state, meaning that it cannot be
// nested.
// DisableInterrupts disables all interrupts, and returns the old interrupt
// state.
func DisableInterrupts() uintptr {
Asm("cpsid if")
return 0
return AsmFull(`
mrs {}, PRIMASK
cpsid i
`, nil)
}
// EnableInterrupts enables all interrupts again. The value passed in must be
// the mask returned by DisableInterrupts.
//
// TODO: it doesn't actually use the old state, meaning that it cannot be
// nested.
func EnableInterrupts(mask uintptr) {
Asm("cpsie if")
AsmFull("msr PRIMASK, {mask}", map[string]interface{}{
"mask": mask,
})
}
// SystemReset performs a hard system reset.
+21
View File
@@ -0,0 +1,21 @@
package device
// Run the given assembly code. The code will be marked as having side effects,
// as it doesn't produce output and thus would normally be eliminated by the
// optimizer.
func Asm(asm string)
// Run the given inline assembly. The code will be marked as having side
// effects, as it would otherwise be optimized away. The inline assembly string
// recognizes template values in the form {name}, like so:
//
// arm.AsmFull(
// "str {value}, {result}",
// map[string]interface{}{
// "value": 1
// "result": &dest,
// })
//
// You can use {} in the asm string (which expands to a register) to set the
// return value.
func AsmFull(asm string, regs map[string]interface{}) uintptr
+4 -1
View File
@@ -15,4 +15,7 @@ func Asm(asm string)
// "value": 1
// "result": &dest,
// })
func AsmFull(asm string, regs map[string]interface{})
//
// You can use {} in the asm string (which expands to a register) to set the
// return value.
func AsmFull(asm string, regs map[string]interface{}) uintptr
+30 -3
View File
@@ -5,6 +5,33 @@ package riscv
// optimizer.
func Asm(asm string)
// ReadRegister returns the contents of the specified register. The register
// must be a processor register, reachable with the "mov" instruction.
func ReadRegister(name string) uintptr
// Run the given inline assembly. The code will be marked as having side
// effects, as it would otherwise be optimized away. The inline assembly string
// recognizes template values in the form {name}, like so:
//
// arm.AsmFull(
// "st {value}, {result}",
// map[string]interface{}{
// "value": 1
// "result": &dest,
// })
//
// You can use {} in the asm string (which expands to a register) to set the
// return value.
func AsmFull(asm string, regs map[string]interface{}) uintptr
// DisableInterrupts disables all interrupts, and returns the old interrupt
// state.
func DisableInterrupts() uintptr {
// Note: this can be optimized with a CSRRW instruction, which atomically
// swaps the value and returns the old value.
mask := MIE.Get()
MIE.Set(0)
return mask
}
// EnableInterrupts enables all interrupts again. The value passed in must be
// the mask returned by DisableInterrupts.
func EnableInterrupts(mask uintptr) {
MIE.Set(mask)
}
+2 -4
View File
@@ -5,16 +5,14 @@ import (
"time"
)
// This example assumes that the button is connected to pin 8. Change the value
// below to use a different pin.
const (
led = machine.LED
button = machine.Pin(8)
button = machine.BUTTON
)
func main() {
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
button.Configure(machine.PinConfig{Mode: machine.PinInput})
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
for {
if button.Get() {
@@ -0,0 +1,10 @@
// +build circuitplay_express
package main
import "machine"
const (
buttonMode = machine.PinInputPulldown
buttonPinChange = machine.PinFalling
)
+10
View File
@@ -0,0 +1,10 @@
// +build pca10040
package main
import "machine"
const (
buttonMode = machine.PinInputPullup
buttonPinChange = machine.PinRising
)
+52
View File
@@ -0,0 +1,52 @@
package main
// This example demonstrates how to use pin change interrupts.
//
// This is only an example and should not be copied directly in any serious
// circuit, because it lacks an important feature: debouncing.
// See: https://en.wikipedia.org/wiki/Switch#Contact_bounce
import (
"machine"
"runtime/volatile"
"time"
)
const (
button = machine.BUTTON
led = machine.LED
)
func main() {
var lightLed volatile.Register8
lightLed.Set(0)
// Configure the LED, defaulting to on (usually setting the pin to low will
// turn the LED on).
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
led.Low()
// Make sure the pin is configured as a pullup to avoid floating inputs.
// Pullup works for most buttons, as most buttons short to ground when
// pressed.
button.Configure(machine.PinConfig{Mode: buttonMode})
// Set an interrupt on this pin.
err := button.SetInterrupt(buttonPinChange, func(machine.Pin) {
if lightLed.Get() != 0 {
lightLed.Set(0)
led.Low()
} else {
lightLed.Set(1)
led.High()
}
})
if err != nil {
println("could not configure pin interrupt:", err.Error())
}
// Make sure the program won't exit.
for {
time.Sleep(time.Hour)
}
}
+10
View File
@@ -0,0 +1,10 @@
// +build wioterminal
package main
import "machine"
const (
buttonMode = machine.PinInput
buttonPinChange = machine.PinFalling
)
+16 -6
View File
@@ -8,9 +8,9 @@ import (
// This example assumes that an RGB LED is connected to pins 3, 5 and 6 on an Arduino.
// Change the values below to use different pins.
const (
redPin = 3
greenPin = 5
bluePin = 6
redPin = machine.D3
greenPin = machine.D5
bluePin = machine.D6
)
// cycleColor is just a placeholder until math/rand or some equivalent is working.
@@ -28,13 +28,16 @@ func main() {
machine.InitPWM()
red := machine.PWM{redPin}
red.Configure()
err := red.Configure()
checkError(err, "failed to configure red pin")
green := machine.PWM{greenPin}
green.Configure()
err = green.Configure()
checkError(err, "failed to configure green pin")
blue := machine.PWM{bluePin}
blue.Configure()
err = blue.Configure()
checkError(err, "failed to configure blue pin")
var rc uint8
var gc uint8 = 20
@@ -52,3 +55,10 @@ func main() {
time.Sleep(time.Millisecond * 500)
}
}
func checkError(err error, msg string) {
if err != nil {
print(msg, ": ", err.Error())
println()
}
}
+126
View File
@@ -0,0 +1,126 @@
package bytealg
const (
// Index can search any valid length of string.
MaxLen = int(-1) >> 31
MaxBruteForce = MaxLen
)
// Compare two byte slices.
// Returns -1 if the first differing byte is lower in a, or 1 if the first differing byte is greater in b.
// If the byte slices are equal, returns 0.
// If the lengths are different and there are no differing bytes, compares based on length.
func Compare(a, b []byte) int {
// Compare for differing bytes.
for i := 0; i < len(a) && i < len(b); i++ {
switch {
case a[0] < b[0]:
return -1
case a[0] > b[0]:
return 1
}
}
// Compare lengths.
switch {
case len(a) > len(b):
return 1
case len(a) < len(b):
return -1
default:
return 0
}
}
// Count the number of instances of a byte in a slice.
func Count(b []byte, c byte) int {
// Use a simple implementation, as there is no intrinsic that does this like we want.
n := 0
for _, v := range b {
if v == c {
n++
}
}
return n
}
// Count the number of instances of a byte in a string.
func CountString(s string, c byte) int {
// Use a simple implementation, as there is no intrinsic that does this like we want.
// Currently, the compiler does not generate zero-copy byte-string conversions, so this needs to be seperate from Count.
n := 0
for i := 0; i < len(s); i++ {
if s[i] == c {
n++
}
}
return n
}
// Cutover is not reachable in TinyGo, but must exist as it is referenced.
func Cutover(n int) int {
// Setting MaxLen and MaxBruteForce should force a different path to be taken.
// This should never be called.
panic("cutover is unreachable")
}
// Equal checks if two byte slices are equal.
// It is equivalent to bytes.Equal.
func Equal(a, b []byte) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
// Index finds the base index of the first instance of the byte sequence b in a.
// If a does not contain b, this returns -1.
func Index(a, b []byte) int {
for i := 0; i <= len(a)-len(b); i++ {
if Equal(a[i:i+len(b)], b) {
return i
}
}
return -1
}
// Index finds the index of the first instance of the specified byte in the slice.
// If the byte is not found, this returns -1.
func IndexByte(b []byte, c byte) int {
for i, v := range b {
if v == c {
return i
}
}
return -1
}
// Index finds the index of the first instance of the specified byte in the string.
// If the byte is not found, this returns -1.
func IndexByteString(s string, c byte) int {
for i := 0; i < len(s); i++ {
if s[i] == c {
return i
}
}
return -1
}
// Index finds the base index of the first instance of a substring in a string.
// If the substring is not found, this returns -1.
func IndexString(str, sub string) int {
for i := 0; i <= len(str)-len(sub); i++ {
if str[i:i+len(sub)] == sub {
return i
}
}
return -1
}
+1 -1
View File
@@ -68,8 +68,8 @@ func (s *Stack) Pop() *Task {
t := s.top
if t != nil {
s.top = t.Next
t.Next = nil
}
t.Next = nil
return t
}
+1 -1
View File
@@ -4,7 +4,7 @@ package task
import "unsafe"
const stackSize = 256
const stackSize = 1024
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see scheduler_avr.S that relies on the
+27 -9
View File
@@ -7,21 +7,39 @@ func CPUFrequency() uint32 {
return 16000000
}
// Digital pins, marked as plain numbers on the board.
const (
D0 = PD0 // RX
D1 = PD1 // TX
D2 = PD2
D3 = PD3
D4 = PD4
D5 = PD5
D6 = PD6
D7 = PD7
D8 = PB0
D9 = PB1
D10 = PB2
D11 = PB3
D12 = PB4
D13 = PB5
)
// LED on the Arduino
const LED Pin = 13
const LED Pin = D13
// ADC on the Arduino
const (
ADC0 Pin = 0
ADC1 Pin = 1
ADC2 Pin = 2
ADC3 Pin = 3
ADC4 Pin = 4 // Used by TWI for SDA
ADC5 Pin = 5 // Used by TWI for SCL
ADC0 Pin = PC0
ADC1 Pin = PC1
ADC2 Pin = PC2
ADC3 Pin = PC3
ADC4 Pin = PC4 // Used by TWI for SDA
ADC5 Pin = PC5 // Used by TWI for SCL
)
// UART pins
const (
UART_TX_PIN Pin = 1
UART_RX_PIN Pin = 0
UART_TX_PIN Pin = PD1
UART_RX_PIN Pin = PD0
)
+27 -9
View File
@@ -7,21 +7,39 @@ func CPUFrequency() uint32 {
return 16000000
}
// Digital pins.
const (
D0 = PD0 // RX0
D1 = PD1 // TX1
D2 = PD2
D3 = PD3
D4 = PD4
D5 = PD5
D6 = PD6
D7 = PD7
D8 = PB0
D9 = PB1
D10 = PB2
D11 = PB3
D12 = PB4
D13 = PB5
)
// LED on the Arduino
const LED Pin = 13
const LED Pin = D13
// ADC on the Arduino
const (
ADC0 Pin = 0
ADC1 Pin = 1
ADC2 Pin = 2
ADC3 Pin = 3
ADC4 Pin = 4 // Used by TWI for SDA
ADC5 Pin = 5 // Used by TWI for SCL
ADC0 Pin = PC0
ADC1 Pin = PC1
ADC2 Pin = PC2
ADC3 Pin = PC3
ADC4 Pin = PC4 // Used by TWI for SDA
ADC5 Pin = PC5 // Used by TWI for SCL
)
// UART pins
const (
UART_TX_PIN Pin = 1
UART_RX_PIN Pin = 0
UART_TX_PIN Pin = PD1
UART_RX_PIN Pin = PD0
)
+37
View File
@@ -0,0 +1,37 @@
// +build avr,atmega328p arduino arduino_nano
package machine
const (
// Note: start at port B because there is no port A.
portB Pin = iota * 8
portC
portD
)
const (
PB0 = portB + 0
PB1 = portB + 1
PB2 = portB + 2
PB3 = portB + 3
PB4 = portB + 4
PB5 = portB + 5
PB6 = portB + 6
PB7 = portB + 7
PC0 = portC + 0
PC1 = portC + 1
PC2 = portC + 2
PC3 = portC + 3
PC4 = portC + 4
PC5 = portC + 5
PC6 = portC + 6
PC7 = portC + 7
PD0 = portD + 0
PD1 = portD + 1
PD2 = portD + 2
PD3 = portD + 3
PD4 = portD + 4
PD5 = portD + 5
PD6 = portD + 6
PD7 = portD + 7
)
+1 -21
View File
@@ -1,9 +1,7 @@
// +build sam,atsamd51,feather_m4
// +build feather_m4
package machine
import "device/sam"
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
@@ -47,13 +45,11 @@ const (
USBCDC_DP_PIN = PA25
)
// UART1 pins
const (
UART_TX_PIN = D1
UART_RX_PIN = D0
)
// UART2 pins
const (
UART2_TX_PIN = A4
UART2_RX_PIN = A5
@@ -65,14 +61,6 @@ const (
SCL_PIN = D21 // SCL: SERCOM2/PAD[1]
)
// I2C on the Feather M4.
var (
I2C0 = I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI pins
const (
SPI0_SCK_PIN = D25 // SCK: SERCOM1/PAD[1]
@@ -80,14 +68,6 @@ const (
SPI0_MISO_PIN = D23 // MISO: SERCOM1/PAD[2]
)
// SPI on the Feather M4.
var (
SPI0 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Adafruit Feather M4"
+43
View File
@@ -0,0 +1,43 @@
// +build sam,atsamd51,feather_m4
package machine
import (
"device/sam"
"runtime/interrupt"
)
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM5_USART_INT,
SERCOM: 5,
}
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT,
SERCOM: 0,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
}
// I2C on the Feather M4.
var (
I2C0 = I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI on the Feather M4.
var (
SPI0 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
+2 -2
View File
@@ -37,8 +37,8 @@ const (
const (
// TODO: figure out the pin numbers for these.
UART_TX_PIN = NoPin
UART_RX_PIN = NoPin
UART_TX_PIN = D1
UART_RX_PIN = D0
)
// SPI pins
+1 -24
View File
@@ -1,9 +1,7 @@
// +build sam,atsamd51,itsybitsy_m4
// +build itsybitsy_m4
package machine
import "device/sam"
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
@@ -51,30 +49,17 @@ const (
UART_RX_PIN = D0
)
// UART1 var is on SERCOM3, defined in atsamd51.go
// UART2 pins
const (
UART2_TX_PIN = A4
UART2_RX_PIN = D2
)
// UART2 var is on SERCOM0, defined in atsamd51.go
// I2C pins
const (
SDA_PIN = PA12 // SDA: SERCOM2/PAD[0]
SCL_PIN = PA13 // SCL: SERCOM2/PAD[1]
)
// I2C on the ItsyBitsy M4.
var (
I2C0 = I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI pins
const (
SPI0_SCK_PIN = PA01 // SCK: SERCOM1/PAD[1]
@@ -82,14 +67,6 @@ const (
SPI0_MISO_PIN = PB23 // MISO: SERCOM1/PAD[3]
)
// SPI on the ItsyBitsy M4.
var (
SPI0 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Adafruit ItsyBitsy M4"
@@ -0,0 +1,43 @@
// +build sam,atsamd51,itsybitsy_m4
package machine
import (
"device/sam"
"runtime/interrupt"
)
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM3_USART_INT,
SERCOM: 3,
}
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT,
SERCOM: 0,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM3_2, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
}
// I2C on the ItsyBitsy M4.
var (
I2C0 = I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI on the ItsyBitsy M4.
var (
SPI0 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
+5 -32
View File
@@ -1,9 +1,7 @@
// +build sam,atsamd51,metro_m4_airlift
// +build metro_m4_airlift
package machine
import "device/sam"
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
@@ -53,7 +51,10 @@ const (
UART_RX_PIN = D0
)
// Note: UART1 is on SERCOM3, defined in machine_atsamd51.go
const (
UART2_TX_PIN = PA04
UART2_RX_PIN = PA07
)
const (
NINA_CS = PA15
@@ -66,23 +67,12 @@ const (
NINA_RTS = PB23
)
// UART2 is on SERCOM0, defined in machine_atsamd51.go, and connects to the
// onboard ESP32-WROOM chip.
// I2C pins
const (
SDA_PIN = PB02 // SDA: SERCOM5/PAD[0]
SCL_PIN = PB03 // SCL: SERCOM5/PAD[1]
)
// I2C on the Metro M4.
var (
I2C0 = I2C{
Bus: sam.SERCOM5_I2CM,
SERCOM: 5,
}
)
// SPI pins
const (
SPI0_SCK_PIN = PA13 // SCK: SERCOM2/PAD[1]
@@ -94,29 +84,12 @@ const (
NINA_SCK = SPI0_SCK_PIN
)
// SPI on the Metro M4.
var (
SPI0 = SPI{
Bus: sam.SERCOM2_SPIM,
SERCOM: 2,
}
NINA_SPI = SPI0
)
const (
SPI1_SCK_PIN = D12 // MISO: SERCOM1/PAD[1]
SPI1_MOSI_PIN = D11 // MOSI: SERCOM1/PAD[3]
SPI1_MISO_PIN = D13 // SCK: SERCOM1/PAD[0]
)
// SPI1 on the Metro M4 on pins 11,12,13
var (
SPI1 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Adafruit Metro M4 Airlift Lite"
@@ -0,0 +1,52 @@
// +build sam,atsamd51,metro_m4_airlift
package machine
import (
"device/sam"
"runtime/interrupt"
)
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM3_USART_INT,
SERCOM: 3,
}
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT,
SERCOM: 0,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM3_2, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
}
// I2C on the Metro M4.
var (
I2C0 = I2C{
Bus: sam.SERCOM5_I2CM,
SERCOM: 5,
}
)
// SPI on the Metro M4.
var (
SPI0 = SPI{
Bus: sam.SERCOM2_SPIM,
SERCOM: 2,
}
NINA_SPI = SPI0
)
// SPI1 on the Metro M4 on pins 11,12,13
var (
SPI1 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
+1 -32
View File
@@ -1,9 +1,7 @@
// +build sam,atsamd51,pybadge
// +build pybadge
package machine
import "device/sam"
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
@@ -80,30 +78,17 @@ const (
UART_RX_PIN = D0
)
// UART1 var is on SERCOM3, defined in atsamd51.go
// UART2 pins
const (
UART2_TX_PIN = A4
UART2_RX_PIN = A5
)
// UART2 var is on SERCOM0, defined in atsamd51.go
// I2C pins
const (
SDA_PIN = PA12 // SDA: SERCOM2/PAD[0]
SCL_PIN = PA13 // SCL: SERCOM2/PAD[1]
)
// I2C on the ItsyBitsy M4.
var (
I2C0 = I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI pins
const (
SPI0_SCK_PIN = PA17 // SCK: SERCOM1/PAD[1]
@@ -111,14 +96,6 @@ const (
SPI0_MISO_PIN = PB22 // MISO: SERCOM1/PAD[2]
)
// SPI on the PyBadge.
var (
SPI0 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
// TFT SPI pins
const (
SPI1_SCK_PIN = PB13 // SCK: SERCOM4/PAD[1]
@@ -126,14 +103,6 @@ const (
SPI1_MISO_PIN = NoPin
)
// TFT SPI on the PyBadge.
var (
SPI1 = SPI{
Bus: sam.SERCOM4_SPIM,
SERCOM: 4,
}
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Adafruit pyBadge M4"
+51
View File
@@ -0,0 +1,51 @@
// +build sam,atsamd51,pybadge
package machine
import (
"device/sam"
"runtime/interrupt"
)
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM5_USART_INT,
SERCOM: 5,
}
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT,
SERCOM: 0,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM5_2, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
}
// I2C on the ItsyBitsy M4.
var (
I2C0 = I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI on the PyBadge.
var (
SPI0 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
// TFT SPI on the PyBadge.
var (
SPI1 = SPI{
Bus: sam.SERCOM4_SPIM,
SERCOM: 4,
}
)
+147
View File
@@ -0,0 +1,147 @@
// +build sam,atsamd51,pygamer
package machine
import "device/sam"
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
// GPIO Pins
const (
D0 = PB17 // UART0 RX/PWM available
D1 = PB16 // UART0 TX/PWM available
D2 = PB03
D3 = PB02
D4 = PA14 // PWM available
D5 = PA16 // PWM available
D6 = PA18 // PWM available
D7 = PB14 // CS for microSD card slot
D8 = PA15 // built-in neopixel
D9 = PA19 // PWM available
D10 = PA20 // can be used for PWM or UART1 TX
D11 = PA21 // can be used for PWM or UART1 RX
D12 = PA22 // PWM available
D13 = PA23 // PWM available
)
// Analog pins
const (
A0 = PA02 // ADC/AIN[0]
A1 = PA05 // ADC/AIN[2]
A2 = PB08 // ADC/AIN[3]
A3 = PB09 // ADC/AIN[4]
A4 = PA04 // ADC/AIN[5]
A5 = PA06 // ADC/AIN[6]
A6 = PB01 // ADC/AIN[12]/VMEAS
A7 = PB04 // ADC/AIN[6]/LIGHT
A8 = D2 // ADC/AIN[14]
A9 = D3 // ADC/AIN[15]
)
const (
LED = D13
NEOPIXELS = D8
SD_CS = D7
LIGHTSENSOR = A7
BUTTON_LATCH = PB00
BUTTON_OUT = PB30
BUTTON_CLK = PB31
JOYY = PB06
JOYX = PB07
TFT_DC = PB05
TFT_CS = PB12
TFT_RST = PA00
TFT_LITE = PA01
SPEAKER_ENABLE = PA27
)
const (
BUTTON_SELECT_MASK = 16
BUTTON_START_MASK = 32
BUTTON_A_MASK = 64
BUTTON_B_MASK = 128
)
// UART0 aka USBCDC pins
const (
USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25
)
// UART1 pins
const (
UART_TX_PIN = D1
UART_RX_PIN = D0
)
// UART1 var is on SERCOM3, defined in atsamd51.go
// UART2 pins
const (
UART2_TX_PIN = A4
UART2_RX_PIN = A5
)
// UART2 var is on SERCOM0, defined in atsamd51.go
// I2C pins
const (
SDA_PIN = PA12 // SDA: SERCOM2/PAD[0]
SCL_PIN = PA13 // SCL: SERCOM2/PAD[1]
)
// I2C on the PyGamer.
var (
I2C0 = I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI pins
const (
SPI0_SCK_PIN = PA17 // SCK: SERCOM1/PAD[1]
SPI0_MOSI_PIN = PB23 // MOSI: SERCOM1/PAD[3]
SPI0_MISO_PIN = PB22 // MISO: SERCOM1/PAD[2]
)
// SPI on the PyGamer.
var (
SPI0 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
// TFT SPI pins
const (
SPI1_SCK_PIN = PB13 // SCK: SERCOM4/PAD[1]
SPI1_MOSI_PIN = PB15 // MOSI: SERCOM4/PAD[3]
SPI1_MISO_PIN = NoPin
)
// TFT SPI on the PyGamer.
var (
SPI1 = SPI{
Bus: sam.SERCOM4_SPIM,
SERCOM: 4,
}
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Adafruit pyGamer M4"
usb_STRING_MANUFACTURER = "Adafruit"
)
var (
usb_VID uint16 = 0x239A
usb_PID uint16 = 0x8033
)
+4 -25
View File
@@ -1,11 +1,7 @@
// +build sam,atsamd51,pyportal
// +build pyportal
package machine
import (
"device/sam"
)
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
@@ -104,10 +100,10 @@ const (
USBCDC_DP_PIN = PA25
)
// TODO: add configuration for UART on SERCOM4 that is connected to TX/RX of ESP32
// UART1 aka NINA_TX/NINA_RX
const (
UART_TX_PIN = NoPin
UART_RX_PIN = NoPin
UART_TX_PIN = D1
UART_RX_PIN = D0
)
// I2C pins
@@ -116,14 +112,6 @@ const (
SCL_PIN = PB03 // SCL: SERCOM2/PAD[1]
)
// I2C on the PyPortal.
var (
I2C0 = I2C{
Bus: sam.SERCOM5_I2CM,
SERCOM: 5,
}
)
// SPI pins
const (
SPI0_SCK_PIN = PA13 // SCK: SERCOM1/PAD[1]
@@ -135,15 +123,6 @@ const (
NINA_SCK = SPI0_SCK_PIN
)
// SPI on the PyPortal.
var (
SPI0 = SPI{
Bus: sam.SERCOM2_SPIM,
SERCOM: 2,
}
NINA_SPI = SPI0
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Adafruit PyPortal M4"
+37
View File
@@ -0,0 +1,37 @@
// +build sam,atsamd51,pyportal
package machine
import (
"device/sam"
"runtime/interrupt"
)
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM4_USART_INT,
SERCOM: 4,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM4_2, UART1.handleInterrupt)
}
// I2C on the PyPortal.
var (
I2C0 = I2C{
Bus: sam.SERCOM5_I2CM,
SERCOM: 5,
}
)
// SPI on the PyPortal.
var (
SPI0 = SPI{
Bus: sam.SERCOM2_SPIM,
SERCOM: 2,
}
NINA_SPI = SPI0
)
+392
View File
@@ -0,0 +1,392 @@
// +build wioterminal
package machine
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
const (
ADC0 = A0
ADC1 = A1
ADC2 = A2
ADC3 = A3
ADC4 = A4
ADC5 = A5
ADC6 = A6
ADC7 = A7
ADC8 = A8
LED = PIN_LED
BUTTON = BUTTON_1
)
const (
// https://github.com/Seeed-Studio/ArduinoCore-samd/blob/master/variants/wio_terminal/variant.h
// LEDs
PIN_LED_13 = PA15
PIN_LED_RXL = PA15
PIN_LED_TXL = PA15
PIN_LED = PIN_LED_13
PIN_LED2 = PIN_LED_RXL
PIN_LED3 = PIN_LED_TXL
LED_BUILTIN = PIN_LED_13
PIN_NEOPIXEL = PA15
//Digital PINs
D0 = PB08
D1 = PB09
D2 = PA07
D3 = PB04
D4 = PB05
D5 = PB06
D6 = PA04
D7 = PB07
D8 = PA06
//Analog PINs
A0 = PB08 // ADC/AIN[0]
A1 = PB09 // ADC/AIN[2]
A2 = PA07 // ADC/AIN[3]
A3 = PB04 // ADC/AIN[4]
A4 = PB05 // ADC/AIN[5]
A5 = PB06 // ADC/AIN[10]
A6 = PA04 // ADC/AIN[10]
A7 = PB07 // ADC/AIN[10]
A8 = PA06 // ADC/AIN[10]
// 3.3V || 5V
// BCM2 || 5V
// BCM3 || GND
// BCM4 || BCM14
// GND || BCM15
// BCM17 || BCM18
// BCM27 || GND
// BCM22 || BCM23
// GND || BCM24
// BCM10 || GND
// BCM9 || BCM25
// BCM11 || BCM8
// GND || BCM7
// BCM0 || BCM1
// BCM5 || GND
// BCM6 || BCM12
// BCM13 || GND
// BCM19 || BCM16
// BCM26 || BCM20
// GND || BCM21
//PIN DEFINE FOR RPI
BCM0 = PA13 // I2C Wire1
BCM1 = PA12 // I2C Wire1
BCM2 = PA17 // I2C Wire2
BCM3 = PA16 // I2C Wire2
BCM4 = PB14 // GCLK
BCM5 = PB12 // GCLK
BCM6 = PB13 // GCLK
BCM7 = PA05 // DAC1
BCM8 = PB01 // SPI SS
BCM9 = PB00 // SPI MISO
BCM10 = PB02 // SPI MOSI
BCM11 = PB03 // SPI SCK
BCM12 = PB06
BCM13 = PA07
BCM14 = PB27 // UART Serial1
BCM15 = PB26 // UART Serial1
BCM16 = PB07
BCM17 = PA02 // DAC0
BCM18 = PB28 // FPC Digital & AD pins
BCM19 = PA20 // WIO_IR
BCM20 = PA21 // I2S SDO
BCM21 = PA22 // I2S SDI
BCM22 = PB09
BCM23 = PA07
BCM24 = PB04
BCM25 = PB05
BCM26 = PA06
BCM27 = PB08
// FPC NEW DEFINE
FPC1 = PB28 // FPC Digital & AD pins
FPC2 = PB17
FPC3 = PB29
FPC4 = PA14
FPC5 = PC01
FPC6 = PC02
FPC7 = PC03
FPC8 = PC04
FPC9 = PC31
FPC10 = PD00
// RPI Analog RPIs
RPI_A0 = PB08
RPI_A1 = PB09
RPI_A2 = PA07
RPI_A3 = PB04
RPI_A4 = PB05
RPI_A5 = PB06
RPI_A6 = PA04
RPI_A7 = PB07
RPI_A8 = PA06
PIN_DAC0 = PA02
PIN_DAC1 = PA05
DAC0 = PIN_DAC0
DAC1 = PIN_DAC1
// FPO Analog RPIs
//FPC_A7 = FPC_D7
//FPC_A8 = FPC_D8
//FPC_A9 = FPC_D9
//FPC_A11 = FPC_D11
//FPC_A12 = FPC_D12
//FPC_A13 = FPC_D13
// USB
PIN_USB_DM = PA24
PIN_USB_DP = PA25
PIN_USB_HOST_ENABLE = PA27
// BUTTON
BUTTON_1 = PC26
BUTTON_2 = PC27
BUTTON_3 = PC28
WIO_KEY_A = PC26
WIO_KEY_B = PC27
WIO_KEY_C = PC28
// SWITCH
SWITCH_X = PD20
SWITCH_Y = PD12
SWITCH_Z = PD09
SWITCH_B = PD08
SWITCH_U = PD10
WIO_5S_UP = PD20
WIO_5S_LEFT = PD12
WIO_5S_RIGHT = PD09
WIO_5S_DOWN = PD08
WIO_5S_PRESS = PD10
// IRQ0 : RTL8720D
IRQ0 = PC20
// BUZZER_CTR
BUZZER_CTR = PD11
WIO_BUZZER = PD11
// MIC_INPUT
MIC_INPUT = PC30
WIO_MIC = PC30
// GCLK
GCLK0 = PB14
GCLK1 = PB12
GCLK2 = PB13
// Serial interfaces
// Serial1
PIN_SERIAL1_RX = PB27
PIN_SERIAL1_TX = PB26
// Serial2 : RTL8720D
PIN_SERIAL2_RX = PC23
PIN_SERIAL2_TX = PC22
// Wire Interfaces
// I2C Wire2
// I2C1
PIN_WIRE_SDA = PA17
PIN_WIRE_SCL = PA16
SDA = PIN_WIRE_SDA
SCL = PIN_WIRE_SCL
// I2C Wire1
// I2C0 : LIS3DHTR and ATECC608
PIN_WIRE1_SDA = PA13
PIN_WIRE1_SCL = PA12
SDA1 = PIN_WIRE1_SDA
SCL1 = PIN_WIRE1_SCL
PIN_GYROSCOPE_WIRE_SDA = PIN_WIRE1_SDA
PIN_GYROSCOPE_WIRE_SCL = PIN_WIRE1_SCL
GYROSCOPE_INT1 = PC21
WIO_LIS3DH_SDA = PIN_WIRE1_SDA
WIO_LIS3DH_SCL = PIN_WIRE1_SCL
WIO_LIS3DH_INT = PC21
// SPI
PIN_SPI_MISO = PB00
PIN_SPI_MOSI = PB02
PIN_SPI_SCK = PB03
PIN_SPI_SS = PB01
SS = PIN_SPI_SS
MOSI = PIN_SPI_MOSI
MISO = PIN_SPI_MISO
SCK = PIN_SPI_SCK
// SPI1 RTL8720D_SPI
PIN_SPI1_MISO = PC24
PIN_SPI1_MOSI = PB24
PIN_SPI1_SCK = PB25
PIN_SPI1_SS = PC25
SS1 = PIN_SPI1_SS
MOSI1 = PIN_SPI1_MOSI
MISO1 = PIN_SPI1_MISO
SCK1 = PIN_SPI1_SCK
// SPI2 SD_SPI
PIN_SPI2_MISO = PC18
PIN_SPI2_MOSI = PC16
PIN_SPI2_SCK = PC17
PIN_SPI2_SS = PC19
SS2 = PIN_SPI2_SS
MOSI2 = PIN_SPI2_MOSI
MISO2 = PIN_SPI2_MISO
SCK2 = PIN_SPI2_SCK
// SPI3 LCD_SPI
PIN_SPI3_MISO = PB18
PIN_SPI3_MOSI = PB19
PIN_SPI3_SCK = PB20
PIN_SPI3_SS = PB21
SS3 = PIN_SPI3_SS
MOSI3 = PIN_SPI3_MOSI
MISO3 = PIN_SPI3_MISO
SCK3 = PIN_SPI3_SCK
// Needed for SD library
SDCARD_MISO_PIN = PIN_SPI2_MISO
SDCARD_MOSI_PIN = PIN_SPI2_MOSI
SDCARD_SCK_PIN = PIN_SPI2_SCK
SDCARD_SS_PIN = PIN_SPI2_SS
SDCARD_DET_PIN = PD21
LCD_MISO_PIN = PIN_SPI3_MISO
LCD_MOSI_PIN = PIN_SPI3_MOSI
LCD_SCK_PIN = PIN_SPI3_SCK
LCD_SS_PIN = PIN_SPI3_SS
LCD_DC = PC06
LCD_RESET = PC07
LCD_BACKLIGHT = PC05
// 4 WIRE LCD TOUCH
LCD_XL = PC10
LCD_YU = PC11
LCD_XR = PC12
LCD_YD = PC13
// Needed for RTL8720D
RTL8720D_MISO_PIN = PIN_SPI1_MISO
RTL8720D_MOSI_PIN = PIN_SPI1_MOSI
RTL8720D_SCK_PIN = PIN_SPI1_SCK
RTL8720D_SS_PIN = PIN_SPI1_SS
//QSPI Pins
PIN_QSPI_IO0 = PA08
PIN_QSPI_IO1 = PA09
PIN_QSPI_IO2 = PA10
PIN_QSPI_IO3 = PA11
PIN_QSPI_SCK = PB10
PIN_QSPI_CS = PB11
// I2S Interfaces
PIN_I2S_FS = PA20
PIN_I2S_SCK = PB16
PIN_I2S_SDO = PA22
PIN_I2S_SDI = PA21
I2S_LRCLK = PA20
I2S_BLCK = PB16
I2S_SDOUT = PA22
I2S_SDIN = PA21
// RTL8720D Interfaces
RTL8720D_CHIP_PU = PA18
RTL8720D_GPIO0 = PA19 // SYNC
// SWD
SWDCLK = PA30
SWDIO = PA31
SWO = PB30
// light sensor
WIO_LIGHT = PD01
// ir sensor
WIO_IR = PB31
// OUTPUT_CTR
OUTPUT_CTR_5V = PC14
OUTPUT_CTR_3V3 = PC15
)
// UART0 aka USBCDC pins
const (
USBCDC_DM_PIN = PIN_USB_DM
USBCDC_DP_PIN = PIN_USB_DP
)
// UART1 pins
const (
UART_TX_PIN = PIN_SERIAL1_TX
UART_RX_PIN = PIN_SERIAL1_RX
)
// UART2 pins RTL8720D
const (
UART2_TX_PIN = PIN_SERIAL2_TX
UART2_RX_PIN = PIN_SERIAL2_RX
)
// I2C pins
const (
SDA0_PIN = PIN_WIRE_SDA // SDA: SERCOM3/PAD[0]
SCL0_PIN = PIN_WIRE_SCL // SCL: SERCOM3/PAD[1]
SDA1_PIN = PIN_WIRE1_SDA // SDA: SERCOM4/PAD[0]
SCL1_PIN = PIN_WIRE1_SCL // SCL: SERCOM4/PAD[1]
SDA_PIN = SDA0_PIN
SCL_PIN = SCL0_PIN
)
// SPI pins
const (
SPI0_SCK_PIN = SCK // SCK: SERCOM5/PAD[1]
SPI0_MOSI_PIN = MOSI // MOSI: SERCOM5/PAD[0]
SPI0_MISO_PIN = MISO // MISO: SERCOM5/PAD[2]
// RTL8720D
SPI1_SCK_PIN = SCK1 // SCK: SERCOM0/PAD[1]
SPI1_MOSI_PIN = MOSI1 // MOSI: SERCOM0/PAD[0]
SPI1_MISO_PIN = MISO1 // MISO: SERCOM0/PAD[2]
// SD
SPI2_SCK_PIN = SCK2 // SCK: SERCOM6/PAD[1]
SPI2_MOSI_PIN = MOSI2 // MOSI: SERCOM6/PAD[0]
SPI2_MISO_PIN = MISO2 // MISO: SERCOM6/PAD[2]
// LCD
SPI3_SCK_PIN = SCK3 // SCK: SERCOM7/PAD[1]
SPI3_MOSI_PIN = MOSI3 // MOSI: SERCOM7/PAD[3]
SPI3_MISO_PIN = MISO3 // MISO: SERCOM7/PAD[2]
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Seeed Wio Terminal"
usb_STRING_MANUFACTURER = "Seeed"
)
var (
usb_VID uint16 = 0x2886
usb_PID uint16 = 0x802D
)
@@ -0,0 +1,67 @@
// +build sam,atsamd51,wioterminal
package machine
import (
"device/sam"
"runtime/interrupt"
)
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM2_USART_INT,
SERCOM: 2,
}
// RTL8720D
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM1_USART_INT,
SERCOM: 1,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM2_2, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM1_2, UART2.handleInterrupt)
}
// I2C on the Wio Terminal
var (
I2C0 = I2C{
Bus: sam.SERCOM4_I2CM,
SERCOM: 4,
}
I2C1 = I2C{
Bus: sam.SERCOM4_I2CM,
SERCOM: 4,
}
)
// SPI on the Wio Terminal
var (
SPI0 = SPI{
Bus: sam.SERCOM5_SPIM,
SERCOM: 5,
}
// RTL8720D
SPI1 = SPI{
Bus: sam.SERCOM0_SPIM,
SERCOM: 0,
}
// SD
SPI2 = SPI{
Bus: sam.SERCOM6_SPIM,
SERCOM: 6,
}
// LCD
SPI3 = SPI{
Bus: sam.SERCOM7_SPIM,
SERCOM: 7,
}
)
+126
View File
@@ -0,0 +1,126 @@
// +build sam,atsamd21,xiao
package machine
import (
"device/sam"
"runtime/interrupt"
)
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
// GPIO Pins
const (
D0 = PA02 // can be used for PWM or DAC
D1 = PA04 // PWM available
D2 = PA10 // PWM available
D3 = PA11 // PWM available
D4 = PA08 // can be used for PWM or I2C SDA
D5 = PA09 // can be used for PWM or I2C SCL
D6 = PB08 // can be used for PWM or UART1 TX
D7 = PB09 // can be used for PWM or UART1 RX
D8 = PA07 // can be used for PWM or SPI SCK
D9 = PA05 // can be used for PWM or SPI MISO
D10 = PA06 // can be used for PWM or SPI MOSI
)
// Analog pins
const (
A0 = PA02 // ADC/AIN[0]
A1 = PA04 // ADC/AIN[4]
A2 = PA10 // ADC/AIN[18]
A3 = PA11 // ADC/AIN[19]
A4 = PA08 // ADC/AIN[16]
A5 = PA09 // ADC/AIN[17]
A6 = PB08 // ADC/AIN[2]
A7 = PB09 // ADC/AIN[3]
A8 = PA07 // ADC/AIN[7]
A9 = PA05 // ADC/AIN[6]
A10 = PA06 // ADC/AIN[5]
)
const (
LED = PA17
LED_RXL = PA18
LED_TXL = PA19
LED2 = LED_RXL
LED3 = LED_TXL
)
// UART0 aka USBCDC pins
const (
USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25
)
// UART1 pins
const (
UART_TX_PIN = D6
UART_RX_PIN = D7
)
// UART1 on the Xiao
var (
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM4_USART,
SERCOM: 4,
}
)
func init() {
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM4, UART1.handleInterrupt)
}
// I2C pins
const (
SDA_PIN = PA08 // SDA: SERCOM2/PAD[0]
SCL_PIN = PA09 // SCL: SERCOM2/PAD[1]
)
// I2C on the Xiao
var (
I2C0 = I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI pins
const (
SPI0_SCK_PIN = PA07 // SCK: SERCOM0/PAD[3]
SPI0_MOSI_PIN = PA06 // MOSI: SERCOM0/PAD[2]
SPI0_MISO_PIN = PA05 // MISO: SERCOM0/PAD[1]
)
// SPI on the Xiao
var (
SPI0 = SPI{
Bus: sam.SERCOM0_SPI,
SERCOM: 0,
}
)
// I2S pins
const (
I2S_SCK_PIN = PA10
I2S_SD_PIN = PA08
I2S_WS_PIN = NoPin // TODO: figure out what this is on Xiao
)
// I2S on the Xiao
var (
I2S0 = I2S{Bus: sam.I2S}
)
// USB CDC identifiers
const (
usb_STRING_PRODUCT = "Seeed XIAO M0"
usb_STRING_MANUFACTURER = "Seeed"
)
var (
usb_VID uint16 = 0x2886
usb_PID uint16 = 0x802F
)
+5 -4
View File
@@ -3,10 +3,11 @@ package machine
import "errors"
var (
ErrInvalidInputPin = errors.New("machine: invalid input pin")
ErrInvalidOutputPin = errors.New("machine: invalid output pin")
ErrInvalidClockPin = errors.New("machine: invalid clock pin")
ErrInvalidDataPin = errors.New("machine: invalid data pin")
ErrInvalidInputPin = errors.New("machine: invalid input pin")
ErrInvalidOutputPin = errors.New("machine: invalid output pin")
ErrInvalidClockPin = errors.New("machine: invalid clock pin")
ErrInvalidDataPin = errors.New("machine: invalid data pin")
ErrNoPinChangeChannel = errors.New("machine: no channel available for pin interrupt")
)
type PinConfig struct {
-66
View File
@@ -7,72 +7,6 @@ import (
"runtime/interrupt"
)
// InitPWM initializes the registers needed for PWM.
func InitPWM() {
// use waveform generation
avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
// set timer 0 prescale factor to 64
avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
// set timer 1 prescale factor to 64
avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
// put timer 1 in 8-bit phase correct pwm mode
avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
// set timer 2 prescale factor to 64
avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
// configure timer 2 for phase correct pwm (8-bit)
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
}
// Configure configures a PWM pin for output.
func (pwm PWM) Configure() {
if pwm.Pin < 8 {
avr.DDRD.SetBits(1 << uint8(pwm.Pin))
} else {
avr.DDRB.SetBits(1 << uint8(pwm.Pin-8))
}
}
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
// 8-bit value ranging from 0 to 255.
func (pwm PWM) Set(value uint16) {
value8 := uint8(value >> 8)
switch pwm.Pin {
case 3:
// connect pwm to pin on timer 2, channel B
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
avr.OCR2B.Set(value8) // set pwm duty
case 5:
// connect pwm to pin on timer 0, channel B
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
avr.OCR0B.Set(value8) // set pwm duty
case 6:
// connect pwm to pin on timer 0, channel A
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
avr.OCR0A.Set(value8) // set pwm duty
case 9:
// connect pwm to pin on timer 1, channel A
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
// this is a 16-bit value, but we only currently allow the low order bits to be set
avr.OCR1AL.Set(value8) // set pwm duty
case 10:
// connect pwm to pin on timer 1, channel B
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
// this is a 16-bit value, but we only currently allow the low order bits to be set
avr.OCR1BL.Set(value8) // set pwm duty
case 11:
// connect pwm to pin on timer 2, channel A
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
avr.OCR2A.Set(value8) // set pwm duty
default:
panic("Invalid PWM pin")
}
}
// I2CConfig is used to store config info for I2C.
type I2CConfig struct {
Frequency uint32
+52 -6
View File
@@ -14,12 +14,58 @@ func CPUFrequency() uint32 {
return 20000000
}
const (
portA Pin = iota * 8
portB
portC
portD
)
const (
PA0 = portA + 0
PA1 = portA + 1
PA2 = portA + 2
PA3 = portA + 3
PA4 = portA + 4
PA5 = portA + 5
PA6 = portA + 6
PA7 = portA + 7
PB0 = portB + 0
PB1 = portB + 1
PB2 = portB + 2
PB3 = portB + 3
PB4 = portB + 4
PB5 = portB + 5
PB6 = portB + 6
PB7 = portB + 7
PC0 = portC + 0
PC1 = portC + 1
PC2 = portC + 2
PC3 = portC + 3
PC4 = portC + 4
PC5 = portC + 5
PC6 = portC + 6
PC7 = portC + 7
PD0 = portD + 0
PD1 = portD + 1
PD2 = portD + 2
PD3 = portD + 3
PD4 = portD + 4
PD5 = portD + 5
PD6 = portD + 6
PD7 = portD + 7
)
// getPortMask returns the PORTx register and mask for the pin.
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
if p < 8 {
return avr.PORTD, 1 << uint8(p)
} else if p < 14 {
return avr.PORTB, 1 << uint8(p-8)
} else {
return avr.PORTC, 1 << uint8(p-14)
switch {
case p >= PA0 && p <= PA7:
return avr.PORTA, 1 << uint8(p-portA)
case p >= PB0 && p <= PB7:
return avr.PORTB, 1 << uint8(p-portB)
case p >= PC0 && p <= PC7:
return avr.PORTC, 1 << uint8(p-portC)
default:
return avr.PORTD, 1 << uint8(p-portD)
}
}
+13 -34
View File
@@ -96,53 +96,32 @@ const (
PL7 = portE + 7
)
// Configure sets the pin to input or output.
func (p Pin) Configure(config PinConfig) {
register, _, mask := p.getRegisterPortMask()
if config.Mode == PinOutput { // set output bit
register.SetBits(mask)
} else { // configure input: clear output bit
register.ClearBits(mask)
}
}
// Get returns the current value of a GPIO pin.
func (p Pin) Get() bool {
_, port, mask := p.getRegisterPortMask()
return (port.Get() & mask) > 0
}
// getPortMask returns the PORTx register and mask for the pin.
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
_, port, mask := p.getRegisterPortMask()
return port, mask
}
// getRegisterPortMask returns the register, port, and mask for the pin
func (p Pin) getRegisterPortMask() (*volatile.Register8, *volatile.Register8, uint8) {
switch {
case p >= PA0 && p <= PA7:
return avr.DDRA, avr.PORTA, 1 << uint8(p-portA)
return avr.PORTA, 1 << uint8(p-portA)
case p >= PB0 && p <= PB7:
return avr.DDRB, avr.PORTB, 1 << uint8(p-portB)
return avr.PORTB, 1 << uint8(p-portB)
case p >= PC0 && p <= PC7:
return avr.DDRC, avr.PORTC, 1 << uint8(p-portC)
return avr.PORTC, 1 << uint8(p-portC)
case p >= PD0 && p <= PD7:
return avr.DDRD, avr.PORTD, 1 << uint8(p-portD)
return avr.PORTD, 1 << uint8(p-portD)
case p >= PE0 && p <= PE6:
return avr.DDRE, avr.PORTE, 1 << uint8(p-portE)
return avr.PORTE, 1 << uint8(p-portE)
case p >= PF0 && p <= PF7:
return avr.DDRF, avr.PORTF, 1 << uint8(p-portF)
return avr.PORTF, 1 << uint8(p-portF)
case p >= PG0 && p <= PG5:
return avr.DDRG, avr.PORTG, 1 << uint8(p-portG)
return avr.PORTG, 1 << uint8(p-portG)
case p >= PH0 && p <= PH6:
return avr.DDRH, avr.PORTH, 1 << uint8(p-portH)
return avr.PORTH, 1 << uint8(p-portH)
case p >= PJ0 && p <= PJ1:
return avr.DDRJ, avr.PORTJ, 1 << uint8(p-portJ)
return avr.PORTJ, 1 << uint8(p-portJ)
case p >= PK0 && p <= PK7:
return avr.DDRK, avr.PORTK, 1 << uint8(p-portK)
return avr.PORTK, 1 << uint8(p-portK)
case p >= PL0 && p <= PL7:
return avr.DDRL, avr.PORTL, 1 << uint8(p-portL)
return avr.PORTL, 1 << uint8(p-portL)
default:
return avr.DDRB, avr.PORTA, 255
return avr.PORTA, 255
}
}
+76 -41
View File
@@ -9,47 +9,82 @@ import (
const irq_USART0_RX = avr.IRQ_USART_RX
// Configure sets the pin to input or output.
func (p Pin) Configure(config PinConfig) {
if config.Mode == PinOutput { // set output bit
if p < 8 {
avr.DDRD.SetBits(1 << uint8(p))
} else if p < 14 {
avr.DDRB.SetBits(1 << uint8(p-8))
} else {
avr.DDRC.SetBits(1 << uint8(p-14))
}
} else { // configure input: clear output bit
if p < 8 {
avr.DDRD.ClearBits(1 << uint8(p))
} else if p < 14 {
avr.DDRB.ClearBits(1 << uint8(p-8))
} else {
avr.DDRC.ClearBits(1 << uint8(p-14))
}
}
}
// Get returns the current value of a GPIO pin.
func (p Pin) Get() bool {
if p < 8 {
val := avr.PIND.Get() & (1 << uint8(p))
return (val > 0)
} else if p < 14 {
val := avr.PINB.Get() & (1 << uint8(p-8))
return (val > 0)
} else {
val := avr.PINC.Get() & (1 << uint8(p-14))
return (val > 0)
}
}
// getPortMask returns the PORTx register and mask for the pin.
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
if p < 8 {
return avr.PORTD, 1 << uint8(p)
} else if p < 14 {
return avr.PORTB, 1 << uint8(p-8)
} else {
return avr.PORTC, 1 << uint8(p-14)
switch {
case p >= PB0 && p <= PB7: // port B
return avr.PORTB, 1 << uint8(p-portB)
case p >= PC0 && p <= PC7: // port C
return avr.PORTC, 1 << uint8(p-portC)
default: // port D
return avr.PORTD, 1 << uint8(p-portD)
}
}
// InitPWM initializes the registers needed for PWM.
func InitPWM() {
// use waveform generation
avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
// set timer 0 prescale factor to 64
avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
// set timer 1 prescale factor to 64
avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
// put timer 1 in 8-bit phase correct pwm mode
avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
// set timer 2 prescale factor to 64
avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
// configure timer 2 for phase correct pwm (8-bit)
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
}
// Configure configures a PWM pin for output.
func (pwm PWM) Configure() error {
switch pwm.Pin / 8 {
case 0: // port B
avr.DDRB.SetBits(1 << uint8(pwm.Pin))
case 2: // port D
avr.DDRD.SetBits(1 << uint8(pwm.Pin-16))
}
return nil
}
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
// 8-bit value ranging from 0 to 255.
func (pwm PWM) Set(value uint16) {
value8 := uint8(value >> 8)
switch pwm.Pin {
case PD3:
// connect pwm to pin on timer 2, channel B
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
avr.OCR2B.Set(value8) // set pwm duty
case PD5:
// connect pwm to pin on timer 0, channel B
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
avr.OCR0B.Set(value8) // set pwm duty
case PD6:
// connect pwm to pin on timer 0, channel A
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
avr.OCR0A.Set(value8) // set pwm duty
case PB1:
// connect pwm to pin on timer 1, channel A
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
// this is a 16-bit value, but we only currently allow the low order bits to be set
avr.OCR1AL.Set(value8) // set pwm duty
case PB2:
// connect pwm to pin on timer 1, channel B
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
// this is a 16-bit value, but we only currently allow the low order bits to be set
avr.OCR1BL.Set(value8) // set pwm duty
case PB3:
// connect pwm to pin on timer 2, channel A
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
avr.OCR2A.Set(value8) // set pwm duty
default:
panic("Invalid PWM pin")
}
}
+175 -19
View File
@@ -33,6 +33,26 @@ const (
PinInputPulldown PinMode = 12
)
type PinChange uint8
// Pin change interrupt constants for SetInterrupt.
const (
PinRising PinChange = sam.EIC_CONFIG_SENSE0_RISE
PinFalling PinChange = sam.EIC_CONFIG_SENSE0_FALL
PinToggle PinChange = sam.EIC_CONFIG_SENSE0_BOTH
)
// Callbacks to be called for pins configured with SetInterrupt. Unfortunately,
// we also need to keep track of which interrupt channel is used by which pin,
// as the only alternative would be iterating through all pins.
//
// We're using the magic constant 16 here because the SAM D21 has 16 interrupt
// channels configurable for pins.
var (
interruptPins [16]Pin // warning: the value is invalid when pinCallbacks[i] is not set!
pinCallbacks [16]func(Pin)
)
const (
pinPadMapSERCOM0Pad0 byte = (0x10 << 1) | 0x00
pinPadMapSERCOM1Pad0 byte = (0x20 << 1) | 0x00
@@ -144,6 +164,114 @@ func findPinPadMapping(sercom uint8, pin Pin) (pinMode PinMode, pad uint32, ok b
return
}
// SetInterrupt sets an interrupt to be executed when a particular pin changes
// state.
//
// 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 {
// Most pins follow a common pattern where the EXTINT value is the pin
// number modulo 16. However, there are a few exceptions, as you can see
// below.
extint := uint8(0)
switch p {
case PA08:
// Connected to NMI. This is not currently supported.
return ErrInvalidInputPin
case PA24:
extint = 12
case PA25:
extint = 13
case PA27:
extint = 15
case PA28:
extint = 8
case PA30:
extint = 10
case PA31:
extint = 11
default:
// All other pins follow a normal pattern.
extint = uint8(p) % 16
}
if callback == nil {
// Disable this pin interrupt (if it was enabled).
sam.EIC.INTENCLR.Set(1 << extint)
if pinCallbacks[extint] != nil {
pinCallbacks[extint] = nil
}
return nil
}
if pinCallbacks[extint] != nil {
// The pin was already configured.
// To properly re-configure a pin, unset it first and set a new
// configuration.
return ErrNoPinChangeChannel
}
pinCallbacks[extint] = callback
interruptPins[extint] = p
if sam.EIC.CTRL.Get() == 0 {
// EIC peripheral has not yet been initialized. Initialize it now.
// The EIC needs two clocks: CLK_EIC_APB and GCLK_EIC. CLK_EIC_APB is
// enabled by default, so doesn't have to be re-enabled. The other is
// required for detecting edges and must be enabled manually.
sam.GCLK.CLKCTRL.Set(sam.GCLK_CLKCTRL_ID_EIC<<sam.GCLK_CLKCTRL_ID_Pos |
sam.GCLK_CLKCTRL_GEN_GCLK0<<sam.GCLK_CLKCTRL_GEN_Pos |
sam.GCLK_CLKCTRL_CLKEN)
// should not be necessary (CLKCTRL is not synchronized)
for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
}
sam.EIC.CTRL.Set(sam.EIC_CTRL_ENABLE)
for sam.EIC.STATUS.HasBits(sam.EIC_STATUS_SYNCBUSY) {
}
}
// Configure this pin. Set the 4 bits of the EIC.CONFIGx register to the
// sense value (filter bit set to 0, sense bits set to the change value).
addr := &sam.EIC.CONFIG0
if extint >= 8 {
addr = &sam.EIC.CONFIG1
}
pos := (extint % 8) * 4 // bit position in register
addr.Set((addr.Get() &^ (0xf << pos)) | uint32(change)<<pos)
// Enable external interrupt for this pin.
sam.EIC.INTENSET.Set(1 << extint)
// Set the PMUXEN flag, while keeping the INEN and PULLEN flags (if they
// were set before). This avoids clearing the pin pull mode while
// configuring the pin interrupt.
p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | (p.getPinCfg() & (sam.PORT_PINCFG0_INEN | sam.PORT_PINCFG0_PULLEN)))
if p&1 > 0 {
// odd pin, so save the even pins
val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk
p.setPMux(val | (sam.PORT_PMUX0_PMUXO_A << sam.PORT_PMUX0_PMUXO_Pos))
} else {
// even pin, so save the odd pins
val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
p.setPMux(val | (sam.PORT_PMUX0_PMUXE_A << sam.PORT_PMUX0_PMUXE_Pos))
}
interrupt.New(sam.IRQ_EIC, func(interrupt.Interrupt) {
flags := sam.EIC.INTFLAG.Get()
sam.EIC.INTFLAG.Set(flags) // clear interrupt
for i := uint(0); i < 16; i++ { // there are 16 channels
if flags&(1<<i) != 0 {
pinCallbacks[i](interruptPins[i])
}
}
}).Enable()
return nil
}
// InitADC initializes the ADC.
func InitADC() {
// ADC Bias Calibration
@@ -260,6 +388,16 @@ func (a ADC) getADCChannel() uint8 {
switch a.Pin {
case PA02:
return 0
case PA03:
return 1
case PB04:
return 12
case PB05:
return 13
case PB06:
return 14
case PB07:
return 15
case PB08:
return 2
case PB09:
@@ -272,14 +410,22 @@ func (a ADC) getADCChannel() uint8 {
return 6
case PA07:
return 7
case PA08:
return 16
case PA09:
return 17
case PA10:
return 18
case PA11:
return 19
case PB00:
return 8
case PB01:
return 9
case PB02:
return 10
case PB03:
return 11
case PA09:
return 17
case PA11:
return 19
default:
return 0
}
@@ -1090,9 +1236,12 @@ func InitPWM() {
}
// Configure configures a PWM pin for output.
func (pwm PWM) Configure() {
func (pwm PWM) Configure() error {
// figure out which TCCX timer for this pin
timer := pwm.getTimer()
if timer == nil {
return ErrInvalidOutputPin
}
// disable timer
timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
@@ -1139,12 +1288,19 @@ func (pwm PWM) Configure() {
val := pwm.getPMux() & sam.PORT_PMUX0_PMUXO_Msk
pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_PMUX0_PMUXE_Pos))
}
return nil
}
// Set turns on the duty cycle for a PWM pin using the provided value.
func (pwm PWM) Set(value uint16) {
// figure out which TCCX timer for this pin
timer := pwm.getTimer()
if timer == nil {
// The Configure call above cannot have succeeded, so simply ignore this
// error.
return
}
// disable output
timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
@@ -1154,7 +1310,7 @@ func (pwm PWM) Set(value uint16) {
}
// Set PWM signal to output duty cycle
pwm.setChannel(uint32(value))
pwm.setChannel(timer, uint32(value))
// Wait for synchronization on all channels
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0 |
@@ -1223,32 +1379,32 @@ func (pwm PWM) getTimer() *sam.TCC_Type {
}
// setChannel sets the value for the correct channel for PWM on this pin
func (pwm PWM) setChannel(val uint32) {
func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
switch pwm.Pin {
case 6:
pwm.getTimer().CC0.Set(val)
timer.CC0.Set(val)
case 7:
pwm.getTimer().CC1.Set(val)
timer.CC1.Set(val)
case 8:
pwm.getTimer().CC0.Set(val)
timer.CC0.Set(val)
case 9:
pwm.getTimer().CC1.Set(val)
timer.CC1.Set(val)
case 14:
pwm.getTimer().CC0.Set(val)
timer.CC0.Set(val)
case 15:
pwm.getTimer().CC1.Set(val)
timer.CC1.Set(val)
case 16:
pwm.getTimer().CC2.Set(val)
timer.CC2.Set(val)
case 17:
pwm.getTimer().CC3.Set(val)
timer.CC3.Set(val)
case 18:
pwm.getTimer().CC2.Set(val)
timer.CC2.Set(val)
case 19:
pwm.getTimer().CC3.Set(val)
timer.CC3.Set(val)
case 20:
pwm.getTimer().CC2.Set(val)
timer.CC2.Set(val)
case 21:
pwm.getTimer().CC3.Set(val)
timer.CC3.Set(val)
default:
return // not supported on this pin
}
+310 -62
View File
@@ -44,6 +44,26 @@ const (
PinInputPulldown PinMode = 18
)
type PinChange uint8
// Pin change interrupt constants for SetInterrupt.
const (
PinRising PinChange = sam.EIC_CONFIG_SENSE0_RISE
PinFalling PinChange = sam.EIC_CONFIG_SENSE0_FALL
PinToggle PinChange = sam.EIC_CONFIG_SENSE0_BOTH
)
// Callbacks to be called for pins configured with SetInterrupt. Unfortunately,
// we also need to keep track of which interrupt channel is used by which pin,
// as the only alternative would be iterating through all pins.
//
// We're using the magic constant 16 here because the SAM D21 has 16 interrupt
// channels configurable for pins.
var (
interruptPins [16]Pin // warning: the value is invalid when pinCallbacks[i] is not set!
pinCallbacks [16]func(Pin)
)
// Hardware pins
const (
PA00 Pin = 0
@@ -110,6 +130,70 @@ const (
PB29 Pin = 61
PB30 Pin = 62
PB31 Pin = 63
PC00 Pin = 64
PC01 Pin = 65
PC02 Pin = 66
PC03 Pin = 67
PC04 Pin = 68
PC05 Pin = 69
PC06 Pin = 70
PC07 Pin = 71
PC08 Pin = 72
PC09 Pin = 73
PC10 Pin = 74
PC11 Pin = 75
PC12 Pin = 76
PC13 Pin = 77
PC14 Pin = 78
PC15 Pin = 79
PC16 Pin = 80
PC17 Pin = 81
PC18 Pin = 82
PC19 Pin = 83
PC20 Pin = 84
PC21 Pin = 85
PC22 Pin = 86
PC23 Pin = 87
PC24 Pin = 88
PC25 Pin = 89
PC26 Pin = 90
PC27 Pin = 91
PC28 Pin = 92
PC29 Pin = 93
PC30 Pin = 94
PC31 Pin = 95
PD00 Pin = 96
PD01 Pin = 97
PD02 Pin = 98
PD03 Pin = 99
PD04 Pin = 100
PD05 Pin = 101
PD06 Pin = 102
PD07 Pin = 103
PD08 Pin = 104
PD09 Pin = 105
PD10 Pin = 106
PD11 Pin = 107
PD12 Pin = 108
PD13 Pin = 109
PD14 Pin = 110
PD15 Pin = 111
PD16 Pin = 112
PD17 Pin = 113
PD18 Pin = 114
PD19 Pin = 115
PD20 Pin = 116
PD21 Pin = 117
PD22 Pin = 118
PD23 Pin = 119
PD24 Pin = 120
PD25 Pin = 121
PD26 Pin = 122
PD27 Pin = 123
PD28 Pin = 124
PD29 Pin = 125
PD30 Pin = 126
PD31 Pin = 127
)
const (
@@ -180,7 +264,7 @@ const (
// easy to check whether a nibble is set at all.
//
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/60001507E.pdf
var pinPadMapping = [32]uint16{
var pinPadMapping = [64]uint16{
// page 32
PA00 / 2: 0 | pinPadMapSERCOM1AltPad0,
@@ -188,28 +272,28 @@ var pinPadMapping = [32]uint16{
PB08 / 2: 0 | pinPadMapSERCOM4AltPad0,
PA04 / 2: 0 | pinPadMapSERCOM0AltPad0,
PA06 / 2: 0 | pinPadMapSERCOM0AltPad2,
//PC04 / 2: pinPadMapSERCOM6Pad0 | 0,
//PC06 / 2: pinPadMapSERCOM6Pad2 | 0,
PC04 / 2: pinPadMapSERCOM6Pad0 | 0,
PC06 / 2: pinPadMapSERCOM6Pad2 | 0,
PA08 / 2: pinPadMapSERCOM0Pad0 | pinPadMapSERCOM2AltPad1,
PA10 / 2: pinPadMapSERCOM0Pad2 | pinPadMapSERCOM2AltPad2,
PB10 / 2: 0 | pinPadMapSERCOM4AltPad2,
PB12 / 2: pinPadMapSERCOM4Pad0 | 0,
PB14 / 2: pinPadMapSERCOM4Pad2 | 0,
//PD08 / 2: pinPadMapSERCOM7Pad0 | pinPadMapSERCOM6AltPad1,
//PD10 / 2: pinPadMapSERCOM7Pad2 | pinPadMapSERCOM6AltPad2,
//PC10 / 2: pinPadMapSERCOM6Pad2 | pinPadMapSERCOM7AltPad2,
PD08 / 2: pinPadMapSERCOM7Pad0 | pinPadMapSERCOM6AltPad1,
PD10 / 2: pinPadMapSERCOM7Pad2 | pinPadMapSERCOM6AltPad2,
PC10 / 2: pinPadMapSERCOM6Pad2 | pinPadMapSERCOM7AltPad2,
// page 34
//PC12 / 2: pinPadMapSERCOM7Pad0 | pinPadMapSERCOM6AltPad1,
//PC14 / 2: pinPadMapSERCOM7Pad2 | pinPadMapSERCOM6AltPad2,
PC12 / 2: pinPadMapSERCOM7Pad0 | pinPadMapSERCOM6AltPad1,
PC14 / 2: pinPadMapSERCOM7Pad2 | pinPadMapSERCOM6AltPad2,
PA12 / 2: pinPadMapSERCOM2Pad0 | pinPadMapSERCOM4AltPad1,
PA14 / 2: pinPadMapSERCOM2Pad2 | pinPadMapSERCOM4AltPad2,
PA16 / 2: pinPadMapSERCOM1Pad0 | pinPadMapSERCOM3AltPad1,
PA18 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM3AltPad2,
//PC16 / 2: pinPadMapSERCOM6Pad0 | pinPadMapSERCOM0AltPad1,
//PC18 / 2: pinPadMapSERCOM6Pad2 | pinPadMapSERCOM0AltPad2,
//PC22 / 2: pinPadMapSERCOM1Pad0 | pinPadMapSERCOM3AltPad1,
//PD20 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM3AltPad2,
PC16 / 2: pinPadMapSERCOM6Pad0 | pinPadMapSERCOM0AltPad1,
PC18 / 2: pinPadMapSERCOM6Pad2 | pinPadMapSERCOM0AltPad2,
PC22 / 2: pinPadMapSERCOM1Pad0 | pinPadMapSERCOM3AltPad1,
PD20 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM3AltPad2,
PB16 / 2: pinPadMapSERCOM5Pad0 | 0,
PB18 / 2: pinPadMapSERCOM5Pad2 | pinPadMapSERCOM7AltPad2,
@@ -222,7 +306,7 @@ var pinPadMapping = [32]uint16{
PB24 / 2: pinPadMapSERCOM0Pad0 | pinPadMapSERCOM2AltPad1,
PB26 / 2: pinPadMapSERCOM2Pad0 | pinPadMapSERCOM4AltPad1,
PB28 / 2: pinPadMapSERCOM2Pad2 | pinPadMapSERCOM4AltPad2,
//PC24 / 2: pinPadMapSERCOM0Pad2 | pinPadMapSERCOM2AltPad2,
PC24 / 2: pinPadMapSERCOM0Pad2 | pinPadMapSERCOM2AltPad2,
//PC26 / 2: pinPadMapSERCOM1Pad1 | 0, // note: PC26 doesn't support SERCOM, but PC27 does
//PC28 / 2: pinPadMapSERCOM1Pad1 | 0, // note: PC29 doesn't exist in the datasheet?
PA30 / 2: 0 | pinPadMapSERCOM1AltPad2,
@@ -268,6 +352,162 @@ func findPinPadMapping(sercom uint8, pin Pin) (pinMode PinMode, pad uint32, ok b
return
}
// SetInterrupt sets an interrupt to be executed when a particular pin changes
// state.
//
// 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 {
// Most pins follow a common pattern where the EXTINT value is the pin
// number modulo 16. However, there are a few exceptions, as you can see
// below.
extint := uint8(0)
switch p {
case PA08:
// Connected to NMI. This is not currently supported.
return ErrInvalidInputPin
case PB26:
extint = 12
case PB27:
extint = 13
case PB28:
extint = 14
case PB29:
extint = 15
case PC07:
extint = 9
case PD08:
extint = 3
case PD09:
extint = 4
case PD10:
extint = 5
case PD11:
extint = 6
case PD12:
extint = 7
case PD20:
extint = 10
case PD21:
extint = 11
default:
// All other pins follow a normal pattern.
extint = uint8(p) % 16
}
if callback == nil {
// Disable this pin interrupt (if it was enabled).
sam.EIC.INTENCLR.Set(1 << extint)
if pinCallbacks[extint] != nil {
pinCallbacks[extint] = nil
}
return nil
}
if pinCallbacks[extint] != nil {
// The pin was already configured.
// To properly re-configure a pin, unset it first and set a new
// configuration.
return ErrNoPinChangeChannel
}
pinCallbacks[extint] = callback
interruptPins[extint] = p
if !sam.EIC.CTRLA.HasBits(sam.EIC_CTRLA_ENABLE) {
// EIC peripheral has not yet been initialized. Initialize it now.
// The EIC needs two clocks: CLK_EIC_APB and GCLK_EIC. CLK_EIC_APB is
// enabled by default, so doesn't have to be re-enabled. The other is
// required for detecting edges and must be enabled manually.
sam.GCLK.PCHCTRL[4].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) | sam.GCLK_PCHCTRL_CHEN)
// should not be necessary (CLKCTRL is not synchronized)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK0 << sam.GCLK_SYNCBUSY_GENCTRL_Pos) {
}
}
// CONFIG register is enable-protected, so disable EIC.
sam.EIC.CTRLA.ClearBits(sam.EIC_CTRLA_ENABLE)
// Configure this pin. Set the 4 bits of the EIC.CONFIGx register to the
// sense value (filter bit set to 0, sense bits set to the change value).
addr := &sam.EIC.CONFIG[0]
if extint >= 8 {
addr = &sam.EIC.CONFIG[1]
}
pos := (extint % 8) * 4 // bit position in register
addr.Set((addr.Get() &^ (0xf << pos)) | uint32(change)<<pos)
// Enable external interrupt for this pin.
sam.EIC.INTENSET.Set(1 << extint)
sam.EIC.CTRLA.Set(sam.EIC_CTRLA_ENABLE)
for sam.EIC.SYNCBUSY.HasBits(sam.EIC_SYNCBUSY_ENABLE) {
}
// Set the PMUXEN flag, while keeping the INEN and PULLEN flags (if they
// were set before). This avoids clearing the pin pull mode while
// configuring the pin interrupt.
p.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN | (p.getPinCfg() & (sam.PORT_GROUP_PINCFG_INEN | sam.PORT_GROUP_PINCFG_PULLEN)))
if p&1 > 0 {
// odd pin, so save the even pins
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
p.setPMux(val | (0 << sam.PORT_GROUP_PMUX_PMUXO_Pos))
} else {
// even pin, so save the odd pins
val := p.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
p.setPMux(val | (0 << sam.PORT_GROUP_PMUX_PMUXE_Pos))
}
handleEICInterrupt := func(interrupt.Interrupt) {
flags := sam.EIC.INTFLAG.Get()
sam.EIC.INTFLAG.Set(flags) // clear interrupt
for i := uint(0); i < 16; i++ { // there are 16 channels
if flags&(1<<i) != 0 {
pinCallbacks[i](interruptPins[i])
}
}
}
switch extint {
case 0:
interrupt.New(sam.IRQ_EIC_EXTINT_0, handleEICInterrupt).Enable()
case 1:
interrupt.New(sam.IRQ_EIC_EXTINT_1, handleEICInterrupt).Enable()
case 2:
interrupt.New(sam.IRQ_EIC_EXTINT_2, handleEICInterrupt).Enable()
case 3:
interrupt.New(sam.IRQ_EIC_EXTINT_3, handleEICInterrupt).Enable()
case 4:
interrupt.New(sam.IRQ_EIC_EXTINT_4, handleEICInterrupt).Enable()
case 5:
interrupt.New(sam.IRQ_EIC_EXTINT_5, handleEICInterrupt).Enable()
case 6:
interrupt.New(sam.IRQ_EIC_EXTINT_6, handleEICInterrupt).Enable()
case 7:
interrupt.New(sam.IRQ_EIC_EXTINT_7, handleEICInterrupt).Enable()
case 8:
interrupt.New(sam.IRQ_EIC_EXTINT_8, handleEICInterrupt).Enable()
case 9:
interrupt.New(sam.IRQ_EIC_EXTINT_9, handleEICInterrupt).Enable()
case 10:
interrupt.New(sam.IRQ_EIC_EXTINT_10, handleEICInterrupt).Enable()
case 11:
interrupt.New(sam.IRQ_EIC_EXTINT_11, handleEICInterrupt).Enable()
case 12:
interrupt.New(sam.IRQ_EIC_EXTINT_12, handleEICInterrupt).Enable()
case 13:
interrupt.New(sam.IRQ_EIC_EXTINT_13, handleEICInterrupt).Enable()
case 14:
interrupt.New(sam.IRQ_EIC_EXTINT_14, handleEICInterrupt).Enable()
case 15:
interrupt.New(sam.IRQ_EIC_EXTINT_15, handleEICInterrupt).Enable()
}
return nil
}
// Return the register and mask to enable a given GPIO pin. This can be used to
// implement bit-banged drivers.
func (p Pin) PortMaskSet() (*uint32, uint32) {
@@ -586,7 +826,7 @@ func (a ADC) Get() uint16 {
}
func (a ADC) getADCBus() *sam.ADC_Type {
if a.Pin >= PB04 && a.Pin <= PB07 {
if (a.Pin >= PB04 && a.Pin <= PB07) || (a.Pin >= PC00) {
return sam.ADC1
}
return sam.ADC0
@@ -629,6 +869,24 @@ func (a ADC) getADCChannel() uint8 {
return 8
case PB07:
return 9
case PC00:
return 10
case PC01:
return 11
case PC02:
return 4
case PC03:
return 5
case PC30:
return 12
case PC31:
return 13
case PD00:
return 14
case PD01:
return 15
default:
panic("Invalid ADC pin")
}
@@ -645,28 +903,8 @@ type UART struct {
var (
// UART0 is actually a USB CDC interface.
UART0 = USBCDC{Buffer: NewRingBuffer()}
// The first hardware serial port on the SAMD51. Uses the SERCOM3 interface.
UART1 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM3_USART_INT,
SERCOM: 3,
}
// The second hardware serial port on the SAMD51. Uses the SERCOM0 interface.
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT,
SERCOM: 0,
}
)
func init() {
// Register RXC interrupts.
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM3_2, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM0_2, UART2.handleInterrupt)
}
const (
sampleRate16X = 16
lsbFirst = 1
@@ -1216,7 +1454,7 @@ const (
const period = 0xFFFF
// Configure configures a PWM pin for output.
func (pwm PWM) Configure() {
func (pwm PWM) Configure() error {
// Set pin as output
sam.PORT.GROUP[0].DIRSET.Set(1 << uint8(pwm.Pin))
// Set pin to low
@@ -1240,6 +1478,9 @@ func (pwm PWM) Configure() {
// figure out which TCCX timer for this pin
timer := pwm.getTimer()
if timer == nil {
return ErrInvalidOutputPin
}
// disable timer
timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
@@ -1264,7 +1505,7 @@ func (pwm PWM) Configure() {
// Set the initial value
// TCCx->CC[tcChannel].reg = (uint32_t) value;
pwm.setChannel(0)
pwm.setChannel(timer, 0)
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
@@ -1282,12 +1523,19 @@ func (pwm PWM) Configure() {
// Wait for synchronization
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
}
return nil
}
// Set turns on the duty cycle for a PWM pin using the provided value.
func (pwm PWM) Set(value uint16) {
// figure out which TCCX timer for this pin
timer := pwm.getTimer()
if timer == nil {
// The Configure call above cannot have succeeded, so simply ignore this
// error.
return
}
// Wait for synchronization
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CTRLB) {
@@ -1297,7 +1545,7 @@ func (pwm PWM) Set(value uint16) {
}
// TCCx->CCBUF[tcChannel].reg = (uint32_t) value;
pwm.setChannelBuffer(uint32(value))
pwm.setChannelBuffer(timer, uint32(value))
for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
@@ -1329,61 +1577,61 @@ func (pwm PWM) setPinCfg(val uint8) {
pwm.Pin.setPinCfg(val)
}
// setChannel sets the value for the correct channel for PWM on this pin
func (pwm PWM) setChannel(val uint32) {
// setChannel sets the value for the correct channel for PWM on this pin.
func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
switch pwm.Pin {
case PA16:
pwm.getTimer().CC[0].Set(val)
timer.CC[0].Set(val)
case PA17:
pwm.getTimer().CC[1].Set(val)
timer.CC[1].Set(val)
case PA14:
pwm.getTimer().CC[0].Set(val)
timer.CC[0].Set(val)
case PA15:
pwm.getTimer().CC[1].Set(val)
timer.CC[1].Set(val)
case PA18:
pwm.getTimer().CC[2].Set(val)
timer.CC[2].Set(val)
case PA19:
pwm.getTimer().CC[3].Set(val)
timer.CC[3].Set(val)
case PA20:
pwm.getTimer().CC[0].Set(val)
timer.CC[0].Set(val)
case PA21:
pwm.getTimer().CC[1].Set(val)
timer.CC[1].Set(val)
case PA23:
pwm.getTimer().CC[3].Set(val)
timer.CC[3].Set(val)
case PA22:
pwm.getTimer().CC[2].Set(val)
timer.CC[2].Set(val)
case PB31:
pwm.getTimer().CC[1].Set(val)
timer.CC[1].Set(val)
default:
return // not supported on this pin
}
}
// setChannelBuffer sets the value for the correct channel buffer for PWM on this pin
func (pwm PWM) setChannelBuffer(val uint32) {
func (pwm PWM) setChannelBuffer(timer *sam.TCC_Type, val uint32) {
switch pwm.Pin {
case PA16:
pwm.getTimer().CCBUF[0].Set(val)
timer.CCBUF[0].Set(val)
case PA17:
pwm.getTimer().CCBUF[1].Set(val)
timer.CCBUF[1].Set(val)
case PA14:
pwm.getTimer().CCBUF[0].Set(val)
timer.CCBUF[0].Set(val)
case PA15:
pwm.getTimer().CCBUF[1].Set(val)
timer.CCBUF[1].Set(val)
case PA18:
pwm.getTimer().CCBUF[2].Set(val)
timer.CCBUF[2].Set(val)
case PA19:
pwm.getTimer().CCBUF[3].Set(val)
timer.CCBUF[3].Set(val)
case PA20:
pwm.getTimer().CCBUF[0].Set(val)
timer.CCBUF[0].Set(val)
case PA21:
pwm.getTimer().CCBUF[1].Set(val)
timer.CCBUF[1].Set(val)
case PA23:
pwm.getTimer().CCBUF[3].Set(val)
timer.CCBUF[3].Set(val)
case PA22:
pwm.getTimer().CCBUF[2].Set(val)
timer.CCBUF[2].Set(val)
case PB31:
pwm.getTimer().CCBUF[1].Set(val)
timer.CCBUF[1].Set(val)
default:
return // not supported on this pin
}
+58
View File
@@ -0,0 +1,58 @@
// +build sam,atsamd51,atsamd51p19
// Peripheral abstraction layer for the atsamd51.
//
// Datasheet:
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
//
package machine
import "device/sam"
const HSRAM_SIZE = 0x00030000
// InitPWM initializes the PWM interface.
func InitPWM() {
// turn on timer clocks used for PWM
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_TCC0_ | sam.MCLK_APBBMASK_TCC1_)
sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_TCC2_)
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_TCC4_)
//use clock generator 0
sam.GCLK.PCHCTRL[25].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[29].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[38].Set((sam.GCLK_PCHCTRL_GEN_GCLK0 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
// getTimer returns the timer to be used for PWM on this pin
func (pwm PWM) getTimer() *sam.TCC_Type {
switch pwm.Pin {
case PA16:
return sam.TCC1
case PA17:
return sam.TCC1
case PA14:
return sam.TCC2
case PA15:
return sam.TCC2
case PA18:
return sam.TCC1
case PA19:
return sam.TCC1
case PA20:
return sam.TCC0
case PA21:
return sam.TCC0
case PA23:
return sam.TCC0
case PA22:
return sam.TCC0
case PB31:
return sam.TCC4
default:
return nil // not supported on this pin
}
}
+2 -15
View File
@@ -16,21 +16,8 @@ const (
PB5
)
// Configure sets the pin to input or output.
func (p Pin) Configure(config PinConfig) {
if config.Mode == PinOutput { // set output bit
avr.DDRB.SetBits(1 << uint8(p))
} else { // configure input: clear output bit
avr.DDRB.ClearBits(1 << uint8(p))
}
}
// getPortMask returns the PORTx register and mask for the pin.
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
// Very simple for the attiny85, which only has a single port.
return avr.PORTB, 1 << uint8(p)
}
// Get returns the current value of a GPIO pin.
func (p Pin) Get() bool {
val := avr.PINB.Get() & (1 << uint8(p))
return (val > 0)
}
+61
View File
@@ -5,15 +5,76 @@ package machine
import (
"device/avr"
"runtime/volatile"
"unsafe"
)
type PinMode uint8
const (
PinInput PinMode = iota
PinInputPullup
PinOutput
)
// In all the AVRs I've looked at, the PIN/DDR/PORT registers followed a regular
// pattern: PINx, DDRx, PORTx in this order without registers in between.
// Therefore, if you know any of them, you can calculate the other two.
//
// For now, I've chosen to let the PORTx register be the one that is returned
// for each specific chip and to calculate the others from that one. Setting an
// output port (done using PORTx) is likely the most common operation and the
// one that is the most time critical. For others, the PINx and DDRx register
// can trivially be calculated using a subtraction.
// Configure sets the pin to input or output.
func (p Pin) Configure(config PinConfig) {
port, mask := p.getPortMask()
// The DDRx register can be found by subtracting one from the PORTx
// register, as this appears to be the case for many (most? all?) AVR chips.
ddr := (*volatile.Register8)(unsafe.Pointer(uintptr(unsafe.Pointer(port)) - 1))
if config.Mode == PinOutput {
// set output bit
ddr.SetBits(mask)
// Note: if the pin was PinInputPullup before, it'll now be high.
// Otherwise it will be low.
} else {
// configure input: clear output bit
ddr.ClearBits(mask)
if config.Mode == PinInput {
// No pullup (floating).
// The transition may be one of the following:
// output high -> input pullup -> input (safe: output high and input pullup are similar)
// output low -> input -> input (safe: no extra transition)
port.ClearBits(mask)
} else {
// Pullup.
// The transition may be one of the following:
// output high -> input pullup -> input pullup (safe: no extra transition)
// output low -> input -> input pullup (possibly problematic)
// For the last transition (output low -> input -> input pullup),
// the transition may be problematic in some cases because there is
// an intermediate floating state (which may cause irratic
// interrupts, for example). If this is a problem, the application
// should set the pin high before configuring it as PinInputPullup.
// We can't do that here because setting it to high as an
// intermediate state may have other problems.
port.SetBits(mask)
}
}
}
// Get returns the current value of a GPIO pin.
func (p Pin) Get() bool {
port, mask := p.getPortMask()
// As noted above, the PINx register is always two registers below the PORTx
// register, so we can find it simply by subtracting two from the PORTx
// register address.
pin := (*volatile.Register8)(unsafe.Pointer(uintptr(unsafe.Pointer(port)) - 2)) // PINA, PINB, etc
return (pin.Get() & mask) > 0
}
// Set changes the value of the GPIO pin. The pin must be configured as output.
func (p Pin) Set(value bool) {
if value { // set bits
-1
View File
@@ -48,7 +48,6 @@ func (p Pin) Set(high bool) {
// Get returns the current value of a GPIO pin.
func (p Pin) Get() bool {
val := sifive.GPIO0.VALUE.Get() & (1 << uint8(p))
println(sifive.GPIO0.VALUE.Get())
return (val > 0)
}
+2 -1
View File
@@ -90,7 +90,8 @@ func InitPWM() {
}
// Configure configures a PWM pin for output.
func (pwm PWM) Configure() {
func (pwm PWM) Configure() error {
return nil
}
// Set turns on the duty cycle for a PWM pin using the provided value.
+73 -2
View File
@@ -21,6 +21,18 @@ const (
PinOutput PinMode = (nrf.GPIO_PIN_CNF_DIR_Output << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Disconnect << nrf.GPIO_PIN_CNF_INPUT_Pos)
)
type PinChange uint8
// Pin change interrupt constants for SetInterrupt.
const (
PinRising PinChange = nrf.GPIOTE_CONFIG_POLARITY_LoToHi
PinFalling PinChange = nrf.GPIOTE_CONFIG_POLARITY_HiToLo
PinToggle PinChange = nrf.GPIOTE_CONFIG_POLARITY_Toggle
)
// Callbacks to be called for pins configured with SetInterrupt.
var pinCallbacks [len(nrf.GPIOTE.CONFIG)]func(Pin)
// Configure this pin with the given configuration.
func (p Pin) Configure(config PinConfig) {
cfg := config.Mode | nrf.GPIO_PIN_CNF_DRIVE_S0S1 | nrf.GPIO_PIN_CNF_SENSE_Disabled
@@ -59,6 +71,65 @@ func (p Pin) Get() bool {
return (port.IN.Get()>>pin)&1 != 0
}
// SetInterrupt sets an interrupt to be executed when a particular pin changes
// state.
//
// 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 {
// Some variables to easily check whether a channel was already configured
// as an event channel for the given pin.
// This is not just an optimization, this is requred: the datasheet says
// that configuring more than one channel for a given pin results in
// unpredictable behavior.
expectedConfigMask := uint32(nrf.GPIOTE_CONFIG_MODE_Msk | nrf.GPIOTE_CONFIG_PSEL_Msk)
expectedConfig := nrf.GPIOTE_CONFIG_MODE_Event<<nrf.GPIOTE_CONFIG_MODE_Pos | uint32(p)<<nrf.GPIOTE_CONFIG_PSEL_Pos
foundChannel := false
for i := range nrf.GPIOTE.CONFIG {
config := nrf.GPIOTE.CONFIG[i].Get()
if config == 0 || config&expectedConfigMask == expectedConfig {
// Found an empty GPIOTE channel or one that was already configured
// for this pin.
if callback == nil {
// Disable this channel.
nrf.GPIOTE.INTENCLR.Set(uint32(1 << uint(i)))
pinCallbacks[i] = nil
return nil
}
// Enable this channel with the given callback.
nrf.GPIOTE.INTENCLR.Set(uint32(1 << uint(i)))
nrf.GPIOTE.CONFIG[i].Set(nrf.GPIOTE_CONFIG_MODE_Event<<nrf.GPIOTE_CONFIG_MODE_Pos |
uint32(p)<<nrf.GPIOTE_CONFIG_PSEL_Pos |
uint32(change)<<nrf.GPIOTE_CONFIG_POLARITY_Pos)
pinCallbacks[i] = callback
nrf.GPIOTE.INTENSET.Set(uint32(1 << uint(i)))
foundChannel = true
break
}
}
if !foundChannel {
return ErrNoPinChangeChannel
}
// Set and enable the GPIOTE interrupt. It's not a problem if this happens
// more than once.
interrupt.New(nrf.IRQ_GPIOTE, func(interrupt.Interrupt) {
for i := range nrf.GPIOTE.EVENTS_IN {
if nrf.GPIOTE.EVENTS_IN[i].Get() != 0 {
nrf.GPIOTE.EVENTS_IN[i].Set(0)
pin := Pin((nrf.GPIOTE.CONFIG[i].Get() & nrf.GPIOTE_CONFIG_PSEL_Msk) >> nrf.GPIOTE_CONFIG_PSEL_Pos)
pinCallbacks[i](pin)
}
}
}).Enable()
// Everything was configured correctly.
return nil
}
// UART on the NRF.
type UART struct {
Buffer *RingBuffer
@@ -372,13 +443,13 @@ func (spi SPI) Tx(w, r []byte) error {
spi.Bus.TXD.Set(uint32(b))
for spi.Bus.EVENTS_READY.Get() == 0 {
}
_ = spi.Bus.RXD.Get()
spi.Bus.EVENTS_READY.Set(0)
_ = spi.Bus.RXD.Get()
}
for spi.Bus.EVENTS_READY.Get() == 0 {
}
_ = spi.Bus.RXD.Get()
spi.Bus.EVENTS_READY.Set(0)
_ = spi.Bus.RXD.Get()
default:
// write/read
+2 -1
View File
@@ -159,7 +159,8 @@ func InitPWM() {
}
// Configure configures a PWM pin for output.
func (pwm PWM) Configure() {
func (pwm PWM) Configure() error {
return nil
}
// Set turns on the duty cycle for a PWM pin using the provided value.
+2 -2
View File
@@ -104,8 +104,8 @@ func InitADC() {
}
// Configure configures an ADC pin to be able to read analog data.
func (a ADC) Configure() {
return // no pin specific setup on nrf52840 machine.
func (a ADC) Configure() error {
return nil // no pin specific setup on nrf52840 machine.
}
// Get returns the current value of a ADC pin in the range 0..0xffff.
+5
View File
@@ -0,0 +1,5 @@
package os
func Getenv(key string) string {
return ""
}
+39
View File
@@ -0,0 +1,39 @@
package os
import (
"errors"
)
var (
ErrInvalid = errors.New("invalid argument")
ErrPermission = errors.New("permission denied")
ErrClosed = errors.New("file already closed")
// Portable analogs of some common system call errors.
// Note that these are exported for use in the Filesystem interface.
ErrUnsupported = errors.New("operation not supported")
ErrNotImplemented = errors.New("operation not implemented")
ErrNotExist = errors.New("file not found")
ErrExist = errors.New("file exists")
)
func IsPermission(err error) bool {
return err == ErrPermission
}
func NewSyscallError(syscall string, err error) error {
if err == nil {
return nil
}
return &SyscallError{syscall, err}
}
// SyscallError records an error from a specific system call.
type SyscallError struct {
Syscall string
Err error
}
func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() }
func (e *SyscallError) Unwrap() error { return e.Err }
+6
View File
@@ -0,0 +1,6 @@
package os
type Signal interface {
String() string
Signal() // to distinguish from other Stringers
}
+108 -58
View File
@@ -6,53 +6,132 @@
package os
import (
"errors"
"syscall"
)
// Portable analogs of some common system call errors.
var (
errUnsupported = errors.New("operation not supported")
notImplemented = errors.New("os: not implemented")
)
// Mkdir creates a directory. If the operation fails, it will return an error of
// type *PathError.
func Mkdir(path string, perm FileMode) error {
fs, suffix := findMount(path)
if fs == nil {
return &PathError{"mkdir", path, ErrNotExist}
}
err := fs.Mkdir(suffix, perm)
if err != nil {
return &PathError{"mkdir", path, err}
}
return nil
}
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
// standard output, and standard error file descriptors.
var (
Stdin = &File{0, "/dev/stdin"}
Stdout = &File{1, "/dev/stdout"}
Stderr = &File{2, "/dev/stderr"}
)
// Remove removes a file or (empty) directory. If the operation fails, it will
// return an error of type *PathError.
func Remove(path string) error {
fs, suffix := findMount(path)
if fs == nil {
return &PathError{"remove", path, ErrNotExist}
}
err := fs.Remove(suffix)
if err != nil {
return &PathError{"remove", path, err}
}
return nil
}
// File represents an open file descriptor.
type File struct {
fd uintptr
name string
handle FileHandle
name string
}
// Name returns the name of the file with which it was opened.
func (f *File) Name() string {
return f.name
}
// OpenFile opens the named file. If the operation fails, the returned error
// will be of type *PathError.
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
fs, suffix := findMount(name)
if fs == nil {
return nil, &PathError{"open", name, ErrNotExist}
}
handle, err := fs.OpenFile(suffix, flag, perm)
if err != nil {
return nil, &PathError{"open", name, err}
}
return &File{name: name, handle: handle}, nil
}
// Open opens the file named for reading.
func Open(name string) (*File, error) {
return OpenFile(name, O_RDONLY, 0)
}
// Create creates the named file, overwriting it if it already exists.
func Create(name string) (*File, error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}
// Read reads up to len(b) bytes from the File. It returns the number of bytes
// read and any error encountered. At end of file, Read returns 0, io.EOF.
func (f *File) Read(b []byte) (n int, err error) {
n, err = f.handle.Read(b)
if err != nil {
err = &PathError{"read", f.name, err}
}
return
}
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
return 0, ErrNotImplemented
}
// Write writes len(b) bytes to the File. It returns the number of bytes written
// and an error, if any. Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
n, err = f.handle.Write(b)
if err != nil {
err = &PathError{"write", f.name, err}
}
return
}
// Close closes the File, rendering it unusable for I/O.
func (f *File) Close() (err error) {
err = f.handle.Close()
if err != nil {
err = &PathError{"close", f.name, err}
}
return
}
// Readdir is a stub, not yet implemented
func (f *File) Readdir(n int) ([]FileInfo, error) {
return nil, notImplemented
return nil, &PathError{"readdir", f.name, ErrNotImplemented}
}
// Readdirnames is a stub, not yet implemented
func (f *File) Readdirnames(n int) (names []string, err error) {
return nil, notImplemented
return nil, &PathError{"readdirnames", f.name, ErrNotImplemented}
}
// Stat is a stub, not yet implemented
func (f *File) Stat() (FileInfo, error) {
return nil, notImplemented
return nil, &PathError{"stat", f.name, ErrNotImplemented}
}
// NewFile returns a new File with the given file descriptor and name.
func NewFile(fd uintptr, name string) *File {
return &File{fd, name}
// Sync is a stub, not yet implemented
func (f *File) Sync() error {
return ErrNotImplemented
}
// Fd returns the integer Unix file descriptor referencing the open file. The
// file descriptor is valid only until f.Close is called.
func (f *File) SyscallConn() (syscall.RawConn, error) {
return nil, ErrNotImplemented
}
// Fd returns the file handle referencing the open file.
func (f *File) Fd() uintptr {
return f.fd
panic("unimplemented: os.file.Fd()")
}
const (
@@ -72,32 +151,8 @@ type PathError struct {
Err error
}
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
// Open is a super simple stub function (for now), only capable of opening stdin, stdout, and stderr
func Open(name string) (*File, error) {
fd := uintptr(999)
switch name {
case "/dev/stdin":
fd = 0
case "/dev/stdout":
fd = 1
case "/dev/stderr":
fd = 2
default:
return nil, &PathError{"open", name, notImplemented}
}
return &File{fd, name}, nil
}
// OpenFile is a stub, passing through to the stub Open() call
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
return Open(name)
}
// Create is a stub, passing through to the stub Open() call
func Create(name string) (*File, error) {
return Open(name)
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
}
type FileMode uint32
@@ -148,19 +203,19 @@ type FileInfo interface {
Name() string // base name of the file
Size() int64 // length in bytes for regular files; system-dependent for others
Mode() FileMode // file mode bits
// ModTime() time.Time // modification time
// TODO ModTime() time.Time // modification time
IsDir() bool // abbreviation for Mode().IsDir()
Sys() interface{} // underlying data source (can return nil)
}
// Stat is a stub, not yet implemented
func Stat(name string) (FileInfo, error) {
return nil, notImplemented
return nil, &PathError{"stat", name, ErrNotImplemented}
}
// Lstat is a stub, not yet implemented
func Lstat(name string) (FileInfo, error) {
return nil, notImplemented
return nil, &PathError{"lstat", name, ErrNotImplemented}
}
// Getwd is a stub (for now), always returning an empty string
@@ -178,11 +233,6 @@ func TempDir() string {
return "/tmp"
}
// Mkdir is a stub, not yet implemented
func Mkdir(name string, perm FileMode) error {
return notImplemented
}
// IsExist is a stub (for now), always returning false
func IsExist(err error) bool {
return false
+24 -8
View File
@@ -6,28 +6,44 @@ import (
_ "unsafe"
)
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
// standard output, and standard error file descriptors.
var (
Stdin = &File{stdioFileHandle(0), "/dev/stdin"}
Stdout = &File{stdioFileHandle(1), "/dev/stdout"}
Stderr = &File{stdioFileHandle(2), "/dev/stderr"}
)
// isOS indicates whether we're running on a real operating system with
// filesystem support.
const isOS = false
// stdioFileHandle represents one of stdin, stdout, or stderr depending on the
// number. It implements the FileHandle interface.
type stdioFileHandle uint8
// Read is unsupported on this system.
func (f *File) Read(b []byte) (n int, err error) {
return 0, errUnsupported
func (f stdioFileHandle) Read(b []byte) (n int, err error) {
return 0, ErrUnsupported
}
// Write writes len(b) bytes to the output. It returns the number of bytes
// written or an error if this file is not stdout or stderr.
func (f *File) Write(b []byte) (n int, err error) {
switch f.fd {
case Stdout.fd, Stderr.fd:
func (f stdioFileHandle) Write(b []byte) (n int, err error) {
switch f {
case 1, 2: // stdout, stderr
for _, c := range b {
putchar(c)
}
return len(b), nil
default:
return 0, errUnsupported
return 0, ErrUnsupported
}
}
// Close is unsupported on this system.
func (f *File) Close() error {
return errUnsupported
func (f stdioFileHandle) Close() error {
return ErrUnsupported
}
//go:linkname putchar runtime.putchar
+92 -6
View File
@@ -6,19 +6,105 @@ import (
"syscall"
)
func init() {
// Mount the host filesystem at the root directory. This is what most
// programs will be expecting.
Mount("/", unixFilesystem{})
}
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
// standard output, and standard error file descriptors.
var (
Stdin = &File{unixFileHandle(0), "/dev/stdin"}
Stdout = &File{unixFileHandle(1), "/dev/stdout"}
Stderr = &File{unixFileHandle(2), "/dev/stderr"}
)
// isOS indicates whether we're running on a real operating system with
// filesystem support.
const isOS = true
// unixFilesystem is an empty handle for a Unix/Linux filesystem. All operations
// are relative to the current working directory.
type unixFilesystem struct {
}
func (fs unixFilesystem) Mkdir(path string, perm FileMode) error {
return handleSyscallError(syscall.Mkdir(path, uint32(perm)))
}
func (fs unixFilesystem) Remove(path string) error {
return handleSyscallError(syscall.Unlink(path))
}
func (fs unixFilesystem) OpenFile(path string, flag int, perm FileMode) (FileHandle, error) {
// Map os package flags to syscall flags.
syscallFlag := 0
if flag&O_RDONLY != 0 {
syscallFlag |= syscall.O_RDONLY
}
if flag&O_WRONLY != 0 {
syscallFlag |= syscall.O_WRONLY
}
if flag&O_RDWR != 0 {
syscallFlag |= syscall.O_RDWR
}
if flag&O_APPEND != 0 {
syscallFlag |= syscall.O_APPEND
}
if flag&O_CREATE != 0 {
syscallFlag |= syscall.O_CREAT
}
if flag&O_EXCL != 0 {
syscallFlag |= syscall.O_EXCL
}
if flag&O_SYNC != 0 {
syscallFlag |= syscall.O_SYNC
}
if flag&O_TRUNC != 0 {
syscallFlag |= syscall.O_TRUNC
}
fp, err := syscall.Open(path, syscallFlag, uint32(perm))
return unixFileHandle(fp), handleSyscallError(err)
}
// unixFileHandle is a Unix file pointer with associated methods that implement
// the FileHandle interface.
type unixFileHandle uintptr
// Read reads up to len(b) bytes from the File. It returns the number of bytes
// read and any error encountered. At end of file, Read returns 0, io.EOF.
func (f *File) Read(b []byte) (n int, err error) {
return syscall.Read(int(f.fd), b)
func (f unixFileHandle) Read(b []byte) (n int, err error) {
n, err = syscall.Read(int(f), b)
err = handleSyscallError(err)
return
}
// Write writes len(b) bytes to the File. It returns the number of bytes written
// and an error, if any. Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
return syscall.Write(int(f.fd), b)
func (f unixFileHandle) Write(b []byte) (n int, err error) {
n, err = syscall.Write(int(f), b)
err = handleSyscallError(err)
return
}
// Close closes the File, rendering it unusable for I/O.
func (f *File) Close() error {
return syscall.Close(int(f.fd))
func (f unixFileHandle) Close() error {
return handleSyscallError(syscall.Close(int(f)))
}
// handleSyscallError converts syscall errors into regular os package errors.
// The err parameter must be either nil or of type syscall.Errno.
func handleSyscallError(err error) error {
if err == nil {
return nil
}
switch err.(syscall.Errno) {
case syscall.EEXIST:
return ErrExist
case syscall.ENOENT:
return ErrNotExist
default:
return err
}
}
+85
View File
@@ -0,0 +1,85 @@
package os
import (
"strings"
)
// mounts lists the mount points currently mounted in the filesystem provided by
// the os package. To resolve a path to a mount point, it is scanned from top to
// bottom looking for the first prefix match.
var mounts []mountPoint
type mountPoint struct {
// prefix is a filesystem prefix, that always starts and ends with a forward
// slash. To denote the root filesystem, use a single slash: "/".
// This allows fast checking whether a path lies within a mount point.
prefix string
// filesystem is the Filesystem implementation that is mounted at this mount
// point.
filesystem Filesystem
}
// Filesystem provides an interface for generic filesystem drivers mounted in
// the os package. The errors returned must be one of the os.Err* errors, or a
// custom error if one doesn't exist. It should not be a *PathError because
// errors will be wrapped with a *PathError by the filesystem abstraction.
//
// WARNING: this interface is not finalized and may change in a future version.
type Filesystem interface {
// OpenFile opens the named file.
OpenFile(name string, flag int, perm FileMode) (FileHandle, error)
// Mkdir creates a new directoy with the specified permission (before
// umask). Some filesystems may not support directories or permissions.
Mkdir(name string, perm FileMode) error
// Remove removes the named file or (empty) directory.
Remove(name string) error
}
// FileHandle is an interface that should be implemented by filesystems
// implementing the Filesystem interface.
//
// WARNING: this interface is not finalized and may change in a future version.
type FileHandle interface {
// Read reads up to len(b) bytes from the file.
Read(b []byte) (n int, err error)
// Write writes up to len(b) bytes to the file.
Write(b []byte) (n int, err error)
// Close closes the file, making it unusable for further writes.
Close() (err error)
}
// findMount returns the appropriate (mounted) filesystem to use for a given
// filename plus the path relative to that filesystem.
func findMount(path string) (Filesystem, string) {
for i := len(mounts) - 1; i >= 0; i-- {
mount := mounts[i]
if strings.HasPrefix(path, mount.prefix) {
return mount.filesystem, path[len(mount.prefix)-1:]
}
}
if isOS {
// Assume that the first entry in the mounts slice is the OS filesystem
// at the root of the directory tree. Use it as-is, to support relative
// paths.
return mounts[0].filesystem, path
}
return nil, path
}
// Mount mounts the given filesystem in the filesystem abstraction layer of the
// os package. It is not possible to unmount filesystems. Filesystems added
// later will override earlier filesystems.
//
// The provided prefix must start and end with a forward slash. This is true for
// the root directory ("/") for example.
func Mount(prefix string, filesystem Filesystem) {
if prefix[0] != '/' || prefix[len(prefix)-1] != '/' {
panic("os.Mount: invalid prefix")
}
mounts = append(mounts, mountPoint{prefix, filesystem})
}
+9
View File
@@ -9,6 +9,15 @@ import (
"syscall"
)
// Args hold the command-line arguments, starting with the program name.
var Args []string
func init() {
Args = runtime_args()
}
func runtime_args() []string // in package runtime
// Exit causes the current program to exit with the given status code.
// Conventionally, code zero indicates success, non-zero an error.
// The program terminates immediately; deferred functions are not run.
+5
View File
@@ -0,0 +1,5 @@
package os
func Hostname() (name string, err error) {
return "", ErrNotImplemented
}
+9
View File
@@ -0,0 +1,9 @@
package reflect
func DeepEqual(x, y interface{}) bool {
if x == nil || y == nil {
return x == y
}
panic("unimplemented: reflect.DeepEqual()")
}
+248
View File
@@ -0,0 +1,248 @@
// Copyright 2009 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 reflect
import (
"unicode/utf8"
)
// errSyntax indicates that a value does not have the right syntax for the target type.
var errSyntax = badSyntax{}
type badSyntax struct{}
func (badSyntax) Error() string {
return "invalid syntax"
}
func unhex(b byte) (v rune, ok bool) {
c := rune(b)
switch {
case '0' <= c && c <= '9':
return c - '0', true
case 'a' <= c && c <= 'f':
return c - 'a' + 10, true
case 'A' <= c && c <= 'F':
return c - 'A' + 10, true
}
return
}
// unquoteChar decodes the first character or byte in the escaped string
// or character literal represented by the string s.
// It returns four values:
//
// 1) value, the decoded Unicode code point or byte value;
// 2) multibyte, a boolean indicating whether the decoded character requires a multibyte UTF-8 representation;
// 3) tail, the remainder of the string after the character; and
// 4) an error that will be nil if the character is syntactically valid.
//
// The second argument, quote, specifies the type of literal being parsed
// and therefore which escaped quote character is permitted.
// If set to a single quote, it permits the sequence \' and disallows unescaped '.
// If set to a double quote, it permits \" and disallows unescaped ".
// If set to zero, it does not permit either escape and allows both quote characters to appear unescaped.
func unquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) {
// easy cases
if len(s) == 0 {
err = errSyntax
return
}
switch c := s[0]; {
case c == quote && (quote == '\'' || quote == '"'):
err = errSyntax
return
case c >= utf8.RuneSelf:
r, size := utf8.DecodeRuneInString(s)
return r, true, s[size:], nil
case c != '\\':
return rune(s[0]), false, s[1:], nil
}
// hard case: c is backslash
if len(s) <= 1 {
err = errSyntax
return
}
c := s[1]
s = s[2:]
switch c {
case 'a':
value = '\a'
case 'b':
value = '\b'
case 'f':
value = '\f'
case 'n':
value = '\n'
case 'r':
value = '\r'
case 't':
value = '\t'
case 'v':
value = '\v'
case 'x', 'u', 'U':
n := 0
switch c {
case 'x':
n = 2
case 'u':
n = 4
case 'U':
n = 8
}
var v rune
if len(s) < n {
err = errSyntax
return
}
for j := 0; j < n; j++ {
x, ok := unhex(s[j])
if !ok {
err = errSyntax
return
}
v = v<<4 | x
}
s = s[n:]
if c == 'x' {
// single-byte string, possibly not UTF-8
value = v
break
}
if v > utf8.MaxRune {
err = errSyntax
return
}
value = v
multibyte = true
case '0', '1', '2', '3', '4', '5', '6', '7':
v := rune(c) - '0'
if len(s) < 2 {
err = errSyntax
return
}
for j := 0; j < 2; j++ { // one digit already; two more
x := rune(s[j]) - '0'
if x < 0 || x > 7 {
err = errSyntax
return
}
v = (v << 3) | x
}
s = s[2:]
if v > 255 {
err = errSyntax
return
}
value = v
case '\\':
value = '\\'
case '\'', '"':
if c != quote {
err = errSyntax
return
}
value = rune(c)
default:
err = errSyntax
return
}
tail = s
return
}
// unquote interprets s as a single-quoted, double-quoted,
// or backquoted Go string literal, returning the string value
// that s quotes. (If s is single-quoted, it would be a Go
// character literal; unquote returns the corresponding
// one-character string.)
func unquote(s string) (string, error) {
n := len(s)
if n < 2 {
return "", errSyntax
}
quote := s[0]
if quote != s[n-1] {
return "", errSyntax
}
s = s[1 : n-1]
if quote == '`' {
if contains(s, '`') {
return "", errSyntax
}
if contains(s, '\r') {
// -1 because we know there is at least one \r to remove.
buf := make([]byte, 0, len(s)-1)
for i := 0; i < len(s); i++ {
if s[i] != '\r' {
buf = append(buf, s[i])
}
}
return string(buf), nil
}
return s, nil
}
if quote != '"' && quote != '\'' {
return "", errSyntax
}
if contains(s, '\n') {
return "", errSyntax
}
// Is it trivial? Avoid allocation.
if !contains(s, '\\') && !contains(s, quote) {
switch quote {
case '"':
if utf8.ValidString(s) {
return s, nil
}
case '\'':
r, size := utf8.DecodeRuneInString(s)
if size == len(s) && (r != utf8.RuneError || size != 1) {
return s, nil
}
}
}
var runeTmp [utf8.UTFMax]byte
buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations.
for len(s) > 0 {
c, multibyte, ss, err := unquoteChar(s, quote)
if err != nil {
return "", err
}
s = ss
if c < utf8.RuneSelf || !multibyte {
buf = append(buf, byte(c))
} else {
n := utf8.EncodeRune(runeTmp[:], c)
buf = append(buf, runeTmp[:n]...)
}
if quote == '\'' && len(s) != 0 {
// single-quoted must be single character
return "", errSyntax
}
}
return string(buf), nil
}
// contains reports whether the string contains the byte c.
func contains(s string, c byte) bool {
return indexByteString(s, c) != -1
}
// Index finds the index of the first instance of the specified byte in the string.
// If the byte is not found, this returns -1.
func indexByteString(s string, c byte) int {
for i := 0; i < len(s); i++ {
if s[i] == c {
return i
}
}
return -1
}
+86 -2
View File
@@ -1,3 +1,7 @@
// Copyright 2009 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 reflect
import (
@@ -227,7 +231,7 @@ func (t Type) Field(i int) StructField {
// There is a tag.
var tagNum uintptr
tagNum, p = readVarint(p)
field.Tag = readStringSidetable(unsafe.Pointer(&structNamesSidetable), tagNum)
field.Tag = StructTag(readStringSidetable(unsafe.Pointer(&structNamesSidetable), tagNum))
} else {
// There is no tag.
field.Tag = ""
@@ -445,6 +449,22 @@ func (t Type) Comparable() bool {
}
}
func (t Type) ConvertibleTo(u Type) bool {
panic("unimplemented: (reflect.Type).ConvertibleTo()")
}
func (t Type) NumMethod() int {
panic("unimplemented: (reflect.Type).NumMethod()")
}
func (t Type) Name() string {
panic("unimplemented: (reflect.Type).Name()")
}
func (t Type) Key() Type {
panic("unimplemented: (reflect.Type).Key()")
}
// A StructField describes a single field in a struct.
type StructField struct {
// Name indicates the field name.
@@ -455,11 +475,75 @@ type StructField struct {
PkgPath string
Type Type
Tag string
Tag StructTag // field tag string
Anonymous bool
Offset uintptr
}
// A StructTag is the tag string in a struct field.
type StructTag string
// TODO: it would be feasible to do the key/value splitting at compile time,
// avoiding the code size cost of doing it at runtime
// Get returns the value associated with key in the tag string.
func (tag StructTag) Get(key string) string {
v, _ := tag.Lookup(key)
return v
}
// Lookup returns the value associated with key in the tag string.
func (tag StructTag) Lookup(key string) (value string, ok bool) {
for tag != "" {
// Skip leading space.
i := 0
for i < len(tag) && tag[i] == ' ' {
i++
}
tag = tag[i:]
if tag == "" {
break
}
// Scan to colon. A space, a quote or a control character is a syntax error.
// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
// as it is simpler to inspect the tag's bytes than the tag's runes.
i = 0
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
i++
}
if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
break
}
name := string(tag[:i])
tag = tag[i+1:]
// Scan quoted string to find value.
i = 1
for i < len(tag) && tag[i] != '"' {
if tag[i] == '\\' {
i++
}
i++
}
if i >= len(tag) {
break
}
qvalue := string(tag[:i+1])
tag = tag[i+1:]
if key == name {
value, err := unquote(qvalue)
if err != nil {
break
}
return value, true
}
}
return "", false
}
// TypeError is the error that is used in a panic when invoking a method on a
// type that is not applicable to that type.
type TypeError struct {
+68 -7
View File
@@ -304,29 +304,48 @@ func (v Value) Slice(i, j int) Value {
panic("unimplemented: (reflect.Value).Slice()")
}
//go:linkname maplen runtime.hashmapLenUnsafePointer
func maplen(p unsafe.Pointer) int
//go:linkname chanlen runtime.chanLenUnsafePointer
func chanlen(p unsafe.Pointer) int
// Len returns the length of this value for slices, strings, arrays, channels,
// and maps. For oter types, it panics.
// and maps. For other types, it panics.
func (v Value) Len() int {
t := v.Type()
switch t.Kind() {
case Array:
return v.Type().Len()
case Chan:
return chanlen(v.value)
case Map:
return maplen(v.value)
case Slice:
return int((*SliceHeader)(v.value).Len)
case String:
return int((*StringHeader)(v.value).Len)
case Array:
return v.Type().Len()
default: // Chan, Map
panic("unimplemented: (reflect.Value).Len()")
default:
panic(&ValueError{"Len"})
}
}
//go:linkname chancap runtime.chanCapUnsafePointer
func chancap(p unsafe.Pointer) int
// Cap returns the capacity of this value for arrays, channels and slices.
// For other types, it panics.
func (v Value) Cap() int {
t := v.Type()
switch t.Kind() {
case Array:
return v.Type().Len()
case Chan:
return chancap(v.value)
case Slice:
return int((*SliceHeader)(v.value).Cap)
default: // Array, Chan
panic("unimplemented: (reflect.Value).Cap()")
default:
panic(&ValueError{"Cap"})
}
}
@@ -642,6 +661,18 @@ func (v Value) checkAddressable() {
}
}
func (v Value) OverflowInt(x int64) bool {
panic("unimplemented: reflect.OverflowInt()")
}
func (v Value) OverflowUint(x uint64) bool {
panic("unimplemented: reflect.OverflowUint()")
}
func (v Value) Convert(t Type) Value {
panic("unimplemented: (reflect.Value).Convert()")
}
func MakeSlice(typ Type, len, cap int) Value {
panic("unimplemented: reflect.MakeSlice()")
}
@@ -681,3 +712,33 @@ func (e *ValueError) Error() string {
// Calls to this function are converted to LLVM intrinsic calls such as
// llvm.memcpy.p0i8.p0i8.i32().
func memcpy(dst, src unsafe.Pointer, size uintptr)
// Copy copies the contents of src into dst until either
// dst has been filled or src has been exhausted.
func Copy(dst, src Value) int {
panic("unimplemented: reflect.Copy()")
}
// Append appends the values x to a slice s and returns the resulting slice.
// As in Go, each x's value must be assignable to the slice's element type.
func Append(s Value, x ...Value) Value {
panic("unimplemented: reflect.Append()")
}
func (v Value) SetMapIndex(key, elem Value) {
panic("unimplemented: (reflect.Value).SetMapIndex()")
}
// FieldByIndex returns the nested field corresponding to index.
func (v Value) FieldByIndex(index []int) Value {
panic("unimplemented: (reflect.Value).FieldByIndex()")
}
func (v Value) FieldByName(name string) Value {
panic("unimplemented: (reflect.Value).FieldByName()")
}
// MakeMap creates a new map with the specified type.
func MakeMap(typ Type) Value {
panic("unimplemented: reflect.MakeMap()")
}
+1 -1
View File
@@ -15,5 +15,5 @@ func align(ptr uintptr) uintptr {
}
func getCurrentStackPointer() uintptr {
return arm.ReadRegister("sp")
return arm.AsmFull("mov {}, sp", nil)
}

Some files were not shown because too many files have changed in this diff Show More