Compare commits

..

53 Commits

Author SHA1 Message Date
Ayke van Laethem 1d913a62bc main: version 0.12.0 2020-01-31 00:01:47 +01:00
Ayke van Laethem 24a0f237d8 wasm: use wasi ABI for basic startup/stdout
This allows TinyGo-built binaries to run under wasmtime, for example:

    tinygo build -o test.wasm -no-debug -target=wasm examples/test
    wasmtime run test.wasm 0
2020-01-28 20:31:09 +01:00
BCG eb9c2c276e Added indexBytePortal from standard library to link as implementation for internal/bytealg.IndexByte 2020-01-28 20:17:11 +01:00
Ayke van Laethem 519adf3aef transform: wasm-abi: create temporary allocas in the entry block
This avoids problems with goroutines in WebAssembly, and is generally a
good thing. It fixes some cases of the following problem:

    LLVM ERROR: Coroutines cannot handle non static allocas yet
2020-01-28 19:29:09 +01:00
Ayke van Laethem 4d79d473c4 compiler: move wasm ABI workaround to transform package
By considering this as a regular transformation, it can be easily
tested.
2020-01-28 19:29:09 +01:00
Ron Evans 91299b6466 docs: correct count for list of supported boards
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-27 23:10:58 +01:00
Ayke van Laethem 8687f3f8f4 targets/gba: implement interrupt handler
Thanks to Kyle Lemons for the inspiration and original design. The
implementation in this commit is very different however, building on top
of the software vectoring needed in RISC-V. The result is a flexible
interrupt handler that does not take up any RAM for configuration.
2020-01-27 21:56:17 +01:00
Ayke van Laethem f14127be76 targets/gba: make linker script cleaner
Make it clearer where the stack is located. Additionally, get the heap
to work (the GC needs to have _stack_top defined to work correctly).
2020-01-27 21:56:17 +01:00
Ron Evans a9b2d8c294 docs: add Arduino Nano to list of supported boards
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-27 21:02:29 +01:00
Ayke van Laethem 960ab3fca4 runtime: fix external address declarations
This is the same problem as in
https://github.com/tinygo-org/tinygo/pull/605, but other targets also
suffer from it.

Discovered with the GBA target, but as pointed out in
https://bugs.llvm.org/show_bug.cgi?id=42881#c1 this appears to be a bug
in the way external globals are declared, not in LLVM. Therefore I
decided that fixing it everywhere would be the best thing to do.
2020-01-27 20:34:10 +01:00
Ayke van Laethem 94fec09b31 machine/fe310: implement UART receive interrupts
This makes the echo example work on the HiFive1 rev B board.
2020-01-27 19:58:39 +01:00
Ayke van Laethem 415c60551e runtime/fe310: add support for PLIC interrupts
This commit adds support for software vectoring in the PLIC interrupt.
The interrupt table is created by the compiler, which leads to very
compact code while retaining the flexibility that the interrupt API
provides.
2020-01-27 19:58:39 +01:00
Ayke van Laethem a9174d9184 runtime/fe310: do peripheral initialization in an explicit call
This avoids some issues with interp in the next commit.
2020-01-27 19:58:39 +01:00
Ayke van Laethem 46ccb6793e main: differentiate between various serial/USB error messages
This way it is possible to determine the source of an error when it
happens.
2020-01-27 19:46:53 +01:00
Ayke van Laethem 15c7d93ea9 avr: use a garbage collector
This might sound crazy, but I think it's better to enable the GC by
default to avoid surprises. It costs 1130 bytes of flash and 16 bytes of
RAM (plus heap overhead) so it's not exactly free, but if needed it can
easily be disabled with `-gc=leaking`. On the Uno (32kB flash, 2kB RAM)
that's not massive, on the DigiSpark (8kB flash, 0.5kB RAM) that may be
too much depending on the application.
2020-01-27 19:01:55 +01:00
Ayke van Laethem 6e26728391 compiler: remove some dead code 2020-01-27 08:27:14 +01:00
Ayke van Laethem 0d34f933eb compiler,runtime: implement maps for arbitrary keys
This implementation simply casts types without special support to an
interface, to make the implementation simpler and possibly reducing the
code size too. It will likely be slower than the canonical Go
implementation though (which builds special compare and hash functions
at compile time).
2020-01-27 08:27:14 +01:00
Ayke van Laethem 440dc8ed4e compiler: move making maps to the map.go file
This is a useful refactor for when I add support for any type in map
keys.
2020-01-27 08:27:14 +01:00
Ayke van Laethem aeb2e6f70a all: add Arduino Nano support 2020-01-26 23:24:45 +01:00
Ayke van Laethem e830acadf3 ci: fix Windows build failure for wasi-libc
The wasi-libc Makefile uses the `find` command line tool. Unfortunately,
it was using the Windows find version instead of the MinGW version,
leading to lots of errors at a later stage.

This commit prepends /usr/bin to `$PATH` to make sure the MinGW version
is found first.
2020-01-25 17:11:45 +01:00
Ron Evans 3f9609560e flash: remove default port check for Digispark as micronucleus communicates directly using HID
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-25 13:49:27 +01:00
Ayke van Laethem e2aa3789c3 wasm: include wasi-libc
This allows CGo code to call some libc functions. Additionally, by
putting memset/memmove/memcpy in an archive they're not included anymore
when not necessary, reducing code size for small programs.
2020-01-23 16:40:09 +01:00
Ayke van Laethem c61657d22d machine: refactor pin handling to auto-detect pin mode
With this change, it's no longer necessary to set a specific pin mode:
it will get autodetected in the Configure() call.

Tested on an ItsyBitsy M4 with the mpu6050 example in the drivers repo.
2020-01-23 00:08:34 +01:00
Ayke van Laethem 4c0ebb5b41 arduino: make avrdude command line compatible with Windows
On Windows, it is common that there is a colon in the path. avrdude will
treat that as a separator and everything behind it as the file format
specifier instead of defaulting to Intel hex format.

By explicitly specifying the Intel hex format (with `:i`), this issue
should be fixed.
2020-01-21 07:49:17 +01:00
Ayke van Laethem e17a2e6776 machine/atsamd51: use only the necessary UART interrupts
A small footnote in the datasheet says that interrupt source numbers
correspond to the bit position in INTFLAG. We only need the RXC
interrupt for UART. In other words, ony the _2 interrupts (RXC is in the
2nd bit position) needs to be used for UART to work correctly.

In the future, more interrupts may be needed. They can then be added as
necessary.
2020-01-20 21:33:53 +01:00
Ayke van Laethem bdfa4d28cf machine/atsamd51: fix obvious bug in I2C code
I2C uses a hardcoded peripheral instead of referring to a specific
peripheral. In addition to that, it refers to the wrong SERCOM
(SERCOM3), which isn't used on any of the atsamd51 boards for I2C.
2020-01-20 21:33:53 +01:00
Ayke van Laethem 3745fb1c40 machine/atsamd51: switch UART to use new pin configuration
This makes UART configuration much more flexible.

I confirmed that UART1 and UART2 still work with this change on the
ItsyBitsy M4.
2020-01-20 21:33:53 +01:00
Ayke van Laethem a5ed993f8d all: add compiler support for interrupts
This commit lets the compiler know about interrupts and allows
optimizations to be performed based on that: interrupts are eliminated
when they appear to be unused in a program. This is done with a new
pseudo-call (runtime/interrupt.New) that is treated specially by the
compiler.
2020-01-20 21:19:12 +01:00
Ayke van Laethem 3729fcfa9e wasm: don't skip the GC test
Finally, all tests run on all targets!
2020-01-20 20:30:42 +01:00
Ayke van Laethem 8f8232aada compileopts: fix CGo when cross compiling
Use the cross compiling toolchains for compiling/linking. This fixes CGo
support, and therefore allows CGo to be used when cross compiling to
Linux on a different architecture.
This commit also removes some redundant testing code.
2020-01-20 20:30:42 +01:00
Ayke van Laethem d5e11fa19b compiler: do not emit debug info for extern globals
This results in a link error in the following commit (undefined
reference to runtime.trackedGlobalsBitmap from .debug_info). Solution:
don't emit debug info for declared but not defined symbols.
2020-01-20 20:30:42 +01:00
Ayke van Laethem 4ee7bf00e1 machine: avoid bytes package in USB logic
This greatly cuts down on compile time (by about 5x for small programs)
and also makes the program a whole lot smaller. Overall it cuts down
`make smoke-test` in the drivers repository by half (from 160s to 80s).

This will probably also fix the timeout issue in the Playground:
https://github.com/tinygo-org/playground/issues/7
2020-01-18 14:23:49 +01:00
Ayke van Laethem c698e99880 gameboy-advance: include compiler-rt in build
This avoids errors like the following:

    ld.lld-9: error: undefined symbol: __umodsi3
    ld.lld-9: error: undefined symbol: __aeabi_uidivmod
2020-01-16 08:11:04 +01:00
Ayke van Laethem 4d5dafd360 main: fix race condition in tests
This caused most tests to run the zeroalloc.go test instead of what they
should have been tested, and in turn explains most of the performance
gains of parallel testing.

This commit fixes it by avoiding race conditions. Luckily, no tests
started failing since then due to this.
2020-01-14 16:40:13 +01:00
Ayke van Laethem 1a32a68674 compiler: add support for CGO_ENABLED environment variable 2020-01-14 07:13:12 +01:00
Ayke van Laethem 9a69c6bcca hifive1b: add support for gdb subcommand
This makes debugging on the HiFive1 rev B much easier:

    tinygo gdb -target=hifive1b examples/echo

Using JLinkGDBServer as I couldn't figure out how to do it with OpenOCD.
2020-01-12 17:02:53 +01:00
Ayke van Laethem 6841f9e245 compiler: add support for debugging through JLinkGDBServer
Some J-Link targets aren't supported in OpenOCD (or would need some
special configuration), so also give the option to use JLinkGDBServer
instead.
2020-01-12 17:02:53 +01:00
Ron Evans ef4ede43d1 machine/pyportal: remove manual SPI pin mapping as now handled by default
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-11 21:31:33 +01:00
Ayke van Laethem 31d3ac725f machine/atsamd51: refactor SPI pin configuration to only look at pin numbers
This commit does the same thing as
https://github.com/tinygo-org/tinygo/pull/597 but for samd51 series
chips. Pin mode and pad numbers are automatically calculated from pin
numbers, returning an error if no valid pinout is possible.
2020-01-11 20:57:10 +01:00
Ron Evans 7ace991a2b machine/digispark: add clock speed and pin mappings 2020-01-11 12:58:37 +01:00
Ayke van Laethem b258f3424b riscv: print exception PC and code
This can be useful for debugging critical bugs in code. I haven't added
human-readable exceptions (such as "illegal instruction" or "stack
overflow") yet, they can be added when they happen in practice (to avoid
increasing code size too much).
2020-01-11 12:27:58 +01:00
Ayke van Laethem b9cdfd9e9a riscv: add bare-bones interrupt support
This commit adds support for timer interrupts, replacing the busy loop
that was used before. It is perhaps the most simple interrupt to
implement and should serve as the basis for further interrupt support in
RISC-V.
2020-01-10 08:04:13 +01:00
Ayke van Laethem 360923abbf compiler,riscv: implement CSR operations as intrinsics
CSR operations must be implemented in assembly. The easiest way to
implement them is with some custom intrinsics in the compiler.
2020-01-10 08:04:13 +01:00
Ayke van Laethem ed9b2dbc03 runtime/hifive1: use CLINT peripheral for timekeeping
The CLINT is implemented both on the fe310-g002 chip and in the sifive_e
QEMU machine type. Therefore, use that peripheral for consistency.

The only difference is the clock speed, which runs at 10MHz in QEMU for
some reason instead of 32.768kHz as on the physical HiFive1 boards.
2020-01-10 08:04:13 +01:00
Ayke van Laethem 3e521f710a compiler: add support for debugging globals
This makes most globals visible from GDB, using `info variables`.
2020-01-07 20:20:19 +01:00
BCG ecff6f8e0c Adafruit PyPortal Support (#800)
* machine/PyPortal: implementation, also includes refinements for other members of samd51x familty.
2020-01-06 11:53:43 +01:00
Jaden Weiss 7363bdc298 Add Jaden Weiss to CONTRIBUTORS 2020-01-06 09:24:20 +01:00
Michael Matloob 1cb9b948bc targets: add target circuitplay-bluefruit
Add a target for the Adafruit Circuit Playground Bluefruit, which is
based on the nRF52840. Adds the necessary code for the machine
package and the json and linker script files in the targets directory.
The machine package code is based on board_circuitplay_express.go,
with modifications made by consulting the wiring diagram on the
adafruit website here:
https://learn.adafruit.com/adafruit-circuit-playground-bluefruit/downloads

Also adds support to the uf2 conversion packacge to set the familyID
field. The Circuit Playground Bluefruit firmware rejects uf2 files
without the family id set to 0xADA52840 (and without the flag specifying
that the family id is present).
2020-01-06 09:23:39 +01:00
Ayke van Laethem 25cff20117 interp: error location for "unknown GEP" error
This commit removes a panic and replaces it with a proper source
location. The message still isn't very helpful, but at least it points
to a location in the source code.

I'm not very happy with all the `err.Error()` calls, but that's the way
to fit this in a `scanner.Error`. Eventually we should make a
replacement for `scanner.Error` that does proper wrapping of the
original error message.
2020-01-05 13:45:23 +01:00
Ayke van Laethem ec467da83c ci: produce zip as artifact in Azure Pipelines
Zip files are far more often used on Windows systems, so we provide zip
files in releases. Unfortunately, previously the provided artifact in CI
was really just a compressed .tar.gz file, which defeats the purpose.

This commit zips the release tarball itself so it can be downloaded as
an artifact.
2020-01-05 12:43:21 +01:00
BCG 5f77447e1a Adding code use 12-bit resolution for ADC and to clear previous ADC INPUTCTRL before subsequent read 2020-01-05 12:34:47 +01:00
Ayke van Laethem 27fafb7ab5 runtime: fix atsamd51 volatile usage
It was still using the (long removed) //go:volatile pragma for volatile
variables, thus it was only accidentally working.
2020-01-05 12:08:33 +01:00
Ayke van Laethem d1cc3c109d compiler: add globaldce pass to start of optimization pipeline
This reduces code size in a few cases when tested against the drivers
smoketests (although there was one minor increase) without significantly
increasing compile time. In fact, in my testing compile time appears to
be going down a little bit (around 1%, within the noise).
2020-01-05 11:37:26 +01:00
104 changed files with 2739 additions and 815 deletions
+32 -2
View File
@@ -23,9 +23,7 @@ commands:
libclang<<parameters.llvm>>-dev \
lld<<parameters.llvm>> \
gcc-arm-linux-gnueabihf \
libc6-dev-armel-cross \
gcc-aarch64-linux-gnu \
libc6-dev-arm64-cross \
qemu-system-arm \
qemu-user \
gcc-avr \
@@ -51,6 +49,18 @@ commands:
key: llvm-source-9-v0
paths:
- llvm-project
build-wasi-libc:
steps:
- restore_cache:
keys:
- wasi-libc-sysroot-v0
- run:
name: "Build wasi-libc"
command: make wasi-libc CLANG=$PWD/llvm-build/bin/clang LLVM_AR=$PWD/llvm-build/bin/llvm-ar LLVM_NM=$PWD/llvm-build/bin/llvm-nm
- save_cache:
key: wasi-libc-sysroot-v0
paths:
- lib/wasi-libc/sysroot
test-linux:
steps:
- checkout
@@ -64,6 +74,14 @@ commands:
- go-cache-v2-{{ checksum "go.mod" }}
- llvm-source-linux
- run: go install .
- restore_cache:
keys:
- wasi-libc-sysroot-systemclang-v0
- run: make wasi-libc
- save_cache:
key: wasi-libc-sysroot-systemclang-v0
paths:
- lib/wasi-libc/sysroot
- run: go test -v ./cgo ./compileopts ./interp ./transform .
- run: make gen-device -j4
- run: make smoketest
@@ -121,6 +139,7 @@ commands:
paths:
llvm-build
- run: make ASSERT=1
- build-wasi-libc
- run:
name: "Test TinyGo"
command: make ASSERT=1 test
@@ -178,6 +197,7 @@ commands:
key: llvm-build-9-linux-v0
paths:
llvm-build
- build-wasi-libc
- run:
name: "Test TinyGo"
command: make test
@@ -244,6 +264,16 @@ commands:
key: llvm-build-9-macos-v0
paths:
llvm-build
- restore_cache:
keys:
- wasi-libc-sysroot-macos-v0
- run:
name: "Build wasi-libc"
command: make wasi-libc CLANG=$PWD/llvm-build/bin/clang LLVM_AR=$PWD/llvm-build/bin/llvm-ar LLVM_NM=$PWD/llvm-build/bin/llvm-nm
- save_cache:
key: wasi-libc-sysroot-macos-v0
paths:
- lib/wasi-libc/sysroot
- run:
name: "Test TinyGo"
command: make test
+3
View File
@@ -14,3 +14,6 @@
path = lib/compiler-rt
url = https://github.com/llvm-mirror/compiler-rt.git
branch = release_80
[submodule "lib/wasi-libc"]
path = lib/wasi-libc
url = https://github.com/CraneStation/wasi-libc
+64
View File
@@ -1,3 +1,67 @@
0.12.0
---
* **command line**
- add initial FreeBSD support
- remove getting a serial port in gdb subcommand
- add support for debugging through JLinkGDBServer
- fix CGo when cross compiling
- remove default port check for Digispark as micronucleus communicates directly using HID
- differentiate between various serial/USB error messages
* **builder**
- improve detection of Clang headers
* **compiler**
- fix assertion on empty interface
- don't crash when encountering `types.Invalid`
- revise defer to use heap allocations when running a variable number of times
- improve error messages for failed imports
- improve "function redeclared" error
- add globaldce pass to start of optimization pipeline
- add support for debugging globals
- implement RISC-V CSR operations as intrinsics
- add support for CGO_ENABLED environment variable
- do not emit debug info for extern globals (bugfix)
- add support for interrupts
- implement maps for arbitrary keys
- interp: error location for "unknown GEP" error
- wasm-abi: create temporary allocas in the entry block
* **cgo**
- add support for symbols in `#define`
- fix a bug in number tokenization
* **standard library**
- `machine`: avoid bytes package in USB logic
- `runtime`: fix external address declarations
- `runtime`: provide implementation for `internal/bytealg.IndexByte`
* **targets**
- `atsamd51`: fix volatile usage
- `atsamd51`: fix ADC, updating to 12-bits precision
- `atsamd51`: refactor SPI pin configuration to only look at pin numbers
- `atsamd51`: switch UART to use new pin configuration
- `atsamd51`: fix obvious bug in I2C code
- `atsamd51`: use only the necessary UART interrupts
- `atsamd51`: refactor I2C pin handling to auto-detect pin mode
- `avr`: use a garbage collector
- `fe310`: use CLINT peripheral for timekeeping
- `fe310`: add support for PLIC interrupts
- `fe310`: implement UART receive interrupts
- `riscv`: support sleeping in QEMU
- `riscv`: add bare-bones interrupt support
- `riscv`: print exception PC and code
- `wasm`: implement memcpy and memset
- `wasm`: include wasi-libc
- `wasm`: use wasi ABI for basic startup/stdout
* **boards**
- `arduino`: make avrdude command line compatible with Windows
- `arduino-nano`: add this board
- `arduino-nano33`: fix UART1 and UART2
- `circuitplay-bluefruit`: add this board
- `digispark`: add clock speed and pin mappings
- `gameboy-advance`: include compiler-rt in build
- `gameboy-advance`: implement interrupt handler
- `hifive1b`: add support for gdb subcommand
- `pyportal`: add this board
- `pyportal`: remove manual SPI pin mapping as now handled by default
0.11.0
---
* **command line**
+1
View File
@@ -15,3 +15,4 @@ Ayke van Laethem <aykevanlaethem@gmail.com>
Daniel Esteban <conejo@conejo.me>
Loon, LLC.
Ron Evans <ron@hybridgroup.com>
Jaden Weiss <jaden@jadendw.dev>
+25 -3
View File
@@ -7,6 +7,11 @@ LLVM_BUILDDIR ?= llvm-build
CLANG_SRC ?= llvm-project/clang
LLD_SRC ?= llvm-project/lld
# Default tool selection.
CLANG ?= clang-9
LLVM_AR ?= llvm-ar-9
LLVM_NM ?= llvm-nm-9
# Go binary and GOROOT to select
GO ?= go
export GOROOT = $(shell $(GO) env GOROOT)
@@ -116,7 +121,7 @@ gen-device-sam: build/gen-device-svd
GO111MODULE=off $(GO) fmt ./src/device/sam
gen-device-sifive: build/gen-device-svd
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/SiFive-Community lib/cmsis-svd/data/SiFive-Community/ src/device/sifive/
./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/SiFive-Community -interrupts=software lib/cmsis-svd/data/SiFive-Community/ src/device/sifive/
GO111MODULE=off $(GO) fmt ./src/device/sifive
gen-device-stm32: build/gen-device-svd
@@ -139,12 +144,19 @@ $(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
cd $(LLVM_BUILDDIR); ninja
# Build wasi-libc sysroot
.PHONY: wasi-libc
wasi-libc: lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a
lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
cd lib/wasi-libc && make -j4 WASM_CC=$(CLANG) WASM_AR=$(LLVM_AR) WASM_NM=$(LLVM_NM)
# Build the Go compiler.
tinygo:
@if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) build -o build/tinygo$(EXE) -tags byollvm .
test:
test: wasi-libc
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test -v -tags byollvm ./cgo ./compileopts ./interp ./transform .
tinygo-test:
@@ -226,6 +238,8 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.gba -target=gameboy-advance examples/gba-display
@@ -238,6 +252,8 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=metro-m4-airlift examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pyportal examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nucleo-f103rb examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pinetime-devkit0 examples/blinky1
@@ -249,20 +265,25 @@ smoketest:
ifneq ($(AVR), 0)
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino-nano examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=digispark -gc=leaking examples/blinky1
@$(MD5SUM) test.hex
endif
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
@$(MD5SUM) test.hex
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/export
$(TINYGO) build -o wasm.wasm -target=wasm examples/wasm/main
release: tinygo gen-device
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
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
@mkdir -p build/release/tinygo/lib/nrfx
@mkdir -p build/release/tinygo/lib/wasi-libc
@mkdir -p build/release/tinygo/pkg/armv6m-none-eabi
@mkdir -p build/release/tinygo/pkg/armv7m-none-eabi
@mkdir -p build/release/tinygo/pkg/armv7em-none-eabi
@@ -275,6 +296,7 @@ release: tinygo gen-device
@cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt
@cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
@cp -rp src build/release/tinygo/src
@cp -rp targets build/release/tinygo/targets
./build/tinygo build-builtins -target=armv6m-none-eabi -o build/release/tinygo/pkg/armv6m-none-eabi/compiler-rt.a
+4 -1
View File
@@ -43,8 +43,9 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
The following 22 microcontroller boards are currently supported:
The following 27 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)
* [Adafruit Feather M0](https://www.adafruit.com/product/2772)
* [Adafruit Feather M4](https://www.adafruit.com/product/3857)
@@ -52,7 +53,9 @@ The following 22 microcontroller boards are currently supported:
* [Adafruit ItsyBitsy M4](https://www.adafruit.com/product/3800)
* [Adafruit Metro M4 Express Airlift](https://www.adafruit.com/product/4000)
* [Adafruit PyBadge](https://www.adafruit.com/product/4200)
* [Adafruit PyPortal](https://www.adafruit.com/product/4116)
* [Adafruit Trinket M0](https://www.adafruit.com/product/3500)
* [Arduino Nano](https://store.arduino.cc/arduino-nano)
* [Arduino Nano33 IoT](https://store.arduino.cc/nano-33-iot)
* [Arduino Uno](https://store.arduino.cc/arduino-uno-rev3)
* [BBC micro:bit](https://microbit.org/)
+13 -3
View File
@@ -45,6 +45,16 @@ jobs:
inputs:
targetType: inline
script: choco install qemu
- task: CacheBeta@0
displayName: Cache wasi-libc sysroot
inputs:
key: wasi-libc-sysroot-v0
path: lib/wasi-libc/sysroot
- task: Bash@3
displayName: Build wasi-libc
inputs:
targetType: inline
script: PATH=/usr/bin:$PATH make wasi-libc CLANG=$PWD/llvm-build/bin/clang LLVM_AR=$PWD/llvm-build/bin/llvm-ar LLVM_NM=$PWD/llvm-build/bin/llvm-nm
- task: Bash@3
displayName: Test TinyGo
inputs:
@@ -61,9 +71,9 @@ jobs:
export PATH="/c/Go1.13/bin:$PATH:./llvm-build/bin:/c/Program Files/qemu"
unset GOROOT
make release -j4
- publish: $(System.DefaultWorkingDirectory)/build/release.tar.gz
displayName: Publish tarball as artifact
artifact: tinygo.windows-amd64.tar.gz
- publish: $(System.DefaultWorkingDirectory)/build/release/tinygo
displayName: Publish zip as artifact
artifact: tinygo
- task: Bash@3
displayName: Smoke tests
inputs:
+3 -2
View File
@@ -16,6 +16,7 @@ import (
"github.com/tinygo-org/tinygo/compiler"
"github.com/tinygo-org/tinygo/goenv"
"github.com/tinygo-org/tinygo/interp"
"github.com/tinygo-org/tinygo/transform"
)
// Build performs a single package to executable Go build. It takes in a package
@@ -61,7 +62,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
// stack-allocated values.
// Use -wasm-abi=generic to disable this behaviour.
if config.Options.WasmAbi == "js" && strings.HasPrefix(config.Triple(), "wasm") {
err := c.ExternalInt64AsPtr()
err := transform.ExternalInt64AsPtr(c.Module())
if err != nil {
return err
}
@@ -207,7 +208,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
} else if outext == ".uf2" {
// Get UF2 from the .elf file.
tmppath = filepath.Join(dir, "main"+outext)
err := convertELFFileToUF2File(executable, tmppath)
err := convertELFFileToUF2File(executable, tmppath, config.Target.UF2FamilyID)
if err != nil {
return err
}
+1 -1
View File
@@ -158,7 +158,7 @@ var aeabiBuiltins = []string{
func builtinFiles(target string) []string {
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
if strings.HasPrefix(target, "arm") {
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
builtins = append(builtins, aeabiBuiltins...)
}
return builtins
+30 -9
View File
@@ -11,26 +11,33 @@ import (
"bytes"
"encoding/binary"
"io/ioutil"
"strconv"
)
// convertELFFileToUF2File converts an ELF file to a UF2 file.
func convertELFFileToUF2File(infile, outfile string) error {
func convertELFFileToUF2File(infile, outfile string, uf2FamilyID string) error {
// Read the .text segment.
targetAddress, data, err := extractROM(infile)
if err != nil {
return err
}
output, _ := convertBinToUF2(data, uint32(targetAddress))
output, _, err := convertBinToUF2(data, uint32(targetAddress), uf2FamilyID)
if err != nil {
return err
}
return ioutil.WriteFile(outfile, output, 0644)
}
// convertBinToUF2 converts the binary bytes in input to UF2 formatted data.
func convertBinToUF2(input []byte, targetAddr uint32) ([]byte, int) {
func convertBinToUF2(input []byte, targetAddr uint32, uf2FamilyID string) ([]byte, int, error) {
blocks := split(input, 256)
output := make([]byte, 0)
bl := newUF2Block(targetAddr)
bl, err := newUF2Block(targetAddr, uf2FamilyID)
if err != nil {
return nil, 0, err
}
bl.SetNumBlocks(len(blocks))
for i := 0; i < len(blocks); i++ {
@@ -41,7 +48,7 @@ func convertBinToUF2(input []byte, targetAddr uint32) ([]byte, int) {
bl.IncrementAddress(bl.payloadSize)
}
return output, len(blocks)
return output, len(blocks), nil
}
const (
@@ -65,18 +72,32 @@ type uf2Block struct {
}
// newUF2Block returns a new uf2Block struct that has been correctly populated
func newUF2Block(targetAddr uint32) *uf2Block {
func newUF2Block(targetAddr uint32, uf2FamilyID string) (*uf2Block, error) {
var flags uint32
var familyID uint32
if uf2FamilyID != "" {
flags |= flagFamilyIDPresent
v, err := strconv.ParseUint(uf2FamilyID, 0, 32)
if err != nil {
return nil, err
}
familyID = uint32(v)
}
return &uf2Block{magicStart0: uf2MagicStart0,
magicStart1: uf2MagicStart1,
magicEnd: uf2MagicEnd,
targetAddr: targetAddr,
flags: 0x0,
familyID: 0x0,
flags: flags,
familyID: familyID,
payloadSize: 256,
data: make([]byte, 476),
}
}, nil
}
const (
flagFamilyIDPresent = 0x00002000
)
// Bytes converts the uf2Block to a slice of bytes that can be written to file.
func (b *uf2Block) Bytes() []byte {
buf := bytes.NewBuffer(make([]byte, 0, 512))
+6
View File
@@ -65,6 +65,12 @@ func (c *Config) BuildTags() []string {
return tags
}
// CgoEnabled returns true if (and only if) CGo is enabled. It is true by
// default and false if CGO_ENABLED is set to "0".
func (c *Config) CgoEnabled() bool {
return goenv.Get("CGO_ENABLED") == "1"
}
// GC returns the garbage collection strategy in use on this platform. Valid
// values are "none", "leaking", and "conservative".
func (c *Config) GC() string {
+21 -1
View File
@@ -43,9 +43,11 @@ type TargetSpec struct {
FlashMethod string `json:"flash-method"`
FlashVolume string `json:"msd-volume-name"`
FlashFilename string `json:"msd-firmware-name"`
UF2FamilyID string `json:"uf2-family-id"`
OpenOCDInterface string `json:"openocd-interface"`
OpenOCDTarget string `json:"openocd-target"`
OpenOCDTransport string `json:"openocd-transport"`
JLinkDevice string `json:"jlink-device"`
}
// copyProperties copies all properties that are set in spec2 into itself.
@@ -109,6 +111,9 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
if spec2.FlashFilename != "" {
spec.FlashFilename = spec2.FlashFilename
}
if spec2.UF2FamilyID != "" {
spec.UF2FamilyID = spec2.UF2FamilyID
}
if spec2.OpenOCDInterface != "" {
spec.OpenOCDInterface = spec2.OpenOCDInterface
}
@@ -118,6 +123,9 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
if spec2.OpenOCDTransport != "" {
spec.OpenOCDTransport = spec2.OpenOCDTransport
}
if spec2.JLinkDevice != "" {
spec.JLinkDevice = spec2.JLinkDevice
}
}
// load reads a target specification from the JSON in the given io.Reader. It
@@ -221,6 +229,14 @@ func LoadTarget(target string) (*TargetSpec, error) {
if len(tripleSplit) < 3 {
return nil, errors.New("expected a full LLVM target or a custom target in -target flag")
}
if tripleSplit[0] == "arm" {
// LLVM and Clang have a different idea of what "arm" means, so
// upgrade to a slightly more modern ARM. In fact, when you pass
// --target=arm--linux-gnueabihf to Clang, it will convert that
// internally to armv7-unknown-linux-gnueabihf. Changing the
// architecture to armv7 will keep things consistent.
tripleSplit[0] = "armv7"
}
goos := tripleSplit[2]
if strings.HasPrefix(goos, "darwin") {
goos = "darwin"
@@ -229,11 +245,12 @@ func LoadTarget(target string) (*TargetSpec, error) {
"i386": "386",
"x86_64": "amd64",
"aarch64": "arm64",
"armv7": "arm",
}[tripleSplit[0]]
if goarch == "" {
goarch = tripleSplit[0]
}
return defaultTarget(goos, goarch, target)
return defaultTarget(goos, goarch, strings.Join(tripleSplit, "-"))
}
}
@@ -247,6 +264,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
BuildTags: []string{goos, goarch},
Compiler: "clang",
Linker: "cc",
CFlags: []string{"--target=" + triple},
GDB: "gdb",
PortReset: "false",
FlashMethod: "native",
@@ -259,11 +277,13 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
if goarch != runtime.GOARCH {
// Some educated guesses as to how to invoke helper programs.
if goarch == "arm" && goos == "linux" {
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/arm-linux-gnueabihf")
spec.Linker = "arm-linux-gnueabihf-gcc"
spec.GDB = "arm-linux-gnueabihf-gdb"
spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabihf"}
}
if goarch == "arm64" && goos == "linux" {
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/aarch64-linux-gnu")
spec.Linker = "aarch64-linux-gnu-gcc"
spec.GDB = "aarch64-linux-gnu-gdb"
spec.Emulator = []string{"qemu-aarch64", "-L", "/usr/aarch64-linux-gnu"}
+6 -153
View File
@@ -193,7 +193,7 @@ func (c *Compiler) Compile(mainPath string) []error {
GOOS: c.GOOS(),
GOROOT: goenv.Get("GOROOT"),
GOPATH: goenv.Get("GOPATH"),
CgoEnabled: true,
CgoEnabled: c.CgoEnabled(),
UseAllFiles: false,
Compiler: "gc", // must be one of the recognized compilers
BuildTags: c.BuildTags(),
@@ -203,7 +203,7 @@ func (c *Compiler) Compile(mainPath string) []error {
GOOS: c.GOOS(),
GOROOT: goenv.Get("TINYGOROOT"),
GOPATH: overlayGopath,
CgoEnabled: true,
CgoEnabled: c.CgoEnabled(),
UseAllFiles: false,
Compiler: "gc", // must be one of the recognized compilers
BuildTags: c.BuildTags(),
@@ -1303,13 +1303,15 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
return c.emitAsmFull(frame, instr)
case strings.HasPrefix(name, "device/arm.SVCall"):
return c.emitSVCall(frame, instr.Args)
case strings.HasPrefix(name, "(device/riscv.CSR)."):
return c.emitCSROperation(frame, instr)
case strings.HasPrefix(name, "syscall.Syscall"):
return c.emitSyscall(frame, instr)
case strings.HasPrefix(name, "runtime/volatile.Load"):
return c.emitVolatileLoad(frame, instr)
case strings.HasPrefix(name, "runtime/volatile.Store"):
return c.emitVolatileStore(frame, instr)
case name == "runtime/interrupt.New" && len(fn.Blocks) == 0:
case name == "runtime/interrupt.New":
return c.emitInterruptGlobal(frame, instr)
}
@@ -1583,24 +1585,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
val := c.getValue(frame, expr.X)
return c.parseMakeInterface(val, expr.X.Type(), expr.Pos()), nil
case *ssa.MakeMap:
mapType := expr.Type().Underlying().(*types.Map)
llvmKeyType := c.getLLVMType(mapType.Key().Underlying())
llvmValueType := c.getLLVMType(mapType.Elem().Underlying())
keySize := c.targetData.TypeAllocSize(llvmKeyType)
valueSize := c.targetData.TypeAllocSize(llvmValueType)
llvmKeySize := llvm.ConstInt(c.ctx.Int8Type(), keySize, false)
llvmValueSize := llvm.ConstInt(c.ctx.Int8Type(), valueSize, false)
sizeHint := llvm.ConstInt(c.uintptrType, 8, false)
if expr.Reserve != nil {
sizeHint = c.getValue(frame, expr.Reserve)
var err error
sizeHint, err = c.parseConvert(expr.Reserve.Type(), types.Typ[types.Uintptr], sizeHint, expr.Pos())
if err != nil {
return llvm.Value{}, err
}
}
hashmap := c.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
return hashmap, nil
return c.emitMakeMap(frame, expr)
case *ssa.MakeSlice:
sliceLen := c.getValue(frame, expr.Len)
sliceCap := c.getValue(frame, expr.Cap)
@@ -2565,138 +2550,6 @@ func (c *Compiler) NonConstGlobals() {
}
}
// When -wasm-abi flag set to "js" (default),
// replace i64 in an external function with a stack-allocated i64*, to work
// around the lack of 64-bit integers in JavaScript (commonly used together with
// WebAssembly). Once that's resolved, this pass may be avoided.
// See also the -wasm-abi= flag
// https://github.com/WebAssembly/design/issues/1172
func (c *Compiler) ExternalInt64AsPtr() error {
int64Type := c.ctx.Int64Type()
int64PtrType := llvm.PointerType(int64Type, 0)
for fn := c.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
if fn.Linkage() != llvm.ExternalLinkage {
// Only change externally visible functions (exports and imports).
continue
}
if strings.HasPrefix(fn.Name(), "llvm.") || strings.HasPrefix(fn.Name(), "runtime.") {
// Do not try to modify the signature of internal LLVM functions and
// assume that runtime functions are only temporarily exported for
// coroutine lowering.
continue
}
hasInt64 := false
paramTypes := []llvm.Type{}
// Check return type for 64-bit integer.
fnType := fn.Type().ElementType()
returnType := fnType.ReturnType()
if returnType == int64Type {
hasInt64 = true
paramTypes = append(paramTypes, int64PtrType)
returnType = c.ctx.VoidType()
}
// Check param types for 64-bit integers.
for param := fn.FirstParam(); !param.IsNil(); param = llvm.NextParam(param) {
if param.Type() == int64Type {
hasInt64 = true
paramTypes = append(paramTypes, int64PtrType)
} else {
paramTypes = append(paramTypes, param.Type())
}
}
if !hasInt64 {
// No i64 in the paramter list.
continue
}
// Add $i64wrapper to the real function name as it is only used
// internally.
// Add a new function with the correct signature that is exported.
name := fn.Name()
fn.SetName(name + "$i64wrap")
externalFnType := llvm.FunctionType(returnType, paramTypes, fnType.IsFunctionVarArg())
externalFn := llvm.AddFunction(c.mod, name, externalFnType)
if fn.IsDeclaration() {
// Just a declaration: the definition doesn't exist on the Go side
// so it cannot be called from external code.
// Update all users to call the external function.
// The old $i64wrapper function could be removed, but it may as well
// be left in place.
for use := fn.FirstUse(); !use.IsNil(); use = use.NextUse() {
call := use.User()
c.builder.SetInsertPointBefore(call)
callParams := []llvm.Value{}
var retvalAlloca llvm.Value
if fnType.ReturnType() == int64Type {
retvalAlloca = c.builder.CreateAlloca(int64Type, "i64asptr")
callParams = append(callParams, retvalAlloca)
}
for i := 0; i < call.OperandsCount()-1; i++ {
operand := call.Operand(i)
if operand.Type() == int64Type {
// Pass a stack-allocated pointer instead of the value
// itself.
alloca := c.builder.CreateAlloca(int64Type, "i64asptr")
c.builder.CreateStore(operand, alloca)
callParams = append(callParams, alloca)
} else {
// Unchanged parameter.
callParams = append(callParams, operand)
}
}
if fnType.ReturnType() == int64Type {
// Pass a stack-allocated pointer as the first parameter
// where the return value should be stored, instead of using
// the regular return value.
c.builder.CreateCall(externalFn, callParams, call.Name())
returnValue := c.builder.CreateLoad(retvalAlloca, "retval")
call.ReplaceAllUsesWith(returnValue)
call.EraseFromParentAsInstruction()
} else {
newCall := c.builder.CreateCall(externalFn, callParams, call.Name())
call.ReplaceAllUsesWith(newCall)
call.EraseFromParentAsInstruction()
}
}
} else {
// The function has a definition in Go. This means that it may still
// be called both Go and from external code.
// Keep existing calls with the existing convention in place (for
// better performance), but export a new wrapper function with the
// correct calling convention.
fn.SetLinkage(llvm.InternalLinkage)
fn.SetUnnamedAddr(true)
entryBlock := c.ctx.AddBasicBlock(externalFn, "entry")
c.builder.SetInsertPointAtEnd(entryBlock)
var callParams []llvm.Value
if fnType.ReturnType() == int64Type {
return errors.New("not yet implemented: exported function returns i64 with -wasm-abi=js; " +
"see https://tinygo.org/compiler-internals/calling-convention/")
}
for i, origParam := range fn.Params() {
paramValue := externalFn.Param(i)
if origParam.Type() == int64Type {
paramValue = c.builder.CreateLoad(paramValue, "i64")
}
callParams = append(callParams, paramValue)
}
retval := c.builder.CreateCall(fn, callParams, "")
if retval.Type().TypeKind() == llvm.VoidTypeKind {
c.builder.CreateRetVoid()
} else {
c.builder.CreateRet(retval)
}
}
}
return nil
}
// Emit object file (.o).
func (c *Compiler) EmitObject(path string) error {
llvmBuf, err := c.machine.EmitToMemoryBuffer(c.mod, llvm.ObjectFile)
+45
View File
@@ -162,3 +162,48 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0)
return c.builder.CreateCall(target, llvmArgs, ""), nil
}
// This is a compiler builtin which emits CSR instructions. It can be one of:
//
// func (csr CSR) Get() uintptr
// func (csr CSR) Set(uintptr)
// func (csr CSR) SetBits(uintptr) uintptr
// func (csr CSR) ClearBits(uintptr) uintptr
//
// The csr parameter (method receiver) must be a constant. Other parameter can
// be any value.
func (c *Compiler) emitCSROperation(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
csrConst, ok := call.Args[0].(*ssa.Const)
if !ok {
return llvm.Value{}, c.makeError(call.Pos(), "CSR must be constant")
}
csr := csrConst.Uint64()
switch name := call.StaticCallee().Name(); name {
case "Get":
// Note that this instruction may have side effects, and thus must be
// marked as such.
fnType := llvm.FunctionType(c.uintptrType, nil, false)
asm := fmt.Sprintf("csrr $0, %d", csr)
target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0)
return c.builder.CreateCall(target, nil, ""), nil
case "Set":
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.uintptrType}, false)
asm := fmt.Sprintf("csrw %d, $0", csr)
target := llvm.InlineAsm(fnType, asm, "r", true, false, 0)
return c.builder.CreateCall(target, []llvm.Value{c.getValue(frame, call.Args[1])}, ""), nil
case "SetBits":
// Note: it may be possible to optimize this to csrrsi in many cases.
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{c.uintptrType}, false)
asm := fmt.Sprintf("csrrs $0, %d, $1", csr)
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0)
return c.builder.CreateCall(target, []llvm.Value{c.getValue(frame, call.Args[1])}, ""), nil
case "ClearBits":
// Note: it may be possible to optimize this to csrrci in many cases.
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{c.uintptrType}, false)
asm := fmt.Sprintf("csrrc $0, %d, $1", csr)
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0)
return c.builder.CreateCall(target, []llvm.Value{c.getValue(frame, call.Args[1])}, ""), nil
default:
return llvm.Value{}, c.makeError(call.Pos(), "unknown CSR operation: "+name)
}
}
+1
View File
@@ -48,6 +48,7 @@ func (c *Compiler) emitInterruptGlobal(frame *Frame, instr *ssa.CallCommon) (llv
global := llvm.AddGlobal(c.mod, globalLLVMType, globalName)
global.SetLinkage(llvm.PrivateLinkage)
global.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
initializer := llvm.ConstNull(globalLLVMType)
initializer = llvm.ConstInsertValue(initializer, funcValue, []uint32{0})
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(c.intType, uint64(id.Int64()), true), []uint32{1, 0})
+63 -26
View File
@@ -6,9 +6,45 @@ import (
"go/token"
"go/types"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
// emitMakeMap creates a new map object (runtime.hashmap) by allocating and
// initializing an appropriately sized object.
func (c *Compiler) emitMakeMap(frame *Frame, expr *ssa.MakeMap) (llvm.Value, error) {
mapType := expr.Type().Underlying().(*types.Map)
keyType := mapType.Key().Underlying()
llvmValueType := c.getLLVMType(mapType.Elem().Underlying())
var llvmKeyType llvm.Type
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// String keys.
llvmKeyType = c.getLLVMType(keyType)
} else if hashmapIsBinaryKey(keyType) {
// Trivially comparable keys.
llvmKeyType = c.getLLVMType(keyType)
} else {
// All other keys. Implemented as map[interface{}]valueType for ease of
// implementation.
llvmKeyType = c.getLLVMRuntimeType("_interface")
}
keySize := c.targetData.TypeAllocSize(llvmKeyType)
valueSize := c.targetData.TypeAllocSize(llvmValueType)
llvmKeySize := llvm.ConstInt(c.ctx.Int8Type(), keySize, false)
llvmValueSize := llvm.ConstInt(c.ctx.Int8Type(), valueSize, false)
sizeHint := llvm.ConstInt(c.uintptrType, 8, false)
if expr.Reserve != nil {
sizeHint = c.getValue(frame, expr.Reserve)
var err error
sizeHint, err = c.parseConvert(expr.Reserve.Type(), types.Typ[types.Uintptr], sizeHint, expr.Pos())
if err != nil {
return llvm.Value{}, err
}
}
hashmap := c.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
return hashmap, nil
}
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
llvmValueType := c.getLLVMType(valueType)
@@ -19,6 +55,7 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
// Do the lookup. How it is done depends on the key type.
var commaOkValue llvm.Value
keyType = keyType.Underlying()
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// key is a string
params := []llvm.Value{m, key, mapValuePtr}
@@ -34,8 +71,14 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
commaOkValue = c.createRuntimeCall("hashmapBinaryGet", params, "")
c.emitLifetimeEnd(mapKeyPtr, mapKeySize)
} else {
// Not trivially comparable using memcmp.
return llvm.Value{}, c.makeError(pos, "only strings, bools, ints, pointers or structs of bools/ints are supported as map keys, but got: "+keyType.String())
// Not trivially comparable using memcmp. Make it an interface instead.
itfKey := key
if _, ok := keyType.(*types.Interface); !ok {
// Not already an interface, so convert it to an interface now.
itfKey = c.parseMakeInterface(key, keyType, pos)
}
params := []llvm.Value{m, itfKey, mapValuePtr}
commaOkValue = c.createRuntimeCall("hashmapInterfaceGet", params, "")
}
// Load the resulting value from the hashmap. The value is set to the zero
@@ -69,7 +112,14 @@ func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, p
c.createRuntimeCall("hashmapBinarySet", params, "")
c.emitLifetimeEnd(keyPtr, keySize)
} else {
c.addError(pos, "only strings, bools, ints, pointers or structs of bools/ints are supported as map keys, but got: "+keyType.String())
// Key is not trivially comparable, so compare it as an interface instead.
itfKey := key
if _, ok := keyType.(*types.Interface); !ok {
// Not already an interface, so convert it to an interface first.
itfKey = c.parseMakeInterface(key, keyType, pos)
}
params := []llvm.Value{m, itfKey, valuePtr}
c.createRuntimeCall("hashmapInterfaceSet", params, "")
}
c.emitLifetimeEnd(valuePtr, valueSize)
}
@@ -89,32 +139,19 @@ func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos toke
c.emitLifetimeEnd(keyPtr, keySize)
return nil
} else {
return c.makeError(pos, "only strings, bools, ints, pointers or structs of bools/ints are supported as map keys, but got: "+keyType.String())
// Key is not trivially comparable, so compare it as an interface
// instead.
itfKey := key
if _, ok := keyType.(*types.Interface); !ok {
// Not already an interface, so convert it to an interface first.
itfKey = c.parseMakeInterface(key, keyType, pos)
}
params := []llvm.Value{m, itfKey}
c.createRuntimeCall("hashmapInterfaceDelete", params, "")
return nil
}
}
// Get FNV-1a hash of this string.
//
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
func hashmapHash(data []byte) uint32 {
var result uint32 = 2166136261 // FNV offset basis
for _, c := range data {
result ^= uint32(c)
result *= 16777619 // FNV prime
}
return result
}
// Get the topmost 8 bits of the hash, without using a special value (like 0).
func hashmapTopHash(hash uint32) uint8 {
tophash := uint8(hash >> 24)
if tophash < 1 {
// 0 means empty slot, so make it bigger.
tophash += 1
}
return tophash
}
// Returns true if this key type does not contain strings, interfaces etc., so
// can be compared with runtime.memequal.
func hashmapIsBinaryKey(keyType types.Type) bool {
+2 -2
View File
@@ -58,7 +58,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) []er
transform.OptimizeAllocs(c.mod)
transform.LowerInterfaces(c.mod)
errs := transform.LowerInterruptRegistrations(c.mod)
errs := transform.LowerInterrupts(c.mod)
if len(errs) > 0 {
return errs
}
@@ -106,7 +106,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) []er
if err != nil {
return []error{err}
}
errs := transform.LowerInterruptRegistrations(c.mod)
errs := transform.LowerInterrupts(c.mod)
if len(errs) > 0 {
return errs
}
+1 -1
View File
@@ -82,7 +82,7 @@ func (c *Compiler) getGlobal(g *ssa.Global) llvm.Value {
}
}
if c.Debug() {
if c.Debug() && !info.extern {
// Add debug info.
// TODO: this should be done for every global in the program, not just
// the ones that are referenced from some code.
+8
View File
@@ -18,6 +18,7 @@ var Keys = []string{
"GOROOT",
"GOPATH",
"GOCACHE",
"CGO_ENABLED",
"TINYGOROOT",
}
@@ -57,6 +58,13 @@ func Get(name string) string {
panic("could not find cache dir: " + err.Error())
}
return filepath.Join(dir, "tinygo")
case "CGO_ENABLED":
val := os.Getenv("CGO_ENABLED")
if val == "1" || val == "0" {
return val
}
// Default to enabling CGo.
return "1"
case "TINYGOROOT":
return sourceDir()
default:
+30 -6
View File
@@ -125,7 +125,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
}
indices[i] = uint32(operand.Value().ZExtValue())
}
result := value.GetElementPtr(indices)
result, err := value.GetElementPtr(indices)
if err != nil {
return nil, nil, fr.errorAt(inst, err.Error())
}
if result.Type() != inst.Type() {
return nil, nil, fr.errorAt(inst, "interp: gep: type does not match")
}
@@ -264,7 +267,11 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
if elementCount == 1 {
fr.locals[resultInst] = result
} else {
fr.locals[resultInst] = result.GetElementPtr([]uint32{0, 0})
result, err := result.GetElementPtr([]uint32{0, 0})
if err != nil {
return nil, nil, errorAt(inst, err.Error())
}
fr.locals[resultInst] = result
}
case callee.Name() == "runtime.hashmapMake":
// create a map
@@ -365,8 +372,16 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
// a bitcast of the original array instead of the GEP,
// which breaks our assumptions.
// Re-add this GEP, in the hope that it it is then of the correct type...
dstArray = dstArray.GetElementPtr([]uint32{0, 0}).(*LocalValue)
srcArray = srcArray.GetElementPtr([]uint32{0, 0}).(*LocalValue)
dstArrayValue, err := dstArray.GetElementPtr([]uint32{0, 0})
if err != nil {
return nil, nil, errorAt(inst, err.Error())
}
dstArray = dstArrayValue.(*LocalValue)
srcArrayValue, err := srcArray.GetElementPtr([]uint32{0, 0})
if err != nil {
return nil, nil, errorAt(inst, err.Error())
}
srcArray = srcArrayValue.(*LocalValue)
}
if fr.Eval.TargetData.TypeAllocSize(dstArray.Type().ElementType()) != elementSize {
return nil, nil, fr.errorAt(inst, "interp: slice dst element size does not match pointer type")
@@ -385,12 +400,21 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
return nil, nil, fr.errorAt(inst, "interp: trying to copy a slice with negative length?")
}
for i := int64(0); i < length; i++ {
var err error
// *dst = *src
dstArray.Store(srcArray.Load())
// dst++
dstArray = dstArray.GetElementPtr([]uint32{1}).(*LocalValue)
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
if err != nil {
return nil, nil, errorAt(inst, err.Error())
}
dstArray = dstArrayValue.(*LocalValue)
// src++
srcArray = srcArray.GetElementPtr([]uint32{1}).(*LocalValue)
srcArrayValue, err := srcArray.GetElementPtr([]uint32{1})
if err != nil {
return nil, nil, errorAt(inst, err.Error())
}
srcArray = srcArrayValue.(*LocalValue)
}
case callee.Name() == "runtime.stringToBytes":
// convert a string to a []byte
+5 -1
View File
@@ -24,7 +24,11 @@ func getStringBytes(strPtr Value, strLen llvm.Value) []byte {
}
buf := make([]byte, strLen.ZExtValue())
for i := range buf {
c := strPtr.GetElementPtr([]uint32{uint32(i)}).Load()
gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
if err != nil {
panic(err) // TODO
}
c := gep.Load()
buf[i] = byte(c.ZExtValue())
}
return buf
+14 -13
View File
@@ -3,6 +3,7 @@ package interp
// This file provides a litte bit of abstraction around LLVM values.
import (
"errors"
"strconv"
"tinygo.org/x/go-llvm"
@@ -11,13 +12,13 @@ import (
// A Value is a LLVM value with some extra methods attached for easier
// interpretation.
type Value interface {
Value() llvm.Value // returns a LLVM value
Type() llvm.Type // equal to Value().Type()
IsConstant() bool // returns true if this value is a constant value
Load() llvm.Value // dereference a pointer
Store(llvm.Value) // store to a pointer
GetElementPtr([]uint32) Value // returns an interior pointer
String() string // string representation, for debugging
Value() llvm.Value // returns a LLVM value
Type() llvm.Type // equal to Value().Type()
IsConstant() bool // returns true if this value is a constant value
Load() llvm.Value // dereference a pointer
Store(llvm.Value) // store to a pointer
GetElementPtr([]uint32) (Value, error) // returns an interior pointer
String() string // string representation, for debugging
}
// A type that simply wraps a LLVM constant value.
@@ -97,21 +98,21 @@ func (v *LocalValue) Store(value llvm.Value) {
}
// GetElementPtr returns a GEP when the underlying value is of pointer type.
func (v *LocalValue) GetElementPtr(indices []uint32) Value {
func (v *LocalValue) GetElementPtr(indices []uint32) (Value, error) {
if !v.Underlying.IsAGlobalVariable().IsNil() {
int32Type := v.Underlying.Type().Context().Int32Type()
gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices))
return &LocalValue{v.Eval, gep}
return &LocalValue{v.Eval, gep}, nil
}
if !v.Underlying.IsAConstantExpr().IsNil() {
switch v.Underlying.Opcode() {
case llvm.GetElementPtr, llvm.IntToPtr, llvm.BitCast:
int32Type := v.Underlying.Type().Context().Int32Type()
llvmIndices := getLLVMIndices(int32Type, indices)
return &LocalValue{v.Eval, llvm.ConstGEP(v.Underlying, llvmIndices)}
return &LocalValue{v.Eval, llvm.ConstGEP(v.Underlying, llvmIndices)}, nil
}
}
panic("interp: unknown GEP")
return nil, errors.New("interp: unknown GEP")
}
// stripPointerCasts removes all const bitcasts from pointer values, if there
@@ -309,8 +310,8 @@ func (v *MapValue) Store(value llvm.Value) {
// GetElementPtr panics: maps are of reference type so their (interior)
// addresses cannot be calculated.
func (v *MapValue) GetElementPtr(indices []uint32) Value {
panic("interp: GEP on a map")
func (v *MapValue) GetElementPtr(indices []uint32) (Value, error) {
return nil, errors.New("interp: GEP on a map")
}
// PutString does a map assign operation, assuming that the map is of type
Submodule
+1
Submodule lib/wasi-libc added at a280fead2a
+49 -15
View File
@@ -145,14 +145,6 @@ func Test(pkgName string, options *compileopts.Options) error {
// Flash builds and flashes the built binary to the given serial port.
func Flash(pkgName, port string, options *compileopts.Options) error {
if port == "" {
var err error
port, err = getDefaultPort()
if err != nil {
return err
}
}
config, err := builder.NewConfig(options)
if err != nil {
return err
@@ -192,6 +184,14 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
return builder.Build(pkgName, fileExt, config, func(tmppath string) error {
// do we need port reset to put MCU into bootloader mode?
if config.Target.PortReset == "true" {
if port == "" {
var err error
port, err = getDefaultPort()
if err != nil {
return err
}
}
err := touchSerialPortAt1200bps(port)
if err != nil {
return &commandError{"failed to reset port", tmppath, err}
@@ -207,6 +207,15 @@ func Flash(pkgName, port string, options *compileopts.Options) error {
flashCmd := config.Target.FlashCommand
fileToken := "{" + fileExt[1:] + "}"
flashCmd = strings.Replace(flashCmd, fileToken, tmppath, -1)
if port == "" && strings.Contains(flashCmd, "{port}") {
var err error
port, err = getDefaultPort()
if err != nil {
return err
}
}
flashCmd = strings.Replace(flashCmd, "{port}", port, -1)
// Execute the command.
@@ -288,12 +297,13 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
gdbInterface, openocdInterface := config.Programmer()
switch gdbInterface {
case "msd", "command", "":
if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
gdbInterface = "openocd"
}
if len(config.Target.Emulator) != 0 {
// Assume QEMU as an emulator.
gdbInterface = "qemu"
} else if openocdInterface != "" && config.Target.OpenOCDTarget != "" {
gdbInterface = "openocd"
} else if config.Target.JLinkDevice != "" {
gdbInterface = "jlink"
}
}
@@ -331,6 +341,31 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
// Maybe we should send a .Kill() after x seconds?
daemon.Wait()
}()
case "jlink":
gdbCommands = append(gdbCommands, "target remote :2331", "load", "monitor reset halt")
// We need a separate debugging daemon for on-chip debugging.
daemon := exec.Command("JLinkGDBServer", "-device", config.Target.JLinkDevice)
if ocdOutput {
// Make it clear which output is from the daemon.
w := &ColorWriter{
Out: os.Stderr,
Prefix: "jlink: ",
Color: TermColorYellow,
}
daemon.Stdout = w
daemon.Stderr = w
}
// Make sure the daemon doesn't receive Ctrl-C that is intended for
// GDB (to break the currently executing program).
setCommandAsDaemon(daemon)
// Start now, and kill it on exit.
daemon.Start()
defer func() {
daemon.Process.Signal(os.Interrupt)
// Maybe we should send a .Kill() after x seconds?
daemon.Wait()
}()
case "qemu":
gdbCommands = append(gdbCommands, "target remote :1234")
@@ -542,7 +577,6 @@ func parseSize(s string) (int64, error) {
}
// getDefaultPort returns the default serial port depending on the operating system.
// Currently only supports macOS and Linux.
func getDefaultPort() (port string, err error) {
var portPath string
switch runtime.GOOS {
@@ -564,7 +598,7 @@ func getDefaultPort() (port string, err error) {
}
if out.String() == "No Instance(s) Available." {
return "", errors.New("unable to locate a USB device to be flashed")
return "", errors.New("no serial ports available")
}
for _, line := range strings.Split(out.String(), "\n") {
@@ -575,7 +609,7 @@ func getDefaultPort() (port string, err error) {
}
}
}
return "", errors.New("unable to locate a USB device to be flashed")
return "", errors.New("unable to locate a serial port")
default:
return "", errors.New("unable to search for a default USB device to be flashed on this OS")
}
@@ -585,7 +619,7 @@ func getDefaultPort() (port string, err error) {
return "", err
}
if d == nil {
return "", errors.New("unable to locate a USB device to be flashed")
return "", errors.New("unable to locate a serial port")
}
return d[0], nil
+1 -16
View File
@@ -72,22 +72,7 @@ func runPlatTests(target string, matches []string, t *testing.T) {
t.Parallel()
for _, path := range matches {
switch {
case target == "wasm":
// testdata/gc.go is known not to work on WebAssembly
if path == filepath.Join("testdata", "gc.go") {
continue
}
case target == "":
// run all tests on host
case target == "cortex-m-qemu":
// all tests are supported
default:
// cross-compilation of cgo is not yet supported
if path == filepath.Join("testdata", "cgo")+string(filepath.Separator) {
continue
}
}
path := path // redefine to avoid race condition
t.Run(filepath.Base(path), func(t *testing.T) {
t.Parallel()
+277
View File
@@ -0,0 +1,277 @@
package riscv
// This file lists constants for CSR operations and defines methods on CSRs that
// are implemented as compiler intrinsics.
// CSR constants are used for use in CSR (Control and Status Register) compiler
// intrinsics.
type CSR int16
// Get returns the value of the given CSR.
func (csr CSR) Get() uintptr
// Set stores a new value in the given CSR.
func (csr CSR) Set(uintptr)
// SetBits atomically sets the given bits in this ISR and returns the old value.
func (csr CSR) SetBits(uintptr) uintptr
// ClearBits atomically clears the given bits in this ISR and returns the old
// value.
func (csr CSR) ClearBits(uintptr) uintptr
// CSR values defined in the RISC-V privileged specification. Not all values may
// be available on any given chip.
//
// Source: https://github.com/riscv/riscv-isa-manual/blob/riscv-priv-1.10/src/priv-csrs.tex
const (
// User Trap Setup
USTATUS CSR = 0x000 // User status register.
UIE CSR = 0x004 // User interrupt-enable register.
UTVEC CSR = 0x005 // User trap handler base address.
// User Trap Handling
USCRATCH CSR = 0x040 // Scratch register for user trap handlers.
UEPC CSR = 0x041 // User exception program counter.
UCAUSE CSR = 0x042 // User trap cause.
UTVAL CSR = 0x043 // User bad address or instruction.
UIP CSR = 0x044 // User interrupt pending.
// User Floating-Point CSRs
FFLAGS CSR = 0x001 // Floating-Point Accrued Exceptions.
FRM CSR = 0x002 // Floating-Point Dynamic Rounding Mode.
FCSR CSR = 0x003 // Floating-Point Control and Status
// User Counter/Timers
CYCLE CSR = 0xC00 // Cycle counter for RDCYCLE instruction.
TIME CSR = 0xC01 // Timer for RDTIME instruction.
INSTRET CSR = 0xC02 // Instructions-retired counter for RDINSTRET instruction.
HPMCOUNTER3 CSR = 0xC03 // Performance-monitoring counter 3.
HPMCOUNTER4 CSR = 0xC04 // Performance-monitoring counter 4.
HPMCOUNTER5 CSR = 0xC05 // Performance-monitoring counter 5.
HPMCOUNTER6 CSR = 0xC06 // Performance-monitoring counter 6.
HPMCOUNTER7 CSR = 0xC07 // Performance-monitoring counter 7.
HPMCOUNTER8 CSR = 0xC08 // Performance-monitoring counter 8.
HPMCOUNTER9 CSR = 0xC09 // Performance-monitoring counter 9.
HPMCOUNTER10 CSR = 0xC0A // Performance-monitoring counter 10.
HPMCOUNTER11 CSR = 0xC0B // Performance-monitoring counter 11.
HPMCOUNTER12 CSR = 0xC0C // Performance-monitoring counter 12.
HPMCOUNTER13 CSR = 0xC0D // Performance-monitoring counter 13.
HPMCOUNTER14 CSR = 0xC0E // Performance-monitoring counter 14.
HPMCOUNTER15 CSR = 0xC0F // Performance-monitoring counter 15.
HPMCOUNTER16 CSR = 0xC10 // Performance-monitoring counter 16.
HPMCOUNTER17 CSR = 0xC11 // Performance-monitoring counter 17.
HPMCOUNTER18 CSR = 0xC12 // Performance-monitoring counter 18.
HPMCOUNTER19 CSR = 0xC13 // Performance-monitoring counter 19.
HPMCOUNTER20 CSR = 0xC14 // Performance-monitoring counter 20.
HPMCOUNTER21 CSR = 0xC15 // Performance-monitoring counter 21.
HPMCOUNTER22 CSR = 0xC16 // Performance-monitoring counter 22.
HPMCOUNTER23 CSR = 0xC17 // Performance-monitoring counter 23.
HPMCOUNTER24 CSR = 0xC18 // Performance-monitoring counter 24.
HPMCOUNTER25 CSR = 0xC19 // Performance-monitoring counter 25.
HPMCOUNTER26 CSR = 0xC1A // Performance-monitoring counter 26.
HPMCOUNTER27 CSR = 0xC1B // Performance-monitoring counter 27.
HPMCOUNTER28 CSR = 0xC1C // Performance-monitoring counter 28.
HPMCOUNTER29 CSR = 0xC1D // Performance-monitoring counter 29.
HPMCOUNTER30 CSR = 0xC1E // Performance-monitoring counter 30.
HPMCOUNTER31 CSR = 0xC1F // Performance-monitoring counter 31.
CYCLEH CSR = 0xC80 // Upper 32 bits of CYCLE, RV32I only.
TIMEH CSR = 0xC81 // Upper 32 bits of TIME, RV32I only.
INSTRETH CSR = 0xC82 // Upper 32 bits of INSTRET, RV32I only.
HPMCOUNTER3H CSR = 0xC83 // Upper 32 bits of HPMCOUNTER3, RV32I only.
HPMCOUNTER4H CSR = 0xC84 // Upper 32 bits of HPMCOUNTER4, RV32I only.
HPMCOUNTER5H CSR = 0xC85 // Upper 32 bits of HPMCOUNTER5, RV32I only.
HPMCOUNTER6H CSR = 0xC86 // Upper 32 bits of HPMCOUNTER6, RV32I only.
HPMCOUNTER7H CSR = 0xC87 // Upper 32 bits of HPMCOUNTER7, RV32I only.
HPMCOUNTER8H CSR = 0xC88 // Upper 32 bits of HPMCOUNTER8, RV32I only.
HPMCOUNTER9H CSR = 0xC89 // Upper 32 bits of HPMCOUNTER9, RV32I only.
HPMCOUNTER10H CSR = 0xC8A // Upper 32 bits of HPMCOUNTER10, RV32I only.
HPMCOUNTER11H CSR = 0xC8B // Upper 32 bits of HPMCOUNTER11, RV32I only.
HPMCOUNTER12H CSR = 0xC8C // Upper 32 bits of HPMCOUNTER12, RV32I only.
HPMCOUNTER13H CSR = 0xC8D // Upper 32 bits of HPMCOUNTER13, RV32I only.
HPMCOUNTER14H CSR = 0xC8E // Upper 32 bits of HPMCOUNTER14, RV32I only.
HPMCOUNTER15H CSR = 0xC8F // Upper 32 bits of HPMCOUNTER15, RV32I only.
HPMCOUNTER16H CSR = 0xC90 // Upper 32 bits of HPMCOUNTER16, RV32I only.
HPMCOUNTER17H CSR = 0xC91 // Upper 32 bits of HPMCOUNTER17, RV32I only.
HPMCOUNTER18H CSR = 0xC92 // Upper 32 bits of HPMCOUNTER18, RV32I only.
HPMCOUNTER19H CSR = 0xC93 // Upper 32 bits of HPMCOUNTER19, RV32I only.
HPMCOUNTER20H CSR = 0xC94 // Upper 32 bits of HPMCOUNTER20, RV32I only.
HPMCOUNTER21H CSR = 0xC95 // Upper 32 bits of HPMCOUNTER21, RV32I only.
HPMCOUNTER22H CSR = 0xC96 // Upper 32 bits of HPMCOUNTER22, RV32I only.
HPMCOUNTER23H CSR = 0xC97 // Upper 32 bits of HPMCOUNTER23, RV32I only.
HPMCOUNTER24H CSR = 0xC98 // Upper 32 bits of HPMCOUNTER24, RV32I only.
HPMCOUNTER25H CSR = 0xC99 // Upper 32 bits of HPMCOUNTER25, RV32I only.
HPMCOUNTER26H CSR = 0xC9A // Upper 32 bits of HPMCOUNTER26, RV32I only.
HPMCOUNTER27H CSR = 0xC9B // Upper 32 bits of HPMCOUNTER27, RV32I only.
HPMCOUNTER28H CSR = 0xC9C // Upper 32 bits of HPMCOUNTER28, RV32I only.
HPMCOUNTER29H CSR = 0xC9D // Upper 32 bits of HPMCOUNTER29, RV32I only.
HPMCOUNTER30H CSR = 0xC9E // Upper 32 bits of HPMCOUNTER30, RV32I only.
HPMCOUNTER31H CSR = 0xC9F // Upper 32 bits of HPMCOUNTER31, RV32I only.
// Supervisor Trap Setup
SSTATUS CSR = 0x100 // Supervisor status register.
SEDELEG CSR = 0x102 // Supervisor exception delegation register.
SIDELEG CSR = 0x103 // Supervisor interrupt delegation register.
SIE CSR = 0x104 // Supervisor interrupt-enable register.
STVEC CSR = 0x105 // Supervisor trap handler base address.
SCOUNTEREN CSR = 0x106 // Supervisor counter enable.
// Supervisor Trap Handling
SSCRATCH CSR = 0x140 // Scratch register for supervisor trap handlers.
SEPC CSR = 0x141 // Supervisor exception program counter.
SCAUSE CSR = 0x142 // Supervisor trap cause.
STVAL CSR = 0x143 // Supervisor bad address or instruction.
SIP CSR = 0x144 // Supervisor interrupt pending.
// Supervisor Protection and Translation
SATP CSR = 0x180 // Supervisor address translation and protection.
// Machine Information Registers
MVENDORID CSR = 0xF11 // Vendor ID.
MARCHID CSR = 0xF12 // Architecture ID.
MIMPID CSR = 0xF13 // Implementation ID.
MHARTID CSR = 0xF14 // Hardware thread ID.
// Machine Trap Setup
MSTATUS CSR = 0x300 // Machine status register.
MISA CSR = 0x301 // ISA and extensions
MEDELEG CSR = 0x302 // Machine exception delegation register.
MIDELEG CSR = 0x303 // Machine interrupt delegation register.
MIE CSR = 0x304 // Machine interrupt-enable register.
MTVEC CSR = 0x305 // Machine trap-handler base address.
MCOUNTEREN CSR = 0x306 // Machine counter enable.
// Machine Trap Handling
MSCRATCH CSR = 0x340 // Scratch register for machine trap handlers.
MEPC CSR = 0x341 // Machine exception program counter.
MCAUSE CSR = 0x342 // Machine trap cause.
MTVAL CSR = 0x343 // Machine bad address or instruction.
MIP CSR = 0x344 // Machine interrupt pending.
// Machine Protection and Translation
PMPCFG0 CSR = 0x3A0 // Physical memory protection configuration.
PMPCFG1 CSR = 0x3A1 // Physical memory protection configuration, RV32 only.
PMPCFG2 CSR = 0x3A2 // Physical memory protection configuration.
PMPCFG3 CSR = 0x3A3 // Physical memory protection configuration, RV32 only.
PMPADDR0 CSR = 0x3B0 // Physical memory protection address register 0.
PMPADDR1 CSR = 0x3B1 // Physical memory protection address register 1.
PMPADDR2 CSR = 0x3B2 // Physical memory protection address register 2.
PMPADDR3 CSR = 0x3B3 // Physical memory protection address register 3.
PMPADDR4 CSR = 0x3B4 // Physical memory protection address register 4.
PMPADDR5 CSR = 0x3B5 // Physical memory protection address register 5.
PMPADDR6 CSR = 0x3B6 // Physical memory protection address register 6.
PMPADDR7 CSR = 0x3B7 // Physical memory protection address register 7.
PMPADDR8 CSR = 0x3B8 // Physical memory protection address register 8.
PMPADDR9 CSR = 0x3B9 // Physical memory protection address register 9.
PMPADDR10 CSR = 0x3BA // Physical memory protection address register 10.
PMPADDR11 CSR = 0x3BB // Physical memory protection address register 11.
PMPADDR12 CSR = 0x3BC // Physical memory protection address register 12.
PMPADDR13 CSR = 0x3BD // Physical memory protection address register 13.
PMPADDR14 CSR = 0x3BE // Physical memory protection address register 14.
PMPADDR15 CSR = 0x3BF // Physical memory protection address register 15.
// Machine Counter/Timers
mcycle CSR = 0xB00 // Machine cycle counter.
minstret CSR = 0xB02 // Machine instructions-retired counter.
MHPMCOUNTER3 CSR = 0xB03 // Machine performance-monitoring counter 3.
MHPMCOUNTER4 CSR = 0xB04 // Machine performance-monitoring counter 4.
MHPMCOUNTER5 CSR = 0xB05 // Machine performance-monitoring counter 5.
MHPMCOUNTER6 CSR = 0xB06 // Machine performance-monitoring counter 6.
MHPMCOUNTER7 CSR = 0xB07 // Machine performance-monitoring counter 7.
MHPMCOUNTER8 CSR = 0xB08 // Machine performance-monitoring counter 8.
MHPMCOUNTER9 CSR = 0xB09 // Machine performance-monitoring counter 9.
MHPMCOUNTER10 CSR = 0xB0A // Machine performance-monitoring counter 10.
MHPMCOUNTER11 CSR = 0xB0B // Machine performance-monitoring counter 11.
MHPMCOUNTER12 CSR = 0xB0C // Machine performance-monitoring counter 12.
MHPMCOUNTER13 CSR = 0xB0D // Machine performance-monitoring counter 13.
MHPMCOUNTER14 CSR = 0xB0E // Machine performance-monitoring counter 14.
MHPMCOUNTER15 CSR = 0xB0F // Machine performance-monitoring counter 15.
MHPMCOUNTER16 CSR = 0xB10 // Machine performance-monitoring counter 16.
MHPMCOUNTER17 CSR = 0xB11 // Machine performance-monitoring counter 17.
MHPMCOUNTER18 CSR = 0xB12 // Machine performance-monitoring counter 18.
MHPMCOUNTER19 CSR = 0xB13 // Machine performance-monitoring counter 19.
MHPMCOUNTER20 CSR = 0xB14 // Machine performance-monitoring counter 20.
MHPMCOUNTER21 CSR = 0xB15 // Machine performance-monitoring counter 21.
MHPMCOUNTER22 CSR = 0xB16 // Machine performance-monitoring counter 22.
MHPMCOUNTER23 CSR = 0xB17 // Machine performance-monitoring counter 23.
MHPMCOUNTER24 CSR = 0xB18 // Machine performance-monitoring counter 24.
MHPMCOUNTER25 CSR = 0xB19 // Machine performance-monitoring counter 25.
MHPMCOUNTER26 CSR = 0xB1A // Machine performance-monitoring counter 26.
MHPMCOUNTER27 CSR = 0xB1B // Machine performance-monitoring counter 27.
MHPMCOUNTER28 CSR = 0xB1C // Machine performance-monitoring counter 28.
MHPMCOUNTER29 CSR = 0xB1D // Machine performance-monitoring counter 29.
MHPMCOUNTER30 CSR = 0xB1E // Machine performance-monitoring counter 30.
MHPMCOUNTER31 CSR = 0xB1F // Machine performance-monitoring counter 31.
MCYCLEH CSR = 0xB80 // Upper 32 bits of MCYCLE, RV32I only.
MINSTRETH CSR = 0xB82 // Upper 32 bits of MINSTRET, RV32I only.
MHPMCOUNTER3H CSR = 0xB83 // Upper 32 bits of MHPMCOUNTER3, RV32I only.
MHPMCOUNTER4H CSR = 0xB84 // Upper 32 bits of MHPMCOUNTER4, RV32I only.
MHPMCOUNTER5H CSR = 0xB85 // Upper 32 bits of MHPMCOUNTER5, RV32I only.
MHPMCOUNTER6H CSR = 0xB86 // Upper 32 bits of MHPMCOUNTER6, RV32I only.
MHPMCOUNTER7H CSR = 0xB87 // Upper 32 bits of MHPMCOUNTER7, RV32I only.
MHPMCOUNTER8H CSR = 0xB88 // Upper 32 bits of MHPMCOUNTER8, RV32I only.
MHPMCOUNTER9H CSR = 0xB89 // Upper 32 bits of MHPMCOUNTER9, RV32I only.
MHPMCOUNTER10H CSR = 0xB8A // Upper 32 bits of MHPMCOUNTER10, RV32I only.
MHPMCOUNTER11H CSR = 0xB8B // Upper 32 bits of MHPMCOUNTER11, RV32I only.
MHPMCOUNTER12H CSR = 0xB8C // Upper 32 bits of MHPMCOUNTER12, RV32I only.
MHPMCOUNTER13H CSR = 0xB8D // Upper 32 bits of MHPMCOUNTER13, RV32I only.
MHPMCOUNTER14H CSR = 0xB8E // Upper 32 bits of MHPMCOUNTER14, RV32I only.
MHPMCOUNTER15H CSR = 0xB8F // Upper 32 bits of MHPMCOUNTER15, RV32I only.
MHPMCOUNTER16H CSR = 0xB90 // Upper 32 bits of MHPMCOUNTER16, RV32I only.
MHPMCOUNTER17H CSR = 0xB91 // Upper 32 bits of MHPMCOUNTER17, RV32I only.
MHPMCOUNTER18H CSR = 0xB92 // Upper 32 bits of MHPMCOUNTER18, RV32I only.
MHPMCOUNTER19H CSR = 0xB93 // Upper 32 bits of MHPMCOUNTER19, RV32I only.
MHPMCOUNTER20H CSR = 0xB94 // Upper 32 bits of MHPMCOUNTER20, RV32I only.
MHPMCOUNTER21H CSR = 0xB95 // Upper 32 bits of MHPMCOUNTER21, RV32I only.
MHPMCOUNTER22H CSR = 0xB96 // Upper 32 bits of MHPMCOUNTER22, RV32I only.
MHPMCOUNTER23H CSR = 0xB97 // Upper 32 bits of MHPMCOUNTER23, RV32I only.
MHPMCOUNTER24H CSR = 0xB98 // Upper 32 bits of MHPMCOUNTER24, RV32I only.
MHPMCOUNTER25H CSR = 0xB99 // Upper 32 bits of MHPMCOUNTER25, RV32I only.
MHPMCOUNTER26H CSR = 0xB9A // Upper 32 bits of MHPMCOUNTER26, RV32I only.
MHPMCOUNTER27H CSR = 0xB9B // Upper 32 bits of MHPMCOUNTER27, RV32I only.
MHPMCOUNTER28H CSR = 0xB9C // Upper 32 bits of MHPMCOUNTER28, RV32I only.
MHPMCOUNTER29H CSR = 0xB9D // Upper 32 bits of MHPMCOUNTER29, RV32I only.
MHPMCOUNTER30H CSR = 0xB9E // Upper 32 bits of MHPMCOUNTER30, RV32I only.
MHPMCOUNTER31H CSR = 0xB9F // Upper 32 bits of MHPMCOUNTER31, RV32I only.
// Machine Counter Setup
MHPMEVENT4 CSR = 0x324 // Machine performance-monitoring event selector 4.
MHPMEVENT5 CSR = 0x325 // Machine performance-monitoring event selector 5.
MHPMEVENT6 CSR = 0x326 // Machine performance-monitoring event selector 6.
MHPMEVENT7 CSR = 0x327 // Machine performance-monitoring event selector 7.
MHPMEVENT8 CSR = 0x328 // Machine performance-monitoring event selector 8.
MHPMEVENT9 CSR = 0x329 // Machine performance-monitoring event selector 9.
MHPMEVENT10 CSR = 0x32A // Machine performance-monitoring event selector 10.
MHPMEVENT11 CSR = 0x32B // Machine performance-monitoring event selector 11.
MHPMEVENT12 CSR = 0x32C // Machine performance-monitoring event selector 12.
MHPMEVENT13 CSR = 0x32D // Machine performance-monitoring event selector 13.
MHPMEVENT14 CSR = 0x32E // Machine performance-monitoring event selector 14.
MHPMEVENT15 CSR = 0x32F // Machine performance-monitoring event selector 15.
MHPMEVENT16 CSR = 0x330 // Machine performance-monitoring event selector 16.
MHPMEVENT17 CSR = 0x331 // Machine performance-monitoring event selector 17.
MHPMEVENT18 CSR = 0x332 // Machine performance-monitoring event selector 18.
MHPMEVENT19 CSR = 0x333 // Machine performance-monitoring event selector 19.
MHPMEVENT20 CSR = 0x334 // Machine performance-monitoring event selector 20.
MHPMEVENT21 CSR = 0x335 // Machine performance-monitoring event selector 21.
MHPMEVENT22 CSR = 0x336 // Machine performance-monitoring event selector 22.
MHPMEVENT23 CSR = 0x337 // Machine performance-monitoring event selector 23.
MHPMEVENT24 CSR = 0x338 // Machine performance-monitoring event selector 24.
MHPMEVENT25 CSR = 0x339 // Machine performance-monitoring event selector 25.
MHPMEVENT26 CSR = 0x33A // Machine performance-monitoring event selector 26.
MHPMEVENT27 CSR = 0x33B // Machine performance-monitoring event selector 27.
MHPMEVENT28 CSR = 0x33C // Machine performance-monitoring event selector 28.
MHPMEVENT29 CSR = 0x33D // Machine performance-monitoring event selector 29.
MHPMEVENT30 CSR = 0x33E // Machine performance-monitoring event selector 30.
MHPMEVENT31 CSR = 0x33F // Machine performance-monitoring event selector 31.
// Debug/Trace Registers (shared with Debug Mode)
TSELECT CSR = 0x7A0 // Debug/Trace trigger register select.
TDATA1 CSR = 0x7A1 // First Debug/Trace trigger data register.
TDATA2 CSR = 0x7A2 // Second Debug/Trace trigger data register.
TDATA3 CSR = 0x7A3 // Third Debug/Trace trigger data register.
// Debug Mode Registers
DCSR CSR = 0x7B0 // Debug control and status register.
DPC CSR = 0x7B1 // Debug PC.
DSCRATCH CSR = 0x7B2 // Debug scratch register.
)
+48
View File
@@ -5,8 +5,56 @@
_start:
// Load the stack pointer.
la sp, _stack_top
// Load the globals pointer. The program will load pointers relative to this
// register, so it must be set to the right value on startup.
// See: https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register
la gp, __global_pointer$
// Jump to runtime.main
call main
.section .text.handleInterruptASM
.global handleInterruptASM
.type handleInterruptASM,@function
handleInterruptASM:
// Save and restore all registers, because the hardware only saves/restores
// the pc.
// Note: we have to do this in assembly because the "interrupt"="machine"
// attribute is broken in LLVM: https://bugs.llvm.org/show_bug.cgi?id=42984
addi sp, sp, -64
sw ra, 60(sp)
sw t0, 56(sp)
sw t1, 52(sp)
sw t2, 48(sp)
sw a0, 44(sp)
sw a1, 40(sp)
sw a2, 36(sp)
sw a3, 32(sp)
sw a4, 28(sp)
sw a5, 24(sp)
sw a6, 20(sp)
sw a7, 16(sp)
sw t3, 12(sp)
sw t4, 8(sp)
sw t5, 4(sp)
sw t6, 0(sp)
call handleInterrupt
lw t6, 0(sp)
lw t5, 4(sp)
lw t4, 8(sp)
lw t3, 12(sp)
lw a7, 16(sp)
lw a6, 20(sp)
lw a5, 24(sp)
lw a4, 28(sp)
lw a3, 32(sp)
lw a2, 36(sp)
lw a1, 40(sp)
lw a0, 44(sp)
lw t2, 48(sp)
lw t1, 52(sp)
lw t0, 56(sp)
lw ra, 60(sp)
addi sp, sp, 64
mret
+27
View File
@@ -0,0 +1,27 @@
// +build arduino_nano
package machine
// Return the current CPU frequency in hertz.
func CPUFrequency() uint32 {
return 16000000
}
// LED on the Arduino
const LED Pin = 13
// 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
)
// UART pins
const (
UART_TX_PIN Pin = 1
UART_RX_PIN Pin = 0
)
@@ -26,9 +26,8 @@ var (
)
func init() {
// Work around circular definitions.
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM3, UART1.handleInterrupt)
UART2.interrupt = interrupt.New(sam.IRQ_SERCOM5, UART2.handleInterrupt)
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM3, UART1.handleInterrupt)
UART2.Interrupt = interrupt.New(sam.IRQ_SERCOM5, UART2.handleInterrupt)
}
// I2C on the Arduino Nano 33.
+1 -1
View File
@@ -69,7 +69,7 @@ var (
)
func init() {
UART0.interrupt = interrupt.New(stm32.IRQ_USART1, UART0.handleInterrupt)
UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, UART0.handleInterrupt)
}
// SPI pins
@@ -0,0 +1,73 @@
// +build circuitplay_bluefruit
package machine
const HasLowFrequencyCrystal = true
// GPIO Pins
const (
D0 = P0_30
D1 = P0_14
D2 = P0_05
D3 = P0_04
D4 = P1_02
D5 = P1_15
D6 = P0_02
D7 = P1_06
D8 = P0_13
D9 = P0_29
D10 = P0_03
D11 = P1_04
D12 = P0_26
D13 = P1_14
)
// Analog Pins
const (
A1 = P0_02
A2 = P0_29
A3 = P0_03
A4 = P0_04
A5 = P0_05
A6 = P0_30
A7 = P0_14
A8 = P0_28
A9 = P0_31
)
const (
LED = D13
NEOPIXELS = D8
BUTTONA = D4
BUTTONB = D5
SLIDER = D7 // built-in slide switch
BUTTON = BUTTONA
BUTTON1 = BUTTONB
LIGHTSENSOR = A8
TEMPSENSOR = A9
)
// UART0 pins (logical UART1)
const (
UART_TX_PIN = P0_14 // PORTB
UART_RX_PIN = P0_30 // PORTB
)
// I2C pins
const (
SDA_PIN = P0_05 // I2C0 external
SCL_PIN = P0_04 // I2C0 external
SDA1_PIN = P0_00 // I2C1 internal
SCL1_PIN = P0_01 // I2C1 internal
)
// SPI pins (internal flash)
const (
SPI0_SCK_PIN = P0_19 // SCK
SPI0_MOSI_PIN = P0_21 // MOSI
SPI0_MISO_PIN = P0_23 // MISO
)
@@ -17,7 +17,7 @@ var (
)
func init() {
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM4, UART1.handleInterrupt)
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM4, UART1.handleInterrupt)
}
// I2C on the Circuit Playground Express.
+13 -1
View File
@@ -2,6 +2,18 @@
package machine
// Return the current CPU frequency in hertz.
func CPUFrequency() uint32 {
return 16000000
}
const (
LED Pin = 1
P0 Pin = 0
P1 Pin = 1
P2 Pin = 2
P3 Pin = 3
P4 Pin = 4
P5 Pin = 5
LED = P1
)
+1 -1
View File
@@ -64,7 +64,7 @@ var (
)
func init() {
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
}
// I2C pins
+7 -13
View File
@@ -67,10 +67,10 @@ const (
// I2C on the Feather M4.
var (
I2C0 = I2C{Bus: sam.SERCOM2_I2CM,
SDA: SDA_PIN,
SCL: SCL_PIN,
PinMode: PinSERCOM}
I2C0 = I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI pins
@@ -82,14 +82,8 @@ const (
// SPI on the Feather M4.
var (
SPI0 = SPI{Bus: sam.SERCOM1_SPIM,
SCK: SPI0_SCK_PIN,
MOSI: SPI0_MOSI_PIN,
MISO: SPI0_MISO_PIN,
DOpad: spiTXPad3SCK1,
DIpad: sercomRXPad2,
MISOPinMode: PinSERCOM,
MOSIPinMode: PinSERCOM,
SCKPinMode: PinSERCOM,
SPI0 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
+1 -1
View File
@@ -64,7 +64,7 @@ var (
)
func init() {
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
}
// I2C pins
+7 -11
View File
@@ -69,10 +69,10 @@ const (
// I2C on the ItsyBitsy M4.
var (
I2C0 = I2C{Bus: sam.SERCOM2_I2CM,
SDA: SDA_PIN,
SCL: SCL_PIN,
PinMode: PinSERCOM}
I2C0 = I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI pins
@@ -84,12 +84,8 @@ const (
// SPI on the ItsyBitsy M4.
var (
SPI0 = SPI{Bus: sam.SERCOM1_SPIM,
SCK: SPI0_SCK_PIN,
MOSI: SPI0_MOSI_PIN,
MISO: SPI0_MISO_PIN,
DOpad: spiTXPad2SCK3,
DIpad: sercomRXPad3,
MISOPinMode: PinSERCOM,
SPI0 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
+8 -22
View File
@@ -77,10 +77,10 @@ const (
// I2C on the Metro M4.
var (
I2C0 = I2C{Bus: sam.SERCOM5_I2CM,
SDA: SDA_PIN,
SCL: SCL_PIN,
PinMode: PinSERCOMAlt}
I2C0 = I2C{
Bus: sam.SERCOM5_I2CM,
SERCOM: 5,
}
)
// SPI pins
@@ -97,15 +97,8 @@ const (
// SPI on the Metro M4.
var (
SPI0 = SPI{
Bus: sam.SERCOM2_SPIM,
SCK: SPI0_SCK_PIN,
MOSI: SPI0_MOSI_PIN,
MISO: SPI0_MISO_PIN,
DOpad: spiTXPad0SCK1,
DIpad: sercomRXPad2,
MISOPinMode: PinSERCOM,
MOSIPinMode: PinSERCOM,
SCKPinMode: PinSERCOM,
Bus: sam.SERCOM2_SPIM,
SERCOM: 2,
}
NINA_SPI = SPI0
)
@@ -119,14 +112,7 @@ const (
// SPI1 on the Metro M4 on pins 11,12,13
var (
SPI1 = SPI{
Bus: sam.SERCOM1_SPIM,
SCK: SPI1_SCK_PIN,
MOSI: SPI1_MOSI_PIN,
MISO: SPI1_MISO_PIN,
DOpad: spiTXPad3SCK1,
DIpad: sercomRXPad0,
MISOPinMode: PinSERCOM,
MOSIPinMode: PinSERCOM,
SCKPinMode: PinSERCOM,
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
+1 -1
View File
@@ -108,7 +108,7 @@ var (
)
func init() {
UART0.interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
}
// SPI pins
+11 -19
View File
@@ -105,10 +105,10 @@ const (
// I2C on the ItsyBitsy M4.
var (
I2C0 = I2C{Bus: sam.SERCOM2_I2CM,
SDA: SDA_PIN,
SCL: SCL_PIN,
PinMode: PinSERCOM}
I2C0 = I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
)
// SPI pins
@@ -120,15 +120,9 @@ const (
// SPI on the PyBadge.
var (
SPI0 = SPI{Bus: sam.SERCOM1_SPIM,
SCK: SPI0_SCK_PIN,
MOSI: SPI0_MOSI_PIN,
MISO: SPI0_MISO_PIN,
DOpad: spiTXPad3SCK1,
DIpad: sercomRXPad2,
MISOPinMode: PinSERCOM,
MOSIPinMode: PinSERCOM,
SCKPinMode: PinSERCOM,
SPI0 = SPI{
Bus: sam.SERCOM1_SPIM,
SERCOM: 1,
}
)
@@ -136,15 +130,13 @@ var (
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 PyBadge.
var (
SPI1 = SPI{Bus: sam.SERCOM4_SPIM,
SCK: SPI1_SCK_PIN,
MOSI: SPI1_MOSI_PIN,
DOpad: spiTXPad3SCK1,
MOSIPinMode: PinSERCOM,
SCKPinMode: PinSERCOM,
SPI1 = SPI{
Bus: sam.SERCOM4_SPIM,
SERCOM: 4,
}
)
+145
View File
@@ -0,0 +1,145 @@
// +build sam,atsamd51,pyportal
package machine
import (
"device/sam"
)
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
// GPIO Pins
const (
D0 = PB13 // NINA_RX
D1 = PB12 // NINA_TX
D2 = PB22 // built-in neopixel
D3 = PA04 // PWM available
D4 = PA05 // PWM available
D5 = PB16 // NINA_ACK
D6 = PB15 // NINA_GPIO0
D7 = PB17 // NINA_RESETN
D8 = PB14 // NINA_CS
D9 = PB04 // TFT_RD
D10 = PB05 // TFT_DC
D11 = PB06 // TFT_CS
D12 = PB07 // TFT_TE
D13 = PB23 // built-in LED
D24 = PA00 // TFT_RESET
D25 = PB31 // TFT_BACKLIGHT
D26 = PB09 // TFT_WR
D27 = PB02 // SDA
D28 = PB03 // SCL
D29 = PA12 // MOSI
D30 = PA13 // SCK
D31 = PA14 // MISO
D32 = PB30 // SD_CS
D33 = PA01 // SD_CARD_DETECT
D34 = PA16 // LCD_DATA0
D35 = PA17 // LCD_DATA1
D36 = PA18 // LCD_DATA2
D37 = PA19 // LCD_DATA3
D38 = PA20 // LCD_DATA4
D39 = PA21 // LCD_DATA5
D40 = PA22 // LCD_DATA6
D41 = PA23 // LCD_DATA7
D42 = PB10 // QSPI
D43 = PB11 // QSPI
D44 = PA08 // QSPI
D45 = PA09 // QSPI
D46 = PA10 // QSPI
D47 = PA11 // QSPI
D50 = PA02 // speaker amplifier shutdown
D51 = PA15 // NINA_RTS
NINA_CS = D8
NINA_ACK = D5
NINA_GPIO0 = D6
NINA_RESETN = D7
NINA_TX = D1
NINA_RX = D0
NINA_RTS = D51
LCD_DATA0 = D34
TFT_RD = D9
TFT_DC = D10
TFT_CS = D11
TFT_TE = D12
TFT_RESET = D24
TFT_BACKLIGHT = D25
TFT_WR = D26
NEOPIXEL = D2
SPK_SD = D50
)
// Analog pins
const (
A0 = PA02 // ADC0/AIN[0]
A1 = D3 // ADC0/AIN[4]
A2 = PA07 // ADC0/AIN[7]
A3 = D4 // ADC0/AIN[5]
A4 = PB00 // ADC0/AIN[12]
A5 = PB01 // ADC0/AIN[13]
A6 = PA06 // ADC0/AIN[6]
A7 = PB08 // ADC1/AIN[0]
AUDIO_OUT = A0
LIGHT = A2
TOUCH_YD = A4
TOUCH_XL = A5
TOUCH_YU = A6
TOUCH_XR = A7
)
const (
LED = D13
)
// UART0 aka USBCDC pins
const (
USBCDC_DM_PIN = PA24
USBCDC_DP_PIN = PA25
)
// TODO: add configuration for UART on SERCOM4 that is connected to TX/RX of ESP32
const (
UART_TX_PIN = NoPin
UART_RX_PIN = NoPin
)
// I2C pins
const (
SDA_PIN = PB02 // SDA: SERCOM2/PAD[0]
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]
SPI0_MOSI_PIN = PA12 // MOSI: SERCOM1/PAD[3]
SPI0_MISO_PIN = PA14 // MISO: SERCOM1/PAD[2]
NINA_MOSI = SPI0_MOSI_PIN
NINA_MISO = SPI0_MISO_PIN
NINA_SCK = SPI0_SCK_PIN
)
// SPI on the PyPortal.
var (
SPI0 = SPI{
Bus: sam.SERCOM2_SPIM,
SERCOM: 2,
}
NINA_SPI = SPI0
)
+1 -1
View File
@@ -55,7 +55,7 @@ var (
)
func init() {
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM0, UART1.handleInterrupt)
UART1.Interrupt = interrupt.New(sam.IRQ_SERCOM0, UART1.handleInterrupt)
}
// SPI pins
+2 -2
View File
@@ -296,7 +296,7 @@ type UART struct {
Buffer *RingBuffer
Bus *sam.SERCOM_USART_Type
SERCOM uint8
interrupt interrupt.Interrupt
Interrupt interrupt.Interrupt
}
var (
@@ -403,7 +403,7 @@ func (uart UART) Configure(config UARTConfig) error {
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INTENSET_RXC)
// Enable RX IRQ.
uart.interrupt.Enable()
uart.Interrupt.Enable()
return nil
}
+302 -191
View File
@@ -113,6 +113,162 @@ const (
PB31 Pin = 63
)
const (
pinPadMapSERCOM0Pad0 uint16 = 0x1000
pinPadMapSERCOM1Pad0 uint16 = 0x2000
pinPadMapSERCOM2Pad0 uint16 = 0x3000
pinPadMapSERCOM3Pad0 uint16 = 0x4000
pinPadMapSERCOM4Pad0 uint16 = 0x5000
pinPadMapSERCOM5Pad0 uint16 = 0x6000
pinPadMapSERCOM6Pad0 uint16 = 0x7000
pinPadMapSERCOM7Pad0 uint16 = 0x8000
pinPadMapSERCOM0Pad2 uint16 = 0x1200
pinPadMapSERCOM1Pad2 uint16 = 0x2200
pinPadMapSERCOM2Pad2 uint16 = 0x3200
pinPadMapSERCOM3Pad2 uint16 = 0x4200
pinPadMapSERCOM4Pad2 uint16 = 0x5200
pinPadMapSERCOM5Pad2 uint16 = 0x6200
pinPadMapSERCOM6Pad2 uint16 = 0x7200
pinPadMapSERCOM7Pad2 uint16 = 0x8200
pinPadMapSERCOM0AltPad0 uint16 = 0x0010
pinPadMapSERCOM1AltPad0 uint16 = 0x0020
pinPadMapSERCOM2AltPad0 uint16 = 0x0030
pinPadMapSERCOM3AltPad0 uint16 = 0x0040
pinPadMapSERCOM4AltPad0 uint16 = 0x0050
pinPadMapSERCOM5AltPad0 uint16 = 0x0060
pinPadMapSERCOM6AltPad0 uint16 = 0x0070
pinPadMapSERCOM7AltPad0 uint16 = 0x0080
pinPadMapSERCOM0AltPad1 uint16 = 0x0011
pinPadMapSERCOM1AltPad1 uint16 = 0x0021
pinPadMapSERCOM2AltPad1 uint16 = 0x0031
pinPadMapSERCOM3AltPad1 uint16 = 0x0041
pinPadMapSERCOM4AltPad1 uint16 = 0x0051
pinPadMapSERCOM5AltPad1 uint16 = 0x0061
pinPadMapSERCOM6AltPad1 uint16 = 0x0071
pinPadMapSERCOM7AltPad1 uint16 = 0x0081
pinPadMapSERCOM0AltPad2 uint16 = 0x0012
pinPadMapSERCOM1AltPad2 uint16 = 0x0022
pinPadMapSERCOM2AltPad2 uint16 = 0x0032
pinPadMapSERCOM3AltPad2 uint16 = 0x0042
pinPadMapSERCOM4AltPad2 uint16 = 0x0052
pinPadMapSERCOM5AltPad2 uint16 = 0x0062
pinPadMapSERCOM6AltPad2 uint16 = 0x0072
pinPadMapSERCOM7AltPad2 uint16 = 0x0082
)
// pinPadMapping lists which pins have which SERCOMs attached to them.
// The encoding is rather dense, with each uint16 encoding two pins and both
// SERCOM and SERCOM-ALT.
//
// Observations:
// * There are eight SERCOMs. Those SERCOM numbers can be encoded in 4 bits.
// * Even pad numbers are usually on even pins, and odd pad numbers are usually
// on odd pins. The exception is SERCOM-ALT, which sometimes swaps pad 0 and 1.
// With that, there is still an invariant that the pad number for an odd pin is
// the pad number for the corresponding even pin with the low bit toggled.
// * Pin pads come in pairs. If PA00 has pad 0, then PA01 has pad 1.
// With this information, we can encode SERCOM pin/pad numbers much more
// efficiently. Due to pads coming in pairs, we can ignore half the pins: the
// information for an odd pin can be calculated easily from the preceding even
// pin.
//
// Each word below is split in two bytes. The 8 high bytes are for SERCOM and
// the 8 low bits are for SERCOM-ALT. Of each byte, the 4 high bits encode the
// SERCOM + 1 while the two low bits encodes the pad number (the pad number for
// the odd pin can be trivially calculated by toggling the low bit of the pad
// number). It encodes SERCOM + 1 instead of just the SERCOM number, to make it
// easy to check whether a nibble is set at all.
//
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/60001507E.pdf
var pinPadMapping = [32]uint16{
// page 32
PA00 / 2: 0 | pinPadMapSERCOM1AltPad0,
// page 33
PB08 / 2: 0 | pinPadMapSERCOM4AltPad0,
PA04 / 2: 0 | pinPadMapSERCOM0AltPad0,
PA06 / 2: 0 | pinPadMapSERCOM0AltPad2,
//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,
// page 34
//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,
PB16 / 2: pinPadMapSERCOM5Pad0 | 0,
PB18 / 2: pinPadMapSERCOM5Pad2 | pinPadMapSERCOM7AltPad2,
// page 35
PB20 / 2: pinPadMapSERCOM3Pad0 | pinPadMapSERCOM7AltPad1,
PA20 / 2: pinPadMapSERCOM5Pad2 | pinPadMapSERCOM3AltPad2,
PA22 / 2: pinPadMapSERCOM3Pad0 | pinPadMapSERCOM5AltPad1,
PA24 / 2: pinPadMapSERCOM3Pad2 | pinPadMapSERCOM5AltPad2,
PB22 / 2: pinPadMapSERCOM1Pad2 | pinPadMapSERCOM5AltPad2,
PB24 / 2: pinPadMapSERCOM0Pad0 | pinPadMapSERCOM2AltPad1,
PB26 / 2: pinPadMapSERCOM2Pad0 | pinPadMapSERCOM4AltPad1,
PB28 / 2: pinPadMapSERCOM2Pad2 | pinPadMapSERCOM4AltPad2,
//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,
// page 36
PB30 / 2: 0 | pinPadMapSERCOM5AltPad1,
PB00 / 2: 0 | pinPadMapSERCOM5AltPad2,
PB02 / 2: 0 | pinPadMapSERCOM5AltPad0,
}
// findPinPadMapping looks up the pad number and the pinmode for a given pin and
// SERCOM number. The result can either be SERCOM, SERCOM-ALT, or "not found"
// (indicated by returning ok=false). The pad number is returned to calculate
// the DOPO/DIPO bitfields of the various serial peripherals.
func findPinPadMapping(sercom uint8, pin Pin) (pinMode PinMode, pad uint32, ok bool) {
bytes := pinPadMapping[pin/2]
upper := byte(bytes >> 8)
lower := byte(bytes & 0xff)
if upper != 0 {
// SERCOM
if (upper>>4)-1 == sercom {
pinMode = PinSERCOM
pad |= uint32(upper % 4)
ok = true
}
}
if lower != 0 {
// SERCOM-ALT
if (lower>>4)-1 == sercom {
pinMode = PinSERCOMAlt
pad |= uint32(lower % 4)
ok = true
}
}
if ok {
// If the pin is uneven, toggle the lowest bit of the pad number.
if pin&1 != 0 {
pad ^= 1
}
}
return
}
// 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) {
@@ -322,7 +478,7 @@ func InitADC() {
sam.ADC0.CTRLA.SetBits(sam.ADC_CTRLA_PRESCALER_DIV32 << sam.ADC_CTRLA_PRESCALER_Pos)
// adcs[i]->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_10BIT_Val;
sam.ADC0.CTRLB.SetBits(sam.ADC_CTRLB_RESSEL_10BIT << sam.ADC_CTRLB_RESSEL_Pos)
sam.ADC0.CTRLB.SetBits(sam.ADC_CTRLB_RESSEL_12BIT << sam.ADC_CTRLB_RESSEL_Pos)
// wait for sync
for sam.ADC0.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_CTRLB) {
@@ -352,7 +508,7 @@ func InitADC() {
// same for ADC1, as for ADC0
sam.ADC1.CTRLA.SetBits(sam.ADC_CTRLA_PRESCALER_DIV32 << sam.ADC_CTRLA_PRESCALER_Pos)
sam.ADC1.CTRLB.SetBits(sam.ADC_CTRLB_RESSEL_10BIT << sam.ADC_CTRLB_RESSEL_Pos)
sam.ADC1.CTRLB.SetBits(sam.ADC_CTRLB_RESSEL_12BIT << sam.ADC_CTRLB_RESSEL_Pos)
for sam.ADC1.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_CTRLB) {
}
sam.ADC1.SAMPCTRL.Set(5)
@@ -390,6 +546,9 @@ func (a ADC) Get() uint16 {
}
// Selection for the positive ADC input channel
bus.INPUTCTRL.ClearBits(sam.ADC_INPUTCTRL_MUXPOS_Msk)
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
}
bus.INPUTCTRL.SetBits((uint16(ch) & sam.ADC_INPUTCTRL_MUXPOS_Msk) << sam.ADC_INPUTCTRL_MUXPOS_Pos)
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
}
@@ -405,7 +564,7 @@ func (a ADC) Get() uint16 {
}
// Clear the Data Ready flag
bus.INTFLAG.SetBits(sam.ADC_INTFLAG_RESRDY)
bus.INTFLAG.ClearBits(sam.ADC_INTFLAG_RESRDY)
for bus.SYNCBUSY.HasBits(sam.ADC_SYNCBUSY_ENABLE) {
}
@@ -447,16 +606,20 @@ func (a ADC) getADCChannel() uint8 {
return 6
case PA07:
return 7
case PB00:
return 12
case PB01:
return 13
case PB02:
return 10
return 14
case PB03:
return 11
return 15
case PA09:
return 17
case PA11:
return 19
default:
return 0
panic("Invalid ADC pin")
}
}
@@ -464,7 +627,8 @@ func (a ADC) getADCChannel() uint8 {
type UART struct {
Buffer *RingBuffer
Bus *sam.SERCOM_USART_INT_Type
Mode PinMode
SERCOM uint8
IRQVal uint32 // RXC interrupt
}
var (
@@ -472,89 +636,71 @@ var (
UART0 = USBCDC{Buffer: NewRingBuffer()}
// The first hardware serial port on the SAMD51. Uses the SERCOM3 interface.
UART1 = UART{Bus: sam.SERCOM3_USART_INT,
UART1 = UART{
Buffer: NewRingBuffer(),
Mode: PinSERCOMAlt,
Bus: sam.SERCOM3_USART_INT,
SERCOM: 3,
IRQVal: sam.IRQ_SERCOM3_2, // RXC interrupt
}
// The second hardware serial port on the SAMD51. Uses the SERCOM0 interface.
UART2 = UART{
Buffer: NewRingBuffer(),
Bus: sam.SERCOM0_USART_INT,
Mode: PinSERCOMAlt,
SERCOM: 0,
IRQVal: sam.IRQ_SERCOM0_2, // RXC interrupt
}
)
const (
sampleRate16X = 16
lsbFirst = 1
sercomRXPad0 = 0
sercomRXPad1 = 1
sercomRXPad2 = 2
sercomRXPad3 = 3
sercomTXPad0 = 0 // Only for UART
sercomTXPad2 = 1 // Only for UART
sercomTXPad023 = 2 // Only for UART with TX on PAD0, RTS on PAD2 and CTS on PAD3
spiTXPad0SCK1 = 0
spiTXPad2SCK3 = 1
spiTXPad3SCK1 = 2
spiTXPad0SCK3 = 3
sampleRate16X = 16
lsbFirst = 1
)
// Configure the UART.
func (uart UART) Configure(config UARTConfig) {
func (uart UART) Configure(config UARTConfig) error {
// Default baud rate to 115200.
if config.BaudRate == 0 {
config.BaudRate = 115200
}
// determine pins
if config.TX == 0 {
if config.TX == 0 && config.RX == 0 {
// use default pins
config.TX = UART_TX_PIN
config.RX = UART_RX_PIN
}
// determine pads
var txpad, rxpad int
switch config.TX {
case PA04:
txpad = sercomTXPad0
case PA10:
txpad = sercomTXPad2
case PA18:
txpad = sercomTXPad2
case PA16:
txpad = sercomTXPad0
// Determine transmit pinout.
txPinMode, txPad, ok := findPinPadMapping(uart.SERCOM, config.TX)
if !ok {
return ErrInvalidOutputPin
}
var txPinOut uint32
// See CTRLA.RXPO bits of the SERCOM USART peripheral (page 945-946) for how
// pads are mapped to pinout values.
switch txPad {
case 0:
txPinOut = 0
default:
panic("Invalid TX pin for UART")
// TODO: flow control (RTS/CTS)
return ErrInvalidOutputPin
}
switch config.RX {
case PA06:
rxpad = sercomRXPad2
case PA07:
rxpad = sercomRXPad3
case PA11:
rxpad = sercomRXPad3
case PA18:
rxpad = sercomRXPad2
case PA16:
rxpad = sercomRXPad0
case PA19:
rxpad = sercomRXPad3
case PA17:
rxpad = sercomRXPad1
default:
panic("Invalid RX pin for UART")
// Determine receive pinout.
rxPinMode, rxPad, ok := findPinPadMapping(uart.SERCOM, config.RX)
if !ok {
return ErrInvalidInputPin
}
// As you can see in the CTRLA.RXPO bits of the SERCOM USART peripheral
// (page 945), input pins are mapped directly.
rxPinOut := rxPad
// configure pins
config.TX.Configure(PinConfig{Mode: uart.Mode})
config.RX.Configure(PinConfig{Mode: uart.Mode})
config.TX.Configure(PinConfig{Mode: txPinMode})
config.RX.Configure(PinConfig{Mode: rxPinMode})
// reset SERCOM0
// reset SERCOM
uart.Bus.CTRLA.SetBits(sam.SERCOM_USART_INT_CTRLA_SWRST)
for uart.Bus.CTRLA.HasBits(sam.SERCOM_USART_INT_CTRLA_SWRST) ||
uart.Bus.SYNCBUSY.HasBits(sam.SERCOM_USART_INT_SYNCBUSY_SWRST) {
@@ -587,8 +733,8 @@ func (uart UART) Configure(config UARTConfig) {
// set UART pads. This is not same as pins...
// SERCOM_USART_CTRLA_TXPO(txPad) |
// SERCOM_USART_CTRLA_RXPO(rxPad);
uart.Bus.CTRLA.SetBits(uint32((txpad << sam.SERCOM_USART_INT_CTRLA_TXPO_Pos) |
(rxpad << sam.SERCOM_USART_INT_CTRLA_RXPO_Pos)))
uart.Bus.CTRLA.SetBits((txPinOut << sam.SERCOM_USART_INT_CTRLA_TXPO_Pos) |
(rxPinOut << sam.SERCOM_USART_INT_CTRLA_RXPO_Pos))
// Enable Transceiver and Receiver
//sercom->USART.CTRLB.reg |= SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_RXEN ;
@@ -604,19 +750,14 @@ func (uart UART) Configure(config UARTConfig) {
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INT_INTENSET_RXC)
// Enable RX IRQ.
switch uart.Bus {
case sam.SERCOM0_USART_INT:
arm.EnableIRQ(sam.IRQ_SERCOM0_0)
arm.EnableIRQ(sam.IRQ_SERCOM0_1)
arm.EnableIRQ(sam.IRQ_SERCOM0_2)
arm.EnableIRQ(sam.IRQ_SERCOM0_OTHER)
default:
// Currently assumes SERCOM3
arm.EnableIRQ(sam.IRQ_SERCOM3_0)
arm.EnableIRQ(sam.IRQ_SERCOM3_1)
arm.EnableIRQ(sam.IRQ_SERCOM3_2)
arm.EnableIRQ(sam.IRQ_SERCOM3_OTHER)
}
// This is a small note at the bottom of the NVIC section of the datasheet:
// > The integer number specified in the source refers to the respective bit
// > position in the INTFLAG register of respective peripheral.
// Therefore, if we only need to listen to the RXC interrupt source (in bit
// position 2), we only need interrupt source 2 for this SERCOM device.
arm.EnableIRQ(uart.IRQVal)
return nil
}
// SetBaudRate sets the communication speed for the UART.
@@ -642,53 +783,15 @@ func (uart UART) WriteByte(c byte) error {
return nil
}
//go:export SERCOM3_0_IRQHandler
func handleSERCOM3_0() {
handleUART1()
}
//go:export SERCOM3_1_IRQHandler
func handleSERCOM3_1() {
handleUART1()
}
//go:export SERCOM3_2_IRQHandler
func handleSERCOM3_2() {
handleUART1()
}
//go:export SERCOM3_OTHER_IRQHandler
func handleSERCOM3_OTHER() {
handleUART1()
}
func handleUART1() {
// should reset IRQ
UART1.Receive(byte((UART1.Bus.DATA.Get() & 0xFF)))
UART1.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
}
//go:export SERCOM0_0_IRQHandler
func handleSERCOM0_0() {
handleUART2()
}
//go:export SERCOM0_1_IRQHandler
func handleSERCOM0_1() {
handleUART2()
}
//go:export SERCOM0_2_IRQHandler
func handleSERCOM0_2() {
handleUART2()
}
//go:export SERCOM0_OTHER_IRQHandler
func handleSERCOM0_OTHER() {
handleUART2()
}
func handleUART2() {
// should reset IRQ
UART2.Receive(byte((UART2.Bus.DATA.Get() & 0xFF)))
UART2.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INT_INTFLAG_RXC)
@@ -696,10 +799,8 @@ func handleUART2() {
// I2C on the SAMD51.
type I2C struct {
Bus *sam.SERCOM_I2CM_Type
SCL Pin
SDA Pin
PinMode PinMode
Bus *sam.SERCOM_I2CM_Type
SERCOM uint8
}
// I2CConfig is used to store config info for I2C.
@@ -732,12 +833,33 @@ const (
const i2cTimeout = 1000
// Configure is intended to setup the I2C interface.
func (i2c I2C) Configure(config I2CConfig) {
func (i2c I2C) Configure(config I2CConfig) error {
// Default I2C bus speed is 100 kHz.
if config.Frequency == 0 {
config.Frequency = TWI_FREQ_100KHZ
}
// Use default I2C pins if not set.
if config.SDA == 0 && config.SCL == 0 {
config.SDA = SDA_PIN
config.SCL = SCL_PIN
}
sclPinMode, sclPad, ok := findPinPadMapping(i2c.SERCOM, config.SCL)
if !ok || sclPad != 1 {
// SCL must be on pad 1, according to section 36.4 of the datasheet.
// Note: this is not an exhaustive test for I2C support on the pin: not
// all pins support I2C.
return ErrInvalidClockPin
}
sdaPinMode, sdaPad, ok := findPinPadMapping(i2c.SERCOM, config.SDA)
if !ok || sdaPad != 0 {
// SDA must be on pad 0, according to section 36.4 of the datasheet.
// Note: this is not an exhaustive test for I2C support on the pin: not
// all pins support I2C.
return ErrInvalidDataPin
}
// reset SERCOM
i2c.Bus.CTRLA.SetBits(sam.SERCOM_I2CM_CTRLA_SWRST)
for i2c.Bus.CTRLA.HasBits(sam.SERCOM_I2CM_CTRLA_SWRST) ||
@@ -763,8 +885,10 @@ func (i2c I2C) Configure(config I2CConfig) {
}
// enable pins
i2c.SDA.Configure(PinConfig{Mode: i2c.PinMode})
i2c.SCL.Configure(PinConfig{Mode: i2c.PinMode})
config.SDA.Configure(PinConfig{Mode: sdaPinMode})
config.SCL.Configure(PinConfig{Mode: sclPinMode})
return nil
}
// SetBaudRate sets the communication speed for the I2C.
@@ -863,7 +987,7 @@ func (i2c I2C) WriteByte(data byte) error {
timeout := i2cTimeout
for !i2c.Bus.INTFLAG.HasBits(sam.SERCOM_I2CM_INTFLAG_MB) {
// check for bus error
if sam.SERCOM3_I2CM.STATUS.HasBits(sam.SERCOM_I2CM_STATUS_BUSERR) {
if i2c.Bus.STATUS.HasBits(sam.SERCOM_I2CM_STATUS_BUSERR) {
return errors.New("I2C bus error")
}
timeout--
@@ -932,15 +1056,8 @@ func (i2c I2C) readByte() byte {
// SPI
type SPI struct {
Bus *sam.SERCOM_SPIM_Type
SCK Pin
MOSI Pin
MISO Pin
DOpad int
DIpad int
SCKPinMode PinMode
MOSIPinMode PinMode
MISOPinMode PinMode
Bus *sam.SERCOM_SPIM_Type
SERCOM uint8
}
// SPIConfig is used to store config info for SPI.
@@ -954,34 +1071,61 @@ type SPIConfig struct {
}
// Configure is intended to setup the SPI interface.
func (spi SPI) Configure(config SPIConfig) {
doPad := spi.DOpad
diPad := spi.DIpad
func (spi SPI) Configure(config SPIConfig) error {
// Use default pins if not set.
if config.SCK == 0 && config.MOSI == 0 && config.MISO == 0 {
config.SCK = SPI0_SCK_PIN
config.MOSI = SPI0_MOSI_PIN
config.MISO = SPI0_MISO_PIN
}
// set default frequency
if config.Frequency == 0 {
config.Frequency = 4000000
}
// Determine the input pinout (for MISO).
var dataInPinout uint32
misoPinMode, misoPad, ok := findPinPadMapping(spi.SERCOM, config.MISO)
if config.MISO != NoPin {
if !ok {
return ErrInvalidInputPin
}
dataInPinout = misoPad // mapped directly
}
// Determine the output pinout (for MOSI/SCK).
// See DOPO field in the CTRLA register on page 986 of the datasheet.
var dataOutPinout uint32
sckPinMode, sckPad, ok := findPinPadMapping(spi.SERCOM, config.SCK)
if !ok || sckPad != 1 {
// SCK pad must always be 1
return ErrInvalidOutputPin
}
mosiPinMode, mosiPad, ok := findPinPadMapping(spi.SERCOM, config.MOSI)
if !ok {
return ErrInvalidOutputPin
}
switch mosiPad {
case 0:
dataOutPinout = 0x0
case 3:
dataOutPinout = 0x2
default:
return ErrInvalidOutputPin
}
// Disable SPI port.
spi.Bus.CTRLA.ClearBits(sam.SERCOM_SPIM_CTRLA_ENABLE)
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_ENABLE) {
}
// enable pins
if spi.SCKPinMode == 0 {
spi.SCKPinMode = PinSERCOMAlt
config.SCK.Configure(PinConfig{Mode: sckPinMode})
config.MOSI.Configure(PinConfig{Mode: mosiPinMode})
if config.MISO != NoPin {
config.MISO.Configure(PinConfig{Mode: misoPinMode})
}
if spi.MOSIPinMode == 0 {
spi.MOSIPinMode = PinSERCOMAlt
}
if spi.MISOPinMode == 0 {
spi.MISOPinMode = PinSERCOMAlt
}
spi.SCK.Configure(PinConfig{Mode: spi.SCKPinMode})
spi.MOSI.Configure(PinConfig{Mode: spi.MOSIPinMode})
spi.MISO.Configure(PinConfig{Mode: spi.MISOPinMode})
// reset SERCOM
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_SWRST)
@@ -990,17 +1134,17 @@ func (spi SPI) Configure(config SPIConfig) {
}
// set bit transfer order
dataOrder := 0
dataOrder := uint32(0)
if config.LSBFirst {
dataOrder = 1
}
// Set SPI master
// SERCOM_SPIM_CTRLA_MODE_SPI_MASTER = 3
spi.Bus.CTRLA.Set(uint32((3 << sam.SERCOM_SPIM_CTRLA_MODE_Pos) |
(doPad << sam.SERCOM_SPIM_CTRLA_DOPO_Pos) |
(diPad << sam.SERCOM_SPIM_CTRLA_DIPO_Pos) |
(dataOrder << sam.SERCOM_SPIM_CTRLA_DORD_Pos)))
spi.Bus.CTRLA.Set((3 << sam.SERCOM_SPIM_CTRLA_MODE_Pos) |
(dataOutPinout << sam.SERCOM_SPIM_CTRLA_DOPO_Pos) |
(dataInPinout << sam.SERCOM_SPIM_CTRLA_DIPO_Pos) |
(dataOrder << sam.SERCOM_SPIM_CTRLA_DORD_Pos))
spi.Bus.CTRLB.SetBits((0 << sam.SERCOM_SPIM_CTRLB_CHSIZE_Pos) | // 8bit char size
sam.SERCOM_SPIM_CTRLB_RXEN) // receive enable
@@ -1034,6 +1178,8 @@ func (spi SPI) Configure(config SPIConfig) {
spi.Bus.CTRLA.SetBits(sam.SERCOM_SPIM_CTRLA_ENABLE)
for spi.Bus.SYNCBUSY.HasBits(sam.SERCOM_SPIM_SYNCBUSY_ENABLE) {
}
return nil
}
// Transfer writes/reads a single byte using the SPI interface.
@@ -1052,19 +1198,6 @@ func (spi SPI) Transfer(w byte) (byte, error) {
// PWM
const period = 0xFFFF
// 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_)
//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)
}
// Configure configures a PWM pin for output.
func (pwm PWM) Configure() {
// Set pin as output
@@ -1179,34 +1312,6 @@ func (pwm PWM) setPinCfg(val uint8) {
pwm.Pin.setPinCfg(val)
}
// 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
default:
return nil // not supported on this pin
}
}
// setChannel sets the value for the correct channel for PWM on this pin
func (pwm PWM) setChannel(val uint32) {
switch pwm.Pin {
@@ -1230,6 +1335,8 @@ func (pwm PWM) setChannel(val uint32) {
pwm.getTimer().CC[3].Set(val)
case PA22:
pwm.getTimer().CC[2].Set(val)
case PB31:
pwm.getTimer().CC[1].Set(val)
default:
return // not supported on this pin
}
@@ -1258,6 +1365,8 @@ func (pwm PWM) setChannelBuffer(val uint32) {
pwm.getTimer().CCBUF[3].Set(val)
case PA22:
pwm.getTimer().CCBUF[2].Set(val)
case PB31:
pwm.getTimer().CCBUF[1].Set(val)
default:
return // not supported on this pin
}
@@ -1286,6 +1395,8 @@ func (pwm PWM) getMux() PinMode {
return PinPWMG
case PA22:
return PinPWMG
case PB31:
return PinPWMF
default:
return 0 // not supported on this pin
}
@@ -2027,14 +2138,14 @@ func setEPINTENSET(ep uint32, val uint8) {
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val)
}
// ResetProcessor should perform a system reset in preperation
// ResetProcessor should perform a system reset in preparation
// to switch to the bootloader to flash new firmware.
func ResetProcessor() {
arm.DisableInterrupts()
// Perform magic reset into bootloader, as mentioned in
// https://github.com/arduino/ArduinoCore-samd/issues/197
*(*uint32)(unsafe.Pointer(uintptr(0x20000000 + 0x00030000 - 4))) = RESET_MAGIC_VALUE
*(*uint32)(unsafe.Pointer(uintptr(0x20000000 + HSRAM_SIZE - 4))) = RESET_MAGIC_VALUE
arm.SystemReset()
}
+45
View File
@@ -6,3 +6,48 @@
// 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_)
//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)
}
// 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
default:
return nil // not supported on this pin
}
}
+50
View File
@@ -6,3 +6,53 @@
// 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
}
}
+58
View File
@@ -0,0 +1,58 @@
// +build sam,atsamd51,atsamd51j20
// Peripheral abstraction layer for the atsamd51.
//
// Datasheet:
// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507C.pdf
//
package machine
import "device/sam"
const HSRAM_SIZE = 0x00040000
// 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
}
}
+18
View File
@@ -4,6 +4,7 @@ package machine
import (
"device/sifive"
"runtime/interrupt"
)
func CPUFrequency() uint32 {
@@ -65,6 +66,23 @@ func (uart UART) Configure(config UARTConfig) {
// 115200 baud rate is 138.
sifive.UART0.DIV.Set(138)
sifive.UART0.TXCTRL.Set(sifive.UART_TXCTRL_ENABLE)
sifive.UART0.RXCTRL.Set(sifive.UART_RXCTRL_ENABLE)
sifive.UART0.IE.Set(sifive.UART_IE_RXWM) // enable the receive interrupt (only)
intr := interrupt.New(sifive.IRQ_UART0, UART0.handleInterrupt)
intr.SetPriority(5)
intr.Enable()
}
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
rxdata := uart.Bus.RXDATA.Get()
c := byte(rxdata)
if uint32(c) != rxdata {
// The rxdata has other bits set than just the low 8 bits. This probably
// means that the 'empty' flag is set, which indicates there is no data
// to be read and the byte is garbage. Ignore this byte.
return
}
uart.Receive(c)
}
func (uart UART) WriteByte(c byte) {
+19
View File
@@ -8,6 +8,25 @@ import (
"unsafe"
)
// Interrupt numbers as used on the GameBoy Advance. Register them with
// runtime/interrupt.New.
const (
IRQ_VBLANK = 0
IRQ_HBLANK = 1
IRQ_VCOUNT = 2
IRQ_TIMER0 = 3
IRQ_TIMER1 = 4
IRQ_TIMER2 = 5
IRQ_TIMER3 = 6
IRQ_COM = 7
IRQ_DMA0 = 8
IRQ_DMA1 = 9
IRQ_DMA2 = 10
IRQ_DMA3 = 11
IRQ_KEYPAD = 12
IRQ_GAMEPAK = 13
)
// Make it easier to directly write to I/O RAM.
var ioram = (*[0x400]volatile.Register8)(unsafe.Pointer(uintptr(0x04000000)))
+2 -4
View File
@@ -88,9 +88,7 @@ func (uart UART) Configure(config UARTConfig) {
nrf.UART0.INTENSET.Set(nrf.UART_INTENSET_RXDRDY_Msk)
// Enable RX IRQ.
intr := interrupt.New(nrf.IRQ_UART0, func(intr interrupt.Interrupt) {
UART0.handleInterrupt()
})
intr := interrupt.New(nrf.IRQ_UART0, UART0.handleInterrupt)
intr.SetPriority(0xc0) // low priority
intr.Enable()
}
@@ -119,7 +117,7 @@ func (uart UART) WriteByte(c byte) error {
return nil
}
func (uart UART) handleInterrupt() {
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
if nrf.UART0.EVENTS_RXDRDY.Get() != 0 {
uart.Receive(byte(nrf.UART0.RXD.Get()))
nrf.UART0.EVENTS_RXDRDY.Set(0x0)
+52
View File
@@ -11,6 +11,58 @@ func CPUFrequency() uint32 {
return 64000000
}
// Hardware pins
const (
P0_00 Pin = 0
P0_01 Pin = 1
P0_02 Pin = 2
P0_03 Pin = 3
P0_04 Pin = 4
P0_05 Pin = 5
P0_06 Pin = 6
P0_07 Pin = 7
P0_08 Pin = 8
P0_09 Pin = 9
P0_10 Pin = 10
P0_11 Pin = 11
P0_12 Pin = 12
P0_13 Pin = 13
P0_14 Pin = 14
P0_15 Pin = 15
P0_16 Pin = 16
P0_17 Pin = 17
P0_18 Pin = 18
P0_19 Pin = 19
P0_20 Pin = 20
P0_21 Pin = 21
P0_22 Pin = 22
P0_23 Pin = 23
P0_24 Pin = 24
P0_25 Pin = 25
P0_26 Pin = 26
P0_27 Pin = 27
P0_28 Pin = 28
P0_29 Pin = 29
P0_30 Pin = 30
P0_31 Pin = 31
P1_00 Pin = 32
P1_01 Pin = 33
P1_02 Pin = 34
P1_03 Pin = 35
P1_04 Pin = 36
P1_05 Pin = 37
P1_06 Pin = 38
P1_07 Pin = 39
P1_08 Pin = 40
P1_09 Pin = 41
P1_10 Pin = 42
P1_11 Pin = 43
P1_12 Pin = 44
P1_13 Pin = 45
P1_14 Pin = 46
P1_15 Pin = 47
)
// Get peripheral and pin number for this GPIO pin.
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
if p >= 32 {
+3 -3
View File
@@ -113,7 +113,7 @@ func (p Pin) Get() bool {
type UART struct {
Buffer *RingBuffer
Bus *stm32.USART_Type
interrupt interrupt.Interrupt
Interrupt interrupt.Interrupt
}
// Configure the UART.
@@ -155,8 +155,8 @@ func (uart UART) Configure(config UARTConfig) {
uart.Bus.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
// Enable RX IRQ
uart.interrupt.SetPriority(0xc0)
uart.interrupt.Enable()
uart.Interrupt.SetPriority(0xc0)
uart.Interrupt.Enable()
}
// SetBaudRate sets the communication speed for the UART.
+12 -13
View File
@@ -3,7 +3,6 @@
package machine
import (
"bytes"
"errors"
"runtime/volatile"
)
@@ -348,18 +347,18 @@ const cdcSize = iadDescriptorSize +
// Bytes returns CDCDescriptor data.
func (d CDCDescriptor) Bytes() []byte {
buf := bytes.NewBuffer(make([]byte, 0))
buf.Write(d.iad.Bytes())
buf.Write(d.cif.Bytes())
buf.Write(d.header.Bytes())
buf.Write(d.controlManagement.Bytes())
buf.Write(d.functionalDescriptor.Bytes())
buf.Write(d.callManagement.Bytes())
buf.Write(d.cifin.Bytes())
buf.Write(d.dif.Bytes())
buf.Write(d.out.Bytes())
buf.Write(d.in.Bytes())
return buf.Bytes()
var buf []byte
buf = append(buf, d.iad.Bytes()...)
buf = append(buf, d.cif.Bytes()...)
buf = append(buf, d.header.Bytes()...)
buf = append(buf, d.controlManagement.Bytes()...)
buf = append(buf, d.functionalDescriptor.Bytes()...)
buf = append(buf, d.callManagement.Bytes()...)
buf = append(buf, d.cifin.Bytes()...)
buf = append(buf, d.dif.Bytes()...)
buf = append(buf, d.out.Bytes()...)
buf = append(buf, d.in.Bytes()...)
return buf
}
// MSCDescriptor is not used yet.
+1 -1
View File
@@ -12,7 +12,7 @@ const GOARCH = "wasm"
const TargetBits = 32
//go:extern __heap_base
var heapStartSymbol unsafe.Pointer
var heapStartSymbol [0]byte
//go:export llvm.wasm.memory.size.i32
func wasm_memory_size(index int32) int32
+5 -5
View File
@@ -7,19 +7,19 @@ import (
)
//go:extern _heap_start
var heapStartSymbol unsafe.Pointer
var heapStartSymbol [0]byte
//go:extern _heap_end
var heapEndSymbol unsafe.Pointer
var heapEndSymbol [0]byte
//go:extern _globals_start
var globalsStartSymbol unsafe.Pointer
var globalsStartSymbol [0]byte
//go:extern _globals_end
var globalsEndSymbol unsafe.Pointer
var globalsEndSymbol [0]byte
//go:extern _stack_top
var stackTopSymbol unsafe.Pointer
var stackTopSymbol [0]byte
var (
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
+11
View File
@@ -0,0 +1,11 @@
package runtime
//go:linkname indexBytePortable internal/bytealg.IndexByte
func indexBytePortable(s []byte, c byte) int {
for i, b := range s {
if b == c {
return i
}
}
return -1
}
+1 -1
View File
@@ -318,7 +318,7 @@ func markRoots(start, end uintptr) {
}
}
for addr := start; addr != end; addr += unsafe.Sizeof(addr) {
for addr := start; addr != end; addr += unsafe.Alignof(addr) {
root := *(*uintptr)(unsafe.Pointer(addr))
markRoot(addr, root)
}
+72
View File
@@ -6,6 +6,7 @@ package runtime
// https://golang.org/src/runtime/map.go
import (
"reflect"
"unsafe"
)
@@ -318,3 +319,74 @@ func hashmapStringDelete(m *hashmap, key string) {
hash := hashmapStringHash(key)
hashmapDelete(m, unsafe.Pointer(&key), hash, hashmapStringEqual)
}
// Hashmap with interface keys (for everything else).
func hashmapInterfaceHash(itf interface{}) uint32 {
x := reflect.ValueOf(itf)
if x.Type() == 0 {
return 0 // nil interface
}
value := (*_interface)(unsafe.Pointer(&itf)).value
ptr := value
if x.Type().Size() <= unsafe.Sizeof(uintptr(0)) {
// Value fits in pointer, so it's directly stored in the pointer.
ptr = unsafe.Pointer(&value)
}
switch x.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return hashmapHash(ptr, x.Type().Size())
case reflect.Bool, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return hashmapHash(ptr, x.Type().Size())
case reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
// It should be possible to just has the contents. However, NaN != NaN
// so if you're using lots of NaNs as map keys (you shouldn't) then hash
// time may become exponential. To fix that, it would be better to
// return a random number instead:
// https://research.swtch.com/randhash
return hashmapHash(ptr, x.Type().Size())
case reflect.String:
return hashmapStringHash(x.String())
case reflect.Chan, reflect.Ptr, reflect.UnsafePointer:
// It might seem better to just return the pointer, but that won't
// result in an evenly distributed hashmap. Instead, hash the pointer
// like most other types.
return hashmapHash(ptr, x.Type().Size())
case reflect.Array:
var hash uint32
for i := 0; i < x.Len(); i++ {
hash |= hashmapInterfaceHash(x.Index(i).Interface())
}
return hash
case reflect.Struct:
var hash uint32
for i := 0; i < x.NumField(); i++ {
hash |= hashmapInterfaceHash(x.Field(i).Interface())
}
return hash
default:
runtimePanic("comparing un-comparable type")
return 0 // unreachable
}
}
func hashmapInterfaceEqual(x, y unsafe.Pointer, n uintptr) bool {
return *(*interface{})(x) == *(*interface{})(y)
}
func hashmapInterfaceSet(m *hashmap, key interface{}, value unsafe.Pointer) {
hash := hashmapInterfaceHash(key)
hashmapSet(m, unsafe.Pointer(&key), value, hash, hashmapInterfaceEqual)
}
func hashmapInterfaceGet(m *hashmap, key interface{}, value unsafe.Pointer) bool {
hash := hashmapInterfaceHash(key)
return hashmapGet(m, unsafe.Pointer(&key), value, hash, hashmapInterfaceEqual)
}
func hashmapInterfaceDelete(m *hashmap, key interface{}) {
hash := hashmapInterfaceHash(key)
hashmapDelete(m, unsafe.Pointer(&key), hash, hashmapInterfaceEqual)
}
+19
View File
@@ -12,3 +12,22 @@ type Interrupt struct {
// some encapsulation.
num int
}
// New is a compiler intrinsic that creates a new Interrupt object. You may call
// it only once, and must pass constant parameters to it. That means that the
// interrupt ID must be a Go constant and that the handler must be a simple
// function: closures are not supported.
func New(id int, handler func(Interrupt)) Interrupt
// handle is used internally, between IR generation and interrupt lowering. The
// frontend will create runtime/interrupt.handle objects, cast them to an int,
// and use that in an Interrupt object. That way the compiler will be able to
// optimize away all interrupt handles that are never used in a program.
// This system only works when interrupts need to be enabled before use and this
// is done only through calling Enable() on this object. If interrups cannot
// individually be enabled/disabled, the compiler should create a pseudo-call
// (like runtime/interrupt.use()) that keeps the interrupt alive.
type handle struct {
handler func(Interrupt)
Interrupt
}
@@ -7,53 +7,30 @@ import (
"unsafe"
)
var handlers = [14]func(Interrupt){}
const (
IRQ_VBLANK = 0
IRQ_HBLANK = 1
IRQ_VCOUNT = 2
IRQ_TIMER0 = 3
IRQ_TIMER1 = 4
IRQ_TIMER2 = 5
IRQ_TIMER3 = 6
IRQ_COM = 7
IRQ_DMA0 = 8
IRQ_DMA1 = 9
IRQ_DMA2 = 10
IRQ_DMA3 = 11
IRQ_KEYPAD = 12
IRQ_GAMEPAK = 13
)
var (
regInterruptEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000200)))
regInterruptEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000200)))
regInterruptRequestFlags = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000202)))
regInterruptMasterEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000208)))
)
// New creates a new Interrupt object. Do not call it multiple times. If you do,
// make sure the interrupt is disabled while you do so. The last call will set
// the active interrupt handler.
func New(id int, handler func(Interrupt)) Interrupt {
handlers[id] = handler
return Interrupt{id}
}
// Enable enables this interrupt. Right after calling this function, the
// interrupt may be invoked if it was already pending.
func (irq Interrupt) Enable() {
regInterruptEnable.SetBits(1 << irq.num)
regInterruptEnable.SetBits(1 << uint(irq.num))
}
//export handleInterrupt
func handleInterrupt() {
flags := regInterruptRequestFlags.Get()
for i := range handlers {
if flags & (1 << i) != 0 {
irq := Interrupt{i}
regInterruptRequestFlags.Set(1 << i) // acknowledge interrupt
handlers[i](irq)
for i := 0; i < 14; i++ {
if flags&(1<<uint(i)) != 0 {
regInterruptRequestFlags.Set(1 << uint(i)) // acknowledge interrupt
callInterruptHandler(i)
}
}
}
// callInterruptHandler is a compiler-generated function that calls the
// appropriate interrupt handler for the given interrupt ID.
//go:linkname callInterruptHandler runtime.callInterruptHandler
func callInterruptHandler(id int)
@@ -0,0 +1,8 @@
// +build avr cortexm
package interrupt
// Register is used to declare an interrupt. You should not normally call this
// function: it is only for telling the compiler about the mapping between an
// interrupt number and the interrupt handler name.
func Register(id int, handlerName string) int
+18
View File
@@ -0,0 +1,18 @@
// +build sifive
package interrupt
import "device/sifive"
// Enable enables this interrupt. Right after calling this function, the
// interrupt may be invoked if it was already pending.
func (irq Interrupt) Enable() {
sifive.PLIC.ENABLE[irq.num/32].SetBits(1 << (uint(irq.num) % 32))
}
// SetPriority sets the interrupt priority for this interrupt. A higher priority
// number means a higher priority (unlike Cortex-M). Priority 0 effectively
// disables the interrupt.
func (irq Interrupt) SetPriority(priority uint8) {
sifive.PLIC.PRIORITY[irq.num].Set(uint32(priority))
}
@@ -1,19 +0,0 @@
// +build avr riscv,baremetal cortexm
package interrupt
// New is a compiler intrinsic that creates a new Interrupt object. You may call
// it only once, and must pass constant parameters to it. That means that the
// interrupt ID must be a Go constant and that the handler must be a simple
// function: closures are not supported.
func New(id int, handler func(Interrupt)) Interrupt
// Register is used to declare an interrupt. You should not normally call this
// function: it is only for telling the compiler about the mapping between an
// interrupt number and the interrupt handler name.
func Register(id int, handlerName string) int
type handle struct {
handler func(Interrupt)
Interrupt
}
+6 -6
View File
@@ -3,8 +3,8 @@
package runtime
import (
"unsafe"
_ "runtime/interrupt" // make sure the interrupt handler is defined
"unsafe"
)
type timeUnit int64
@@ -16,19 +16,19 @@ func putchar(c byte) {
}
//go:extern _sbss
var _sbss unsafe.Pointer
var _sbss [0]byte
//go:extern _ebss
var _ebss unsafe.Pointer
var _ebss [0]byte
//go:extern _sdata
var _sdata unsafe.Pointer
var _sdata [0]byte
//go:extern _sidata
var _sidata unsafe.Pointer
var _sidata [0]byte
//go:extern _edata
var _edata unsafe.Pointer
var _edata [0]byte
// Entry point for Go. Initialize all packages and call main.main().
//go:export main
+13 -16
View File
@@ -6,6 +6,8 @@ import (
"device/arm"
"device/sam"
"machine"
"runtime/interrupt"
"runtime/volatile"
)
type timeUnit int64
@@ -201,8 +203,14 @@ func initRTC() {
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_ENABLE) {
}
arm.SetPriority(sam.IRQ_RTC, 0xc0)
arm.EnableIRQ(sam.IRQ_RTC)
irq := interrupt.New(sam.IRQ_RTC, func(interrupt.Interrupt) {
// disable IRQ for CMP0 compare
sam.RTC_MODE0.INTFLAG.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
timerWakeup.Set(1)
})
irq.SetPriority(0xc0)
irq.Enable()
}
func waitForSync() {
@@ -218,10 +226,7 @@ var (
timerLastCounter uint64
)
//go:volatile
type isrFlag bool
var timerWakeup isrFlag
var timerWakeup volatile.Register8
const asyncScheduler = false
@@ -248,7 +253,7 @@ func ticks() timeUnit {
// ticks are in microseconds
func timerSleep(ticks uint32) {
timerWakeup = false
timerWakeup.Set(0)
if ticks < 260 {
// due to delay waiting for the register value to sync, the minimum sleep value
// for the SAMD51 is 260us.
@@ -268,19 +273,11 @@ func timerSleep(ticks uint32) {
// enable IRQ for CMP0 compare
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
for !timerWakeup {
for timerWakeup.Get() == 0 {
arm.Asm("wfi")
}
}
//go:export RTC_IRQHandler
func handleRTC() {
// disable IRQ for CMP0 compare
sam.RTC_MODE0.INTFLAG.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
timerWakeup = true
}
func initUSBClock() {
// Turn on clock(s) for USB
//MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB;
+43
View File
@@ -0,0 +1,43 @@
// +build sam,atsamd51,atsamd51j20
package runtime
import (
"device/sam"
)
func initSERCOMClocks() {
// Turn on clock to SERCOM0 for UART0
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM0_)
sam.GCLK.PCHCTRL[7].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// sets the "slow" clock shared by all SERCOM
sam.GCLK.PCHCTRL[3].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM1
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_SERCOM1_)
sam.GCLK.PCHCTRL[8].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM2
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM2_)
sam.GCLK.PCHCTRL[23].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM3
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_SERCOM3_)
sam.GCLK.PCHCTRL[24].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM4
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM4_)
sam.GCLK.PCHCTRL[34].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
// Turn on clock to SERCOM5
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_SERCOM5_)
sam.GCLK.PCHCTRL[35].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
+5 -5
View File
@@ -8,19 +8,19 @@ import (
)
//go:extern _sbss
var _sbss unsafe.Pointer
var _sbss [0]byte
//go:extern _ebss
var _ebss unsafe.Pointer
var _ebss [0]byte
//go:extern _sdata
var _sdata unsafe.Pointer
var _sdata [0]byte
//go:extern _sidata
var _sidata unsafe.Pointer
var _sidata [0]byte
//go:extern _edata
var _edata unsafe.Pointer
var _edata [0]byte
func preinit() {
// Initialize .bss: zero-initialized global variables.
+111 -11
View File
@@ -9,42 +9,87 @@ import (
"machine"
"unsafe"
"device/riscv"
"device/sifive"
"runtime/volatile"
)
type timeUnit int64
const tickMicros = 32768 // RTC runs at 32.768kHz
//go:extern _sbss
var _sbss unsafe.Pointer
var _sbss [0]byte
//go:extern _ebss
var _ebss unsafe.Pointer
var _ebss [0]byte
//go:extern _sdata
var _sdata unsafe.Pointer
var _sdata [0]byte
//go:extern _sidata
var _sidata unsafe.Pointer
var _sidata [0]byte
//go:extern _edata
var _edata unsafe.Pointer
var _edata [0]byte
//go:export main
func main() {
// Zero the PLIC enable bits on startup: they are not zeroed at reset.
sifive.PLIC.ENABLE[0].Set(0)
sifive.PLIC.ENABLE[1].Set(0)
// Set the interrupt address.
// Note that this address must be aligned specially, otherwise the MODE bits
// of MTVEC won't be zero.
riscv.MTVEC.Set(uintptr(unsafe.Pointer(&handleInterruptASM)))
// Reset the MIE register and enable external interrupts.
// It must be reset here because it not zeroed at startup.
riscv.MIE.Set(1 << 11) // bit 11 is for machine external interrupts
// Enable global interrupts now that they've been set up.
riscv.MSTATUS.SetBits(1 << 3) // MIE
preinit()
initPeripherals()
initAll()
callMain()
abort()
}
func init() {
pric_init()
machine.UART0.Configure(machine.UARTConfig{})
//go:extern handleInterruptASM
var handleInterruptASM [0]uintptr
//export handleInterrupt
func handleInterrupt() {
cause := riscv.MCAUSE.Get()
code := uint(cause &^ (1 << 31))
if cause&(1<<31) != 0 {
// Topmost bit is set, which means that it is an interrupt.
switch code {
case 7: // Machine timer interrupt
// Signal timeout.
timerWakeup.Set(1)
// Disable the timer, to avoid triggering the interrupt right after
// this interrupt returns.
riscv.MIE.ClearBits(1 << 7) // MTIE bit
case 11: // Machine external interrupt
// Claim this interrupt.
id := sifive.PLIC.CLAIM.Get()
// Call the interrupt handler, if any is registered for this ID.
callInterruptHandler(int(id))
// Complete this interrupt.
sifive.PLIC.CLAIM.Set(id)
}
} else {
// Topmost bit is clear, so it is an exception of some sort.
// We could implement support for unsupported instructions here (such as
// misaligned loads). However, for now we'll just print a fatal error.
handleException(code)
}
}
func pric_init() {
// initPeripherals configures periperhals the way the runtime expects them.
func initPeripherals() {
// Make sure the HFROSC is on
sifive.PRCI.HFROSCCFG.SetBits(sifive.PRCI_HFROSCCFG_ENABLE)
@@ -57,6 +102,9 @@ func pric_init() {
// Enable the RTC.
sifive.RTC.RTCCFG.Set(sifive.RTC_RTCCFG_ENALWAYS)
// Configure the UART.
machine.UART0.Configure(machine.UARTConfig{})
}
func preinit() {
@@ -82,3 +130,55 @@ func putchar(c byte) {
}
const asyncScheduler = false
var timerWakeup volatile.Register8
func ticks() timeUnit {
// Combining the low bits and the high bits yields a time span of over 270
// years without counter rollover.
highBits := sifive.CLINT.MTIMEH.Get()
for {
lowBits := sifive.CLINT.MTIME.Get()
newHighBits := sifive.CLINT.MTIMEH.Get()
if newHighBits == highBits {
// High bits stayed the same.
return timeUnit(lowBits) | (timeUnit(highBits) << 32)
}
// Retry, because there was a rollover in the low bits (happening every
// 1.5 days).
highBits = newHighBits
}
}
func sleepTicks(d timeUnit) {
target := uint64(ticks() + d)
sifive.CLINT.MTIMECMPH.Set(uint32(target >> 32))
sifive.CLINT.MTIMECMP.Set(uint32(target))
riscv.MIE.SetBits(1 << 7) // MTIE
for {
if timerWakeup.Get() != 0 {
timerWakeup.Set(0)
// Disable timer.
break
}
riscv.Asm("wfi")
}
}
// handleException is called from the interrupt handler for any exception.
// Exceptions can be things like illegal instructions, invalid memory
// read/write, and similar issues.
func handleException(code uint) {
// For a list of exception codes, see:
// https://content.riscv.org/wp-content/uploads/2019/08/riscv-privileged-20190608-1.pdf#page=49
print("fatal error: exception with mcause=")
print(code)
print(" pc=")
print(riscv.MEPC.Get())
println()
abort()
}
// callInterruptHandler is a compiler-generated function that calls the
// appropriate interrupt handler for the given interrupt ID.
func callInterruptHandler(id int)
+2 -25
View File
@@ -4,36 +4,13 @@ package runtime
import (
"device/riscv"
"device/sifive"
)
const tickMicros = 32768 // RTC clock runs at 32.768kHz
func abort() {
// lock up forever
for {
riscv.Asm("wfi")
}
}
func ticks() timeUnit {
// Combining the low bits and the high bits yields a time span of over 270
// years without counter rollover.
highBits := sifive.RTC.RTCHI.Get()
for {
lowBits := sifive.RTC.RTCLO.Get()
newHighBits := sifive.RTC.RTCHI.Get()
if newHighBits == highBits {
// High bits stayed the same.
println("bits:", highBits, lowBits)
return timeUnit(lowBits) | (timeUnit(highBits) << 32)
}
// Retry, because there was a rollover in the low bits (happening every
// 1.5 days).
highBits = newHighBits
}
}
func sleepTicks(d timeUnit) {
target := ticks() + d
for ticks() < target {
}
}
+2 -10
View File
@@ -7,6 +7,8 @@ import (
"unsafe"
)
const tickMicros = 100 // CLINT.MTIME increments every 100ns
// Special memory-mapped device to exit tests, created by SiFive.
var testExit = (*volatile.Register32)(unsafe.Pointer(uintptr(0x100000)))
@@ -16,13 +18,3 @@ func abort() {
// Signal a successful exit.
testExit.Set(0x5555)
}
func ticks() timeUnit {
return timestamp
}
func sleepTicks(d timeUnit) {
// Note: QEMU doesn't seem to support the RTC peripheral at the time of
// writing so just simulate sleeping here.
timestamp += d
}
+24 -42
View File
@@ -2,39 +2,43 @@
package runtime
import (
"unsafe"
)
import "unsafe"
type timeUnit float64 // time in milliseconds, just like Date.now() in JavaScript
const tickMicros = 1000000
//go:export io_get_stdout
func io_get_stdout() int32
//go:export resource_write
func resource_write(id int32, ptr *uint8, len int32) int32
var stdout int32
func init() {
stdout = io_get_stdout()
// Implements __wasi_ciovec_t and __wasi_iovec_t.
type wasiIOVec struct {
buf unsafe.Pointer
bufLen uint
}
//go:export _start
//go:wasm-module wasi_unstable
//export fd_write
func fd_write(id uint32, iovs *wasiIOVec, iovs_len uint, nwritten *uint) (errno uint)
//export _start
func _start() {
initAll()
}
//go:export cwa_main
func cwa_main() {
initAll() // _start is not called by olin/cwa so has to be called here
callMain()
}
// Using global variables to avoid heap allocation.
var (
putcharBuf = byte(0)
putcharIOVec = wasiIOVec{
buf: unsafe.Pointer(&putcharBuf),
bufLen: 1,
}
)
func putchar(c byte) {
resource_write(stdout, &c, 1)
// write to stdout
const stdout = 1
var nwritten uint
putcharBuf = c
fd_write(stdout, &putcharIOVec, 1, &nwritten)
}
var handleEvent func()
@@ -68,25 +72,3 @@ func ticks() timeUnit
func abort() {
trap()
}
//go:export memset
func memset(ptr unsafe.Pointer, c byte, size uintptr) unsafe.Pointer {
for i := uintptr(0); i < size; i++ {
*(*byte)(unsafe.Pointer(uintptr(ptr) + i)) = c
}
return ptr
}
// Implement memmove for LLVM and compiler-rt.
//go:export memmove
func libc_memmove(dst, src unsafe.Pointer, size uintptr) unsafe.Pointer {
memmove(dst, src, size)
return dst
}
// Implement memcpy for LLVM and compiler-rt.
//go:export memcpy
func libc_memcpy(dst, src unsafe.Pointer, size uintptr) unsafe.Pointer {
memcpy(dst, src, size)
return dst
}
+9
View File
@@ -0,0 +1,9 @@
{
"inherits": ["atmega328p"],
"build-tags": ["arduino_nano"],
"ldflags": [
"-Wl,--defsym=_bootloader_size=512",
"-Wl,--defsym=_stack_size=512"
],
"flash-command": "avrdude -c arduino -p atmega328p -b 57600 -P {port} -U flash:w:{hex}:i"
}
+3 -13
View File
@@ -1,19 +1,9 @@
{
"inherits": ["avr"],
"llvm-target": "avr-atmel-none",
"cpu": "atmega328p",
"build-tags": ["arduino", "atmega328p", "atmega", "avr5"],
"cflags": [
"-mmcu=atmega328p"
],
"inherits": ["atmega328p"],
"build-tags": ["arduino"],
"ldflags": [
"-Wl,--defsym=_bootloader_size=512",
"-Wl,--defsym=_stack_size=512"
],
"linkerscript": "src/device/avr/atmega328p.ld",
"extra-files": [
"targets/avr.S",
"src/device/avr/atmega328p.s"
],
"flash-command": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}"
"flash-command": "avrdude -c arduino -p atmega328p -P {port} -U flash:w:{hex}:i"
}
+14
View File
@@ -0,0 +1,14 @@
{
"inherits": ["avr"],
"llvm-target": "avr-atmel-none",
"cpu": "atmega328p",
"build-tags": ["atmega328p", "atmega", "avr5"],
"cflags": [
"-mmcu=atmega328p"
],
"linkerscript": "src/device/avr/atmega328p.ld",
"extra-files": [
"targets/avr.S",
"src/device/avr/atmega328p.s"
]
}
+13
View File
@@ -0,0 +1,13 @@
{
"inherits": ["cortex-m"],
"llvm-target": "armv7em-none-eabi",
"build-tags": ["sam", "atsamd51", "atsamd51j20", "atsamd51j20a"],
"cflags": [
"--target=armv7em-none-eabi",
"-Qunused-arguments"
],
"linkerscript": "targets/atsamd51.ld",
"extra-files": [
"src/device/sam/atsamd51j20a.s"
]
}
+9
View File
@@ -60,3 +60,12 @@ __vector_WDT:
pop r16
reti
; This is necessary for the garbage collector.
; It returns the stack pointer as an uintptr.
.section .text.runtime.getCurrentStackPointer
.global runtime.getCurrentStackPointer
runtime.getCurrentStackPointer:
in r24, 0x3d; SPL
in r25, 0x3e; SPH
ret
+1 -1
View File
@@ -3,7 +3,7 @@
"goos": "linux",
"goarch": "arm",
"compiler": "avr-gcc",
"gc": "leaking",
"gc": "conservative",
"linker": "avr-gcc",
"ldflags": [
"-T", "targets/avr.ld",
+2
View File
@@ -46,3 +46,5 @@ SECTIONS
/* For the memory allocator. */
_heap_start = _ebss;
_heap_end = ORIGIN(RAM) + LENGTH(RAM);
_globals_start = _sdata;
_globals_end = _ebss;
+9
View File
@@ -0,0 +1,9 @@
{
"inherits": ["nrf52840"],
"build-tags": ["circuitplay_bluefruit"],
"flash-method": "msd",
"msd-volume-name": "CPLAYBTBOOT",
"msd-firmware-name": "firmware.uf2",
"uf2-family-id": "0xADA52840",
"linkerscript": "targets/circuitplay-bluefruit.ld"
}
+10
View File
@@ -0,0 +1,10 @@
MEMORY
{
FLASH_TEXT (rw) : ORIGIN = 0x00000000+0x26000, LENGTH = 0xED000-0x26000 /* SoftDevice S140. See https://learn.adafruit.com/introducing-the-adafruit-nrf52840-feather/hathach-memory-map. Application starts at 0x26000; user data starts at 0xED000 */
RAM (xrw) : ORIGIN = 0x20004180, LENGTH = 37K
}
_stack_size = 2K;
INCLUDE "targets/arm.ld"
+1
View File
@@ -6,6 +6,7 @@
"goarch": "arm",
"compiler": "clang",
"linker": "ld.lld",
"rtlib": "compiler-rt",
"cflags": [
"-g",
"--target=thumb4-none-eabi",
+2 -2
View File
@@ -36,9 +36,9 @@ SECTIONS
.stack (NOLOAD) :
{
. = ALIGN(4);
__sp_irq = .;
_stack_top_irq = .;
. += __stack_size_irq;
__sp_usr = .;
_stack_top = .;
. += __stack_size_usr;
} >iwram
+2 -2
View File
@@ -20,10 +20,10 @@ start_vector:
// Configure stacks
mov r0, #0x12 // Switch to IRQ Mode
msr cpsr, r0
ldr sp, =__sp_irq // Set IRQ stack
ldr sp, =_stack_top_irq // Set IRQ stack
mov r0, #0x1f // Switch to System Mode
msr cpsr, r0
ldr sp, =__sp_usr // Set user stack
ldr sp, =_stack_top // Set user stack
// Configure interrupt handler
mov r0, #0x4000000 // REG_BASE
+2 -1
View File
@@ -4,5 +4,6 @@
"linkerscript": "targets/hifive1b.ld",
"flash-method": "msd",
"msd-volume-name": "HiFive",
"msd-firmware-name": "firmware.hex"
"msd-firmware-name": "firmware.hex",
"jlink-device": "fe310"
}
+8
View File
@@ -0,0 +1,8 @@
{
"inherits": ["atsamd51j20a"],
"build-tags": ["pyportal"],
"flash-1200-bps-reset": "true",
"flash-method": "msd",
"msd-volume-name": "PORTALBOOT",
"msd-firmware-name": "firmware.uf2"
}
+3
View File
@@ -2,7 +2,10 @@ SECTIONS
{
.text :
{
. = ALIGN(4);
KEEP(*(.init))
. = ALIGN(4);
*(.text.handleInterruptASM)
*(.text)
*(.text.*)
*(.rodata)
+5 -5
View File
@@ -1,21 +1,21 @@
{
"llvm-target": "wasm32-unknown-unknown-wasm",
"llvm-target": "wasm32--wasi",
"build-tags": ["js", "wasm"],
"goos": "js",
"goarch": "wasm",
"compiler": "clang",
"linker": "wasm-ld",
"cflags": [
"--target=wasm32",
"-nostdlibinc",
"-Wno-macro-redefined",
"--target=wasm32--wasi",
"--sysroot={root}/lib/wasi-libc/sysroot",
"-Oz"
],
"ldflags": [
"--allow-undefined",
"--no-threads",
"--stack-first",
"--export-all"
"--export-all",
"{root}/lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a"
],
"emulator": ["node", "targets/wasm_exec.js"]
}
+25 -19
View File
@@ -181,31 +181,37 @@
const timeOrigin = Date.now() - performance.now();
this.importObject = {
env: {
io_get_stdout: function() {
return 1;
},
resource_write: function(fd, ptr, len) {
wasi_unstable: {
// https://github.com/bytecodealliance/wasmtime/blob/master/docs/WASI-api.md#__wasi_fd_write
fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) {
let nwritten = 0;
if (fd == 1) {
for (let i=0; i<len; i++) {
let c = mem().getUint8(ptr+i);
if (c == 13) { // CR
// ignore
} else if (c == 10) { // LF
// write line
let line = decoder.decode(new Uint8Array(logLine));
logLine = [];
console.log(line);
} else {
logLine.push(c);
for (let iovs_i=0; iovs_i<iovs_len;iovs_i++) {
let iov_ptr = iovs_ptr+iovs_i*8; // assuming wasm32
let ptr = mem().getUint32(iov_ptr + 0, true);
let len = mem().getUint32(iov_ptr + 4, true);
for (let i=0; i<len; i++) {
let c = mem().getUint8(ptr+i);
if (c == 13) { // CR
// ignore
} else if (c == 10) { // LF
// write line
let line = decoder.decode(new Uint8Array(logLine));
logLine = [];
console.log(line);
} else {
logLine.push(c);
}
}
}
} else {
console.error('invalid file descriptor:', fd);
}
mem().setUint32(nwritten_ptr, nwritten, true);
return 0;
},
},
env: {
// func ticks() float64
"runtime.ticks": () => {
return timeOrigin + performance.now();
@@ -344,7 +350,7 @@
setTimeout(resolve, 0); // make sure it is asynchronous
};
});
this._inst.exports.cwa_main();
this._inst.exports._start();
if (this.exited) {
break;
}
+9 -2
View File
@@ -23,6 +23,13 @@ var scalarSlices [4][]byte
var randSeeds [4]uint32
func testNonPointerHeap() {
maxSliceSize := uint32(1024)
if ^uintptr(0) <= 0xffff {
// 16-bit and lower devices, such as AVR.
// Heap size is a real issue there, while it is still useful to run
// these tests. Therefore, lower the max slice size.
maxSliceSize = 64
}
// Allocate roughly 0.5MB of memory.
for i := 0; i < 1000; i++ {
// Pick a random index that the optimizer can't predict.
@@ -38,9 +45,9 @@ func testNonPointerHeap() {
}
// Allocate a randomly-sized slice, randomly sliced to be smaller.
sliceLen := randuint32() % 1024
sliceLen := randuint32() % maxSliceSize
slice := make([]byte, sliceLen)
cutLen := randuint32() % 1024
cutLen := randuint32() % maxSliceSize
if cutLen < sliceLen {
slice = slice[cutLen:]
}
+44 -1
View File
@@ -24,6 +24,11 @@ var testMapArrayKey = map[ArrayKey]int{
}
var testmapIntInt = map[int]int{1: 1, 2: 4, 3: 9}
type namedFloat struct {
s string
f float32
}
func main() {
m := map[string]int{"answer": 42, "foo": 3}
readMap(m, "answer")
@@ -48,6 +53,44 @@ func main() {
testMapArrayKey[arrKey] = 5555
println(testMapArrayKey[arrKey])
// test maps with interface keys
itfMap := map[interface{}]int{
3.14: 3,
8: 8,
uint8(8): 80,
"eight": 800,
[2]int{5, 2}: 52,
true: 1,
}
println("itfMap[3]:", itfMap[3]) // doesn't exist
println("itfMap[3.14]:", itfMap[3.14])
println("itfMap[8]:", itfMap[8])
println("itfMap[uint8(8)]:", itfMap[uint8(8)])
println(`itfMap["eight"]:`, itfMap["eight"])
println(`itfMap[[2]int{5, 2}]:`, itfMap[[2]int{5, 2}])
println("itfMap[true]:", itfMap[true])
delete(itfMap, 8)
println("itfMap[8]:", itfMap[8])
// test map with float keys
floatMap := map[float32]int{
42: 84,
}
println("floatMap[42]:", floatMap[42])
println("floatMap[43]:", floatMap[43])
delete(floatMap, 42)
println("floatMap[42]:", floatMap[42])
// test maps with struct keys
structMap := map[namedFloat]int{
namedFloat{"tau", 6.28}: 5,
}
println(`structMap[{"tau", 6.28}]:`, structMap[namedFloat{"tau", 6.28}])
println(`structMap[{"Tau", 6.28}]:`, structMap[namedFloat{"Tau", 6.28}])
println(`structMap[{"tau", 3.14}]:`, structMap[namedFloat{"tau", 3.14}])
delete(structMap, namedFloat{"tau", 6.28})
println(`structMap[{"tau", 6.28}]:`, structMap[namedFloat{"tau", 6.28}])
// test preallocated map
squares := make(map[int]int, 200)
testBigMap(squares, 100)
@@ -79,7 +122,7 @@ func testBigMap(squares map[int]int, n int) {
if len(squares) != i {
println("unexpected length:", len(squares), "at i =", i)
}
squares[i] = i*i
squares[i] = i * i
for j := 0; j <= i; j++ {
if v, ok := squares[j]; !ok || v != j*j {
if !ok {
+15
View File
@@ -54,5 +54,20 @@ true false 0
42
4321
5555
itfMap[3]: 0
itfMap[3.14]: 3
itfMap[8]: 8
itfMap[uint8(8)]: 80
itfMap["eight"]: 800
itfMap[[2]int{5, 2}]: 52
itfMap[true]: 1
itfMap[8]: 0
floatMap[42]: 84
floatMap[43]: 0
floatMap[42]: 0
structMap[{"tau", 6.28}]: 5
structMap[{"Tau", 6.28}]: 0
structMap[{"tau", 3.14}]: 0
structMap[{"tau", 6.28}]: 0
tested preallocated map
tested growing of a map
+24 -13
View File
@@ -595,7 +595,7 @@ func parseRegister(groupName string, regEl *SVDRegister, baseAddress uint64, bit
}
// The Go module for this device.
func writeGo(outdir string, device *Device) error {
func writeGo(outdir string, device *Device, interruptSystem string) error {
outf, err := os.Create(filepath.Join(outdir, device.metadata["nameLower"]+".go"))
if err != nil {
return err
@@ -621,7 +621,7 @@ func writeGo(outdir string, device *Device) error {
package {{.pkgName}}
import (
"runtime/interrupt"
{{if eq .interruptSystem "hardware"}}"runtime/interrupt"{{end}}
"runtime/volatile"
"unsafe"
)
@@ -637,11 +637,13 @@ const ({{range .interrupts}}
IRQ_max = {{.interruptMax}} // Highest interrupt number on this device.
)
{{if eq .interruptSystem "hardware"}}
// Map interrupt numbers to function names.
// These aren't real calls, they're removed by the compiler.
var ({{range .interrupts}}
_ = interrupt.Register(IRQ_{{.Name}}, "{{.HandlerName}}"){{end}}
)
{{end}}
// Peripherals.
var (
@@ -649,11 +651,12 @@ var (
{{end}})
`))
err = t.Execute(w, map[string]interface{}{
"metadata": device.metadata,
"interrupts": device.interrupts,
"peripherals": device.peripherals,
"pkgName": filepath.Base(strings.TrimRight(outdir, "/")),
"interruptMax": maxInterruptValue,
"metadata": device.metadata,
"interrupts": device.interrupts,
"peripherals": device.peripherals,
"pkgName": filepath.Base(strings.TrimRight(outdir, "/")),
"interruptMax": maxInterruptValue,
"interruptSystem": interruptSystem,
})
if err != nil {
return err
@@ -910,7 +913,7 @@ Default_Handler:
return w.Flush()
}
func generate(indir, outdir, sourceURL string) error {
func generate(indir, outdir, sourceURL, interruptSystem string) error {
if _, err := os.Stat(indir); os.IsNotExist(err) {
fmt.Fprintln(os.Stderr, "cannot find input directory:", indir)
os.Exit(1)
@@ -929,13 +932,20 @@ func generate(indir, outdir, sourceURL string) error {
if err != nil {
return fmt.Errorf("failed to read: %w", err)
}
err = writeGo(outdir, device)
err = writeGo(outdir, device, interruptSystem)
if err != nil {
return fmt.Errorf("failed to write Go file: %w", err)
}
err = writeAsm(outdir, device)
if err != nil {
return fmt.Errorf("failed to write assembly file: %w", err)
switch interruptSystem {
case "software":
// Nothing to do.
case "hardware":
err = writeAsm(outdir, device)
if err != nil {
return fmt.Errorf("failed to write assembly file: %w", err)
}
default:
return fmt.Errorf("unknown interrupt system: %s", interruptSystem)
}
}
return nil
@@ -943,6 +953,7 @@ func generate(indir, outdir, sourceURL string) error {
func main() {
sourceURL := flag.String("source", "<unknown>", "source SVD file")
interruptSystem := flag.String("interrupts", "hardware", "interrupt system in use (software, hardware)")
flag.Parse()
if flag.NArg() != 2 {
fmt.Fprintln(os.Stderr, "provide exactly two arguments: input directory (with .svd files) and output directory for generated files")
@@ -951,7 +962,7 @@ func main() {
}
indir := flag.Arg(0)
outdir := flag.Arg(1)
err := generate(indir, outdir, *sourceURL)
err := generate(indir, outdir, *sourceURL, *interruptSystem)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
+130 -21
View File
@@ -2,12 +2,27 @@ package transform
import (
"fmt"
"sort"
"strconv"
"strings"
"tinygo.org/x/go-llvm"
)
func LowerInterruptRegistrations(mod llvm.Module) []error {
// LowerInterrupts creates interrupt handlers for the interrupts created by
// runtime/interrupt.New.
//
// The operation is as follows. The compiler creates the following during IR
// generation:
// * calls to runtime/interrupt.Register that map interrupt IDs to ISR names.
// * runtime/interrupt.handle objects that store the (constant) interrupt ID and
// interrupt handler func value.
//
// This pass then creates the specially named interrupt handler names that
// simply call the registered handlers. This might seem like it causes extra
// overhead, but in fact inlining and const propagation will eliminate most if
// not all of that.
func LowerInterrupts(mod llvm.Module) []error {
var errs []error
// Discover interrupts. The runtime/interrupt.Register call is a compiler
@@ -47,6 +62,9 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
call.EraseFromParentAsInstruction()
}
hasSoftwareVectoring := hasUses(mod.NamedFunction("runtime.callInterruptHandler"))
softwareVector := make(map[int64]llvm.Value)
ctx := mod.Context()
nullptr := llvm.ConstNull(llvm.PointerType(ctx.Int8Type(), 0))
builder := ctx.NewBuilder()
@@ -55,34 +73,54 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
// Create a function type with the signature of an interrupt handler.
fnType := llvm.FunctionType(ctx.VoidType(), nil, false)
handleType := mod.GetTypeByName("runtime/interrupt.handle")
if handleType.IsNil() {
// Nothing to do here.
return errs
}
handlePtrType := llvm.PointerType(handleType, 0)
// Collect a slice of interrupt handle objects. The fact that they still
// exist in the IR indicates that they could not be optimized away,
// therefore we need to make real interrupt handlers for them.
var handlers []llvm.Value
for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
if global.Type() != handlePtrType {
continue
handleType := mod.GetTypeByName("runtime/interrupt.handle")
if !handleType.IsNil() {
handlePtrType := llvm.PointerType(handleType, 0)
for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
if global.Type() != handlePtrType {
continue
}
handlers = append(handlers, global)
}
handlers = append(handlers, global)
}
// Iterate over all handler objects, replacing their ptrtoint uses with a
// real interrupt ID and creating an interrupt handler for them.
for _, global := range handlers {
initializer := global.Initializer()
num := llvm.ConstExtractValue(initializer, []uint32{1, 0})
name := handlerNames[num.SExtValue()]
isSoftwareVectored := false
if name == "" {
errs = append(errs, errorAt(global, fmt.Sprintf("cannot find interrupt name for number %d", num.SExtValue())))
continue
// No function name was defined for this interrupt number, which
// probably means one of two things:
// * runtime/interrupt.Register wasn't called to give the interrupt
// number a function name (such as on Cortex-M).
// * We're using software vectoring instead of hardware vectoring,
// which means the name of the handler doesn't matter (it will
// probably be inlined anyway).
if hasSoftwareVectoring {
isSoftwareVectored = true
if name == "" {
// Name doesn't matter, so pick something unique.
name = "runtime/interrupt.interruptHandler" + strconv.FormatInt(num.SExtValue(), 10)
}
} else {
errs = append(errs, errorAt(global, fmt.Sprintf("cannot find interrupt name for number %d", num.SExtValue())))
continue
}
}
// Create the func value.
// Extract the func value.
handlerContext := llvm.ConstExtractValue(initializer, []uint32{0, 0})
handlerFuncPtr := llvm.ConstExtractValue(initializer, []uint32{0, 1})
if isFunctionLocal(handlerContext) || isFunctionLocal(handlerFuncPtr) {
if !handlerContext.IsConstant() || !handlerFuncPtr.IsConstant() {
// This should have been checked already in the compiler.
errs = append(errs, errorAt(global, "func value must be constant"))
continue
}
@@ -111,15 +149,14 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
continue
}
// Check for an existing handler, and report it as an error if there is
// one.
// Check for an existing interrupt handler, and report it as an error if
// there is one.
fn := mod.NamedFunction(name)
if fn.IsNil() {
fn = llvm.AddFunction(mod, name, fnType)
} else if fn.Type().ElementType() != fnType {
// Don't bother with a precise error message (listing the
// previsous location) because this should not normally happen
// anyway.
// Don't bother with a precise error message (listing the previsous
// location) because this should not normally happen anyway.
errs = append(errs, errorAt(global, name+" redeclared with a different signature"))
continue
} else if !fn.IsDeclaration() {
@@ -142,20 +179,38 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
continue
}
// Create the wrapper function.
// Create the wrapper function which is the actual interrupt handler
// that is inserted in the interrupt vector.
fn.SetUnnamedAddr(true)
fn.SetSection(".text." + name)
if isSoftwareVectored {
fn.SetLinkage(llvm.InternalLinkage)
softwareVector[num.SExtValue()] = fn
}
entryBlock := ctx.AddBasicBlock(fn, "entry")
builder.SetInsertPointAtEnd(entryBlock)
// Set the 'interrupt' flag if needed on this platform.
if strings.HasPrefix(mod.Target(), "avr") {
// This special calling convention is needed on AVR to save and
// restore all clobbered registers, instead of just the ones that
// would need to be saved/restored in a normal function call.
// Note that the AVR_INTERRUPT calling convention would enable
// interrupts right at the beginning of the handler, potentially
// leading to lots of nested interrupts and a stack overflow.
fn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
}
// Fill the function declaration with the forwarding call.
// In practice, the called function will often be inlined which avoids
// the extra indirection.
builder.CreateCall(handlerFuncPtr, []llvm.Value{num, handlerContext, nullptr}, "")
builder.CreateRetVoid()
// Replace all ptrtoint uses of the global with the interrupt constant.
// That can only now be safely done after the interrupt handler has been
// created, doing it before the interrupt handler is created might
// result in this interrupt handler being optimized away entirely.
for _, user := range getUses(global) {
if user.IsAConstantExpr().IsNil() || user.Opcode() != llvm.PtrToInt {
errs = append(errs, errorAt(global, "internal error: expected a ptrtoint"))
@@ -163,9 +218,63 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
}
user.ReplaceAllUsesWith(num)
}
// The runtime/interrput.handle struct can finally be removed.
// It would probably be eliminated anyway by a globaldce pass but it's
// better to do it now to be sure.
global.EraseFromParentAsGlobal()
}
// Create a dispatcher function that calls the appropriate interrupt handler
// for each interrupt ID. This is used in the case of software vectoring.
// The function looks like this:
// func callInterruptHandler(id int) {
// switch id {
// case IRQ_UART:
// interrupt.interruptHandler3()
// case IRQ_FOO:
// interrupt.interruptHandler7()
// default:
// // do nothing
// }
if hasSoftwareVectoring {
// Create a sorted list of interrupt vector IDs.
ids := make([]int64, 0, len(softwareVector))
for id := range softwareVector {
ids = append(ids, id)
}
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
// Start creating the function body with the big switch.
dispatcher := mod.NamedFunction("runtime.callInterruptHandler")
entryBlock := ctx.AddBasicBlock(dispatcher, "entry")
defaultBlock := ctx.AddBasicBlock(dispatcher, "default")
builder.SetInsertPointAtEnd(entryBlock)
interruptID := dispatcher.Param(0)
sw := builder.CreateSwitch(interruptID, defaultBlock, len(ids))
// Create a switch case for each interrupt ID that calls the appropriate
// handler.
for _, id := range ids {
block := ctx.AddBasicBlock(dispatcher, "interrupt"+strconv.FormatInt(id, 10))
builder.SetInsertPointAtEnd(block)
builder.CreateCall(softwareVector[id], nil, "")
builder.CreateRetVoid()
sw.AddCase(llvm.ConstInt(interruptID.Type(), uint64(id), true), block)
}
// Create a default case that just returns.
// Perhaps it is better to call some default interrupt handler here that
// logs an error?
builder.SetInsertPointAtEnd(defaultBlock)
builder.CreateRetVoid()
// Make sure the dispatcher is optimized.
// Without this, it will probably not get inlined.
dispatcher.SetLinkage(llvm.InternalLinkage)
dispatcher.SetUnnamedAddr(true)
}
// Remove now-useless runtime/interrupt.use calls. These are used for some
// platforms like AVR that do not need to enable interrupts to use them, so
// need another way to keep them alive.
+24
View File
@@ -0,0 +1,24 @@
package transform
import (
"testing"
"tinygo.org/x/go-llvm"
)
func TestInterruptLowering(t *testing.T) {
t.Parallel()
for _, subtest := range []string{"avr", "cortexm"} {
t.Run(subtest, func(t *testing.T) {
testTransform(t, "testdata/interrupt-"+subtest, func(mod llvm.Module) {
errs := LowerInterrupts(mod)
if len(errs) != 0 {
t.Fail()
for _, err := range errs {
t.Error(err)
}
}
})
})
}
}
+9 -16
View File
@@ -21,6 +21,15 @@ func getUses(value llvm.Value) []llvm.Value {
return uses
}
// hasUses returns whether the given value has any uses. It is equivalent to
// getUses(value) != nil but faster.
func hasUses(value llvm.Value) bool {
if value.IsNil() {
return false
}
return !value.FirstUse().IsNil()
}
// makeGlobalArray creates a new LLVM global with the given name and integers as
// contents, and returns the global.
// Note that it is left with the default linkage etc., you should set
@@ -89,19 +98,3 @@ func typeHasPointers(t llvm.Type) bool {
return false
}
}
// isFunctionLocal returns true if (and only if) this value is local to a
// function. That is, it returns true for instructions and parameters, and false
// for constants and globals.
func isFunctionLocal(val llvm.Value) bool {
if !val.IsAConstant().IsNil() {
return false
}
if !val.IsAInstruction().IsNil() {
return true
}
if !val.IsAGlobalValue().IsNil() {
return false
}
panic("unknown value kind")
}
+33
View File
@@ -0,0 +1,33 @@
target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"
target triple = "avr-atmel-none"
%"runtime/interrupt.handle" = type { %runtime.funcValue, %"runtime/interrupt.Interrupt" } %runtime.funcValue = type { i8*, i16 }
%runtime.typecodeID = type { %runtime.typecodeID*, i16 }
%runtime.funcValueWithSignature = type { i16, %runtime.typecodeID* }
%machine.UART = type { %machine.RingBuffer* }
%machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" }
%"runtime/volatile.Register8" = type { i8 }
%"runtime/interrupt.Interrupt" = type { i32 }
@"reflect/types.type:func:{named:runtime/interrupt.Interrupt}{}" = external constant %runtime.typecodeID
@"(machine.UART).Configure$1$withSignature" = internal constant %runtime.funcValueWithSignature { i16 ptrtoint (void (i32, i8*, i8*) addrspace(1)* @"(machine.UART).Configure$1" to i16), %runtime.typecodeID* @"reflect/types.type:func:{named:runtime/interrupt.Interrupt}{}" }
@"runtime/interrupt.$interrupt18" = private unnamed_addr constant %"runtime/interrupt.handle" { %runtime.funcValue { i8* undef, i16 ptrtoint (%runtime.funcValueWithSignature* @"(machine.UART).Configure$1$withSignature" to i16) }, %"runtime/interrupt.Interrupt" { i32 18 } }
@machine.UART0 = internal global %machine.UART zeroinitializer
@"device/avr.init$string.18" = internal unnamed_addr constant [17 x i8] c"__vector_USART_RX"
declare void @"(machine.UART).Configure$1"(i32, i8*, i8*) unnamed_addr addrspace(1)
declare i32 @"runtime/interrupt.Register"(i32, i8*, i16, i8*, i8*) addrspace(1)
declare void @"runtime/interrupt.use"(%"runtime/interrupt.Interrupt") addrspace(1)
define void @"(machine.UART).Configure"(%machine.RingBuffer*, i32, i8, i8, i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) {
call addrspace(1) void @"runtime/interrupt.use"(%"runtime/interrupt.Interrupt" { i32 ptrtoint (%"runtime/interrupt.handle"* @"runtime/interrupt.$interrupt18" to i32) })
ret void
}
define void @"device/avr.init"(i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) {
entry:
%0 = call addrspace(1) i32 @"runtime/interrupt.Register"(i32 18, i8* getelementptr inbounds ([17 x i8], [17 x i8]* @"device/avr.init$string.18", i32 0, i32 0), i16 17, i8* undef, i8* undef)
ret void
}
+35
View File
@@ -0,0 +1,35 @@
target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"
target triple = "avr-atmel-none"
%runtime.typecodeID = type { %runtime.typecodeID*, i16 }
%runtime.funcValueWithSignature = type { i16, %runtime.typecodeID* }
%machine.UART = type { %machine.RingBuffer* }
%machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" }
%"runtime/volatile.Register8" = type { i8 }
%"runtime/interrupt.Interrupt" = type { i32 }
@"reflect/types.type:func:{named:runtime/interrupt.Interrupt}{}" = external constant %runtime.typecodeID
@"(machine.UART).Configure$1$withSignature" = internal constant %runtime.funcValueWithSignature { i16 ptrtoint (void (i32, i8*, i8*) addrspace(1)* @"(machine.UART).Configure$1" to i16), %runtime.typecodeID* @"reflect/types.type:func:{named:runtime/interrupt.Interrupt}{}" }
@machine.UART0 = internal global %machine.UART zeroinitializer
@"device/avr.init$string.18" = internal unnamed_addr constant [17 x i8] c"__vector_USART_RX"
declare void @"(machine.UART).Configure$1"(i32, i8*, i8*) unnamed_addr addrspace(1)
declare i32 @"runtime/interrupt.Register"(i32, i8*, i16, i8*, i8*) addrspace(1)
declare void @"runtime/interrupt.use"(%"runtime/interrupt.Interrupt") addrspace(1)
define void @"(machine.UART).Configure"(%machine.RingBuffer*, i32, i8, i8, i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) {
ret void
}
define void @"device/avr.init"(i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) {
entry:
ret void
}
define avr_signalcc void @__vector_USART_RX() unnamed_addr addrspace(1) section ".text.__vector_USART_RX" {
entry:
call addrspace(1) void @"(machine.UART).Configure$1"(i32 18, i8* undef, i8* null)
ret void
}
+38
View File
@@ -0,0 +1,38 @@
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "armv7em-none-eabi"
%machine.UART = type { %machine.RingBuffer* }
%machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" }
%"runtime/volatile.Register8" = type { i8 }
%"runtime/interrupt.handle" = type { { i8*, void (i32, i8*, i8*)* }, %"runtime/interrupt.Interrupt" }
%"runtime/interrupt.Interrupt" = type { i32 }
@"runtime/interrupt.$interrupt2" = private unnamed_addr constant %"runtime/interrupt.handle" { { i8*, void (i32, i8*, i8*)* } { i8* bitcast (%machine.UART* @machine.UART0 to i8*), void (i32, i8*, i8*)* @"(*machine.UART).handleInterrupt$bound" }, %"runtime/interrupt.Interrupt" { i32 2 } }
@machine.UART0 = internal global %machine.UART { %machine.RingBuffer* @"machine$alloc.335" }
@"machine$alloc.335" = internal global %machine.RingBuffer zeroinitializer
@"device/nrf.init$string.2" = internal unnamed_addr constant [23 x i8] c"UARTE0_UART0_IRQHandler"
@"device/nrf.init$string.3" = internal unnamed_addr constant [44 x i8] c"SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler"
declare i32 @"runtime/interrupt.Register"(i32, i8*, i32, i8*, i8*) local_unnamed_addr
declare void @"device/arm.EnableIRQ"(i32, i8* nocapture readnone, i8* nocapture readnone)
declare void @"device/arm.SetPriority"(i32, i32, i8* nocapture readnone, i8* nocapture readnone)
define void @runtime.initAll(i8* nocapture readnone, i8* nocapture readnone) unnamed_addr {
entry:
%2 = call i32 @"runtime/interrupt.Register"(i32 2, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"device/nrf.init$string.2", i32 0, i32 0), i32 23, i8* undef, i8* undef)
%3 = call i32 @"runtime/interrupt.Register"(i32 3, i8* getelementptr inbounds ([44 x i8], [44 x i8]* @"device/nrf.init$string.3", i32 0, i32 0), i32 44, i8* undef, i8* undef)
call void @"device/arm.SetPriority"(i32 ptrtoint (%"runtime/interrupt.handle"* @"runtime/interrupt.$interrupt2" to i32), i32 192, i8* undef, i8* undef)
call void @"device/arm.EnableIRQ"(i32 ptrtoint (%"runtime/interrupt.handle"* @"runtime/interrupt.$interrupt2" to i32), i8* undef, i8* undef)
ret void
}
define internal void @"(*machine.UART).handleInterrupt$bound"(i32, i8* nocapture %context, i8* nocapture readnone %parentHandle) {
entry:
%unpack.ptr = bitcast i8* %context to %machine.UART*
call void @"(*machine.UART).handleInterrupt"(%machine.UART* %unpack.ptr, i32 %0, i8* undef, i8* undef)
ret void
}
declare void @"(*machine.UART).handleInterrupt"(%machine.UART* nocapture, i32, i8* nocapture readnone, i8* nocapture readnone)
+39
View File
@@ -0,0 +1,39 @@
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "armv7em-none-eabi"
%machine.UART = type { %machine.RingBuffer* }
%machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" }
%"runtime/volatile.Register8" = type { i8 }
@machine.UART0 = internal global %machine.UART { %machine.RingBuffer* @"machine$alloc.335" }
@"machine$alloc.335" = internal global %machine.RingBuffer zeroinitializer
@"device/nrf.init$string.2" = internal unnamed_addr constant [23 x i8] c"UARTE0_UART0_IRQHandler"
@"device/nrf.init$string.3" = internal unnamed_addr constant [44 x i8] c"SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler"
declare i32 @"runtime/interrupt.Register"(i32, i8*, i32, i8*, i8*) local_unnamed_addr
declare void @"device/arm.EnableIRQ"(i32, i8* nocapture readnone, i8* nocapture readnone)
declare void @"device/arm.SetPriority"(i32, i32, i8* nocapture readnone, i8* nocapture readnone)
define void @runtime.initAll(i8* nocapture readnone, i8* nocapture readnone) unnamed_addr {
entry:
call void @"device/arm.SetPriority"(i32 2, i32 192, i8* undef, i8* undef)
call void @"device/arm.EnableIRQ"(i32 2, i8* undef, i8* undef)
ret void
}
define internal void @"(*machine.UART).handleInterrupt$bound"(i32, i8* nocapture %context, i8* nocapture readnone %parentHandle) {
entry:
%unpack.ptr = bitcast i8* %context to %machine.UART*
call void @"(*machine.UART).handleInterrupt"(%machine.UART* %unpack.ptr, i32 %0, i8* undef, i8* undef)
ret void
}
declare void @"(*machine.UART).handleInterrupt"(%machine.UART* nocapture, i32, i8* nocapture readnone, i8* nocapture readnone)
define void @UARTE0_UART0_IRQHandler() unnamed_addr section ".text.UARTE0_UART0_IRQHandler" {
entry:
call void @"(*machine.UART).handleInterrupt$bound"(i32 2, i8* bitcast (%machine.UART* @machine.UART0 to i8*), i8* null)
ret void
}
+28
View File
@@ -0,0 +1,28 @@
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown-wasm"
declare i64 @externalCall(i8*, i32, i64)
define internal i64 @testCall(i8* %ptr, i32 %len, i64 %foo) {
%val = call i64 @externalCall(i8* %ptr, i32 %len, i64 %foo)
ret i64 %val
}
define internal i64 @testCallNonEntry(i8* %ptr, i32 %len) {
entry:
br label %bb1
bb1:
%val = call i64 @externalCall(i8* %ptr, i32 %len, i64 3)
ret i64 %val
}
define void @exportedFunction(i64 %foo) {
%unused = shl i64 %foo, 1
ret void
}
define internal void @callExportedFunction(i64 %foo) {
call void @exportedFunction(i64 %foo)
ret void
}

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