Compare commits

..

1 Commits

Author SHA1 Message Date
Ayke van Laethem 91b0bbaae6 testing: switch to upstream version 2025-10-05 15:33:49 +02:00
175 changed files with 1526 additions and 8645 deletions
+7 -8
View File
@@ -16,15 +16,14 @@ jobs:
name: build-macos name: build-macos
strategy: strategy:
matrix: matrix:
# macos-14: arm64 (oldest supported version as of 18-11-2025) # macos-13: amd64 (oldest supported version as of 18-10-2024)
# macos-15-intel: amd64 (last intel version to be supported by github runners) # macos-14: arm64 (oldest arm64 version)
# See https://github.com/actions/runner-images/issues/13046 os: [macos-13, macos-14]
os: [macos-14, macos-15-intel]
include: include:
- os: macos-13
goarch: amd64
- os: macos-14 - os: macos-14
goarch: arm64 goarch: arm64
- os: macos-15-intel
goarch: amd64
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Install Dependencies - name: Install Dependencies
@@ -40,7 +39,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v6
with: with:
go-version: '1.25.5' go-version: '1.25.1'
cache: true cache: true
- name: Restore LLVM source cache - name: Restore LLVM source cache
uses: actions/cache/restore@v4 uses: actions/cache/restore@v4
@@ -135,7 +134,7 @@ jobs:
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v6
with: with:
go-version: '1.25.5' go-version: '1.25.1'
cache: true cache: true
- name: Build TinyGo (LLVM ${{ matrix.version }}) - name: Build TinyGo (LLVM ${{ matrix.version }})
run: go install -tags=llvm${{ matrix.version }} run: go install -tags=llvm${{ matrix.version }}
+44 -2
View File
@@ -31,7 +31,7 @@ jobs:
sudo rm -rf /usr/local/share/boost sudo rm -rf /usr/local/share/boost
df -h df -h
- name: Check out the repo - name: Check out the repo
uses: actions/checkout@v5 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: Set up Docker Buildx - name: Set up Docker Buildx
@@ -58,7 +58,7 @@ jobs:
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v6 uses: docker/build-push-action@v5
with: with:
context: . context: .
push: true push: true
@@ -66,3 +66,45 @@ jobs:
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max
- name: Trigger Drivers repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/drivers/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger Bluetooth repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/bluetooth/actions/workflows/linux.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger TinyFS repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/tinyfs/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger TinyFont repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/tinyfont/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger TinyDraw repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/tinydraw/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
- name: Trigger TinyTerm repo build on Github Actions
run: |
curl -X POST \
-H "Authorization: Bearer ${{secrets.GHA_ACCESS_TOKEN}}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/tinygo-org/tinyterm/actions/workflows/build.yml/dispatches \
-d '{"ref": "dev"}'
+10 -10
View File
@@ -31,7 +31,7 @@ jobs:
# We're not on a multi-user machine, so this is safe. # We're not on a multi-user machine, so this is safe.
run: git config --global --add safe.directory "$GITHUB_WORKSPACE" run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- name: Extract TinyGo version - name: Extract TinyGo version
@@ -131,13 +131,13 @@ jobs:
needs: build-linux needs: build-linux
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.25.5' go-version: '1.25.0'
cache: true cache: true
- name: Install wasmtime - name: Install wasmtime
uses: bytecodealliance/actions/wasmtime/setup@v1 uses: bytecodealliance/actions/wasmtime/setup@v1
@@ -164,7 +164,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- name: Install apt dependencies - name: Install apt dependencies
@@ -179,9 +179,9 @@ jobs:
simavr \ simavr \
ninja-build ninja-build
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.25.5' go-version: '1.25.0'
cache: true cache: true
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
@@ -284,7 +284,7 @@ jobs:
needs: build-linux needs: build-linux
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Get TinyGo version - name: Get TinyGo version
id: version id: version
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT" run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
@@ -296,9 +296,9 @@ jobs:
g++-${{ matrix.toolchain }} \ g++-${{ matrix.toolchain }} \
libc6-dev-${{ matrix.libc }}-cross libc6-dev-${{ matrix.libc }}-cross
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.25.5' go-version: '1.25.0'
cache: true cache: true
- name: Restore LLVM source cache - name: Restore LLVM source cache
uses: actions/cache/restore@v4 uses: actions/cache/restore@v4
+2 -2
View File
@@ -25,7 +25,7 @@ jobs:
contents: read contents: read
steps: steps:
- name: Check out the repo - name: Check out the repo
uses: actions/checkout@v5 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: Set up Docker Buildx - name: Set up Docker Buildx
@@ -52,7 +52,7 @@ jobs:
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v6 uses: docker/build-push-action@v5
with: with:
target: tinygo-llvm-build target: tinygo-llvm-build
context: . context: .
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668 # See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
run: sudo apt-get remove llvm-18 run: sudo apt-get remove llvm-18
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Pull musl, bdwgc - name: Pull musl, bdwgc
run: | run: |
git submodule update --init lib/musl lib/bdwgc git submodule update --init lib/musl lib/bdwgc
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
run: | run: |
echo "$HOME/go/bin" >> $GITHUB_PATH echo "$HOME/go/bin" >> $GITHUB_PATH
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
with: with:
fetch-depth: 0 # fetch all history (no sparse checkout) fetch-depth: 0 # fetch all history (no sparse checkout)
submodules: true submodules: true
+12 -12
View File
@@ -31,7 +31,7 @@ jobs:
run: | run: |
scoop install ninja binaryen scoop install ninja binaryen
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- name: Extract TinyGo version - name: Extract TinyGo version
@@ -39,9 +39,9 @@ jobs:
shell: bash shell: bash
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT" run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.25.5' go-version: '1.25.0'
cache: true cache: true
- name: Restore cached LLVM source - name: Restore cached LLVM source
uses: actions/cache/restore@v4 uses: actions/cache/restore@v4
@@ -143,11 +143,11 @@ jobs:
run: | run: |
scoop install binaryen scoop install binaryen
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.25.5' go-version: '1.25.0'
cache: true cache: true
- name: Download TinyGo build - name: Download TinyGo build
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
@@ -173,11 +173,11 @@ jobs:
maximum-size: 24GB maximum-size: 24GB
disk-root: "C:" disk-root: "C:"
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.25.5' go-version: '1.25.0'
cache: true cache: true
- name: Download TinyGo build - name: Download TinyGo build
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
@@ -209,11 +209,11 @@ jobs:
run: | run: |
scoop install binaryen && scoop install wasmtime@29.0.1 scoop install binaryen && scoop install wasmtime@29.0.1
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Install Go - name: Install Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: '1.25.5' go-version: '1.25.0'
cache: true cache: true
- name: Download TinyGo build - name: Download TinyGo build
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
+1 -1
View File
@@ -16,7 +16,7 @@
url = https://github.com/WebAssembly/wasi-libc url = https://github.com/WebAssembly/wasi-libc
[submodule "lib/picolibc"] [submodule "lib/picolibc"]
path = lib/picolibc path = lib/picolibc
url = https://github.com/picolibc/picolibc.git url = https://github.com/keith-packard/picolibc.git
[submodule "lib/stm32-svd"] [submodule "lib/stm32-svd"]
path = lib/stm32-svd path = lib/stm32-svd
url = https://github.com/tinygo-org/stm32-svd url = https://github.com/tinygo-org/stm32-svd
-101
View File
@@ -1,104 +1,3 @@
0.40.1
---
* **machine**
- nrf: fix flash writes when SoftDevice is enabled
* **runtime**
- runtime: avoid fixed math/rand sequence on RP2040/RP2350 (#5124)
- runtime: add calls to initRand() during run() for all schedulers
- runtime: call initRand() before initHeap() during initialization
- runtime: use rand_hwrng hardwareRand for RP2040/RP2350 (#5135)
* **libs**
- picolibc: use updated location for git repo
0.40.0
---
* **general**
- all: add full LLVM 20 support
- core: feat: enable //go:linkname pragma for globals
- core: feature: Add flag to ignore go compatibility matrix (#5078)
- chore: update version for 0.40 development cycle
* **compiler**
- emit an error when the actual arch doesn't match GOARCH
- mark string parameters as readonly
- use Tarjan's SCC algorithm to detect loops for defer
- lower "large stack" limit to 16kb
* **core**
- shrink bdwgc library
- Fix linker errors for runtime.vgetrandom and crypto/internal/sysrand.fatal
- fix: add TryLock to sync.RWMutex
- fix: correct linter issues exposed by the fix in #4679
- fix: don't hardcode success return state
- fix: expand RTT debugger compatibility
- internal/task (threads): save stack bounds instead of scanning under a lock
- internal/task: create detached threads and fix error handling
- interp: better errors when debugging interp
- transform (gc): create stack slots in callers of external functions
- internal/task: prevent semaphore resource leak for threads scheduler
* **machine**
- cortexm: optimize code size for the HardFault_Handler
- fe310: add I2C pins for the HiFive1b
- clarify WriteAt semantics of BlockDevice
- fix deprecated AsmFull comment (#5005)
- make sure DMA buffers do not escape unnecessarily
- only enable USB-CDC when needed
- use larger SPI MAXCNT on nrf52833 and nrf52840
- fix: update m.queuedBytes when clamping output to avoid corrupting sentBytes
- fix: use int64 in ReadTemperature to avoid overflow
- fix(rp2): disable DBGPAUSE on startup
- fix(rp2): possible integer overflow while computing factors for SPI baudrate
- fix(rp2): reset spinlocks at startup
- fix(rp2): switch spinlock busy loop to wfe
- fix(rp2): use side-effect-free spinlocks
- nrf: add ADC_VDDH which is an ADC pin for VDDH
- nrf: don't block SPI transfer
- nrf: don't set PSELN, it's ignored in single ended mode anyway
- nrf: fix typo in ADC configuration
- nrf: refactor SoftDevice enabled check
- nrf: rename pwmPin to adcPin
- nrf: support flash operations while the SoftDevice is enabled
- rp2040: allow writing to the UART inside interrupts
- machine,nrf528: stop the bus only once on I2C bus error and ensures the first error is returned
* **net**
- update submodule to latest commits
* **runtime**
- (avr): fix infinite longjmp loop if stack is aligned to 256 bytes
- (gc_blocks.go): clear full size of allocation
- (gc_blocks.go): make sweep branchless
- (gc_blocks.go): simplify scanning logic
- (gc_blocks.go): use a linked stack to scan marked objects
- (gc_blocks.go): use best-fit allocation
- (gc_boehm.go): fix world already stopped check
- (wasm): scan the system stack
- fix sleep duration for long sleeps
- remove copied code for nrf52840
- src/syscall: update src buffer after write
- wasm: fix C realloc and optimize it a bit
* **targets**
- add xiao-esp32s3 board target
- Add ESP32-S3 support (#5091)
- Added Gopher ARCADE board
- Create "pico2-ice" target board (#5062)
* **build/test**
- Add testing.T.Context() and testing.B.Context()
- create separate go.mod file for testing dependencies for wasm tests that use Chromium headless browser to avoid use of older incompatible version.
- go back to normal scheduler instead of tasks scheduler for macos CI
- update CI to use Go 1.25.5
- update macOS GH actions builds to handle sunset of macOS 13
- use task scheduler on macOS builds to avoid test race condition lockups
- update all CI builds to use latest stable Go release. Also update some of the actions to their latest releases.
- update GH actions builds to use Go 1.25.4
- uninstall cmake before install
- fix: point the submodule for musl-lib to a mirror in the TinyGo GitHub org
- fix: remove macOS 15 from CI build matrix (conflicts with macOS 14 build)
- fix: separate host expected bytes from device intended bytes
- fix/typo: makeESPFirmwareImage
- make: GNUmakefile: shrink TinyGo binaries on Linux
- move the directory list into a variable
- several improvements to the macOS GH actions build
- Fix for #4678: top-level 'make lint' wasn't working
- fix: increase the timeout for chromedp to connect to the headless browser used for running the wasm tests.
- testdata: some more packages for the test corpus
0.39.0 0.39.0
--- ---
* **general** * **general**
+2 -8
View File
@@ -363,7 +363,6 @@ TEST_PACKAGES_FAST = \
path \ path \
reflect \ reflect \
sync \ sync \
testing \
testing/iotest \ testing/iotest \
text/scanner \ text/scanner \
unicode \ unicode \
@@ -480,8 +479,7 @@ TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
TEST_IOFS := false TEST_IOFS := false
endif endif
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic' TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic|^Fuzz'
TEST_ADDITIONAL_FLAGS ?=
# Test known-working standard library packages. # Test known-working standard library packages.
# TODO: parallelize, and only show failing tests (no implied -v flag). # TODO: parallelize, and only show failing tests (no implied -v flag).
@@ -489,7 +487,7 @@ TEST_ADDITIONAL_FLAGS ?=
tinygo-test: tinygo-test:
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented. @# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented.
@# TestParseAndBytesRoundTrip/P256/Generic: relies on t.Skip() which is not implemented @# TestParseAndBytesRoundTrip/P256/Generic: relies on t.Skip() which is not implemented
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW) $(TINYGO) test $(TEST_SKIP_FLAG) $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also @# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also
@# requires a large stack-size. Hence, io/fs is only run conditionally. @# requires a large stack-size. Hence, io/fs is only run conditionally.
@# For more details, see the comments on issue #3143. @# For more details, see the comments on issue #3143.
@@ -622,8 +620,6 @@ smoketest: testchdir
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id $(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id
@$(MD5SUM) test.hex @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pico2-ice examples/blinky1
@$(MD5SUM) test.hex
# test simulated boards on play.tinygo.org # test simulated boards on play.tinygo.org
ifneq ($(WASM), 0) ifneq ($(WASM), 0)
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1 GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
@@ -915,8 +911,6 @@ ifneq ($(XTENSA), 0)
@$(MD5SUM) test.bin @$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target mch2022 examples/machinetest $(TINYGO) build -size short -o test.bin -target mch2022 examples/machinetest
@$(MD5SUM) test.bin @$(MD5SUM) test.bin
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinky1
@$(MD5SUM) test.bin
endif endif
$(TINYGO) build -size short -o test.bin -target=esp-c3-32s-kit examples/blinky1 $(TINYGO) build -size short -o test.bin -target=esp-c3-32s-kit examples/blinky1
@$(MD5SUM) test.bin @$(MD5SUM) test.bin
+2 -3
View File
@@ -1,8 +1,7 @@
Copyright (c) 2018-2026 The TinyGo Authors. All rights reserved. Copyright (c) 2018-2025 The TinyGo Authors. All rights reserved.
TinyGo includes portions of the Go standard library. TinyGo includes portions of the Go standard library.
Copyright 2009 The Go Authors. All rights reserved. Copyright (c) 2009-2024 The Go Authors. All rights reserved.
See https://github.com/golang/go/blob/master/LICENSE for license information.
TinyGo includes portions of LLVM, which is under the Apache License v2.0 with TinyGo includes portions of LLVM, which is under the Apache License v2.0 with
LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information. LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information.
+1 -1
View File
@@ -1042,7 +1042,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if err != nil { if err != nil {
return result, err return result, err
} }
case "esp32", "esp32-img", "esp32c3", "esp32s3", "esp8266": case "esp32", "esp32-img", "esp32c3", "esp8266":
// Special format for the ESP family of chips (parsed by the ROM // Special format for the ESP family of chips (parsed by the ROM
// bootloader). // bootloader).
result.Binary = filepath.Join(tmpdir, "main"+outext) result.Binary = filepath.Join(tmpdir, "main"+outext)
-1
View File
@@ -28,7 +28,6 @@ func TestClangAttributes(t *testing.T) {
"cortex-m4", "cortex-m4",
"cortex-m7", "cortex-m7",
"esp32c3", "esp32c3",
"esp32s3",
"fe310", "fe310",
"gameboy-advance", "gameboy-advance",
"k210", "k210",
-3
View File
@@ -33,14 +33,11 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if options.GoCompatibility {
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax { if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax {
// Note: when this gets updated, also update the Go compatibility matrix: // Note: when this gets updated, also update the Go compatibility matrix:
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md // https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", minorMin, minorMax, gorootMajor, gorootMinor) return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", minorMin, minorMax, gorootMajor, gorootMinor)
} }
}
// Check that the Go toolchain version isn't too new, if we haven't been // Check that the Go toolchain version isn't too new, if we haven't been
// compiled with the latest Go version. // compiled with the latest Go version.
+1 -2
View File
@@ -100,12 +100,11 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
chip_id := map[string]uint16{ chip_id := map[string]uint16{
"esp32": 0x0000, "esp32": 0x0000,
"esp32c3": 0x0005, "esp32c3": 0x0005,
"esp32s3": 0x0009,
}[chip] }[chip]
// Image header. // Image header.
switch chip { switch chip {
case "esp32", "esp32c3", "esp32s3": case "esp32", "esp32c3":
// Header format: // Header format:
// https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71 // https://github.com/espressif/esp-idf/blob/v4.3/components/bootloader_support/include/esp_app_format.h#L71
// Note: not adding a SHA256 hash as the binary is modified by // Note: not adding a SHA256 hash as the binary is modified by
+3 -3
View File
@@ -42,9 +42,9 @@ func TestBinarySize(t *testing.T) {
// This is a small number of very diverse targets that we want to test. // This is a small number of very diverse targets that we want to test.
tests := []sizeTest{ tests := []sizeTest{
// microcontrollers // microcontrollers
{"hifive1b", "examples/echo", 3668, 280, 0, 2244}, {"hifive1b", "examples/echo", 3884, 280, 0, 2268},
{"microbit", "examples/serial", 2694, 342, 8, 2248}, {"microbit", "examples/serial", 2852, 360, 8, 2272},
{"wioterminal", "examples/pininterrupt", 6837, 1491, 120, 6888}, {"wioterminal", "examples/pininterrupt", 7337, 1491, 116, 6912},
// TODO: also check wasm. Right now this is difficult, because // TODO: also check wasm. Right now this is difficult, because
// wasm binaries are run through wasm-opt and therefore the // wasm binaries are run through wasm-opt and therefore the
+1 -1
View File
@@ -226,7 +226,7 @@ func (c *Config) StackSize() uint64 {
// MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap. // MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap.
func (c *Config) MaxStackAlloc() uint64 { func (c *Config) MaxStackAlloc() uint64 {
if c.StackSize() >= 16*1024 { if c.StackSize() > 32*1024 {
return 1024 return 1024
} }
-1
View File
@@ -59,7 +59,6 @@ type Options struct {
WITPackage string // pass through to wasm-tools component embed invocation WITPackage string // pass through to wasm-tools component embed invocation
WITWorld string // pass through to wasm-tools component embed -w option WITWorld string // pass through to wasm-tools component embed -w option
ExtLDFlags []string ExtLDFlags []string
GoCompatibility bool // enable to check for Go version compatibility
} }
// Verify performs a validation on the given options, raising an error if options are not valid. // Verify performs a validation on the given options, raising an error if options are not valid.
+1 -1
View File
@@ -245,7 +245,7 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
// current insert position. // current insert position.
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw") faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
nextBlock := b.insertBasicBlock(blockPrefix + ".next") nextBlock := b.insertBasicBlock(blockPrefix + ".next")
b.currentBlockInfo.exit = nextBlock // adjust outgoing block for phi nodes b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
// Now branch to the out-of-bounds or the regular block. // Now branch to the out-of-bounds or the regular block.
b.CreateCondBr(assert, faultBlock, nextBlock) b.CreateCondBr(assert, faultBlock, nextBlock)
-8
View File
@@ -32,9 +32,6 @@ const (
// Whether this is a full or partial Go parameter (int, slice, etc). // Whether this is a full or partial Go parameter (int, slice, etc).
// The extra context parameter is not a Go parameter. // The extra context parameter is not a Go parameter.
paramIsGoParam = 1 << iota paramIsGoParam = 1 << iota
// Whether this is a readonly parameter (for example, a string pointer).
paramIsReadonly
) )
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or // createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
@@ -170,7 +167,6 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
continue continue
} }
suffix := strconv.Itoa(i) suffix := strconv.Itoa(i)
isString := false
if goType != nil { if goType != nil {
// Try to come up with a good suffix for this struct field, // Try to come up with a good suffix for this struct field,
// depending on which Go type it's based on. // depending on which Go type it's based on.
@@ -187,16 +183,12 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
suffix = []string{"r", "i"}[i] suffix = []string{"r", "i"}[i]
case types.String: case types.String:
suffix = []string{"data", "len"}[i] suffix = []string{"data", "len"}[i]
isString = true
} }
case *types.Signature: case *types.Signature:
suffix = []string{"context", "funcptr"}[i] suffix = []string{"context", "funcptr"}[i]
} }
} }
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i)) subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
if isString {
subInfos[0].flags |= paramIsReadonly
}
paramInfos = append(paramInfos, subInfos...) paramInfos = append(paramInfos, subInfos...)
} }
return paramInfos return paramInfos
+18 -122
View File
@@ -153,11 +153,9 @@ type builder struct {
llvmFn llvm.Value llvmFn llvm.Value
info functionInfo info functionInfo
locals map[ssa.Value]llvm.Value // local variables locals map[ssa.Value]llvm.Value // local variables
blockInfo []blockInfo blockEntries map[*ssa.BasicBlock]llvm.BasicBlock // a *ssa.BasicBlock may be split up
blockExits map[*ssa.BasicBlock]llvm.BasicBlock // these are the exit blocks
currentBlock *ssa.BasicBlock currentBlock *ssa.BasicBlock
currentBlockInfo *blockInfo
tarjanStack []uint
tarjanIndex uint
phis []phiNode phis []phiNode
deferPtr llvm.Value deferPtr llvm.Value
deferFrame llvm.Value deferFrame llvm.Value
@@ -189,22 +187,11 @@ func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *bu
info: c.getFunctionInfo(f), info: c.getFunctionInfo(f),
locals: make(map[ssa.Value]llvm.Value), locals: make(map[ssa.Value]llvm.Value),
dilocals: make(map[*types.Var]llvm.Metadata), dilocals: make(map[*types.Var]llvm.Metadata),
blockEntries: make(map[*ssa.BasicBlock]llvm.BasicBlock),
blockExits: make(map[*ssa.BasicBlock]llvm.BasicBlock),
} }
} }
type blockInfo struct {
// entry is the LLVM basic block corresponding to the start of this *ssa.Block.
entry llvm.BasicBlock
// exit is the LLVM basic block corresponding to the end of this *ssa.Block.
// It will be different than entry if any of the block's instructions contain internal branches.
exit llvm.BasicBlock
// tarjan holds state for applying Tarjan's strongly connected components algorithm to the CFG.
// This is used by defer.go to determine whether to stack- or heap-allocate defer data.
tarjan tarjanNode
}
type deferBuiltin struct { type deferBuiltin struct {
callName string callName string
pos token.Pos pos token.Pos
@@ -1233,29 +1220,14 @@ func (b *builder) createFunctionStart(intrinsic bool) {
// intrinsic (like an atomic operation). Create the entry block // intrinsic (like an atomic operation). Create the entry block
// manually. // manually.
entryBlock = b.ctx.AddBasicBlock(b.llvmFn, "entry") entryBlock = b.ctx.AddBasicBlock(b.llvmFn, "entry")
// Intrinsics may create internal branches (e.g. nil checks).
// They will attempt to access b.currentBlockInfo to update the exit block.
// Create some fake block info for them to access.
blockInfo := []blockInfo{
{
entry: entryBlock,
exit: entryBlock,
},
}
b.blockInfo = blockInfo
b.currentBlockInfo = &blockInfo[0]
} else { } else {
blocks := b.fn.Blocks
blockInfo := make([]blockInfo, len(blocks))
for _, block := range b.fn.DomPreorder() { for _, block := range b.fn.DomPreorder() {
info := &blockInfo[block.Index]
llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment) llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
info.entry = llvmBlock b.blockEntries[block] = llvmBlock
info.exit = llvmBlock b.blockExits[block] = llvmBlock
} }
b.blockInfo = blockInfo
// Normal functions have an entry block. // Normal functions have an entry block.
entryBlock = blockInfo[0].entry entryBlock = b.blockEntries[b.fn.Blocks[0]]
} }
b.SetInsertPointAtEnd(entryBlock) b.SetInsertPointAtEnd(entryBlock)
@@ -1351,9 +1323,8 @@ func (b *builder) createFunction() {
if b.DumpSSA { if b.DumpSSA {
fmt.Printf("%d: %s:\n", block.Index, block.Comment) fmt.Printf("%d: %s:\n", block.Index, block.Comment)
} }
b.SetInsertPointAtEnd(b.blockEntries[block])
b.currentBlock = block b.currentBlock = block
b.currentBlockInfo = &b.blockInfo[block.Index]
b.SetInsertPointAtEnd(b.currentBlockInfo.entry)
for _, instr := range block.Instrs { for _, instr := range block.Instrs {
if instr, ok := instr.(*ssa.DebugRef); ok { if instr, ok := instr.(*ssa.DebugRef); ok {
if !b.Debug { if !b.Debug {
@@ -1413,7 +1384,7 @@ func (b *builder) createFunction() {
block := phi.ssa.Block() block := phi.ssa.Block()
for i, edge := range phi.ssa.Edges { for i, edge := range phi.ssa.Edges {
llvmVal := b.getValue(edge, getPos(phi.ssa)) llvmVal := b.getValue(edge, getPos(phi.ssa))
llvmBlock := b.blockInfo[block.Preds[i].Index].exit llvmBlock := b.blockExits[block.Preds[i]]
phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock}) phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock})
} }
} }
@@ -1527,11 +1498,11 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
case *ssa.If: case *ssa.If:
cond := b.getValue(instr.Cond, getPos(instr)) cond := b.getValue(instr.Cond, getPos(instr))
block := instr.Block() block := instr.Block()
blockThen := b.blockInfo[block.Succs[0].Index].entry blockThen := b.blockEntries[block.Succs[0]]
blockElse := b.blockInfo[block.Succs[1].Index].entry blockElse := b.blockEntries[block.Succs[1]]
b.CreateCondBr(cond, blockThen, blockElse) b.CreateCondBr(cond, blockThen, blockElse)
case *ssa.Jump: case *ssa.Jump:
blockJump := b.blockInfo[instr.Block().Succs[0].Index].entry blockJump := b.blockEntries[instr.Block().Succs[0]]
b.CreateBr(blockJump) b.CreateBr(blockJump)
case *ssa.MapUpdate: case *ssa.MapUpdate:
m := b.getValue(instr.Map, getPos(instr)) m := b.getValue(instr.Map, getPos(instr))
@@ -1599,8 +1570,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen") elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem()) elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false) elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
elemLayout := b.createObjectLayout(elemType, pos) result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize}, "append.new")
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize, elemLayout}, "append.new")
newPtr := b.CreateExtractValue(result, 0, "append.newPtr") newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
newLen := b.CreateExtractValue(result, 1, "append.newLen") newLen := b.CreateExtractValue(result, 1, "append.newLen")
newCap := b.CreateExtractValue(result, 2, "append.newCap") newCap := b.CreateExtractValue(result, 2, "append.newCap")
@@ -1682,41 +1652,13 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
case "copy": case "copy":
dst := argValues[0] dst := argValues[0]
src := argValues[1] src := argValues[1]
// Fetch the lengths.
dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen") dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen")
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen") srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
// Find the minimum of the lengths. dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
minFuncName := "llvm.umin.i" + strconv.Itoa(b.uintptrType.IntTypeWidth()) srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
minFunc := b.mod.NamedFunction(minFuncName)
if minFunc.IsNil() {
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType, b.uintptrType}, false)
minFunc = llvm.AddFunction(b.mod, minFuncName, fnType)
}
minLen := b.CreateCall(minFunc.GlobalValueType(), minFunc, []llvm.Value{dstLen, srcLen}, "copy.n")
// Multiply the length by the element size.
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem()) elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false) elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
// NOTE: This is also NSW when uintptr is int, but we can only choose one through the C API? return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
size := b.CreateNUWMul(minLen, elemSize, "copy.size")
// Fetch the pointers.
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstPtr")
srcBuf := b.CreateExtractValue(src, 0, "copy.srcPtr")
// Create a memcpy.
call := b.createMemCopy("memmove", dstBuf, srcBuf, size)
align := b.targetData.ABITypeAlignment(elemType)
if align > 1 {
// Apply the type's alignment to the arguments.
// LLVM sometimes turns constant-length moves into loads and stores.
// It may use this alignment for the created loads and stores.
alignAttr := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align))
call.AddCallSiteAttribute(1, alignAttr)
call.AddCallSiteAttribute(2, alignAttr)
}
// Extend and return the copied length.
if b.targetData.TypeAllocSize(minLen.Type()) < b.targetData.TypeAllocSize(b.intType) {
minLen = b.CreateZExt(minLen, b.intType, "copy.n.zext")
}
return minLen, nil
case "delete": case "delete":
m := argValues[0] m := argValues[0]
key := argValues[1] key := argValues[1]
@@ -1744,25 +1686,8 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
return llvmLen, nil return llvmLen, nil
case "min", "max": case "min", "max":
// min and max builtins, added in Go 1.21. // min and max builtins, added in Go 1.21.
// Find the corresponding intrinsic name. // We can simply reuse the existing binop comparison code, which has all
ty := argTypes[0].Underlying().(*types.Basic) // the edge cases figured out already.
llvmType := b.getLLVMType(ty)
info := ty.Info()
var prefix, delimeter, typeName string
if info&types.IsInteger != 0 {
// This is an integer value.
// Use the LLVM int min/max intrinsics.
prefix = "llvm.s"
if info&types.IsUnsigned != 0 {
prefix = "llvm.u"
}
delimeter = ".i"
typeName = strconv.Itoa(llvmType.IntTypeWidth())
} else {
switch ty.Kind() {
case types.String:
// Strings do not have an equivalent intrinsic.
// Implement with compares and selects.
tok := token.LSS tok := token.LSS
if callName == "max" { if callName == "max" {
tok = token.GTR tok = token.GTR
@@ -1777,35 +1702,6 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
result = b.CreateSelect(cmp, result, arg, "") result = b.CreateSelect(cmp, result, arg, "")
} }
return result, nil return result, nil
case types.Float32:
typeName = "f32"
case types.Float64:
typeName = "f64"
default:
return llvm.Value{}, b.makeError(pos, "todo: min/max: unknown type")
}
// There are a few edge cases with floating point min/max:
// min(-0.0, +0.0) = -0.0
// min(NaN, number) = NaN
// The llvm.minimum.*/llvm.maximum.* intrinsics match this behavior.
// Neither Go nor LLVM defines the bit representation of resulting NaNs.
prefix = "llvm."
delimeter = "imum."
}
intrinsicName := prefix + callName + delimeter + typeName
// Find or create the intrinsic.
llvmFn := b.mod.NamedFunction(intrinsicName)
if llvmFn.IsNil() {
fnType := llvm.FunctionType(llvmType, []llvm.Type{llvmType, llvmType}, false)
llvmFn = llvm.AddFunction(b.mod, intrinsicName, fnType)
}
// Call the intrinsic repeatedly to merge the arguments.
callType := llvmFn.GlobalValueType()
result := argValues[0]
for _, arg := range argValues[1:] {
result = b.CreateCall(callType, llvmFn, []llvm.Value{result, arg}, "")
}
return result, nil
case "panic": case "panic":
// This is rare, but happens in "defer panic()". // This is rare, but happens in "defer panic()".
b.createRuntimeInvoke("_panic", argValues, "") b.createRuntimeInvoke("_panic", argValues, "")
+27 -97
View File
@@ -100,7 +100,7 @@ func (b *builder) createLandingPad() {
// Continue at the 'recover' block, which returns to the parent in an // Continue at the 'recover' block, which returns to the parent in an
// appropriate way. // appropriate way.
b.CreateBr(b.blockInfo[b.fn.Recover.Index].entry) b.CreateBr(b.blockEntries[b.fn.Recover])
} }
// Create a checkpoint (similar to setjmp). This emits inline assembly that // Create a checkpoint (similar to setjmp). This emits inline assembly that
@@ -234,108 +234,41 @@ func (b *builder) createInvokeCheckpoint() {
continueBB := b.insertBasicBlock("") continueBB := b.insertBasicBlock("")
b.CreateCondBr(isZero, continueBB, b.landingpad) b.CreateCondBr(isZero, continueBB, b.landingpad)
b.SetInsertPointAtEnd(continueBB) b.SetInsertPointAtEnd(continueBB)
b.currentBlockInfo.exit = continueBB b.blockExits[b.currentBlock] = continueBB
} }
// isInLoop checks if there is a path from the current block to itself. // isInLoop checks if there is a path from a basic block to itself.
// Use Tarjan's strongly connected components algorithm to search for cycles. func isInLoop(start *ssa.BasicBlock) bool {
// A one-node SCC is a cycle iff there is an edge from the node to itself. // Use a breadth-first search to scan backwards through the block graph.
// A multi-node SCC is always a cycle. queue := []*ssa.BasicBlock{start}
// https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm checked := map[*ssa.BasicBlock]struct{}{}
func (b *builder) isInLoop() bool {
if b.currentBlockInfo.tarjan.lowLink == 0 { for len(queue) > 0 {
b.strongConnect(b.currentBlock) // pop a block off of the queue
block := queue[len(queue)-1]
queue = queue[:len(queue)-1]
// Search through predecessors.
// Searching backwards means that this is pretty fast when the block is close to the start of the function.
// Defers are often placed near the start of the function.
for _, pred := range block.Preds {
if pred == start {
// cycle found
return true
} }
return b.currentBlockInfo.tarjan.cyclic
}
func (b *builder) strongConnect(block *ssa.BasicBlock) { if _, ok := checked[pred]; ok {
// Assign a new index. // block already checked
// Indices start from 1 so that 0 can be used as a sentinel.
assignedIndex := b.tarjanIndex + 1
b.tarjanIndex = assignedIndex
// Apply the new index.
blockIndex := block.Index
node := &b.blockInfo[blockIndex].tarjan
node.lowLink = assignedIndex
// Push the node onto the stack.
node.onStack = true
b.tarjanStack = append(b.tarjanStack, uint(blockIndex))
// Process the successors.
for _, successor := range block.Succs {
// Look up the successor's state.
successorIndex := successor.Index
if successorIndex == blockIndex {
// Handle a self-cycle specially.
node.cyclic = true
continue
}
successorNode := &b.blockInfo[successorIndex].tarjan
switch {
case successorNode.lowLink == 0:
// This node has not yet been visisted.
b.strongConnect(successor)
case !successorNode.onStack:
// This node has been visited, but is in a different SCC.
// Ignore it, and do not update lowLink.
continue continue
} }
// Update the lowLink index. // add to queue and checked map
// This always uses the min-of-lowlink instead of using index in the on-stack case. queue = append(queue, pred)
// This is done for two reasons: checked[pred] = struct{}{}
// 1. The lowLink update can be shared between the new-node and on-stack cases.
// 2. The assigned index does not need to be saved - it is only needed for root node detection.
if successorNode.lowLink < node.lowLink {
node.lowLink = successorNode.lowLink
} }
} }
if node.lowLink == assignedIndex { return false
// This is a root node.
// Pop the SCC off the stack.
stack := b.tarjanStack
top := stack[len(stack)-1]
stack = stack[:len(stack)-1]
blocks := b.blockInfo
topNode := &blocks[top].tarjan
topNode.onStack = false
if top != uint(blockIndex) {
// The root node is not the only node in the SCC.
// Mark all nodes in this SCC as cyclic.
topNode.cyclic = true
for top != uint(blockIndex) {
top = stack[len(stack)-1]
stack = stack[:len(stack)-1]
topNode = &blocks[top].tarjan
topNode.onStack = false
topNode.cyclic = true
}
}
b.tarjanStack = stack
}
}
// tarjanNode holds per-block state for isInLoop and strongConnect.
type tarjanNode struct {
// lowLink is the index of the first visited node that is reachable from this block.
// The lowLink indices are assigned by the SCC search, and do not correspond to b.Index.
// A lowLink of 0 is used as a sentinel to mark a node which has not yet been visited.
lowLink uint
// onStack tracks whether this node is currently on the SCC search stack.
onStack bool
// cyclic indicates whether this block is in a loop.
// If lowLink is 0, strongConnect must be called before reading this field.
cyclic bool
} }
// createDefer emits a single defer instruction, to be run when this function // createDefer emits a single defer instruction, to be run when this function
@@ -477,10 +410,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
// Put this struct in an allocation. // Put this struct in an allocation.
var alloca llvm.Value var alloca llvm.Value
if instr.Block() != b.currentBlock { if !isInLoop(instr.Block()) {
panic("block mismatch")
}
if !b.isInLoop() {
// This can safely use a stack allocation. // This can safely use a stack allocation.
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca") alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca")
} else { } else {
+1 -1
View File
@@ -737,7 +737,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
prevBlock := b.GetInsertBlock() prevBlock := b.GetInsertBlock()
okBlock := b.insertBasicBlock("typeassert.ok") okBlock := b.insertBasicBlock("typeassert.ok")
nextBlock := b.insertBasicBlock("typeassert.next") nextBlock := b.insertBasicBlock("typeassert.next")
b.currentBlockInfo.exit = nextBlock // adjust outgoing block for phi nodes b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
b.CreateCondBr(commaOk, okBlock, nextBlock) b.CreateCondBr(commaOk, okBlock, nextBlock)
// Retrieve the value from the interface if the type assert was // Retrieve the value from the interface if the type assert was
+8 -29
View File
@@ -27,8 +27,6 @@ func (b *builder) defineIntrinsicFunction() {
b.createStackSaveImpl() b.createStackSaveImpl()
case name == "runtime.KeepAlive": case name == "runtime.KeepAlive":
b.createKeepAliveImpl() b.createKeepAliveImpl()
case name == "machine.keepAliveNoEscape":
b.createMachineKeepAliveImpl()
case strings.HasPrefix(name, "runtime/volatile.Load"): case strings.HasPrefix(name, "runtime/volatile.Load"):
b.createVolatileLoad() b.createVolatileLoad()
case strings.HasPrefix(name, "runtime/volatile.Store"): case strings.HasPrefix(name, "runtime/volatile.Store"):
@@ -50,24 +48,19 @@ func (b *builder) defineIntrinsicFunction() {
// and will otherwise be lowered to regular libc memcpy/memmove calls. // and will otherwise be lowered to regular libc memcpy/memmove calls.
func (b *builder) createMemoryCopyImpl() { func (b *builder) createMemoryCopyImpl() {
b.createFunctionStart(true) b.createFunctionStart(true)
params := b.fn.Params[0:3] fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
b.createMemCopy(
b.fn.Name(),
b.getValue(params[0], getPos(b.fn)),
b.getValue(params[1], getPos(b.fn)),
b.getValue(params[2], getPos(b.fn)),
)
b.CreateRetVoid()
}
func (b *builder) createMemCopy(kind string, dst, src, len llvm.Value) llvm.Value {
fnName := "llvm." + kind + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
llvmFn := b.mod.NamedFunction(fnName) llvmFn := b.mod.NamedFunction(fnName)
if llvmFn.IsNil() { if llvmFn.IsNil() {
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType, b.dataPtrType, b.uintptrType, b.ctx.Int1Type()}, false) fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType, b.dataPtrType, b.uintptrType, b.ctx.Int1Type()}, false)
llvmFn = llvm.AddFunction(b.mod, fnName, fnType) llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
} }
return b.CreateCall(llvmFn.GlobalValueType(), llvmFn, []llvm.Value{dst, src, len, llvm.ConstInt(b.ctx.Int1Type(), 0, false)}, "") var params []llvm.Value
for _, param := range b.fn.Params {
params = append(params, b.getValue(param, getPos(b.fn)))
}
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
b.CreateCall(llvmFn.GlobalValueType(), llvmFn, params, "")
b.CreateRetVoid()
} }
// createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of // createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of
@@ -151,20 +144,6 @@ func (b *builder) createAbiEscapeImpl() {
b.CreateRet(result) b.CreateRet(result)
} }
// Implement machine.keepAliveNoEscape, which makes sure the compiler keeps the
// pointer parameter alive until this point (for GC).
func (b *builder) createMachineKeepAliveImpl() {
b.createFunctionStart(true)
pointerValue := b.getValue(b.fn.Params[0], getPos(b.fn))
// See createKeepAliveImpl for details.
asmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.dataPtrType}, false)
asmFn := llvm.InlineAsm(asmType, "", "r", true, false, 0, false)
b.createCall(asmType, asmFn, []llvm.Value{pointerValue}, "")
b.CreateRetVoid()
}
var mathToLLVMMapping = map[string]string{ var mathToLLVMMapping = map[string]string{
"math.Ceil": "llvm.ceil.f64", "math.Ceil": "llvm.ceil.f64",
"math.Exp": "llvm.exp.f64", "math.Exp": "llvm.exp.f64",
-3
View File
@@ -250,9 +250,6 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
func hashmapIsBinaryKey(keyType types.Type) bool { func hashmapIsBinaryKey(keyType types.Type) bool {
switch keyType := keyType.Underlying().(type) { switch keyType := keyType.Underlying().(type) {
case *types.Basic: case *types.Basic:
// TODO: unsafe.Pointer is also a binary key, but to support that we
// need to fix an issue with interp first (see
// https://github.com/tinygo-org/tinygo/pull/4898).
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
case *types.Pointer: case *types.Pointer:
return true return true
+6 -7
View File
@@ -142,11 +142,6 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0) nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
llvmFn.AddAttributeAtIndex(i+1, nocapture) llvmFn.AddAttributeAtIndex(i+1, nocapture)
} }
if paramInfo.flags&paramIsReadonly != 0 && paramInfo.llvmType.TypeKind() == llvm.PointerTypeKind {
// Readonly pointer parameters (like strings) benefit from being marked as readonly.
readonly := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)
llvmFn.AddAttributeAtIndex(i+1, readonly)
}
} }
// Set a number of function or parameter attributes, depending on the // Set a number of function or parameter attributes, depending on the
@@ -159,8 +154,6 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0)) llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0))
case "internal/abi.NoEscape": case "internal/abi.NoEscape":
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
case "machine.keepAliveNoEscape", "machine.unsafeNoEscape":
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
case "runtime.alloc": case "runtime.alloc":
// Tell the optimizer that runtime.alloc is an allocator, meaning that it // Tell the optimizer that runtime.alloc is an allocator, meaning that it
// returns values that are never null and never alias to an existing value. // returns values that are never null and never alias to an existing value.
@@ -184,6 +177,12 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
// be modified. // be modified.
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
case "runtime.sliceCopy":
// Copying a slice won't capture any of the parameters.
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("writeonly"), 0))
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
case "runtime.stringFromBytes": case "runtime.stringFromBytes":
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
-8
View File
@@ -74,14 +74,6 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
return b.CreateCall(fnType, target, args, ""), nil return b.CreateCall(fnType, target, args, ""), nil
case b.GOARCH == "arm" && b.GOOS == "linux": case b.GOARCH == "arm" && b.GOOS == "linux":
if arch := b.archFamily(); arch != "arm" {
// Some targets pretend to be linux/arm for compatibility but aren't
// actually such a system. Make sure we emit an error instead of
// creating inline assembly that will fail to compile.
// See: https://github.com/tinygo-org/tinygo/issues/4959
return llvm.Value{}, b.makeError(call.Pos(), "system calls are not supported: target emulates a linux/arm system on "+arch)
}
// Implement the EABI system call convention for Linux. // Implement the EABI system call convention for Linux.
// Source: syscall(2) man page. // Source: syscall(2) man page.
args := []llvm.Value{} args := []llvm.Value{}
-235
View File
@@ -270,241 +270,6 @@ entry:
ret void ret void
} }
; Function Attrs: nounwind
define hidden void @main.deferInfiniteLoop(ptr %context) unnamed_addr #1 {
entry:
%deferPtr = alloca ptr, align 4
store ptr null, ptr %deferPtr, align 4
%deferframe.buf = alloca %runtime.deferFrame, align 4
%0 = call ptr @llvm.stacksave.p0()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
br label %for.body
for.body: ; preds = %for.body, %entry
%defer.next = load ptr, ptr %deferPtr, align 4
%defer.alloc.call = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #4
store i32 0, ptr %defer.alloc.call, align 4
%defer.alloc.call.repack1 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 4
store ptr %defer.next, ptr %defer.alloc.call.repack1, align 4
%defer.alloc.call.repack3 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 8
store i32 8, ptr %defer.alloc.call.repack3, align 4
store ptr %defer.alloc.call, ptr %deferPtr, align 4
br label %for.body
recover: ; preds = %rundefers.end
ret void
lpad: ; No predecessors!
br label %rundefers.loophead
rundefers.loophead: ; preds = %rundefers.callback0, %lpad
br i1 poison, label %rundefers.end, label %rundefers.loop
rundefers.loop: ; preds = %rundefers.loophead
switch i32 poison, label %rundefers.default [
i32 0, label %rundefers.callback0
]
rundefers.callback0: ; preds = %rundefers.loop
br label %rundefers.loophead
rundefers.default: ; preds = %rundefers.loop
unreachable
rundefers.end: ; preds = %rundefers.loophead
br label %recover
}
; Function Attrs: nounwind
define hidden void @main.deferLoop(ptr %context) unnamed_addr #1 {
entry:
%deferPtr = alloca ptr, align 4
store ptr null, ptr %deferPtr, align 4
%deferframe.buf = alloca %runtime.deferFrame, align 4
%0 = call ptr @llvm.stacksave.p0()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
br label %for.loop
for.loop: ; preds = %for.body, %entry
%1 = phi i32 [ 0, %entry ], [ %3, %for.body ]
%2 = icmp slt i32 %1, 10
br i1 %2, label %for.body, label %for.done
for.body: ; preds = %for.loop
%defer.next = load ptr, ptr %deferPtr, align 4
%defer.alloc.call = call dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #4
store i32 0, ptr %defer.alloc.call, align 4
%defer.alloc.call.repack13 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 4
store ptr %defer.next, ptr %defer.alloc.call.repack13, align 4
%defer.alloc.call.repack15 = getelementptr inbounds nuw i8, ptr %defer.alloc.call, i32 8
store i32 %1, ptr %defer.alloc.call.repack15, align 4
store ptr %defer.alloc.call, ptr %deferPtr, align 4
%3 = add i32 %1, 1
br label %for.loop
for.done: ; preds = %for.loop
br label %rundefers.block
rundefers.after: ; preds = %rundefers.end
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #4
ret void
rundefers.block: ; preds = %for.done
br label %rundefers.loophead
rundefers.loophead: ; preds = %rundefers.callback0, %rundefers.block
%4 = load ptr, ptr %deferPtr, align 4
%stackIsNil = icmp eq ptr %4, null
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
rundefers.loop: ; preds = %rundefers.loophead
%stack.next.gep = getelementptr inbounds nuw i8, ptr %4, i32 4
%stack.next = load ptr, ptr %stack.next.gep, align 4
store ptr %stack.next, ptr %deferPtr, align 4
%callback = load i32, ptr %4, align 4
switch i32 %callback, label %rundefers.default [
i32 0, label %rundefers.callback0
]
rundefers.callback0: ; preds = %rundefers.loop
%gep = getelementptr inbounds nuw i8, ptr %4, i32 8
%param = load i32, ptr %gep, align 4
call void @runtime.printlock(ptr undef) #4
call void @runtime.printint32(i32 %param, ptr undef) #4
call void @runtime.printunlock(ptr undef) #4
br label %rundefers.loophead
rundefers.default: ; preds = %rundefers.loop
unreachable
rundefers.end: ; preds = %rundefers.loophead
br label %rundefers.after
recover: ; preds = %rundefers.end1
ret void
lpad: ; No predecessors!
br label %rundefers.loophead4
rundefers.loophead4: ; preds = %rundefers.callback010, %lpad
br i1 poison, label %rundefers.end1, label %rundefers.loop3
rundefers.loop3: ; preds = %rundefers.loophead4
switch i32 poison, label %rundefers.default2 [
i32 0, label %rundefers.callback010
]
rundefers.callback010: ; preds = %rundefers.loop3
br label %rundefers.loophead4
rundefers.default2: ; preds = %rundefers.loop3
unreachable
rundefers.end1: ; preds = %rundefers.loophead4
br label %recover
}
; Function Attrs: nounwind
define hidden void @main.deferBetweenLoops(ptr %context) unnamed_addr #1 {
entry:
%defer.alloca = alloca { i32, ptr, i32 }, align 4
%deferPtr = alloca ptr, align 4
store ptr null, ptr %deferPtr, align 4
%deferframe.buf = alloca %runtime.deferFrame, align 4
%0 = call ptr @llvm.stacksave.p0()
call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4
br label %for.loop
for.loop: ; preds = %for.body, %entry
%1 = phi i32 [ 0, %entry ], [ %3, %for.body ]
%2 = icmp slt i32 %1, 10
br i1 %2, label %for.body, label %for.done
for.body: ; preds = %for.loop
%3 = add i32 %1, 1
br label %for.loop
for.done: ; preds = %for.loop
%defer.next = load ptr, ptr %deferPtr, align 4
store i32 0, ptr %defer.alloca, align 4
%defer.alloca.repack16 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4
store ptr %defer.next, ptr %defer.alloca.repack16, align 4
%defer.alloca.repack18 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 8
store i32 1, ptr %defer.alloca.repack18, align 4
store ptr %defer.alloca, ptr %deferPtr, align 4
br label %for.loop1
for.loop1: ; preds = %for.body2, %for.done
%4 = phi i32 [ 0, %for.done ], [ %6, %for.body2 ]
%5 = icmp slt i32 %4, 10
br i1 %5, label %for.body2, label %for.done3
for.body2: ; preds = %for.loop1
%6 = add i32 %4, 1
br label %for.loop1
for.done3: ; preds = %for.loop1
br label %rundefers.block
rundefers.after: ; preds = %rundefers.end
call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #4
ret void
rundefers.block: ; preds = %for.done3
br label %rundefers.loophead
rundefers.loophead: ; preds = %rundefers.callback0, %rundefers.block
%7 = load ptr, ptr %deferPtr, align 4
%stackIsNil = icmp eq ptr %7, null
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
rundefers.loop: ; preds = %rundefers.loophead
%stack.next.gep = getelementptr inbounds nuw i8, ptr %7, i32 4
%stack.next = load ptr, ptr %stack.next.gep, align 4
store ptr %stack.next, ptr %deferPtr, align 4
%callback = load i32, ptr %7, align 4
switch i32 %callback, label %rundefers.default [
i32 0, label %rundefers.callback0
]
rundefers.callback0: ; preds = %rundefers.loop
%gep = getelementptr inbounds nuw i8, ptr %7, i32 8
%param = load i32, ptr %gep, align 4
call void @runtime.printlock(ptr undef) #4
call void @runtime.printint32(i32 %param, ptr undef) #4
call void @runtime.printunlock(ptr undef) #4
br label %rundefers.loophead
rundefers.default: ; preds = %rundefers.loop
unreachable
rundefers.end: ; preds = %rundefers.loophead
br label %rundefers.after
recover: ; preds = %rundefers.end4
ret void
lpad: ; No predecessors!
br label %rundefers.loophead7
rundefers.loophead7: ; preds = %rundefers.callback013, %lpad
br i1 poison, label %rundefers.end4, label %rundefers.loop6
rundefers.loop6: ; preds = %rundefers.loophead7
switch i32 poison, label %rundefers.default5 [
i32 0, label %rundefers.callback013
]
rundefers.callback013: ; preds = %rundefers.loop6
br label %rundefers.loophead7
rundefers.default5: ; preds = %rundefers.loop6
unreachable
rundefers.end4: ; preds = %rundefers.loophead7
br label %recover
}
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" } attributes #1 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" } attributes #2 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
-20
View File
@@ -18,23 +18,3 @@ func deferMultiple() {
}() }()
external() external()
} }
func deferInfiniteLoop() {
for {
defer print(8)
}
}
func deferLoop() {
for i := 0; i < 10; i++ {
defer print(i)
}
}
func deferBetweenLoops() {
for i := 0; i < 10; i++ {
}
defer print(1)
for i := 0; i < 10; i++ {
}
}
+1 -1
View File
@@ -50,7 +50,7 @@ unsafe.String.throw: ; preds = %entry
declare void @runtime.unsafeSlicePanic(ptr) #1 declare void @runtime.unsafeSlicePanic(ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden ptr @main.unsafeStringData(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 { define hidden ptr @main.unsafeStringData(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3 call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
+30 -36
View File
@@ -22,9 +22,6 @@ entry:
ret i32 %a ret i32 %a
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smin.i32(i32, i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 { define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry: entry:
@@ -56,9 +53,6 @@ entry:
ret i8 %0 ret i8 %0
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i8 @llvm.umin.i8(i8, i8) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 { define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry: entry:
@@ -66,31 +60,24 @@ entry:
ret i32 %0 ret i32 %0
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umin.i32(i32, i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 { define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call float @llvm.minimum.f32(float %a, float %b) %0 = fcmp olt float %a, %b
ret float %0 %1 = select i1 %0, float %a, float %b
ret float %1
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.minimum.f32(float, float) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 { define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call double @llvm.minimum.f64(double %a, double %b) %0 = fcmp olt double %a, %b
ret double %0 %1 = select i1 %0, double %a, double %b
ret double %1
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare double @llvm.minimum.f64(double, double) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._string @main.minString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 { define hidden %runtime._string @main.minString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0 %0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
%1 = insertvalue %runtime._string %0, i32 %a.len, 1 %1 = insertvalue %runtime._string %0, i32 %a.len, 1
@@ -104,7 +91,7 @@ entry:
ret %runtime._string %5 ret %runtime._string %5
} }
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1 declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 { define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
@@ -113,9 +100,6 @@ entry:
ret i32 %0 ret i32 %0
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smax.i32(i32, i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 { define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
entry: entry:
@@ -123,21 +107,16 @@ entry:
ret i32 %0 ret i32 %0
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umax.i32(i32, i32) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 { define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call float @llvm.maximum.f32(float %a, float %b) %0 = fcmp ogt float %a, %b
ret float %0 %1 = select i1 %0, float %a, float %b
ret float %1
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.maximum.f32(float, float) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %runtime._string @main.maxString(ptr readonly %a.data, i32 %a.len, ptr readonly %b.data, i32 %b.len, ptr %context) unnamed_addr #2 { define hidden %runtime._string @main.maxString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0 %0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
%1 = insertvalue %runtime._string %0, i32 %a.len, 1 %1 = insertvalue %runtime._string %0, i32 %a.len, 1
@@ -160,7 +139,7 @@ entry:
} }
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #4 declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 { define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 {
@@ -177,9 +156,24 @@ entry:
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1 declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smin.i32(i32, i32) #4
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i8 @llvm.umin.i8(i8, i8) #4
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umin.i32(i32, i32) #4
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.smax.i32(i32, i32) #4
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.umax.i32(i32, i32) #4
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #5 = { nounwind } attributes #5 = { nounwind }
+28 -35
View File
@@ -17,8 +17,8 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
entry: entry:
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #11 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #9
ret void ret void
} }
@@ -28,7 +28,7 @@ declare void @main.regularFunction(i32, ptr) #2
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 { define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
entry: entry:
%unpack.int = ptrtoint ptr %0 to i32 %unpack.int = ptrtoint ptr %0 to i32
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11 call void @main.regularFunction(i32 %unpack.int, ptr undef) #9
ret void ret void
} }
@@ -39,8 +39,8 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
entry: entry:
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #11 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #9
ret void ret void
} }
@@ -61,18 +61,18 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 { define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
entry: entry:
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11 %n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
store i32 3, ptr %n, align 4 store i32 3, ptr %n, align 4
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #11 %0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
store i32 5, ptr %0, align 4 store i32 5, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4 %1 = getelementptr inbounds nuw i8, ptr %0, i32 4
store ptr %n, ptr %1, align 4 store ptr %n, ptr %1, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #11 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
%2 = load i32, ptr %n, align 4 %2 = load i32, ptr %n, align 4
call void @runtime.printlock(ptr undef) #11 call void @runtime.printlock(ptr undef) #9
call void @runtime.printint32(i32 %2, ptr undef) #11 call void @runtime.printint32(i32 %2, ptr undef) #9
call void @runtime.printunlock(ptr undef) #11 call void @runtime.printunlock(ptr undef) #9
ret void ret void
} }
@@ -102,14 +102,14 @@ declare void @runtime.printunlock(ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 { define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11 %0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
store i32 5, ptr %0, align 4 store i32 5, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4 %1 = getelementptr inbounds nuw i8, ptr %0, i32 4
store ptr %fn.context, ptr %1, align 4 store ptr %fn.context, ptr %1, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8 %2 = getelementptr inbounds nuw i8, ptr %0, i32 8
store ptr %fn.funcptr, ptr %2, align 4 store ptr %fn.funcptr, ptr %2, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #11 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
ret void ret void
} }
@@ -121,7 +121,7 @@ entry:
%3 = load ptr, ptr %2, align 4 %3 = load ptr, ptr %2, align 4
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8 %4 = getelementptr inbounds nuw i8, ptr %0, i32 8
%5 = load ptr, ptr %4, align 4 %5 = load ptr, ptr %4, align 4
call void %5(i32 %1, ptr %3) #11 call void %5(i32 %1, ptr %3) #9
ret void ret void
} }
@@ -134,21 +134,16 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 { define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 {
entry: entry:
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len) %copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #9
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
ret void ret void
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #2
declare i32 @llvm.umin.i32(i32, i32) #7
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 { define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
entry: entry:
call void @runtime.chanClose(ptr %ch, ptr undef) #11 call void @runtime.chanClose(ptr %ch, ptr undef) #9
ret void ret void
} }
@@ -157,7 +152,7 @@ declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11 %0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
store ptr %itf.value, ptr %0, align 4 store ptr %itf.value, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4 %1 = getelementptr inbounds nuw i8, ptr %0, i32 4
store ptr @"main$string", ptr %1, align 4 store ptr @"main$string", ptr %1, align 4
@@ -165,15 +160,15 @@ entry:
store i32 4, ptr %2, align 4 store i32 4, ptr %2, align 4
%3 = getelementptr inbounds nuw i8, ptr %0, i32 12 %3 = getelementptr inbounds nuw i8, ptr %0, i32 12
store ptr %itf.typecode, ptr %3, align 4 store ptr %itf.typecode, ptr %3, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #11 %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #9
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
ret void ret void
} }
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #9 declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #7
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #10 { define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #8 {
entry: entry:
%1 = load ptr, ptr %0, align 4 %1 = load ptr, ptr %0, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4 %2 = getelementptr inbounds nuw i8, ptr %0, i32 4
@@ -182,7 +177,7 @@ entry:
%5 = load i32, ptr %4, align 4 %5 = load i32, ptr %4, align 4
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12 %6 = getelementptr inbounds nuw i8, ptr %0, i32 12
%7 = load ptr, ptr %6, align 4 %7 = load ptr, ptr %6, align 4
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #11 call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #9
ret void ret void
} }
@@ -193,8 +188,6 @@ attributes #3 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb
attributes #4 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" } attributes #4 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" } attributes #5 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" } attributes #6 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper" }
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #7 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } attributes #8 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #9 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" } attributes #9 = { nounwind }
attributes #10 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #11 = { nounwind }
+34 -41
View File
@@ -19,7 +19,7 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 { define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
entry: entry:
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #9
ret void ret void
} }
@@ -31,8 +31,8 @@ declare void @runtime.deadlock(ptr) #1
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 { define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
entry: entry:
%unpack.int = ptrtoint ptr %0 to i32 %unpack.int = ptrtoint ptr %0 to i32
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11 call void @main.regularFunction(i32 %unpack.int, ptr undef) #9
call void @runtime.deadlock(ptr undef) #11 call void @runtime.deadlock(ptr undef) #9
unreachable unreachable
} }
@@ -41,7 +41,7 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 { define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
entry: entry:
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #9
ret void ret void
} }
@@ -56,7 +56,7 @@ define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unn
entry: entry:
%unpack.int = ptrtoint ptr %0 to i32 %unpack.int = ptrtoint ptr %0 to i32
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef) call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
call void @runtime.deadlock(ptr undef) #11 call void @runtime.deadlock(ptr undef) #9
unreachable unreachable
} }
@@ -64,21 +64,21 @@ entry:
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 { define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11 %n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #11 call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #9
store i32 3, ptr %n, align 4 store i32 3, ptr %n, align 4
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #11 call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #11 call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #9
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #11 %0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11 call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
store i32 5, ptr %0, align 4 store i32 5, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4 %1 = getelementptr inbounds nuw i8, ptr %0, i32 4
store ptr %n, ptr %1, align 4 store ptr %n, ptr %1, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
%2 = load i32, ptr %n, align 4 %2 = load i32, ptr %n, align 4
call void @runtime.printlock(ptr undef) #11 call void @runtime.printlock(ptr undef) #9
call void @runtime.printint32(i32 %2, ptr undef) #11 call void @runtime.printint32(i32 %2, ptr undef) #9
call void @runtime.printunlock(ptr undef) #11 call void @runtime.printunlock(ptr undef) #9
ret void ret void
} }
@@ -96,7 +96,7 @@ entry:
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4 %2 = getelementptr inbounds nuw i8, ptr %0, i32 4
%3 = load ptr, ptr %2, align 4 %3 = load ptr, ptr %2, align 4
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3) call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
call void @runtime.deadlock(ptr undef) #11 call void @runtime.deadlock(ptr undef) #9
unreachable unreachable
} }
@@ -110,14 +110,14 @@ declare void @runtime.printunlock(ptr) #1
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 { define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11 %0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11 call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
store i32 5, ptr %0, align 4 store i32 5, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4 %1 = getelementptr inbounds nuw i8, ptr %0, i32 4
store ptr %fn.context, ptr %1, align 4 store ptr %fn.context, ptr %1, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8 %2 = getelementptr inbounds nuw i8, ptr %0, i32 8
store ptr %fn.funcptr, ptr %2, align 4 store ptr %fn.funcptr, ptr %2, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 65536, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 65536, ptr undef) #9
ret void ret void
} }
@@ -129,8 +129,8 @@ entry:
%3 = load ptr, ptr %2, align 4 %3 = load ptr, ptr %2, align 4
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8 %4 = getelementptr inbounds nuw i8, ptr %0, i32 8
%5 = load ptr, ptr %4, align 4 %5 = load ptr, ptr %4, align 4
call void %5(i32 %1, ptr %3) #11 call void %5(i32 %1, ptr %3) #9
call void @runtime.deadlock(ptr undef) #11 call void @runtime.deadlock(ptr undef) #9
unreachable unreachable
} }
@@ -143,21 +143,16 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 { define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
entry: entry:
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len) %copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #9
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
ret void ret void
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #1
declare i32 @llvm.umin.i32(i32, i32) #7
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #8
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 { define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
entry: entry:
call void @runtime.chanClose(ptr %ch, ptr undef) #11 call void @runtime.chanClose(ptr %ch, ptr undef) #9
ret void ret void
} }
@@ -167,8 +162,8 @@ declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #1
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 { define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11 %0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #11 call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9
store ptr %itf.value, ptr %0, align 4 store ptr %itf.value, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4 %1 = getelementptr inbounds nuw i8, ptr %0, i32 4
store ptr @"main$string", ptr %1, align 4 store ptr @"main$string", ptr %1, align 4
@@ -176,14 +171,14 @@ entry:
store i32 4, ptr %2, align 4 store i32 4, ptr %2, align 4
%3 = getelementptr inbounds nuw i8, ptr %0, i32 12 %3 = getelementptr inbounds nuw i8, ptr %0, i32 12
store ptr %itf.typecode, ptr %3, align 4 store ptr %itf.typecode, ptr %3, align 4
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #11 call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
ret void ret void
} }
declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #9 declare void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr, ptr, i32, ptr, ptr) #7
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #10 { define linkonce_odr void @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper"(ptr %0) unnamed_addr #8 {
entry: entry:
%1 = load ptr, ptr %0, align 4 %1 = load ptr, ptr %0, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4 %2 = getelementptr inbounds nuw i8, ptr %0, i32 4
@@ -192,8 +187,8 @@ entry:
%5 = load i32, ptr %4, align 4 %5 = load i32, ptr %4, align 4
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12 %6 = getelementptr inbounds nuw i8, ptr %0, i32 12
%7 = load ptr, ptr %6, align 4 %7 = load ptr, ptr %6, align 4
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #11 call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #9
call void @runtime.deadlock(ptr undef) #11 call void @runtime.deadlock(ptr undef) #9
unreachable unreachable
} }
@@ -204,8 +199,6 @@ attributes #3 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+cal
attributes #4 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" } attributes #4 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" }
attributes #5 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" } attributes #5 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" }
attributes #6 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper" } attributes #6 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper" }
attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #7 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" }
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } attributes #8 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #9 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" } attributes #9 = { nounwind }
attributes #10 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #11 = { nounwind }
+36 -44
View File
@@ -38,7 +38,7 @@ lookup.next: ; preds = %entry
ret i32 %1 ret i32 %1
lookup.throw: ; preds = %entry lookup.throw: ; preds = %entry
call void @runtime.lookupPanic(ptr undef) #5 call void @runtime.lookupPanic(ptr undef) #3
unreachable unreachable
} }
@@ -48,55 +48,49 @@ declare void @runtime.lookupPanic(ptr) #1
define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 { define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 %varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) #3
store i32 1, ptr %varargs, align 4 store i32 1, ptr %varargs, align 4
%0 = getelementptr inbounds nuw i8, ptr %varargs, i32 4 %0 = getelementptr inbounds nuw i8, ptr %varargs, i32 4
store i32 2, ptr %0, align 4 store i32 2, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %varargs, i32 8 %1 = getelementptr inbounds nuw i8, ptr %varargs, i32 8
store i32 3, ptr %1, align 4 store i32 3, ptr %1, align 4
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 %append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, ptr undef) #3
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0 %append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0
%append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1 %append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2 %append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2
%2 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0 %2 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0
%3 = insertvalue { ptr, i32, i32 } %2, i32 %append.newLen, 1 %3 = insertvalue { ptr, i32, i32 } %2, i32 %append.newLen, 1
%4 = insertvalue { ptr, i32, i32 } %3, i32 %append.newCap, 2 %4 = insertvalue { ptr, i32, i32 } %3, i32 %append.newCap, 2
call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #3
ret { ptr, i32, i32 } %4 ret { ptr, i32, i32 } %4
} }
declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr, ptr) #1 declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #2 { define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr %added.data, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 %append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr %added.data, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, ptr undef) #3
%append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0 %append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0
%append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1 %append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2 %append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2
%0 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0 %0 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %append.newLen, 1 %1 = insertvalue { ptr, i32, i32 } %0, i32 %append.newLen, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %append.newCap, 2 %2 = insertvalue { ptr, i32, i32 } %1, i32 %append.newCap, 2
call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #3
ret { ptr, i32, i32 } %2 ret { ptr, i32, i32 } %2
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.sliceCopy(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 { define hidden i32 @main.sliceCopy(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 {
entry: entry:
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len) %copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 4, ptr undef) #3
%copy.size = shl nuw i32 %copy.n, 2
call void @llvm.memmove.p0.p0.i32(ptr align 4 %dst.data, ptr align 4 %src.data, i32 %copy.size, i1 false)
ret i32 %copy.n ret i32 %copy.n
} }
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #1
declare i32 @llvm.umin.i32(i32, i32) #3
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #4
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #2 { define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #2 {
@@ -106,15 +100,15 @@ entry:
br i1 %slice.maxcap, label %slice.throw, label %slice.next br i1 %slice.maxcap, label %slice.throw, label %slice.next
slice.next: ; preds = %entry slice.next: ; preds = %entry
%makeslice.buf = call align 1 ptr @runtime.alloc(i32 %len, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 %makeslice.buf = call align 1 ptr @runtime.alloc(i32 %len, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 %2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #3
ret { ptr, i32, i32 } %2 ret { ptr, i32, i32 } %2
slice.throw: ; preds = %entry slice.throw: ; preds = %entry
call void @runtime.slicePanic(ptr undef) #5 call void @runtime.slicePanic(ptr undef) #3
unreachable unreachable
} }
@@ -129,15 +123,15 @@ entry:
slice.next: ; preds = %entry slice.next: ; preds = %entry
%makeslice.cap = shl nuw i32 %len, 1 %makeslice.cap = shl nuw i32 %len, 1
%makeslice.buf = call align 2 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 %makeslice.buf = call align 2 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 %2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #3
ret { ptr, i32, i32 } %2 ret { ptr, i32, i32 } %2
slice.throw: ; preds = %entry slice.throw: ; preds = %entry
call void @runtime.slicePanic(ptr undef) #5 call void @runtime.slicePanic(ptr undef) #3
unreachable unreachable
} }
@@ -150,15 +144,15 @@ entry:
slice.next: ; preds = %entry slice.next: ; preds = %entry
%makeslice.cap = mul i32 %len, 3 %makeslice.cap = mul i32 %len, 3
%makeslice.buf = call align 1 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 %makeslice.buf = call align 1 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 %2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #3
ret { ptr, i32, i32 } %2 ret { ptr, i32, i32 } %2
slice.throw: ; preds = %entry slice.throw: ; preds = %entry
call void @runtime.slicePanic(ptr undef) #5 call void @runtime.slicePanic(ptr undef) #3
unreachable unreachable
} }
@@ -171,15 +165,15 @@ entry:
slice.next: ; preds = %entry slice.next: ; preds = %entry
%makeslice.cap = shl nuw i32 %len, 2 %makeslice.cap = shl nuw i32 %len, 2
%makeslice.buf = call align 4 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 %makeslice.buf = call align 4 ptr @runtime.alloc(i32 %makeslice.cap, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
%0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
%2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 %2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2
call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #3
ret { ptr, i32, i32 } %2 ret { ptr, i32, i32 } %2
slice.throw: ; preds = %entry slice.throw: ; preds = %entry
call void @runtime.slicePanic(ptr undef) #5 call void @runtime.slicePanic(ptr undef) #3
unreachable unreachable
} }
@@ -188,7 +182,7 @@ define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = getelementptr i8, ptr %p, i32 %len %0 = getelementptr i8, ptr %p, i32 %len
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #3
ret ptr %0 ret ptr %0
} }
@@ -198,7 +192,7 @@ entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = trunc i64 %len to i32 %0 = trunc i64 %len to i32
%1 = getelementptr i8, ptr %p, i32 %0 %1 = getelementptr i8, ptr %p, i32 %0
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #3
ret ptr %1 ret ptr %1
} }
@@ -212,7 +206,7 @@ slicetoarray.next: ; preds = %entry
ret ptr %s.data ret ptr %s.data
slicetoarray.throw: ; preds = %entry slicetoarray.throw: ; preds = %entry
call void @runtime.sliceToArrayPointerPanic(ptr undef) #5 call void @runtime.sliceToArrayPointerPanic(ptr undef) #3
unreachable unreachable
} }
@@ -222,8 +216,8 @@ declare void @runtime.sliceToArrayPointerPanic(ptr) #1
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #2 { define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%makeslice = call align 4 dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #5 %makeslice = call align 4 dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3
br i1 false, label %slicetoarray.throw, label %slicetoarray.next br i1 false, label %slicetoarray.throw, label %slicetoarray.next
slicetoarray.next: ; preds = %entry slicetoarray.next: ; preds = %entry
@@ -248,11 +242,11 @@ unsafe.Slice.next: ; preds = %entry
%5 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0 %5 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
%6 = insertvalue { ptr, i32, i32 } %5, i32 %len, 1 %6 = insertvalue { ptr, i32, i32 } %5, i32 %len, 1
%7 = insertvalue { ptr, i32, i32 } %6, i32 %len, 2 %7 = insertvalue { ptr, i32, i32 } %6, i32 %len, 2
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
ret { ptr, i32, i32 } %7 ret { ptr, i32, i32 } %7
unsafe.Slice.throw: ; preds = %entry unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(ptr undef) #5 call void @runtime.unsafeSlicePanic(ptr undef) #3
unreachable unreachable
} }
@@ -272,11 +266,11 @@ unsafe.Slice.next: ; preds = %entry
%4 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0 %4 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
%5 = insertvalue { ptr, i32, i32 } %4, i32 %3, 1 %5 = insertvalue { ptr, i32, i32 } %4, i32 %3, 1
%6 = insertvalue { ptr, i32, i32 } %5, i32 %3, 2 %6 = insertvalue { ptr, i32, i32 } %5, i32 %3, 2
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
ret { ptr, i32, i32 } %6 ret { ptr, i32, i32 } %6
unsafe.Slice.throw: ; preds = %entry unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(ptr undef) #5 call void @runtime.unsafeSlicePanic(ptr undef) #3
unreachable unreachable
} }
@@ -296,11 +290,11 @@ unsafe.Slice.next: ; preds = %entry
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0 %6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1 %7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2 %8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
ret { ptr, i32, i32 } %8 ret { ptr, i32, i32 } %8
unsafe.Slice.throw: ; preds = %entry unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(ptr undef) #5 call void @runtime.unsafeSlicePanic(ptr undef) #3
unreachable unreachable
} }
@@ -320,17 +314,15 @@ unsafe.Slice.next: ; preds = %entry
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0 %6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1 %7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
%8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2 %8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2
call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #5 call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3
ret { ptr, i32, i32 } %8 ret { ptr, i32, i32 } %8
unsafe.Slice.throw: ; preds = %entry unsafe.Slice.throw: ; preds = %entry
call void @runtime.unsafeSlicePanic(ptr undef) #5 call void @runtime.unsafeSlicePanic(ptr undef) #3
unreachable unreachable
} }
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #3 = { nounwind }
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #5 = { nounwind }
+8 -8
View File
@@ -31,13 +31,13 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i32 @main.stringLen(ptr readonly %s.data, i32 %s.len, ptr %context) unnamed_addr #2 { define hidden i32 @main.stringLen(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 {
entry: entry:
ret i32 %s.len ret i32 %s.len
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.stringIndex(ptr readonly %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 { define hidden i8 @main.stringIndex(ptr %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 {
entry: entry:
%.not = icmp ult i32 %index, %s.len %.not = icmp ult i32 %index, %s.len
br i1 %.not, label %lookup.next, label %lookup.throw br i1 %.not, label %lookup.next, label %lookup.throw
@@ -55,16 +55,16 @@ lookup.throw: ; preds = %entry
declare void @runtime.lookupPanic(ptr) #1 declare void @runtime.lookupPanic(ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.stringCompareEqual(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 { define hidden i1 @main.stringCompareEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3 %0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
ret i1 %0 ret i1 %0
} }
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #1 declare i1 @runtime.stringEqual(ptr, i32, ptr, i32, ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.stringCompareUnequal(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 { define hidden i1 @main.stringCompareUnequal(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3 %0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
%1 = xor i1 %0, true %1 = xor i1 %0, true
@@ -72,16 +72,16 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i1 @main.stringCompareLarger(ptr readonly %s1.data, i32 %s1.len, ptr readonly %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 { define hidden i1 @main.stringCompareLarger(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3 %0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3
ret i1 %0 ret i1 %0
} }
declare i1 @runtime.stringLess(ptr readonly, i32, ptr readonly, i32, ptr) #1 declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.stringLookup(ptr readonly %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 { define hidden i8 @main.stringLookup(ptr %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 {
entry: entry:
%0 = zext i8 %x to i32 %0 = zext i8 %x to i32
%.not = icmp ugt i32 %s.len, %0 %.not = icmp ugt i32 %s.len, %0
+1 -1
View File
@@ -10,7 +10,7 @@ import (
// Version of TinyGo. // Version of TinyGo.
// Update this value before release of new version of software. // Update this value before release of new version of software.
const version = "0.40.1" const version = "0.40.0-dev"
// Return TinyGo version, either in the form 0.30.0 or as a development version // Return TinyGo version, either in the form 0.30.0 or as a development version
// (like 0.30.0-dev-abcd012). // (like 0.30.0-dev-abcd012).
+11 -2
View File
@@ -2,6 +2,7 @@ package interp
import ( import (
"os" "os"
"strconv"
"strings" "strings"
"testing" "testing"
"time" "time"
@@ -10,17 +11,25 @@ import (
) )
func TestInterp(t *testing.T) { func TestInterp(t *testing.T) {
llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
if err != nil {
// Note: this should never happen and if it does, it will always happen
// for a particular build because llvm.Version is a constant.
panic(err)
}
for _, name := range []string{ for _, name := range []string{
"basic", "basic",
"phi", "phi",
"slice-copy",
"consteval", "consteval",
"intrinsics",
"copy",
"interface", "interface",
"revert", "revert",
"alloc", "alloc",
} { } {
name := name // make local to this closure name := name // make local to this closure
if name == "slice-copy" && llvmVersion < 14 {
continue
}
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
t.Parallel() t.Parallel()
runTest(t, "testdata/"+name) runTest(t, "testdata/"+name)
+50 -21
View File
@@ -312,28 +312,33 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
fmt.Fprintln(os.Stderr, indent+"runtime.alloc:", size, "->", ptr) fmt.Fprintln(os.Stderr, indent+"runtime.alloc:", size, "->", ptr)
} }
locals[inst.localIndex] = ptr locals[inst.localIndex] = ptr
case strings.HasPrefix(callFn.name, "llvm.umin."): case callFn.name == "runtime.sliceCopy":
locals[inst.localIndex] = makeLiteralInt(min(operands[1].Uint(r), operands[2].Uint(r)), inst.llvmInst.Type().IntTypeWidth()) // sliceCopy implements the built-in copy function for slices.
case strings.HasPrefix(callFn.name, "llvm.smin."): // It is implemented here so that it can be used even if the
locals[inst.localIndex] = makeLiteralInt(uint64(min(operands[1].Int(r), operands[2].Int(r))), inst.llvmInst.Type().IntTypeWidth()) // runtime implementation is not available. Doing it this way
case strings.HasPrefix(callFn.name, "llvm.umax."): // may also be faster.
locals[inst.localIndex] = makeLiteralInt(max(operands[1].Uint(r), operands[2].Uint(r)), inst.llvmInst.Type().IntTypeWidth()) // Code:
case strings.HasPrefix(callFn.name, "llvm.smax."): // func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int {
locals[inst.localIndex] = makeLiteralInt(uint64(max(operands[1].Int(r), operands[2].Int(r))), inst.llvmInst.Type().IntTypeWidth()) // n := srcLen
case strings.HasPrefix(callFn.name, "llvm.memcpy.p0") || strings.HasPrefix(callFn.name, "llvm.memmove.p0"): // if n > dstLen {
// Copy a block of memory from one pointer to another. // n = dstLen
if operands[4].Uint(r) != 0 { // }
// This is a volatile copy/move. // memmove(dst, src, n*elemSize)
err := r.runAtRuntime(fn, inst, locals, &mem, indent) // return int(n)
if err != nil { // }
return nil, mem, err dstLen := operands[3].Uint(r)
srcLen := operands[4].Uint(r)
elemSize := operands[5].Uint(r)
n := srcLen
if n > dstLen {
n = dstLen
} }
continue if r.debug {
fmt.Fprintln(os.Stderr, indent+"copy:", operands[1], operands[2], n)
} }
nBytes := operands[3].Uint(r) if n != 0 {
if nBytes != 0 {
// Only try to copy bytes when there are any bytes to copy. // Only try to copy bytes when there are any bytes to copy.
// This is not just an optimization. If one of the pointers // This is not just an optimization. If one of the slices
// (or both) are nil, the asPointer method call will fail // (or both) are nil, the asPointer method call will fail
// even though copying a nil slice is allowed. // even though copying a nil slice is allowed.
dst, err := operands[1].asPointer(r) dst, err := operands[1].asPointer(r)
@@ -358,10 +363,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
} }
continue continue
} }
nBytes := uint32(n * elemSize)
srcObj := mem.get(src.index()) srcObj := mem.get(src.index())
dstObj := mem.getWritable(dst.index()) dstObj := mem.getWritable(dst.index())
if srcObj.buffer == nil || dstObj.buffer == nil { if srcObj.buffer == nil || dstObj.buffer == nil {
// If the buffer is nil, it means the memory is external. // If the buffer is nil, it means the slice is external.
// This can happen for example when copying data out of // This can happen for example when copying data out of
// a //go:embed slice, which is not available at interp // a //go:embed slice, which is not available at interp
// time. // time.
@@ -374,10 +380,33 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
} }
dstBuf := dstObj.buffer.asRawValue(r) dstBuf := dstObj.buffer.asRawValue(r)
srcBuf := srcObj.buffer.asRawValue(r) srcBuf := srcObj.buffer.asRawValue(r)
copy(dstBuf.buf[dst.offset():][:nBytes], srcBuf.buf[src.offset():][:nBytes]) copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
dstObj.buffer = dstBuf dstObj.buffer = dstBuf
mem.put(dst.index(), dstObj) mem.put(dst.index(), dstObj)
} }
locals[inst.localIndex] = makeLiteralInt(n, inst.llvmInst.Type().IntTypeWidth())
case strings.HasPrefix(callFn.name, "llvm.memcpy.p0") || strings.HasPrefix(callFn.name, "llvm.memmove.p0"):
// Copy a block of memory from one pointer to another.
dst, err := operands[1].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
src, err := operands[2].asPointer(r)
if err != nil {
return nil, mem, r.errorAt(inst, err)
}
nBytes := uint32(operands[3].Uint(r))
dstObj := mem.getWritable(dst.index())
dstBuf := dstObj.buffer.asRawValue(r)
if mem.get(src.index()).buffer == nil {
// Looks like the source buffer is not defined.
// This can happen with //extern or //go:embed.
return nil, mem, r.errorAt(inst, errUnsupportedRuntimeInst)
}
srcBuf := mem.get(src.index()).buffer.asRawValue(r)
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
dstObj.buffer = dstBuf
mem.put(dst.index(), dstObj)
case callFn.name == "runtime.typeAssert": case callFn.name == "runtime.typeAssert":
// This function must be implemented manually as it is normally // This function must be implemented manually as it is normally
// implemented by the interface lowering pass. // implemented by the interface lowering pass.
-68
View File
@@ -1,68 +0,0 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
@string = internal unnamed_addr constant [3 x i8] c"foo"
@moveDst = global [3 x i8] zeroinitializer
@copyDst = global [3 x i8] zeroinitializer
@externalSrc = external global [2 x i8]
@moveExternalDst = global [2 x i8] zeroinitializer
@moveEscapedSrc = global [4 x i8] c"abcd"
@moveEscapedDst = global [4 x i8] zeroinitializer
@volatileSrc = global [2 x i8] c"xy"
@volatileDst = global [2 x i8] zeroinitializer
declare void @use(ptr)
define void @runtime.initAll() {
call void @main.init()
ret void
}
define internal void @main.init() {
call void @testMove()
call void @testCopy()
call void @testMoveExternal()
call void @testMoveEscaped()
call void @testVolatileCopy()
ret void
}
; Test a simple memmove between globals.
define internal void @testMove() {
call void @llvm.memmove.p0.p0.i64(ptr @moveDst, ptr @string, i64 3, i1 false)
ret void
}
; Test a simple memcpy between globals.
define internal void @testCopy() {
call void @llvm.memcpy.p0.p0.i64(ptr @copyDst, ptr @string, i64 3, i1 false)
ret void
}
; Test a memmove from an external global.
; This should be run at runtime.
define internal void @testMoveExternal() {
call void @llvm.memmove.p0.p0.i64(ptr @moveExternalDst, ptr @externalSrc, i64 2, i1 false)
ret void
}
; Test a memmove from an escaped (and potentially modified) source buffer.
define internal void @testMoveEscaped() {
call void @use(ptr @moveEscapedSrc)
call void @llvm.memmove.p0.p0.i64(ptr @moveEscapedDst, ptr @moveEscapedSrc, i64 4, i1 false)
ret void
}
; Test a volatile memcpy.
; This should always be run at runtime.
define internal void @testVolatileCopy() {
call void @llvm.memcpy.p0.p0.i64(ptr @volatileDst, ptr @volatileSrc, i64 2, i1 true)
ret void
}
declare void @llvm.memmove.p0.p0.i64(ptr, ptr, i64, i1)
declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
-27
View File
@@ -1,27 +0,0 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
@moveDst = local_unnamed_addr global [3 x i8] c"foo"
@copyDst = local_unnamed_addr global [3 x i8] c"foo"
@externalSrc = external local_unnamed_addr global [2 x i8]
@moveExternalDst = local_unnamed_addr global [2 x i8] zeroinitializer
@moveEscapedSrc = global [4 x i8] c"abcd"
@moveEscapedDst = local_unnamed_addr global [4 x i8] zeroinitializer
@volatileSrc = global [2 x i8] c"xy"
@volatileDst = global [2 x i8] zeroinitializer
declare void @use(ptr) local_unnamed_addr
define void @runtime.initAll() local_unnamed_addr {
call void @llvm.memmove.p0.p0.i64(ptr @moveExternalDst, ptr @externalSrc, i64 2, i1 false)
call void @use(ptr @moveEscapedSrc)
call void @llvm.memmove.p0.p0.i64(ptr @moveEscapedDst, ptr @moveEscapedSrc, i64 4, i1 false)
call void @llvm.memcpy.p0.p0.i64(ptr @volatileDst, ptr @volatileSrc, i64 2, i1 true)
ret void
}
declare void @llvm.memmove.p0.p0.i64(ptr nocapture writeonly, ptr nocapture readonly, i64, i1 immarg) #0
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
-52
View File
@@ -1,52 +0,0 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
@uminResult = global i32 0
@sminResult = global i32 0
@umaxResult = global i32 0
@smaxResult = global i32 0
define void @runtime.initAll() {
call void @main.init()
ret void
}
define internal void @main.init() {
call void @testUMin()
call void @testSMin()
call void @testUMax()
call void @testSMax()
ret void
}
define internal void @testUMin() {
%umin = call i32 @llvm.umin.i32(i32 12, i32 -1)
store i32 %umin, ptr @uminResult
ret void
}
declare i32 @llvm.umin.i32(i32, i32)
define internal void @testSMin() {
%smin = call i32 @llvm.smin.i32(i32 12, i32 -1)
store i32 %smin, ptr @sminResult
ret void
}
declare i32 @llvm.smin.i32(i32, i32)
define internal void @testUMax() {
%umax = call i32 @llvm.umax.i32(i32 12, i32 -1)
store i32 %umax, ptr @umaxResult
ret void
}
declare i32 @llvm.umax.i32(i32, i32)
define internal void @testSMax() {
%smax = call i32 @llvm.smax.i32(i32 12, i32 -1)
store i32 %smax, ptr @smaxResult
ret void
}
declare i32 @llvm.smax.i32(i32, i32)
-11
View File
@@ -1,11 +0,0 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
@uminResult = local_unnamed_addr global i32 12
@sminResult = local_unnamed_addr global i32 -1
@umaxResult = local_unnamed_addr global i32 -1
@smaxResult = local_unnamed_addr global i32 12
define void @runtime.initAll() local_unnamed_addr {
ret void
}
+124
View File
@@ -0,0 +1,124 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
@main.uint8SliceSrc.buf = internal global [2 x i8] c"\03d"
@main.uint8SliceSrc = internal unnamed_addr global { ptr, i64, i64 } { ptr @main.uint8SliceSrc.buf, i64 2, i64 2 }
@main.uint8SliceDst = internal unnamed_addr global { ptr, i64, i64 } zeroinitializer
@main.int16SliceSrc.buf = internal global [3 x i16] [i16 5, i16 123, i16 1024]
@main.int16SliceSrc = internal unnamed_addr global { ptr, i64, i64 } { ptr @main.int16SliceSrc.buf, i64 3, i64 3 }
@main.int16SliceDst = internal unnamed_addr global { ptr, i64, i64 } zeroinitializer
@main.sliceSrcUntaint.buf = internal global [2 x i8] c"ab"
@main.sliceDstUntaint.buf = internal global [2 x i8] zeroinitializer
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
@main.sliceSrcExternal1.buf = external global [2 x i8]
@main.sliceDstExternal1.buf = internal global [2 x i8] zeroinitializer
@main.sliceSrcExternal2.buf = internal global [2 x i8] zeroinitializer
@main.sliceDstExternal2.buf = external global [2 x i8]
declare i64 @runtime.sliceCopy(ptr %dst, ptr %src, i64 %dstLen, i64 %srcLen, i64 %elemSize) unnamed_addr
declare ptr @runtime.alloc(i64, ptr) unnamed_addr
declare void @runtime.printuint8(i8)
declare void @runtime.printint16(i16)
declare void @use(ptr)
define void @runtime.initAll() unnamed_addr {
entry:
call void @main.init()
ret void
}
define void @main() unnamed_addr {
entry:
; print(uintSliceSrc[0])
%uint8SliceSrc.buf = load ptr, ptr @main.uint8SliceSrc
%uint8SliceSrc.val = load i8, ptr %uint8SliceSrc.buf
call void @runtime.printuint8(i8 %uint8SliceSrc.val)
; print(uintSliceDst[0])
%uint8SliceDst.buf = load ptr, ptr @main.uint8SliceDst
%uint8SliceDst.val = load i8, ptr %uint8SliceDst.buf
call void @runtime.printuint8(i8 %uint8SliceDst.val)
; print(int16SliceSrc[0])
%int16SliceSrc.buf = load ptr, ptr @main.int16SliceSrc
%int16SliceSrc.val = load i16, ptr %int16SliceSrc.buf
call void @runtime.printint16(i16 %int16SliceSrc.val)
; print(int16SliceDst[0])
%int16SliceDst.buf = load ptr, ptr @main.int16SliceDst
%int16SliceDst.val = load i16, ptr %int16SliceDst.buf
call void @runtime.printint16(i16 %int16SliceDst.val)
; print(sliceDstUntaint[0])
%sliceDstUntaint.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstUntaint.buf, i32 0)
call void @runtime.printuint8(i8 %sliceDstUntaint.val)
; print(sliceDstTaint[0])
%sliceDstTaint.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstTaint.buf, i32 0)
call void @runtime.printuint8(i8 %sliceDstTaint.val)
; print(sliceDstExternal1[0])
%sliceDstExternal1.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstExternal1.buf, i32 0)
call void @runtime.printuint8(i8 %sliceDstExternal1.val)
; print(sliceDstExternal2[0])
%sliceDstExternal2.val = load i8, ptr getelementptr inbounds (i8, ptr @main.sliceDstExternal2.buf, i32 0)
call void @runtime.printuint8(i8 %sliceDstExternal2.val)
ret void
}
define internal void @main.init() unnamed_addr {
entry:
; equivalent of:
; uint8SliceDst = make([]uint8, len(uint8SliceSrc))
%uint8SliceSrc = load { ptr, i64, i64 }, ptr @main.uint8SliceSrc
%uint8SliceSrc.len = extractvalue { ptr, i64, i64 } %uint8SliceSrc, 1
%uint8SliceDst.buf = call ptr @runtime.alloc(i64 %uint8SliceSrc.len, ptr null)
%0 = insertvalue { ptr, i64, i64 } undef, ptr %uint8SliceDst.buf, 0
%1 = insertvalue { ptr, i64, i64 } %0, i64 %uint8SliceSrc.len, 1
%2 = insertvalue { ptr, i64, i64 } %1, i64 %uint8SliceSrc.len, 2
store { ptr, i64, i64 } %2, ptr @main.uint8SliceDst
; equivalent of:
; copy(uint8SliceDst, uint8SliceSrc)
%uint8SliceSrc.buf = extractvalue { ptr, i64, i64 } %uint8SliceSrc, 0
%copy.n = call i64 @runtime.sliceCopy(ptr %uint8SliceDst.buf, ptr %uint8SliceSrc.buf, i64 %uint8SliceSrc.len, i64 %uint8SliceSrc.len, i64 1)
; equivalent of:
; int16SliceDst = make([]int16, len(int16SliceSrc))
%int16SliceSrc = load { ptr, i64, i64 }, ptr @main.int16SliceSrc
%int16SliceSrc.len = extractvalue { ptr, i64, i64 } %int16SliceSrc, 1
%int16SliceSrc.len.bytes = mul i64 %int16SliceSrc.len, 2
%int16SliceDst.buf = call ptr @runtime.alloc(i64 %int16SliceSrc.len.bytes, ptr null)
%3 = insertvalue { ptr, i64, i64 } undef, ptr %int16SliceDst.buf, 0
%4 = insertvalue { ptr, i64, i64 } %3, i64 %int16SliceSrc.len, 1
%5 = insertvalue { ptr, i64, i64 } %4, i64 %int16SliceSrc.len, 2
store { ptr, i64, i64 } %5, ptr @main.int16SliceDst
; equivalent of:
; copy(int16SliceDst, int16SliceSrc)
%int16SliceSrc.buf = extractvalue { ptr, i64, i64 } %int16SliceSrc, 0
%copy.n2 = call i64 @runtime.sliceCopy(ptr %int16SliceDst.buf, ptr %int16SliceSrc.buf, i64 %int16SliceSrc.len, i64 %int16SliceSrc.len, i64 2)
; Copy slice that has a known value.
%copy.n3 = call i64 @runtime.sliceCopy(ptr @main.sliceDstUntaint.buf, ptr @main.sliceSrcUntaint.buf, i64 2, i64 2, i64 1)
; Copy slice that might have been modified by the external @use call.
; This is a fix for https://github.com/tinygo-org/tinygo/issues/3890.
call void @use(ptr @main.sliceSrcTaint.buf)
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
; Test that copying from or into external buffers works correctly.
; These copy operations must be done at runtime.
; https://github.com/tinygo-org/tinygo/issues/4895
%copy.n5 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal1.buf, ptr @main.sliceSrcExternal1.buf, i64 2, i64 2, i64 1)
%copy.n6 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal2.buf, ptr @main.sliceSrcExternal2.buf, i64 2, i64 2, i64 1)
ret void
}
+42
View File
@@ -0,0 +1,42 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"
@main.sliceSrcTaint.buf = internal global [2 x i8] c"cd"
@main.sliceDstTaint.buf = internal global [2 x i8] zeroinitializer
@main.sliceSrcExternal1.buf = external global [2 x i8]
@main.sliceDstExternal1.buf = internal global [2 x i8] zeroinitializer
@main.sliceSrcExternal2.buf = internal global [2 x i8] zeroinitializer
@main.sliceDstExternal2.buf = external global [2 x i8]
declare i64 @runtime.sliceCopy(ptr, ptr, i64, i64, i64) unnamed_addr
declare void @runtime.printuint8(i8) local_unnamed_addr
declare void @runtime.printint16(i16) local_unnamed_addr
declare void @use(ptr) local_unnamed_addr
define void @runtime.initAll() unnamed_addr {
entry:
call void @use(ptr @main.sliceSrcTaint.buf)
%copy.n4 = call i64 @runtime.sliceCopy(ptr @main.sliceDstTaint.buf, ptr @main.sliceSrcTaint.buf, i64 2, i64 2, i64 1)
%copy.n5 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal1.buf, ptr @main.sliceSrcExternal1.buf, i64 2, i64 2, i64 1)
%copy.n6 = call i64 @runtime.sliceCopy(ptr @main.sliceDstExternal2.buf, ptr @main.sliceSrcExternal2.buf, i64 2, i64 2, i64 1)
ret void
}
define void @main() unnamed_addr {
entry:
call void @runtime.printuint8(i8 3)
call void @runtime.printuint8(i8 3)
call void @runtime.printint16(i16 5)
call void @runtime.printint16(i16 5)
call void @runtime.printuint8(i8 97)
%sliceDstTaint.val = load i8, ptr @main.sliceDstTaint.buf, align 1
call void @runtime.printuint8(i8 %sliceDstTaint.val)
%sliceDstExternal1.val = load i8, ptr @main.sliceDstExternal1.buf, align 1
call void @runtime.printuint8(i8 %sliceDstExternal1.val)
%sliceDstExternal2.val = load i8, ptr @main.sliceDstExternal2.buf, align 1
call void @runtime.printuint8(i8 %sliceDstExternal2.val)
ret void
}
-1
View File
@@ -256,7 +256,6 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
"reflect/": false, "reflect/": false,
"runtime/": false, "runtime/": false,
"sync/": true, "sync/": true,
"testing/": true,
"tinygo/": false, "tinygo/": false,
"unique/": false, "unique/": false,
} }
-12
View File
@@ -1635,7 +1635,6 @@ func main() {
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output") cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
monitor := flag.Bool("monitor", false, "enable serial monitor") monitor := flag.Bool("monitor", false, "enable serial monitor")
baudrate := flag.Int("baudrate", 115200, "baudrate of serial monitor") baudrate := flag.Int("baudrate", 115200, "baudrate of serial monitor")
gocompatibility := flag.Bool("go-compatibility", true, "enable to check for Go versions compatibility, you can also configure this by setting the TINYGO_GOCOMPATIBILITY environment variable")
// Internal flags, that are only intended for TinyGo development. // Internal flags, that are only intended for TinyGo development.
printIR := flag.Bool("internal-printir", false, "print LLVM IR") printIR := flag.Bool("internal-printir", false, "print LLVM IR")
@@ -1713,16 +1712,6 @@ func main() {
ocdCommands = strings.Split(*ocdCommandsString, ",") ocdCommands = strings.Split(*ocdCommandsString, ",")
} }
val, ok := os.LookupEnv("TINYGO_GOCOMPATIBILITY")
if ok {
b, err := strconv.ParseBool(val)
if err != nil {
fmt.Fprintf(os.Stderr, "could not parse TINYGO_GOCOMPATIBILITY value %q: %v\n", val, err)
os.Exit(1)
}
*gocompatibility = b
}
options := &compileopts.Options{ options := &compileopts.Options{
GOOS: goenv.Get("GOOS"), GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"), GOARCH: goenv.Get("GOARCH"),
@@ -1759,7 +1748,6 @@ func main() {
Timeout: *timeout, Timeout: *timeout,
WITPackage: witPackage, WITPackage: witPackage,
WITWorld: witWorld, WITWorld: witWorld,
GoCompatibility: *gocompatibility,
} }
if *printCommands { if *printCommands {
options.PrintCommands = printCommand options.PrintCommands = printCommand
-1
View File
@@ -222,7 +222,6 @@ func TestBuild(t *testing.T) {
// to be sure. // to be sure.
t.Parallel() t.Parallel()
options := optionsFromOSARCH("linux/mipsle/softfloat", sema) options := optionsFromOSARCH("linux/mipsle/softfloat", sema)
emuCheck(t, options)
runTest("cgo/", options, t, nil, nil) runTest("cgo/", options, t, nil, nil)
}) })
} else if runtime.GOOS == "windows" { } else if runtime.GOOS == "windows" {
+4 -6
View File
@@ -13,14 +13,12 @@ const (
// 64-bit int => bits = 6 // 64-bit int => bits = 6
sizeBits = 4 + unsafe.Sizeof(uintptr(0))/4 sizeBits = 4 + unsafe.Sizeof(uintptr(0))/4
ptrAlign = unsafe.Alignof(uintptr(0))
sizeShift = sizeBits + 1 sizeShift = sizeBits + 1
NoPtrs = Layout((0 << sizeShift) | (1 << 1) | 1) NoPtrs = Layout(uintptr(0b0<<sizeShift) | uintptr(0b1<<1) | uintptr(1))
Pointer = Layout((1 << sizeShift) | ((unsafe.Sizeof(unsafe.Pointer(nil)) / ptrAlign) << 1) | 1) Pointer = Layout(uintptr(0b1<<sizeShift) | uintptr(0b1<<1) | uintptr(1))
String = Layout((1 << sizeShift) | ((unsafe.Sizeof("") / ptrAlign) << 1) | 1) String = Layout(uintptr(0b01<<sizeShift) | uintptr(0b10<<1) | uintptr(1))
Slice = Layout((1 << sizeShift) | ((unsafe.Sizeof([]byte{}) / ptrAlign) << 1) | 1) Slice = Layout(uintptr(0b001<<sizeShift) | uintptr(0b11<<1) | uintptr(1))
) )
func (l Layout) AsPtr() unsafe.Pointer { return unsafe.Pointer(l) } func (l Layout) AsPtr() unsafe.Pointer { return unsafe.Pointer(l) }
+9 -75
View File
@@ -86,64 +86,6 @@ func (v Value) Interface() interface{} {
return valueInterfaceUnsafe(v) return valueInterfaceUnsafe(v)
} }
func TypeAssert[T any](v Value) (T, bool) {
if v.typecode == nil {
panic("reflect.TypeAssert: zero Value")
}
if !v.isExported() {
// Do not allow access to unexported values via TypeAssert,
// because they might be pointers that should not be
// writable or methods or function that should not be callable.
panic("reflect.TypeAssert: cannot return value obtained from unexported field or method")
}
typ := TypeFor[T]()
// If v is an interface, return the element inside the interface.
//
// T is a concrete type and v is an interface. For example:
//
// var v any = int(1)
// val := ValueOf(&v).Elem()
// TypeAssert[int](val) == val.Interface().(int)
//
// T is a interface and v is a non-nil interface value. For example:
//
// var v any = &someError{}
// val := ValueOf(&v).Elem()
// TypeAssert[error](val) == val.Interface().(error)
//
// T is a interface and v is a nil interface value. For example:
//
// var v error = nil
// val := ValueOf(&v).Elem()
// TypeAssert[error](val) == val.Interface().(error)
if v.Kind() == Interface {
val, ok := valueInterfaceUnsafe(v).(T)
return val, ok
}
// If T is an interface and v is a concrete type. For example:
//
// TypeAssert[any](ValueOf(1)) == ValueOf(1).Interface().(any)
// TypeAssert[error](ValueOf(&someError{})) == ValueOf(&someError{}).Interface().(error)
if typ.Kind() == Interface {
val, ok := valueInterfaceUnsafe(v).(T)
return val, ok
}
// Both v and T must be concrete types.
// The only way for an type-assertion to match is if the types are equal.
if typ != v.typecode {
var zero T
return zero, false
}
if !v.isIndirect() {
return *(*T)(unsafe.Pointer(&v.value)), true
}
return *(*T)(v.value), true
}
// valueInterfaceUnsafe is used by the runtime to hash map keys. It should not // valueInterfaceUnsafe is used by the runtime to hash map keys. It should not
// be subject to the isExported check. // be subject to the isExported check.
func valueInterfaceUnsafe(v Value) interface{} { func valueInterfaceUnsafe(v Value) interface{} {
@@ -1796,9 +1738,6 @@ func (e *ValueError) Error() string {
//go:linkname memcpy runtime.memcpy //go:linkname memcpy runtime.memcpy
func memcpy(dst, src unsafe.Pointer, size uintptr) func memcpy(dst, src unsafe.Pointer, size uintptr)
//go:linkname memmove runtime.memmove
func memmove(dst, src unsafe.Pointer, size uintptr)
//go:linkname memzero runtime.memzero //go:linkname memzero runtime.memzero
func memzero(ptr unsafe.Pointer, size uintptr) func memzero(ptr unsafe.Pointer, size uintptr)
@@ -1806,7 +1745,10 @@ func memzero(ptr unsafe.Pointer, size uintptr)
func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer
//go:linkname sliceAppend runtime.sliceAppend //go:linkname sliceAppend runtime.sliceAppend
func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr, layout unsafe.Pointer) (unsafe.Pointer, uintptr, uintptr) func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr)
//go:linkname sliceCopy runtime.sliceCopy
func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int
// Copy copies the contents of src into dst until either // Copy copies the contents of src into dst until either
// dst has been filled or src has been exhausted. // dst has been filled or src has been exhausted.
@@ -1837,10 +1779,7 @@ func Copy(dst, src Value) int {
dst.checkRO() dst.checkRO()
} }
minLen := min(dstlen, srclen) return sliceCopy(dstbuf, srcbuf, dstlen, srclen, dst.typecode.elem().Size())
elemSize := dst.typecode.elem().Size()
memmove(dstbuf, srcbuf, minLen*elemSize)
return int(minLen)
} }
func buflen(v Value) (unsafe.Pointer, uintptr) { func buflen(v Value) (unsafe.Pointer, uintptr) {
@@ -1871,7 +1810,7 @@ func buflen(v Value) (unsafe.Pointer, uintptr) {
} }
//go:linkname sliceGrow runtime.sliceGrow //go:linkname sliceGrow runtime.sliceGrow
func sliceGrow(buf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr, layout unsafe.Pointer) (unsafe.Pointer, uintptr, uintptr) func sliceGrow(buf unsafe.Pointer, oldLen, oldCap, newCap, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr)
// extend slice to hold n new elements // extend slice to hold n new elements
func extendSlice(v Value, n int) sliceHeader { func extendSlice(v Value, n int) sliceHeader {
@@ -1884,10 +1823,7 @@ func extendSlice(v Value, n int) sliceHeader {
old = *(*sliceHeader)(v.value) old = *(*sliceHeader)(v.value)
} }
elem := v.typecode.elem() nbuf, nlen, ncap := sliceGrow(old.data, old.len, old.cap, old.len+uintptr(n), v.typecode.elem().Size())
elemSize := elem.Size()
elemLayout := elem.gcLayout()
nbuf, nlen, ncap := sliceGrow(old.data, old.len, old.cap, old.len+uintptr(n), elemSize, elemLayout)
return sliceHeader{ return sliceHeader{
data: nbuf, data: nbuf,
@@ -1926,10 +1862,8 @@ func AppendSlice(s, t Value) Value {
} }
sSlice := (*sliceHeader)(s.value) sSlice := (*sliceHeader)(s.value)
tSlice := (*sliceHeader)(t.value) tSlice := (*sliceHeader)(t.value)
elem := s.typecode.elem() elemSize := s.typecode.elem().Size()
elemSize := elem.Size() ptr, len, cap := sliceAppend(sSlice.data, tSlice.data, sSlice.len, sSlice.cap, tSlice.len, elemSize)
elemLayout := elem.gcLayout()
ptr, len, cap := sliceAppend(sSlice.data, tSlice.data, sSlice.len, sSlice.cap, tSlice.len, elemSize, elemLayout)
result := &sliceHeader{ result := &sliceHeader{
data: ptr, data: ptr,
len: len, len: len,
-7
View File
@@ -108,10 +108,6 @@ func (*stackState) unwind()
func (t *Task) Resume() { func (t *Task) Resume() {
// The current task must be saved and restored because this can nest on WASM with JS. // The current task must be saved and restored because this can nest on WASM with JS.
prevTask := currentTask prevTask := currentTask
if prevTask == nil {
// Save the system stack pointer.
saveStackPointer()
}
t.gcData.swap() t.gcData.swap()
currentTask = t currentTask = t
if !t.state.launched { if !t.state.launched {
@@ -127,9 +123,6 @@ func (t *Task) Resume() {
} }
} }
//go:linkname saveStackPointer runtime.saveStackPointer
func saveStackPointer()
//export tinygo_rewind //export tinygo_rewind
func (*state) rewind() func (*state) rewind()
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build scheduler.tasks && (esp32 || esp32s3) //go:build scheduler.tasks && esp32
package task package task
-6
View File
@@ -120,21 +120,15 @@ int tinygo_task_start(uintptr_t fn, void *args, void *task, pthread_t *thread, u
#endif #endif
pthread_attr_t attrs; pthread_attr_t attrs;
pthread_attr_init(&attrs); pthread_attr_init(&attrs);
pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&attrs, stackSize); pthread_attr_setstacksize(&attrs, stackSize);
int result = pthread_create(thread, &attrs, &start_wrapper, &state); int result = pthread_create(thread, &attrs, &start_wrapper, &state);
pthread_attr_destroy(&attrs); pthread_attr_destroy(&attrs);
if (result != 0) {
return result;
}
// Wait until the thread has been created and read all state_pass variables. // Wait until the thread has been created and read all state_pass variables.
#if __APPLE__ #if __APPLE__
dispatch_semaphore_wait(state.startlock, DISPATCH_TIME_FOREVER); dispatch_semaphore_wait(state.startlock, DISPATCH_TIME_FOREVER);
dispatch_release(state.startlock);
#else #else
sem_wait(&state.startlock); sem_wait(&state.startlock);
sem_destroy(&state.startlock);
#endif #endif
return result; return result;
+59 -95
View File
@@ -23,15 +23,16 @@ type state struct {
// is needed to be able to scan the stack. // is needed to be able to scan the stack.
stackTop uintptr stackTop uintptr
// Lowest address of the stack.
// This is populated when the thread is stopped by the GC.
stackBottom uintptr
// Next task in the activeTasks queue. // Next task in the activeTasks queue.
QueueNext *Task QueueNext *Task
// Semaphore to pause/resume the thread atomically. // Semaphore to pause/resume the thread atomically.
pauseSem Semaphore pauseSem Semaphore
// Semaphore used for stack scanning.
// We can't reuse pauseSem here since the thread might have been paused for
// other reasons (for example, because it was waiting on a channel).
gcSem Semaphore
} }
// Goroutine counter, starting at 0 for the main goroutine. // Goroutine counter, starting at 0 for the main goroutine.
@@ -95,9 +96,6 @@ func (t *Task) Resume() {
t.state.pauseSem.Post() t.state.pauseSem.Post()
} }
// otherGoroutines is the total number of live goroutines minus one.
var otherGoroutines uint32
// Start a new OS thread. // Start a new OS thread.
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) { func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
t := &Task{} t := &Task{}
@@ -117,7 +115,6 @@ func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
} }
t.state.QueueNext = activeTasks t.state.QueueNext = activeTasks
activeTasks = t activeTasks = t
otherGoroutines++
activeTaskLock.Unlock() activeTaskLock.Unlock()
} }
@@ -138,7 +135,6 @@ func taskExited(t *Task) {
break break
} }
} }
otherGoroutines--
activeTaskLock.Unlock() activeTaskLock.Unlock()
// Sanity check. // Sanity check.
@@ -147,42 +143,9 @@ func taskExited(t *Task) {
} }
} }
// scanWaitGroup is used to wait on until all threads have finished the current state transition. // Futex to wait on until all tasks have finished scanning the stack.
var scanWaitGroup waitGroup // This is basically a sync.WaitGroup.
var scanDoneFutex Futex
type waitGroup struct {
f Futex
}
func initWaitGroup(n uint32) waitGroup {
var wg waitGroup
wg.f.Store(n)
return wg
}
func (wg *waitGroup) done() {
if wg.f.Add(^uint32(0)) == 0 {
wg.f.WakeAll()
}
}
func (wg *waitGroup) wait() {
for {
val := wg.f.Load()
if val == 0 {
return
}
wg.f.Wait(val)
}
}
// gcState is used to track and notify threads when the GC is stopping/resuming.
var gcState Futex
const (
gcStateResumed = iota
gcStateStopped
)
// GC scan phase. Because we need to stop the world while scanning, this kinda // GC scan phase. Because we need to stop the world while scanning, this kinda
// needs to be done in the tasks package. // needs to be done in the tasks package.
@@ -192,71 +155,65 @@ const (
func GCStopWorldAndScan() { func GCStopWorldAndScan() {
current := Current() current := Current()
// NOTE: This does not need to be atomic.
if gcState.Load() == gcStateResumed {
// Don't allow new goroutines to be started while pausing/resuming threads // Don't allow new goroutines to be started while pausing/resuming threads
// in the stop-the-world phase. // in the stop-the-world phase.
activeTaskLock.Lock() activeTaskLock.Lock()
// Wait for threads to finish resuming.
scanWaitGroup.wait()
// Change the gc state to stopped.
// NOTE: This does not need to be atomic.
gcState.Store(gcStateStopped)
// Set the number of threads to wait for.
scanWaitGroup = initWaitGroup(otherGoroutines)
// Pause all other threads. // Pause all other threads.
numOtherThreads := uint32(0)
for t := activeTasks; t != nil; t = t.state.QueueNext { for t := activeTasks; t != nil; t = t.state.QueueNext {
if t != current { if t != current {
numOtherThreads++
tinygo_task_send_gc_signal(t.state.thread) tinygo_task_send_gc_signal(t.state.thread)
} }
} }
// Wait for the threads to finish stopping. // Store the number of threads to wait for in the futex.
scanWaitGroup.wait() // This is the equivalent of doing an initial wg.Add(numOtherThreads).
} scanDoneFutex.Store(numOtherThreads)
// Scan other thread stacks.
for t := activeTasks; t != nil; t = t.state.QueueNext {
if t != current {
markRoots(t.state.stackBottom, t.state.stackTop)
}
}
// Scan the current stack, and all current registers. // Scan the current stack, and all current registers.
scanCurrentStack() scanCurrentStack()
// Wake each paused thread for the first time so it will scan the stack.
for t := activeTasks; t != nil; t = t.state.QueueNext {
if t != current {
t.state.gcSem.Post()
}
}
// Wait until all threads have finished scanning their stack.
// This is the equivalent of wg.Wait()
for {
val := scanDoneFutex.Load()
if val == 0 {
break
}
scanDoneFutex.Wait(val)
}
// Scan all globals (implemented in the runtime). // Scan all globals (implemented in the runtime).
gcScanGlobals() gcScanGlobals()
} }
// After the GC is done scanning, resume all other threads. // After the GC is done scanning, resume all other threads.
//
// This must only be called after a GCStopWorldAndScan call.
func GCResumeWorld() { func GCResumeWorld() {
// NOTE: This does not need to be atomic. current := Current()
if gcState.Load() == gcStateResumed {
// This is already resumed. // Wake each paused thread for the second time, so they will resume normal
return // operation.
for t := activeTasks; t != nil; t = t.state.QueueNext {
if t != current {
t.state.gcSem.Post()
}
} }
// Set the wait group to track resume progress.
scanWaitGroup = initWaitGroup(otherGoroutines)
// Set the state to resumed.
gcState.Store(gcStateResumed)
// Wake all of the stopped threads.
gcState.WakeAll()
// Allow goroutines to start and exit again. // Allow goroutines to start and exit again.
activeTaskLock.Unlock() activeTaskLock.Unlock()
} }
//go:linkname markRoots runtime.markRoots
func markRoots(start, end uintptr)
// Scan globals, implemented in the runtime package. // Scan globals, implemented in the runtime package.
func gcScanGlobals() func gcScanGlobals()
@@ -264,27 +221,34 @@ var stackScanLock PMutex
//export tinygo_task_gc_pause //export tinygo_task_gc_pause
func tingyo_task_gc_pause(sig int32) { func tingyo_task_gc_pause(sig int32) {
// Write the entrty stack pointer to the state. // Wait until we get the signal to start scanning the stack.
Current().state.stackBottom = uintptr(stacksave()) Current().state.gcSem.Wait()
// Notify the GC that we are stopped. // Scan the thread stack.
scanWaitGroup.done() // Only scan a single thread stack at a time, because the GC marking phase
// doesn't support parallelism.
// TODO: it may be possible to call markRoots directly (without saving
// registers) since we are in a signal handler that already saved a bunch of
// registers. This is an optimization left for a future time.
stackScanLock.Lock()
scanCurrentStack()
stackScanLock.Unlock()
// Wait for the GC to resume. // Equivalent of wg.Done(): subtract one from the futex and if the result is
for gcState.Load() == gcStateStopped { // 0 (meaning we were the last in the waitgroup), wake the waiting thread.
gcState.Wait(gcStateStopped) n := uint32(1)
if scanDoneFutex.Add(-n) == 0 {
scanDoneFutex.Wake()
} }
// Notify the GC that we have resumed. // Wait until we get the signal we can resume normally (after the mark phase
scanWaitGroup.done() // has finished).
Current().state.gcSem.Wait()
} }
//go:export tinygo_scanCurrentStack //go:export tinygo_scanCurrentStack
func scanCurrentStack() func scanCurrentStack()
//go:linkname stacksave runtime.stacksave
func stacksave() unsafe.Pointer
// Return the highest address of the current stack. // Return the highest address of the current stack.
func StackTop() uintptr { func StackTop() uintptr {
return Current().state.stackTop return Current().state.stackTop
-153
View File
@@ -1,153 +0,0 @@
//go:build amken_trio
// RabbitPNP Toolhead Board
// MCU: STM32G0B1CBTx (LQFP48, 128KB Flash, 144KB RAM)
package machine
import (
"device/stm32"
"runtime/interrupt"
)
// Vacuum sensors (PWM input via TIM2)
const (
VAC1 = PA0 // TIM2_CH1
VAC2 = PA1 // TIM2_CH2
)
// Motor 1 pins (stepper driver)
const (
M1_CS = PC7
M1_DIR = PB13 // REFL
M1_STEP = PB14 // REFR
M1_ENN = PA10 // Enable (active low)
)
// Motor 2 pins (stepper driver)
const (
M2_CS = PA9
M2_DIR = PB12 // REFL
M2_STEP = PB11 // REFR
M2_ENN = PC6 // Enable (active low)
)
// Motor 3 pins (stepper driver)
const (
M3_CS = PB15
M3_DIR = PB2 // REFL
M3_STEP = PB1 // REFR
M3_ENN = PA8 // Enable (active low)
)
// LED
const (
LED = LED1
LED_BUILTIN = LED1
LED1 = PB7
)
// Solenoid and Neopixel (PWM via TIM4)
const (
SOLENOID = PB8 // TIM4_CH3
NEOPIXEL = PB9 // TIM4_CH4
)
// Endstops
const (
ENDSTOP_IN1 = PC13
ENDSTOP_IN2 = PC14
)
// Magnetic sensor
const (
MAG1 = PB3
)
// Accelerometer chip select (LIS2D on SPI2)
const (
LIS2D_CS = PB5
)
// SPI1 pins (motor drivers)
const (
SPI1_SCK_PIN = PA5
SPI1_SDO_PIN = PA2 // MOSI
SPI1_SDI_PIN = PA6 // MISO
SPI0_SCK_PIN = SPI1_SCK_PIN
SPI0_SDO_PIN = SPI1_SDO_PIN
SPI0_SDI_PIN = SPI1_SDI_PIN
)
// SPI2 pins (accelerometer)
const (
SPI2_SCK_PIN = PB10
SPI2_SDO_PIN = PA4 // MOSI
SPI2_SDI_PIN = PA3 // MISO
)
// I2C2 pins
const (
I2C2_SCL_PIN = PA7
I2C2_SDA_PIN = PB4
I2C0_SCL_PIN = I2C2_SCL_PIN
I2C0_SDA_PIN = I2C2_SDA_PIN
)
// FDCAN1 pins
const (
CAN_RX = PD0
CAN_TX = PD1
)
// USB pins
const (
USB_DM = PA11
USB_DP = PA12
)
// UART pins (not directly connected but required by machine package)
const (
UART_TX_PIN = NoPin
UART_RX_PIN = NoPin
)
var (
// SPI1 for motor drivers
SPI1 = &SPI{
Bus: stm32.SPI1,
AltFuncSelector: AF0_SYSTEM,
}
SPI0 = SPI1
// SPI2 for accelerometer
SPI2 = &SPI{
Bus: stm32.SPI2,
AltFuncSelector: AF1_TIM1_TIM2_TIM3_LPTIM1,
}
// I2C2
I2C2 = &I2C{
Bus: stm32.I2C2,
AltFuncSelector: AF6_SPI2_USART3_USART4_I2C1,
}
I2C0 = I2C2
// FDCAN1 on PD0 (RX) / PD1 (TX) with onboard transceiver
CAN1 = &_CAN1
_CAN1 = FDCAN{
Bus: stm32.FDCAN1,
TxAltFuncSelect: AF3_FDCAN1_FDCAN2,
RxAltFuncSelect: AF3_FDCAN1_FDCAN2,
instance: 0,
}
// Alias for convenience
CAN0 = CAN1
)
// Suppress unused import warning for interrupt package
var _ = interrupt.New
func init() {
// No UART configured on this board - uses USB or CAN for communication
}
-131
View File
@@ -1,131 +0,0 @@
//go:build nucleog0b1re
// Schematic: https://www.st.com/resource/en/user_manual/um2324-stm32-nucleo64-boards-mb1360-stmicroelectronics.pdf
// Datasheet: https://www.st.com/resource/en/datasheet/stm32g0b1re.pdf
package machine
import (
"device/stm32"
"runtime/interrupt"
)
const (
// Arduino Pins
A0 = PA0
A1 = PA1
A2 = PA4
A3 = PB1
A4 = PA11
A5 = PA12
D0 = PB7
D1 = PB6
D2 = PA10
D3 = PB3
D4 = PB5
D5 = PB4
D6 = PB10
D7 = PA8
D8 = PA9
D9 = PC7
D10 = PB0
D11 = PA7
D12 = PA6
D13 = PA5
D14 = PB9
D15 = PB8
)
// User LD4: the green LED is a user LED connected to ARDUINO signal D13 corresponding
// to STM32 I/O PA5.
const (
LED = LED_BUILTIN
LED_BUILTIN = LED_GREEN
LED_GREEN = PA5
)
// User B1: the user button is connected to PC13.
const (
BUTTON = PC13
)
const (
// UART pins
// PA2 and PA3 are connected to the ST-Link Virtual Com Port (VCP)
UART_TX_PIN = PA2
UART_RX_PIN = PA3
// I2C pins
// PB8 is SCL (connected to Arduino connector D15)
// PB9 is SDA (connected to Arduino connector D14)
I2C0_SCL_PIN = PB8
I2C0_SDA_PIN = PB9
// SPI pins
SPI1_SCK_PIN = PA5
SPI1_SDI_PIN = PA6
SPI1_SDO_PIN = PA7
SPI0_SCK_PIN = SPI1_SCK_PIN
SPI0_SDI_PIN = SPI1_SDI_PIN
SPI0_SDO_PIN = SPI1_SDO_PIN
// CAN pins (directly accessible on Nucleo-G0B1RE board)
// FDCAN1: PA11 (TX) / PA12 (RX) using AF9
// FDCAN2: PD12 (TX) / PD13 (RX) using AF3
CAN1_TX_PIN = PA11
CAN1_RX_PIN = PA12
CAN2_TX_PIN = PD12
CAN2_RX_PIN = PD13
)
var (
// USART2 is the hardware serial port connected to the onboard ST-LINK
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
UART1 = &_UART1
_UART1 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART2,
TxAltFuncSelector: AF1_TIM1_TIM2_TIM3_LPTIM1,
RxAltFuncSelector: AF1_TIM1_TIM2_TIM3_LPTIM1,
}
DefaultUART = UART1
// I2C1 is documented, alias to I2C0 as well
I2C1 = &I2C{
Bus: stm32.I2C1,
AltFuncSelector: AF6_SPI2_USART3_USART4_I2C1,
}
I2C0 = I2C1
// SPI1 is documented, alias to SPI0 as well
SPI1 = &SPI{
Bus: stm32.SPI1,
AltFuncSelector: AF0_SYSTEM,
}
SPI0 = SPI1
// FDCAN1 on PA11 (TX) / PA12 (RX)
CAN1 = &_CAN1
_CAN1 = FDCAN{
Bus: stm32.FDCAN1,
TxAltFuncSelect: AF9_FDCAN1_FDCAN2,
RxAltFuncSelect: AF9_FDCAN1_FDCAN2,
instance: 0,
}
// FDCAN2 on PD12 (TX) / PD13 (RX)
CAN2 = &_CAN2
_CAN2 = FDCAN{
Bus: stm32.FDCAN2,
TxAltFuncSelect: AF3_FDCAN1_FDCAN2,
RxAltFuncSelect: AF3_FDCAN1_FDCAN2,
instance: 1,
}
)
func init() {
UART1.Interrupt = interrupt.New(stm32.IRQ_USART2_LPUART2, _UART1.handleInterrupt)
// Note: FDCAN interrupts share with USB (IRQ_UCPD1_UCPD2_USB = 8)
// User should configure interrupts via SetInterrupt method if needed
}
-173
View File
@@ -1,173 +0,0 @@
//go:build pico2_ice
// Most of the info is from
// https://pico2-ice.tinyvision.ai/md_pinout.html although
// (2025-09-07) RP4 appears twice in that pinout - the schematic is
// more clear. Consistent with other RPi boards, we use GPn instead of
// RPn to reference the RPi connected pins.
package machine
// GPIO pins
const (
GP0 = GPIO0
GP1 = GPIO1
GP2 = GPIO2
GP3 = GPIO3
GP4 = GPIO4
GP5 = GPIO5
GP6 = GPIO6
GP7 = GPIO7
GP8 = GPIO8
GP9 = GPIO9
GP10 = GPIO10
GP11 = GPIO11
GP12 = GPIO12
GP13 = GPIO13
GP14 = GPIO14
GP15 = GPIO15
GP16 = GPIO16
GP17 = GPIO17
GP18 = GPIO18
GP19 = GPIO19
GP20 = GPIO20
GP21 = GPIO21
GP22 = GPIO22
GP23 = GPIO23
GP24 = GPIO24
GP25 = GPIO25
GP26 = GPIO26
GP27 = GPIO27
GP28 = GPIO28
GP29 = GPIO29
GP30 = GPIO30
GP31 = GPIO31
GP32 = GPIO32
GP33 = GPIO33
GP34 = GPIO34
GP35 = GPIO35
GP36 = GPIO36
GP37 = GPIO37
GP38 = GPIO38
GP39 = GPIO39
GP40 = GPIO40
GP41 = GPIO41
GP42 = GPIO42
GP43 = GPIO43
GP44 = GPIO44
GP45 = GPIO45
GP46 = GPIO46
GP47 = GPIO47
// RPi pins shared with ICE. The ICE number is what appears on
// the board silkscreen.
ICE9 = GP28
ICE11 = GP29
ICE14 = GP7
ICE15 = GP6
ICE16 = GP5
ICE17 = GP4
ICE18 = GP27
ICE19 = GP23
ICE20 = GP22
ICE21 = GP26
ICE23 = GP25
ICE25 = GP30
ICE26 = GP24
ICE27 = GP20
// FPGA Clock pin.
ICE35_G0 = GP21
// Silkscreen & Pinout names
ICE_SSN = ICE16
ICE_SO = ICE14
ICE_SI = ICE17
ICE_CK = ICE15
SD = GP2
SC = GP3
FPGA_RSTN = GP31
A3 = GP32
A1 = GP33
A4 = GP34
A2 = GP35
B3 = GP36
B1 = GP37
B4 = GP38
B2 = GP39
N0 = GP40 // On the board these are labeled "~0"
N1 = GP41
N2 = GP42
N3 = GP43
N4 = GP44
N5 = GP45
N6 = GP46
// Functions from Schematic.
ICE_DONE = GP40
USB_BOOT = GP42
// Button
SW1 = GP42
BOOTSEL = GP42
// Tricolor LEDs
LED_RED = GP1
LED_GREEN = GP0
LED_BLUE = GP9
// Onboard LED
LED = LED_GREEN
// Onboard crystal oscillator frequency, in MHz.
xoscFreq = 12 // MHz
)
// This board does not define default i2c pins.
const (
I2C0_SDA_PIN = NoPin
I2C0_SCL_PIN = NoPin
I2C1_SDA_PIN = NoPin
I2C1_SCL_PIN = NoPin
)
// SPI default pins
const (
// Default Serial Clock Bus 0 for SPI communications
SPI0_SCK_PIN = GPIO18
// Default Serial Out Bus 0 for SPI communications
SPI0_SDO_PIN = GPIO19 // Tx
// Default Serial In Bus 0 for SPI communications
SPI0_SDI_PIN = GPIO16 // Rx
// Default Serial Clock Bus 1 for SPI communications
SPI1_SCK_PIN = GPIO10
// Default Serial Out Bus 1 for SPI communications
SPI1_SDO_PIN = GPIO11 // Tx
// Default Serial In Bus 1 for SPI communications
SPI1_SDI_PIN = GPIO12 // Rx
)
// UART pins
const (
UART0_TX_PIN = GPIO0
UART0_RX_PIN = GPIO1
UART1_TX_PIN = GPIO8
UART1_RX_PIN = GPIO9
UART_TX_PIN = UART0_TX_PIN
UART_RX_PIN = UART0_RX_PIN
)
var DefaultUART = UART0
// USB identifiers
const (
usb_STRING_PRODUCT = "Pico2"
usb_STRING_MANUFACTURER = "Raspberry Pi"
)
var (
usb_VID uint16 = 0x2E8A
usb_PID uint16 = 0x000A
)
-58
View File
@@ -1,58 +0,0 @@
//go:build xiao_esp32s3
// This file contains the pin mappings for the Seeed XIAO ESP32S3 boards.
//
// Seeed Studio XIAO ESP32S3 is an IoT mini development board based on
// the Espressif ESP32-S3 WiFi/Bluetooth dual-mode chip.
//
// - https://www.seeedstudio.com/XIAO-ESP32S3-p-5627.html
// - https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
package machine
// Digital Pins
const (
D0 = GPIO1
D1 = GPIO2
D2 = GPIO3
D3 = GPIO4
D4 = GPIO5
D5 = GPIO6
D6 = GPIO43
D7 = GPIO44
D8 = GPIO7
D9 = GPIO8
D10 = GPIO9
)
// Analog pins
const (
A0 = GPIO1
A1 = GPIO2
A2 = GPIO3
A3 = GPIO4
)
// UART pins
const (
UART_RX_PIN = GPIO44
UART_TX_PIN = GPIO43
)
// I2C pins
const (
SDA_PIN = GPIO5
SCL_PIN = GPIO6
)
// SPI pins
const (
SPI_SCK_PIN = GPIO7
SPI_SDI_PIN = GPIO9
SPI_SDO_PIN = GPIO8
)
// Onboard LEDs
const (
LED = GPIO21
)
+1 -6
View File
@@ -1,4 +1,4 @@
//go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l0 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350 //go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350
package machine package machine
@@ -43,11 +43,6 @@ type BlockDevice interface {
io.ReaderAt io.ReaderAt
// WriteAt writes the given number of bytes to the block device. // WriteAt writes the given number of bytes to the block device.
//
// This interface directly writes data to the underlying block device.
// Different kinds of devices have different requirements: most can only
// write data after the page has been erased, and many can only write data
// with specific alignment (such as 4-byte alignment).
io.WriterAt io.WriterAt
// Size returns the number of bytes in this block device. // Size returns the number of bytes in this block device.
+1 -31
View File
@@ -1,9 +1,6 @@
package machine package machine
import ( import "errors"
"errors"
"unsafe"
)
var ( var (
ErrTimeoutRNG = errors.New("machine: RNG Timeout") ErrTimeoutRNG = errors.New("machine: RNG Timeout")
@@ -65,30 +62,3 @@ func (p Pin) Low() {
type ADC struct { type ADC struct {
Pin Pin Pin Pin
} }
// Convert the pointer to a uintptr, to be used for memory I/O (DMA for
// example). It also means the pointer is "gone" as far as the compiler is
// concerned, and a GC cycle might deallocate the object. To prevent this from
// happening, also call keepAliveNoEscape at a point after the address isn't
// accessed anymore by the hardware.
// The only exception is if the pointer is accessed later in a volatile way
// (volatile read/write), which also forces the value to stay alive until that
// point.
//
// This function is treated specially by the compiler to mark the 'ptr'
// parameter as not escaping.
//
// TODO: this function should eventually be replaced with the proposed ptrtoaddr
// instruction in LLVM. See:
// https://discourse.llvm.org/t/clarifiying-the-semantics-of-ptrtoint/83987/10
// https://github.com/llvm/llvm-project/pull/139357
func unsafeNoEscape(ptr unsafe.Pointer) uintptr {
return uintptr(ptr)
}
// Make sure the given pointer stays alive until this point. This is similar to
// runtime.KeepAlive, with the difference that it won't let the pointer escape.
// This is typically used together with unsafeNoEscape.
//
// This is a compiler intrinsic.
func keepAliveNoEscape(ptr unsafe.Pointer)
+16 -17
View File
@@ -337,7 +337,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in). // SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
func SendUSBInPacket(ep uint32, data []byte) bool { func SendUSBInPacket(ep uint32, data []byte) bool {
sendUSBPacket(ep, data) sendUSBPacket(ep, data, 0)
// clear transfer complete flag // clear transfer complete flag
setEPINTFLAG(ep, sam.USB_DEVICE_EPINTFLAG_TRCPT1) setEPINTFLAG(ep, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
@@ -351,28 +351,27 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998 // Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
// //
//go:noinline //go:noinline
func sendUSBPacket(ep uint32, data []byte) { func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
// Select the corresponding buffer. l := uint16(len(data))
buffer := udd_ep_control_cache_buffer[:] if 0 < maxsize && maxsize < l {
if ep != 0 { l = maxsize
buffer = udd_ep_in_cache_buffer[ep][:]
} }
// Copy the packet to the buffer. // Set endpoint address for sending data
copy(buffer[:len(data)], data) if ep == 0 {
copy(udd_ep_control_cache_buffer[:], data[:l])
// Select the corresponding endpoint descriptor. usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_control_cache_buffer))))
endpoint := &usbEndpointDescriptors[ep].DeviceDescBank[1] } else {
copy(udd_ep_in_cache_buffer[ep][:], data[:l])
// Set the endpoint address. usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
endpoint.ADDR.Set(uint32(uintptr(unsafe.Pointer(unsafe.SliceData(buffer))))) }
// clear multi-packet size which is total bytes already sent // clear multi-packet size which is total bytes already sent
endpoint.PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
// set byte count, which is total number of bytes to be sent // set byte count, which is total number of bytes to be sent
endpoint.PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
endpoint.PCKSIZE.SetBits((uint32(len(data)) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
} }
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) { func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
+16 -17
View File
@@ -340,7 +340,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in). // SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
func SendUSBInPacket(ep uint32, data []byte) bool { func SendUSBInPacket(ep uint32, data []byte) bool {
sendUSBPacket(ep, data) sendUSBPacket(ep, data, 0)
// clear transfer complete flag // clear transfer complete flag
setEPINTFLAG(ep, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) setEPINTFLAG(ep, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
@@ -354,28 +354,27 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998 // Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
// //
//go:noinline //go:noinline
func sendUSBPacket(ep uint32, data []byte) { func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
// Select the corresponding buffer. l := uint16(len(data))
buffer := udd_ep_control_cache_buffer[:] if 0 < maxsize && maxsize < l {
if ep != 0 { l = maxsize
buffer = udd_ep_in_cache_buffer[ep][:]
} }
// Copy the packet to the buffer. // Set endpoint address for sending data
copy(buffer[:len(data)], data) if ep == 0 {
copy(udd_ep_control_cache_buffer[:], data[:l])
// Select the corresponding endpoint descriptor. usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_control_cache_buffer))))
endpoint := &usbEndpointDescriptors[ep].DeviceDescBank[1] } else {
copy(udd_ep_in_cache_buffer[ep][:], data[:l])
// Set the endpoint address. usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
endpoint.ADDR.Set(uint32(uintptr(unsafe.Pointer(unsafe.SliceData(buffer))))) }
// clear multi-packet size which is total bytes already sent // clear multi-packet size which is total bytes already sent
endpoint.PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
// set byte count, which is total number of bytes to be sent // set byte count, which is total number of bytes to be sent
endpoint.PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
endpoint.PCKSIZE.SetBits((uint32(len(data)) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
} }
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) { func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
-4
View File
@@ -519,10 +519,6 @@ type Serialer interface {
RTS() bool RTS() bool
} }
func initUSB() {
// nothing to do here
}
// USB Serial/JTAG Controller // USB Serial/JTAG Controller
// See esp32-c3_technical_reference_manual_en.pdf // See esp32-c3_technical_reference_manual_en.pdf
// pg. 736 // pg. 736
-312
View File
@@ -1,312 +0,0 @@
//go:build esp32s3
package machine
import (
"device/esp"
"errors"
"runtime/volatile"
"unsafe"
)
const deviceName = esp.Device
const xtalClock = 40_000000 // 40MHz
const apbClock = 80_000000 // 80MHz
const cryptoPWMClock = 160_000000 // 160MHz
// GetCPUFrequency returns the current CPU frequency of the chip.
func GetCPUFrequency() (uint32, error) {
switch esp.SYSTEM.GetSYSCLK_CONF_SOC_CLK_SEL() {
case 0:
return xtalClock / (esp.SYSTEM.GetSYSCLK_CONF_PRE_DIV_CNT() + 1), nil
case 1:
switch esp.SYSTEM.GetCPU_PER_CONF_CPUPERIOD_SEL() {
case 0:
return 80e6, nil
case 1:
return 160e6, nil
case 2:
// If esp.SYSTEM.GetCPU_PER_CONF_PLL_FREQ_SEL() == 1, this is undefined
return 240e6, nil
}
case 2:
//RC Fast Clock
return (175e5) / (esp.SYSTEM.GetSYSCLK_CONF_PRE_DIV_CNT() + 1), nil
}
return 0, errors.New("machine: Unable to determine current cpu frequency")
}
// SetCPUFrequency sets the frequency of the CPU to one of several targets
func SetCPUFrequency(frequency uint32) error {
// Always assume we are on PLL. Lower frequencies can be set with a different
// clock source, but this will change the behavior of APB clock and Crypto PWM
// clock
//esp.SYSTEM.SetSYSCLK_CONF_SOC_CLK_SEL(1)
switch frequency {
case 80_000000:
esp.SYSTEM.SetCPU_PER_CONF_CPUPERIOD_SEL(0)
esp.SYSTEM.SetCPU_PER_CONF_PLL_FREQ_SEL(0) // Reduce PLL freq when possible
return nil
case 160_000000:
esp.SYSTEM.SetCPU_PER_CONF_CPUPERIOD_SEL(1)
esp.SYSTEM.SetCPU_PER_CONF_PLL_FREQ_SEL(0)
return nil
case 240_000000:
esp.SYSTEM.SetCPU_PER_CONF_PLL_FREQ_SEL(1) // Increase PLL freq when needed
esp.SYSTEM.SetCPU_PER_CONF_CPUPERIOD_SEL(2)
return nil
}
return errors.New("machine: Unsupported CPU frequency selected. Supported: 80, 160, 240 MHz")
}
var (
ErrInvalidSPIBus = errors.New("machine: invalid SPI bus")
)
const (
PinOutput PinMode = iota
PinInput
PinInputPullup
PinInputPulldown
)
// Hardware pin numbers
const (
GPIO0 Pin = 0
GPIO1 Pin = 1
GPIO2 Pin = 2
GPIO3 Pin = 3
GPIO4 Pin = 4
GPIO5 Pin = 5
GPIO6 Pin = 6
GPIO7 Pin = 7
GPIO8 Pin = 8
GPIO9 Pin = 9
GPIO10 Pin = 10
GPIO11 Pin = 11
GPIO12 Pin = 12
GPIO13 Pin = 13
GPIO14 Pin = 14
GPIO15 Pin = 15
GPIO16 Pin = 16
GPIO17 Pin = 17
GPIO18 Pin = 18
GPIO19 Pin = 19
GPIO20 Pin = 20
GPIO21 Pin = 21
GPIO26 Pin = 26
GPIO27 Pin = 27
GPIO28 Pin = 28
GPIO29 Pin = 29
GPIO30 Pin = 30
GPIO31 Pin = 31
GPIO32 Pin = 32
GPIO33 Pin = 33
GPIO34 Pin = 34
GPIO35 Pin = 35
GPIO36 Pin = 36
GPIO37 Pin = 37
GPIO38 Pin = 38
GPIO39 Pin = 39
GPIO40 Pin = 40
GPIO41 Pin = 41
GPIO42 Pin = 42
GPIO43 Pin = 43
GPIO44 Pin = 44
GPIO45 Pin = 45
GPIO46 Pin = 46
GPIO47 Pin = 47
GPIO48 Pin = 48
)
// Configure this pin with the given configuration.
func (p Pin) Configure(config PinConfig) {
// Output function 256 is a special value reserved for use as a regular GPIO
// pin. Peripherals (SPI etc) can set a custom output function by calling
// lowercase configure() instead with a signal name.
p.configure(config, 256)
}
// configure is the same as Configure, but allows for setting a specific input
// or output signal.
// Signals are always routed through the GPIO matrix for simplicity. Output
// signals are configured in FUNCx_OUT_SEL_CFG which selects a particular signal
// to output on a given pin. Input signals are configured in FUNCy_IN_SEL_CFG,
// which sets the pin to use for a particular input signal.
func (p Pin) configure(config PinConfig, signal uint32) {
if p == NoPin {
// This simplifies pin configuration in peripherals such as SPI.
return
}
ioConfig := uint32(0)
// MCU_SEL: Function 1 is always GPIO
ioConfig |= (1 << esp.IO_MUX_GPIO_MCU_SEL_Pos)
// FUN_IE: Make this pin an input pin (always set for GPIO operation)
ioConfig |= esp.IO_MUX_GPIO_FUN_IE
// DRV: Set drive strength to 20 mA as a default. Pins 17 and 18 are special
var drive uint32
if p == GPIO17 || p == GPIO18 {
drive = 1 // 20 mA
} else {
drive = 2 // 20 mA
}
ioConfig |= (drive << esp.IO_MUX_GPIO_FUN_DRV_Pos)
// WPU/WPD: Select pull mode.
if config.Mode == PinInputPullup {
ioConfig |= esp.IO_MUX_GPIO_FUN_WPU
} else if config.Mode == PinInputPulldown {
ioConfig |= esp.IO_MUX_GPIO_FUN_WPD
}
// Set configuration
ioRegister := p.ioMuxReg()
ioRegister.Set(ioConfig)
switch config.Mode {
case PinOutput:
// Set the 'output enable' bit.
if p < 32 {
esp.GPIO.ENABLE_W1TS.Set(1 << p)
} else {
esp.GPIO.ENABLE1_W1TS.Set(1 << (p - 32))
}
// Set the signal to read the output value from. It can be a peripheral
// output signal, or the special value 256 which indicates regular GPIO
// usage.
p.outFunc().Set(signal)
case PinInput, PinInputPullup, PinInputPulldown:
// Clear the 'output enable' bit.
if p < 32 {
esp.GPIO.ENABLE_W1TC.Set(1 << p)
} else {
esp.GPIO.ENABLE1_W1TC.Set(1 << (p - 32))
}
if signal != 256 {
// Signal is a peripheral function (not a simple GPIO). Connect this
// signal to the pin.
// Note that outFunc and inFunc work in the opposite direction.
// outFunc configures a pin to use a given output signal, while
// inFunc specifies a pin to use to read the signal from.
inFunc(signal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(p)<<esp.GPIO_FUNC_IN_SEL_CFG_IN_SEL_Pos)
}
}
}
// ioMuxReg returns the IO_MUX_n_REG register used for configuring the io mux for
// this pin
func (p Pin) ioMuxReg() *volatile.Register32 {
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.IO_MUX.GPIO0), uintptr(p)*4))
}
// outFunc returns the FUNCx_OUT_SEL_CFG register used for configuring the
// output function selection.
func (p Pin) outFunc() *volatile.Register32 {
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.FUNC0_OUT_SEL_CFG), uintptr(p)*4))
}
// inFunc returns the FUNCy_IN_SEL_CFG register used for configuring the input
// function selection.
func inFunc(signal uint32) *volatile.Register32 {
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.FUNC0_IN_SEL_CFG), uintptr(signal)*4))
}
// Set the pin to high or low.
// Warning: only use this on an output pin!
func (p Pin) Set(value bool) {
if value {
reg, mask := p.portMaskSet()
reg.Set(mask)
} else {
reg, mask := p.portMaskClear()
reg.Set(mask)
}
}
// Return the register and mask to enable a given GPIO pin. This can be used to
// implement bit-banged drivers.
//
// Warning: only use this on an output pin!
func (p Pin) PortMaskSet() (*uint32, uint32) {
reg, mask := p.portMaskSet()
return &reg.Reg, mask
}
// Return the register and mask to disable a given GPIO pin. This can be used to
// implement bit-banged drivers.
//
// Warning: only use this on an output pin!
func (p Pin) PortMaskClear() (*uint32, uint32) {
reg, mask := p.portMaskClear()
return &reg.Reg, mask
}
func (p Pin) portMaskSet() (*volatile.Register32, uint32) {
if p < 32 {
return &esp.GPIO.OUT_W1TS, 1 << p
} else {
return &esp.GPIO.OUT1_W1TS, 1 << (p - 32)
}
}
func (p Pin) portMaskClear() (*volatile.Register32, uint32) {
if p < 32 {
return &esp.GPIO.OUT_W1TC, 1 << p
} else {
return &esp.GPIO.OUT1_W1TC, 1 << (p - 32)
}
}
// Get returns the current value of a GPIO pin when the pin is configured as an
// input or as an output.
func (p Pin) Get() bool {
if p < 32 {
return esp.GPIO.IN.Get()&(1<<p) != 0
} else {
return esp.GPIO.IN1.Get()&(1<<(p-32)) != 0
}
}
var DefaultUART = UART0
var (
UART0 = &_UART0
_UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()}
UART1 = &_UART1
_UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()}
UART2 = &_UART2
_UART2 = UART{Bus: esp.UART2, Buffer: NewRingBuffer()}
)
type UART struct {
Bus *esp.UART_Type
Buffer *RingBuffer
}
func (uart *UART) Configure(config UARTConfig) {
if config.BaudRate == 0 {
config.BaudRate = 115200
}
// Crystal clock source is selected by default
uart.Bus.CLKDIV.Set(xtalClock / config.BaudRate)
}
func (uart *UART) writeByte(b byte) error {
for (uart.Bus.STATUS.Get()>>16)&0xff >= 128 {
// Read UART_TXFIFO_CNT from the status register, which indicates how
// many bytes there are in the transmit buffer. Wait until there are
// less than 128 bytes in this buffer (the default buffer size).
}
uart.Bus.FIFO.Set(uint32(b))
return nil
}
func (uart *UART) flush() {}
// TODO: SPI
-118
View File
@@ -3,9 +3,7 @@
package machine package machine
import ( import (
"device/arm"
"device/nrf" "device/nrf"
"errors"
"internal/binary" "internal/binary"
"runtime/interrupt" "runtime/interrupt"
"unsafe" "unsafe"
@@ -382,10 +380,6 @@ func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
// If the length of p is not long enough it will be padded with 0xFF bytes. // If the length of p is not long enough it will be padded with 0xFF bytes.
// This method assumes that the destination is already erased. // This method assumes that the destination is already erased.
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) { func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
if len(p) == 0 {
return 0, nil // nothing to do (and it would fail in sd_flash_write)
}
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() { if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
return 0, errFlashCannotWritePastEOF return 0, errFlashCannotWritePastEOF
} }
@@ -393,35 +387,6 @@ func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
address := FlashDataStart() + uintptr(off) address := FlashDataStart() + uintptr(off)
padded := flashPad(p, int(f.WriteBlockSize())) padded := flashPad(p, int(f.WriteBlockSize()))
// When the SoftDevice is enabled, access to the flash is restricted and
// must go through the SoftDevice API.
if isSoftDeviceEnabled() {
// Call sd_flash_write, which is SVC_SOC_BASE + 9 in all the
// SoftDevices I've checked.
// Documentation:
// https://docs.nordicsemi.com/bundle/s140_v6.0.0_api/page/group_n_r_f_s_o_c_f_u_n_c_t_i_o_n_s.html
numberOfWords := len(padded) / 4 // flash access goes in 32-bit words
result := arm.SVCall3(0x20+9, address, &padded[0], uint32(numberOfWords))
if result != 0 {
// Could not queue flash operation? Not sure when this can
// happen.
return 0, flashError
}
// Wait until the SoftDevice is finished.
flashStatus = flashStatusBusy
for flashStatus == flashStatusBusy {
handleSoftDeviceEvents()
}
// Check whether the operation was successful.
if flashStatus != flashStatusOk {
flashStatus = flashStatusOk
return 0, flashError
}
return len(p), nil
}
waitWhileFlashBusy() waitWhileFlashBusy()
nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Wen) nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Wen)
@@ -463,40 +428,6 @@ func (f flashBlockDevice) EraseBlockSize() int64 {
// supports this. The start and len parameters are in block numbers, use // supports this. The start and len parameters are in block numbers, use
// EraseBlockSize to map addresses to blocks. // EraseBlockSize to map addresses to blocks.
func (f flashBlockDevice) EraseBlocks(start, len int64) error { func (f flashBlockDevice) EraseBlocks(start, len int64) error {
// When the SoftDevice is enabled, access to the flash is restricted and
// must go through the SoftDevice API.
if isSoftDeviceEnabled() {
for i := range uint32(len) {
flashPage := uint32(FlashDataStart())/eraseBlockSizeValue + uint32(start) + i
// Call sd_flash_page_erase, which is SVC_SOC_BASE + 8 in all the
// SoftDevices I've checked.
// Documentation:
// https://docs.nordicsemi.com/bundle/s140_v6.0.0_api/page/group_n_r_f_s_o_c_f_u_n_c_t_i_o_n_s.html#ga9c93dd94a138ad8b5ed3693ea38ffb3e
result := arm.SVCall1(0x20+8, flashPage)
if result != 0 {
// Could not queue flash operation? Not sure when this can
// happen.
return flashError
}
// Wait until the SoftDevice is finished.
flashStatus = flashStatusBusy
for flashStatus == flashStatusBusy {
handleSoftDeviceEvents()
}
// Check whether the operation was successful.
if flashStatus != flashStatusOk {
flashStatus = flashStatusOk
return flashError
}
}
return nil
}
// SoftDevice is not used or enabled. Use NVIC directly.
address := FlashDataStart() + uintptr(start*f.EraseBlockSize()) address := FlashDataStart() + uintptr(start*f.EraseBlockSize())
waitWhileFlashBusy() waitWhileFlashBusy()
@@ -516,52 +447,3 @@ func waitWhileFlashBusy() {
for nrf.NVMC.GetREADY() != nrf.NVMC_READY_READY_Ready { for nrf.NVMC.GetREADY() != nrf.NVMC_READY_READY_Ready {
} }
} }
var flashError = errors.New("machine: flash operation failed")
const (
flashStatusOk = iota
flashStatusError
flashStatusBusy
)
var flashStatus uint8 = flashStatusOk
var sdEvent uint32
// Process all queued SoftDevice events. May only be called when the SoftDevice is enabled.
//
// Normally these are handled in the same interrupt where Bluetooth events are
// handled. But in TinyGo, that's complicated. One option would be to put it in
// the tinygo.org/x/bluetooth package, but that would cause a circular
// dependency between the machine and the bluetooth package. Another would be to
// put it here, but let the bluetooth package call handleSoftDeviceEvents, but
// that relies on updating the bluetooth package at the same time. As a
// compromise, these events are handled directly where they are expected (here
// in the machine package). This works in practice since there are only very few
// of such events (at the moment, only flash-related ones which is in the
// machine package anyway).
func handleSoftDeviceEvents() {
for {
var result uintptr
if nrf.Device == "nrf52" || nrf.Device == "nrf52840" || nrf.Device == "nrf52833" {
// sd_evt_get: SOC_SVC_BASE_NOT_AVAILABLE + 31
result = arm.SVCall1(0x2C+31, &sdEvent)
} else {
return // TODO: nrf51 etc
}
if result != 0 {
// Some error occured. The only possible error is
// NRF_ERROR_NOT_FOUND, which means there are no more events.
return
}
// The following events are the same numbers in all SoftDevices I've checked.
switch sdEvent {
case 2: // NRF_EVT_FLASH_OPERATION_SUCCESS
flashStatus = flashStatusOk
case 3: // NRF_EVT_FLASH_OPERATION_ERROR
flashStatus = flashStatusError
}
}
}
-2
View File
@@ -69,5 +69,3 @@ const eraseBlockSizeValue = 4096
func eraseBlockSize() int64 { func eraseBlockSize() int64 {
return eraseBlockSizeValue return eraseBlockSizeValue
} }
const spiMaxBufferSize = 255 // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
-2
View File
@@ -90,5 +90,3 @@ const eraseBlockSizeValue = 4096
func eraseBlockSize() int64 { func eraseBlockSize() int64 {
return eraseBlockSizeValue return eraseBlockSizeValue
} }
const spiMaxBufferSize = 0xffff // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
-6
View File
@@ -108,9 +108,3 @@ const eraseBlockSizeValue = 4096
func eraseBlockSize() int64 { func eraseBlockSize() int64 {
return eraseBlockSizeValue return eraseBlockSizeValue
} }
const spiMaxBufferSize = 0xffff // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
// ADC instance for the VDDH input pin. This pin is typically connected to USB
// input voltage (~5V) or directly to a battery.
var ADC_VDDH = ADC{adcVDDHPin}
+17 -16
View File
@@ -256,7 +256,7 @@ func initEndpoint(ep, config uint32) {
// SendUSBInPacket sends a packet for USBHID (interrupt in / bulk in). // SendUSBInPacket sends a packet for USBHID (interrupt in / bulk in).
func SendUSBInPacket(ep uint32, data []byte) bool { func SendUSBInPacket(ep uint32, data []byte) bool {
sendUSBPacket(ep, data) sendUSBPacket(ep, data, 0)
// clear transfer complete flag // clear transfer complete flag
nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << 4) nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << 4)
@@ -267,32 +267,33 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998 // Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
// //
//go:noinline //go:noinline
func sendUSBPacket(ep uint32, data []byte) { func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
// Select the corresponding buffer.
count := len(data) count := len(data)
var buffer []byte if 0 < int(maxsize) && int(maxsize) < count {
count = int(maxsize)
}
if ep == 0 { if ep == 0 {
buffer = udd_ep_control_cache_buffer[:] copy(udd_ep_control_cache_buffer[:], data[:count])
if count > usb.EndpointPacketSize { if count > usb.EndpointPacketSize {
// The packet must be sent in chunks.
sendOnEP0DATADONE.offset = usb.EndpointPacketSize sendOnEP0DATADONE.offset = usb.EndpointPacketSize
sendOnEP0DATADONE.ptr = &udd_ep_control_cache_buffer[usb.EndpointPacketSize] sendOnEP0DATADONE.ptr = &udd_ep_control_cache_buffer[sendOnEP0DATADONE.offset]
sendOnEP0DATADONE.count = count - usb.EndpointPacketSize sendOnEP0DATADONE.count = count - usb.EndpointPacketSize
count = usb.EndpointPacketSize count = usb.EndpointPacketSize
} }
} else {
buffer = udd_ep_in_cache_buffer[ep][:]
}
// Copy the packet to the buffer.
copy(buffer[:len(data)], data)
// Send the first chunk of the packet.
sendViaEPIn( sendViaEPIn(
ep, ep,
&buffer[0], &udd_ep_control_cache_buffer[0],
count, count,
) )
} else {
copy(udd_ep_in_cache_buffer[ep][:], data[:count])
sendViaEPIn(
ep,
&udd_ep_in_cache_buffer[ep][0],
count,
)
}
} }
func handleEndpointRx(ep uint32) []byte { func handleEndpointRx(ep uint32) []byte {
+4 -17
View File
@@ -49,7 +49,7 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
// Configure for a single shot to perform both write and read (as applicable) // Configure for a single shot to perform both write and read (as applicable)
if len(w) != 0 { if len(w) != 0 {
i2c.Bus.TXD.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(unsafe.SliceData(w))))) i2c.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0]))))
i2c.Bus.TXD.MAXCNT.Set(uint32(len(w))) i2c.Bus.TXD.MAXCNT.Set(uint32(len(w)))
// If no read, immediately signal stop after TX // If no read, immediately signal stop after TX
@@ -58,7 +58,7 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
} }
} }
if len(r) != 0 { if len(r) != 0 {
i2c.Bus.RXD.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(unsafe.SliceData(r))))) i2c.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0]))))
i2c.Bus.RXD.MAXCNT.Set(uint32(len(r))) i2c.Bus.RXD.MAXCNT.Set(uint32(len(r)))
// Auto-start Rx after Tx and Stop after Rx // Auto-start Rx after Tx and Stop after Rx
@@ -89,11 +89,6 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
} }
} }
// Make sure the w and r buffers stay alive until this point, so they won't
// be garbage collected while the buffers are used by the hardware.
keepAliveNoEscape(unsafe.Pointer(unsafe.SliceData(w)))
keepAliveNoEscape(unsafe.Pointer(unsafe.SliceData(r)))
return return
} }
@@ -122,7 +117,7 @@ func (i2c *I2C) Listen(addr uint8) error {
// //
// For request events, the caller MUST call `Reply` to avoid hanging the i2c bus indefinitely. // For request events, the caller MUST call `Reply` to avoid hanging the i2c bus indefinitely.
func (i2c *I2C) WaitForEvent(buf []byte) (evt I2CTargetEvent, count int, err error) { func (i2c *I2C) WaitForEvent(buf []byte) (evt I2CTargetEvent, count int, err error) {
i2c.BusT.RXD.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(unsafe.SliceData(buf))))) i2c.BusT.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&buf[0]))))
i2c.BusT.RXD.MAXCNT.Set(uint32(len(buf))) i2c.BusT.RXD.MAXCNT.Set(uint32(len(buf)))
i2c.BusT.TASKS_PREPARERX.Set(nrf.TWIS_TASKS_PREPARERX_TASKS_PREPARERX_Trigger) i2c.BusT.TASKS_PREPARERX.Set(nrf.TWIS_TASKS_PREPARERX_TASKS_PREPARERX_Trigger)
@@ -139,10 +134,6 @@ func (i2c *I2C) WaitForEvent(buf []byte) (evt I2CTargetEvent, count int, err err
} }
} }
// Make sure buf stays alive until this point, so it won't be garbage
// collected while it is used by the hardware.
keepAliveNoEscape(unsafe.Pointer(unsafe.SliceData(buf)))
count = 0 count = 0
evt = I2CFinish evt = I2CFinish
err = nil err = nil
@@ -172,7 +163,7 @@ func (i2c *I2C) WaitForEvent(buf []byte) (evt I2CTargetEvent, count int, err err
// Reply supplies the response data the controller. // Reply supplies the response data the controller.
func (i2c *I2C) Reply(buf []byte) error { func (i2c *I2C) Reply(buf []byte) error {
i2c.BusT.TXD.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(unsafe.SliceData(buf))))) i2c.BusT.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&buf[0]))))
i2c.BusT.TXD.MAXCNT.Set(uint32(len(buf))) i2c.BusT.TXD.MAXCNT.Set(uint32(len(buf)))
i2c.BusT.EVENTS_STOPPED.Set(0) i2c.BusT.EVENTS_STOPPED.Set(0)
@@ -189,10 +180,6 @@ func (i2c *I2C) Reply(buf []byte) error {
} }
} }
// Make sure the buffer stays alive until this point, so it won't be garbage
// collected while it is used by the hardware.
keepAliveNoEscape(unsafe.Pointer(unsafe.SliceData(buf)))
i2c.BusT.EVENTS_STOPPED.Set(0) i2c.BusT.EVENTS_STOPPED.Set(0)
return nil return nil
+24 -37
View File
@@ -12,8 +12,6 @@ func CPUFrequency() uint32 {
return 64000000 return 64000000
} }
var adcVDDHPin = Pin(254) // special pin number for VDDH on the nrf52840
// InitADC initializes the registers needed for ADC. // InitADC initializes the registers needed for ADC.
func InitADC() { func InitADC() {
// Enable ADC. // Enable ADC.
@@ -28,7 +26,7 @@ func InitADC() {
// Samples can be 1(default), 2, 4, 8, 16, 32, 64, 128, 256 samples // Samples can be 1(default), 2, 4, 8, 16, 32, 64, 128, 256 samples
func (a *ADC) Configure(config ADCConfig) { func (a *ADC) Configure(config ADCConfig) {
var configVal uint32 = nrf.SAADC_CH_CONFIG_RESP_Bypass<<nrf.SAADC_CH_CONFIG_RESP_Pos | var configVal uint32 = nrf.SAADC_CH_CONFIG_RESP_Bypass<<nrf.SAADC_CH_CONFIG_RESP_Pos |
nrf.SAADC_CH_CONFIG_RESN_Bypass<<nrf.SAADC_CH_CONFIG_RESN_Pos | nrf.SAADC_CH_CONFIG_RESP_Bypass<<nrf.SAADC_CH_CONFIG_RESN_Pos |
nrf.SAADC_CH_CONFIG_REFSEL_Internal<<nrf.SAADC_CH_CONFIG_REFSEL_Pos | nrf.SAADC_CH_CONFIG_REFSEL_Internal<<nrf.SAADC_CH_CONFIG_REFSEL_Pos |
nrf.SAADC_CH_CONFIG_MODE_SE<<nrf.SAADC_CH_CONFIG_MODE_Pos nrf.SAADC_CH_CONFIG_MODE_SE<<nrf.SAADC_CH_CONFIG_MODE_Pos
@@ -118,43 +116,36 @@ func (a *ADC) Configure(config ADCConfig) {
// Get returns the current value of an ADC pin in the range 0..0xffff. // Get returns the current value of an ADC pin in the range 0..0xffff.
func (a *ADC) Get() uint16 { func (a *ADC) Get() uint16 {
var adcPin uint32 var pwmPin uint32
var rawValue volatile.Register16 var rawValue volatile.Register16
switch a.Pin { switch a.Pin {
case 2: case 2:
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput0 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput0
case 3: case 3:
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput1 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput1
case 4: case 4:
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput2 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput2
case 5: case 5:
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput3 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput3
case 28: case 28:
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput4 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput4
case 29: case 29:
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput5 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput5
case 30: case 30:
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput6 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput6
case 31: case 31:
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput7 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput7
case adcVDDHPin:
if Device == "nrf52840" {
adcPin = 0x0D // VDDHDIV5 on the nrf52840
} else {
return 0
}
default: default:
return 0 return 0
} }
// Set pin to read. // Set pin to read.
nrf.SAADC.CH[0].PSELP.Set(adcPin) nrf.SAADC.CH[0].PSELN.Set(pwmPin)
nrf.SAADC.CH[0].PSELP.Set(pwmPin)
// Destination for sample result. // Destination for sample result.
// Note: rawValue doesn't need to be kept alive for the GC, since the nrf.SAADC.RESULT.PTR.Set(uint32(uintptr(unsafe.Pointer(&rawValue))))
// volatile read later will force it to stay alive.
nrf.SAADC.RESULT.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(&rawValue))))
nrf.SAADC.RESULT.MAXCNT.Set(1) // One sample nrf.SAADC.RESULT.MAXCNT.Set(1) // One sample
// Start tasks. // Start tasks.
@@ -310,18 +301,20 @@ func (spi *SPI) Transfer(w byte) (byte, error) {
// padded until they fit: if len(w) > len(r) the extra bytes received will be // padded until they fit: if len(w) > len(r) the extra bytes received will be
// dropped and if len(w) < len(r) extra 0 bytes will be sent. // dropped and if len(w) < len(r) extra 0 bytes will be sent.
func (spi *SPI) Tx(w, r []byte) error { func (spi *SPI) Tx(w, r []byte) error {
// Unfortunately the hardware (on the nrf52832) only supports a limited // Unfortunately the hardware (on the nrf52832) only supports up to 255
// amount of bytes in the buffers (depending on the chip), so if either w or // bytes in the buffers, so if either w or r is longer than that the
// r is longer than that the transfer needs to be broken up in pieces. // transfer needs to be broken up in pieces.
// The nrf52840 supports far larger buffers however, which isn't yet
// supported.
for len(r) != 0 || len(w) != 0 { for len(r) != 0 || len(w) != 0 {
// Prepare the SPI transfer: set the DMA pointers and lengths. // Prepare the SPI transfer: set the DMA pointers and lengths.
// read buffer // read buffer
nr := uint32(len(r)) nr := uint32(len(r))
if nr > 0 { if nr > 0 {
if nr > spiMaxBufferSize { if nr > 255 {
nr = spiMaxBufferSize nr = 255
} }
spi.Bus.RXD.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(unsafe.SliceData(r))))) spi.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0]))))
r = r[nr:] r = r[nr:]
} }
spi.Bus.RXD.MAXCNT.Set(nr) spi.Bus.RXD.MAXCNT.Set(nr)
@@ -329,10 +322,10 @@ func (spi *SPI) Tx(w, r []byte) error {
// write buffer // write buffer
nw := uint32(len(w)) nw := uint32(len(w))
if nw > 0 { if nw > 0 {
if nw > spiMaxBufferSize { if nw > 255 {
nw = spiMaxBufferSize nw = 255
} }
spi.Bus.TXD.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(unsafe.SliceData(w))))) spi.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0]))))
w = w[nw:] w = w[nw:]
} }
spi.Bus.TXD.MAXCNT.Set(nw) spi.Bus.TXD.MAXCNT.Set(nw)
@@ -342,16 +335,10 @@ func (spi *SPI) Tx(w, r []byte) error {
// finished if the transfer is send-only (a common case). // finished if the transfer is send-only (a common case).
spi.Bus.TASKS_START.Set(1) spi.Bus.TASKS_START.Set(1)
for spi.Bus.EVENTS_END.Get() == 0 { for spi.Bus.EVENTS_END.Get() == 0 {
gosched()
} }
spi.Bus.EVENTS_END.Set(0) spi.Bus.EVENTS_END.Set(0)
} }
// Make sure the w and r buffers stay alive for the GC until this point,
// since they are used by the hardware but not otherwise visible.
keepAliveNoEscape(unsafe.Pointer(unsafe.SliceData(r)))
keepAliveNoEscape(unsafe.Pointer(unsafe.SliceData(w)))
return nil return nil
} }
-4
View File
@@ -7,7 +7,3 @@ package machine
func GetRNG() (ret uint32, err error) { func GetRNG() (ret uint32, err error) {
return getRNG() return getRNG()
} }
func isSoftDeviceEnabled() bool {
return false
}
+4 -6
View File
@@ -11,6 +11,7 @@ import (
// avoid a heap allocation in GetRNG. // avoid a heap allocation in GetRNG.
var ( var (
softdeviceEnabled uint8
bytesAvailable uint8 bytesAvailable uint8
buf [4]uint8 buf [4]uint8
@@ -23,7 +24,9 @@ var (
func GetRNG() (ret uint32, err error) { func GetRNG() (ret uint32, err error) {
// First check whether the SoftDevice is enabled. // First check whether the SoftDevice is enabled.
// sd_rand_application_bytes_available_get cannot be called when the SoftDevice is not enabled. // sd_rand_application_bytes_available_get cannot be called when the SoftDevice is not enabled.
if !isSoftDeviceEnabled() { arm.SVCall1(0x12, &softdeviceEnabled) // sd_softdevice_is_enabled
if softdeviceEnabled == 0 {
return getRNG() return getRNG()
} }
@@ -54,8 +57,3 @@ func GetRNG() (ret uint32, err error) {
return uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24, nil return uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24, nil
} }
// This function is defined in the runtime, but we need it too.
//
//go:linkname isSoftDeviceEnabled runtime.isSoftDeviceEnabled
func isSoftDeviceEnabled() bool
+6 -1
View File
@@ -49,10 +49,15 @@ func machineInit() {
bits := ^uint32(initDontReset) bits := ^uint32(initDontReset)
resetBlock(bits) resetBlock(bits)
// Remove reset from peripherals which are clocked only by clkSys and
// clkRef. Other peripherals stay in reset until we've configured clocks.
bits = ^uint32(initUnreset)
unresetBlockWait(bits)
clocks.init() clocks.init()
// Peripheral clocks should now all be running // Peripheral clocks should now all be running
unresetBlockWait(initUnreset) unresetBlockWait(RESETS_RESET_Msk)
// DBGPAUSE pauses the timer when a debugger is connected. This prevents // DBGPAUSE pauses the timer when a debugger is connected. This prevents
// sleep functions from ever returning, so disable it. // sleep functions from ever returning, so disable it.
+1 -1
View File
@@ -128,7 +128,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
const ackTimeout = 570 const ackTimeout = 570
rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_ACK_REC) rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_ACK_REC)
sendUSBPacket(0, []byte{}) sendUSBPacket(0, []byte{}, 0)
// Wait for transfer to complete with a timeout. // Wait for transfer to complete with a timeout.
t := timer.timeElapsed() t := timer.timeElapsed()
+1 -1
View File
@@ -131,7 +131,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
const ackTimeout = 570 const ackTimeout = 570
rp.USB.SIE_STATUS.Set(rp.USB_SIE_STATUS_ACK_REC) rp.USB.SIE_STATUS.Set(rp.USB_SIE_STATUS_ACK_REC)
sendUSBPacket(0, []byte{}) sendUSBPacket(0, []byte{}, 0)
// Wait for transfer to complete with a timeout. // Wait for transfer to complete with a timeout.
t := timer.timeElapsed() t := timer.timeElapsed()
+7 -27
View File
@@ -15,8 +15,13 @@ const (
_NUMIRQ = 32 _NUMIRQ = 32
rp2350ExtraReg = 0 rp2350ExtraReg = 0
RESETS_RESET_Msk = 0x01ffffff RESETS_RESET_Msk = 0x01ffffff
initUnreset = rp.RESETS_RESET_IO_BANK0 | initUnreset = rp.RESETS_RESET_ADC |
rp.RESETS_RESET_TIMER rp.RESETS_RESET_RTC |
rp.RESETS_RESET_SPI0 |
rp.RESETS_RESET_SPI1 |
rp.RESETS_RESET_UART0 |
rp.RESETS_RESET_UART1 |
rp.RESETS_RESET_USBCTRL
initDontReset = rp.RESETS_RESET_IO_QSPI | initDontReset = rp.RESETS_RESET_IO_QSPI |
rp.RESETS_RESET_PADS_QSPI | rp.RESETS_RESET_PADS_QSPI |
rp.RESETS_RESET_PLL_USB | rp.RESETS_RESET_PLL_USB |
@@ -119,31 +124,6 @@ const (
fnXIP pinFunc = 0 fnXIP pinFunc = 0
) )
// validPins confirms that the SPI pin selection is a legitimate one
// for the the 2040 chip.
func (spi *SPI) validPins(config SPIConfig) error {
var okSDI, okSDO, okSCK bool
switch spi.Bus {
case rp.SPI0:
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22
case rp.SPI1:
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
}
switch {
case !okSDI:
return errSPIInvalidSDI
case !okSDO:
return errSPIInvalidSDO
case !okSCK:
return errSPIInvalidSCK
}
return nil
}
// Configure configures the gpio pin as per mode. // Configure configures the gpio pin as per mode.
func (p Pin) Configure(config PinConfig) { func (p Pin) Configure(config PinConfig) {
if p == NoPin { if p == NoPin {
+6 -2
View File
@@ -16,8 +16,12 @@ const (
_NUMIRQ = 51 _NUMIRQ = 51
notimpl = "rp2350: not implemented" notimpl = "rp2350: not implemented"
RESETS_RESET_Msk = 0x1fffffff RESETS_RESET_Msk = 0x1fffffff
initUnreset = rp.RESETS_RESET_IO_BANK0 | initUnreset = rp.RESETS_RESET_ADC |
rp.RESETS_RESET_TIMER0 rp.RESETS_RESET_SPI0 |
rp.RESETS_RESET_SPI1 |
rp.RESETS_RESET_UART0 |
rp.RESETS_RESET_UART1 |
rp.RESETS_RESET_USBCTRL
initDontReset = rp.RESETS_RESET_USBCTRL | initDontReset = rp.RESETS_RESET_USBCTRL |
rp.RESETS_RESET_SYSCFG | rp.RESETS_RESET_SYSCFG |
rp.RESETS_RESET_PLL_USB | rp.RESETS_RESET_PLL_USB |
-27
View File
@@ -2,8 +2,6 @@
package machine package machine
import "device/rp"
// Analog pins on RP2350a. // Analog pins on RP2350a.
const ( const (
ADC0 Pin = GPIO26 ADC0 Pin = GPIO26
@@ -14,28 +12,3 @@ const (
// fifth ADC channel. // fifth ADC channel.
thermADC = 30 thermADC = 30
) )
// validPins confirms that the SPI pin selection is a legitimate one
// for the the 2350a chip.
func (spi *SPI) validPins(config SPIConfig) error {
var okSDI, okSDO, okSCK bool
switch spi.Bus {
case rp.SPI0:
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22
case rp.SPI1:
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
}
switch {
case !okSDI:
return errSPIInvalidSDI
case !okSDO:
return errSPIInvalidSDO
case !okSCK:
return errSPIInvalidSCK
}
return nil
}
-27
View File
@@ -2,8 +2,6 @@
package machine package machine
import "device/rp"
// RP2350B has additional pins. // RP2350B has additional pins.
const ( const (
@@ -48,28 +46,3 @@ var (
PWM10 = getPWMGroup(10) PWM10 = getPWMGroup(10)
PWM11 = getPWMGroup(11) PWM11 = getPWMGroup(11)
) )
// validPins confirms that the SPI pin selection is a legitimate one
// for the the 2350b chip.
func (spi *SPI) validPins(config SPIConfig) error {
var okSDI, okSDO, okSCK bool
switch spi.Bus {
case rp.SPI0:
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20 || config.SDI == 32 || config.SDI == 36
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23 || config.SDO == 35 || config.SDO == 39
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22 || config.SCK == 34 || config.SCK == 38
case rp.SPI1:
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28 || config.SDI == 40 || config.SDI == 44
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27 || config.SDO == 31 || config.SDO == 43 || config.SDO == 47
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26 || config.SCK == 30 || config.SCK == 42 || config.SCK == 46
}
switch {
case !okSDI:
return errSPIInvalidSDI
case !okSDO:
return errSPIInvalidSDO
case !okSCK:
return errSPIInvalidSCK
}
return nil
}
+4 -4
View File
@@ -19,10 +19,10 @@ var adcAref uint32
// InitADC resets the ADC peripheral. // InitADC resets the ADC peripheral.
func InitADC() { func InitADC() {
// reset ADC controller rp.RESETS.RESET.SetBits(rp.RESETS_RESET_ADC)
resetBlock(rp.RESETS_RESET_ADC) rp.RESETS.RESET.ClearBits(rp.RESETS_RESET_ADC)
unresetBlockWait(rp.RESETS_RESET_ADC) for !rp.RESETS.RESET_DONE.HasBits(rp.RESETS_RESET_ADC) {
}
// enable ADC // enable ADC
rp.ADC.CS.Set(rp.ADC_CS_EN) rp.ADC.CS.Set(rp.ADC_CS_EN)
adcAref = 3300 adcAref = 3300
+5 -2
View File
@@ -259,7 +259,10 @@ func (i2c *I2C) init(config I2CConfig) error {
//go:inline //go:inline
func (i2c *I2C) reset() { func (i2c *I2C) reset() {
resetVal := i2c.deinit() resetVal := i2c.deinit()
unresetBlockWait(resetVal) rp.RESETS.RESET.ClearBits(resetVal)
// Wait until reset is done.
for !rp.RESETS.RESET_DONE.HasBits(resetVal) {
}
} }
// deinit sets reset bit for I2C. Must call reset to reenable I2C after deinit. // deinit sets reset bit for I2C. Must call reset to reenable I2C after deinit.
@@ -273,7 +276,7 @@ func (i2c *I2C) deinit() (resetVal uint32) {
resetVal = rp.RESETS_RESET_I2C1 resetVal = rp.RESETS_RESET_I2C1
} }
// Perform I2C reset. // Perform I2C reset.
resetBlock(resetVal) rp.RESETS.RESET.SetBits(resetVal)
return resetVal return resetVal
} }
+1 -1
View File
@@ -153,7 +153,7 @@ func (p *pwmGroup) Period() uint64 {
top := p.getWrap() top := p.getWrap()
phc := p.getPhaseCorrect() phc := p.getPhaseCorrect()
Int, frac := p.getClockDiv() Int, frac := p.getClockDiv()
return (16*uint64(Int) + uint64(frac)) * uint64((top+1)*(phc+1)) * uint64(1e9) / (16 * freq) // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16) return (16*uint64(Int) + uint64(frac)) * uint64((top+1)*(phc+1)*1e9) / (16 * freq) // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
} }
// SetInverting sets whether to invert the output of this channel. // SetInverting sets whether to invert the output of this channel.
+27 -11
View File
@@ -108,7 +108,7 @@ func (spi *SPI) SetBaudRate(br uint32) error {
var prescale, postdiv uint32 var prescale, postdiv uint32
freq := CPUFrequency() freq := CPUFrequency()
for prescale = 2; prescale < 255; prescale += 2 { for prescale = 2; prescale < 255; prescale += 2 {
if uint64(freq) < uint64((prescale+2)*256)*uint64(br) { if freq < (prescale+2)*256*br {
break break
} }
} }
@@ -165,9 +165,27 @@ func (spi *SPI) Configure(config SPIConfig) error {
config.SDI = SPI1_SDI_PIN config.SDI = SPI1_SDI_PIN
} }
} }
if err := spi.validPins(config); err != nil { var okSDI, okSDO, okSCK bool
return err switch spi.Bus {
case rp.SPI0:
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16 || config.SDI == 20
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19 || config.SDO == 23
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18 || config.SCK == 22
case rp.SPI1:
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 24 || config.SDI == 28
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
} }
switch {
case !okSDI:
return errSPIInvalidSDI
case !okSDO:
return errSPIInvalidSDO
case !okSCK:
return errSPIInvalidSCK
}
if config.Frequency == 0 { if config.Frequency == 0 {
config.Frequency = defaultBaud config.Frequency = defaultBaud
} }
@@ -212,7 +230,10 @@ func (spi *SPI) setFormat(mode uint8) {
//go:inline //go:inline
func (spi *SPI) reset() { func (spi *SPI) reset() {
resetVal := spi.deinit() resetVal := spi.deinit()
unresetBlockWait(resetVal) rp.RESETS.RESET.ClearBits(resetVal)
// Wait until reset is done.
for !rp.RESETS.RESET_DONE.HasBits(resetVal) {
}
} }
//go:inline //go:inline
@@ -224,7 +245,7 @@ func (spi *SPI) deinit() (resetVal uint32) {
resetVal = rp.RESETS_RESET_SPI1 resetVal = rp.RESETS_RESET_SPI1
} }
// Perform SPI reset. // Perform SPI reset.
resetBlock(resetVal) rp.RESETS.RESET.SetBits(resetVal)
return resetVal return resetVal
} }
@@ -288,7 +309,7 @@ func (spi *SPI) tx(tx []byte) error {
// - set data size to single bytes // - set data size to single bytes
// - set the DREQ so that the DMA will fill the SPI FIFO as needed // - set the DREQ so that the DMA will fill the SPI FIFO as needed
// - start the transfer // - start the transfer
ch.READ_ADDR.Set(uint32(unsafeNoEscape(unsafe.Pointer(unsafe.SliceData(tx))))) ch.READ_ADDR.Set(uint32(uintptr(unsafe.Pointer(&tx[0]))))
ch.WRITE_ADDR.Set(uint32(uintptr(unsafe.Pointer(&spi.Bus.SSPDR)))) ch.WRITE_ADDR.Set(uint32(uintptr(unsafe.Pointer(&spi.Bus.SSPDR))))
ch.TRANS_COUNT.Set(uint32(len(tx))) ch.TRANS_COUNT.Set(uint32(len(tx)))
ch.CTRL_TRIG.Set(rp.DMA_CH0_CTRL_TRIG_INCR_READ | ch.CTRL_TRIG.Set(rp.DMA_CH0_CTRL_TRIG_INCR_READ |
@@ -307,11 +328,6 @@ func (spi *SPI) tx(tx []byte) error {
for ch.CTRL_TRIG.Get()&rp.DMA_CH0_CTRL_TRIG_BUSY != 0 { for ch.CTRL_TRIG.Get()&rp.DMA_CH0_CTRL_TRIG_BUSY != 0 {
} }
// Make sure the read buffer stays alive until this point (in the unlikely
// case the tx slice wasn't read after this function returns and a GC cycle
// happened inbetween).
keepAliveNoEscape(unsafe.Pointer(unsafe.SliceData(tx)))
// We didn't read any result values, which means the RX FIFO has likely // We didn't read any result values, which means the RX FIFO has likely
// overflown. We have to clean up this mess now. // overflown. We have to clean up this mess now.
+4 -2
View File
@@ -148,8 +148,10 @@ func initUART(uart *UART) {
} }
// reset UART // reset UART
resetBlock(resetVal) rp.RESETS.RESET.SetBits(resetVal)
unresetBlockWait(resetVal) rp.RESETS.RESET.ClearBits(resetVal)
for !rp.RESETS.RESET_DONE.HasBits(resetVal) {
}
} }
// handleInterrupt should be called from the appropriate interrupt handler for // handleInterrupt should be called from the appropriate interrupt handler for
+7 -3
View File
@@ -72,15 +72,19 @@ func initEndpoint(ep, config uint32) {
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in). // SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
func SendUSBInPacket(ep uint32, data []byte) bool { func SendUSBInPacket(ep uint32, data []byte) bool {
sendUSBPacket(ep, data) sendUSBPacket(ep, data, 0)
return true return true
} }
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998 // Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
// //
//go:noinline //go:noinline
func sendUSBPacket(ep uint32, data []byte) { func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
count := len(data) count := len(data)
if 0 < int(maxsize) && int(maxsize) < count {
count = int(maxsize)
}
if ep == 0 { if ep == 0 {
if count > usb.EndpointPacketSize { if count > usb.EndpointPacketSize {
count = usb.EndpointPacketSize count = usb.EndpointPacketSize
@@ -141,7 +145,7 @@ func setEPDataPID(ep uint32, dataOne bool) {
} }
func SendZlp() { func SendZlp() {
sendUSBPacket(0, []byte{}) sendUSBPacket(0, []byte{}, 0)
} }
func sendViaEPIn(ep uint32, data []byte, count int) { func sendViaEPIn(ep uint32, data []byte, count int) {
-188
View File
@@ -1,188 +0,0 @@
//go:build stm32g0
package machine
import (
"device/stm32"
"unsafe"
)
// ADC sampling time constants for STM32G0
const (
ADC_SMPR_1_5 = 0x0 // 1.5 ADC clock cycles
ADC_SMPR_3_5 = 0x1 // 3.5 ADC clock cycles
ADC_SMPR_7_5 = 0x2 // 7.5 ADC clock cycles
ADC_SMPR_12_5 = 0x3 // 12.5 ADC clock cycles
ADC_SMPR_19_5 = 0x4 // 19.5 ADC clock cycles
ADC_SMPR_39_5 = 0x5 // 39.5 ADC clock cycles
ADC_SMPR_79_5 = 0x6 // 79.5 ADC clock cycles
ADC_SMPR_160_5 = 0x7 // 160.5 ADC clock cycles
)
// InitADC initializes the registers needed for ADC.
func InitADC() {
// Enable ADC clock
enableAltFuncClock(unsafe.Pointer(stm32.ADC))
// Ensure ADC is disabled before configuration
if stm32.ADC.GetCR_ADEN() != 0 {
// Clear ADEN by setting ADDIS
stm32.ADC.SetCR_ADDIS(1)
// Wait for ADC to be disabled
for stm32.ADC.GetCR_ADEN() != 0 {
}
}
// Enable ADC voltage regulator
stm32.ADC.SetCR_ADVREGEN(1)
// Wait for ADC voltage regulator startup time (20us at max)
// Using simple busy loop - approximately 1280 cycles at 64MHz = 20us
for i := 0; i < 1280; i++ {
// nop
}
// Configure ADC:
// - 12-bit resolution (RES = 0b00)
// - Right alignment (ALIGN = 0)
// - Single conversion mode (CONT = 0)
// - Software trigger (EXTEN = 0b00)
stm32.ADC.CFGR1.Set(0)
// Set clock mode to synchronous with PCLK/2
stm32.ADC.SetCFGR2_CKMODE(0x1) // PCLK/2
// Set sample time to 12.5 cycles for all channels using SMP1
stm32.ADC.SetSMPR_SMP1(ADC_SMPR_12_5)
// Calibrate ADC
stm32.ADC.SetCR_ADCAL(1)
for stm32.ADC.GetCR_ADCAL() != 0 {
}
// Clear ADRDY by writing 1
stm32.ADC.SetISR_ADRDY(1)
// Enable ADC
stm32.ADC.SetCR_ADEN(1)
// Wait until ADC is ready
for stm32.ADC.GetISR_ADRDY() == 0 {
}
}
// Configure configures an ADC pin to be able to read analog data.
func (a ADC) Configure(config ADCConfig) {
// Configure pin as analog input
a.Pin.Configure(PinConfig{Mode: PinInputAnalog})
// Set sampling time based on config
// Use SMP2 and set SMPSEL bit for this channel to select SMP2
ch := a.getChannel()
if ch <= 18 {
// Select sampling time based on config (using SMP2 for per-channel control)
// Map microseconds to sample cycles (at ~32MHz ADC clock after /2 prescaler)
// Each cycle = 1/32MHz = 31.25ns
var smpTime int
switch {
case config.SampleTime == 0:
smpTime = ADC_SMPR_79_5 // Default to 79.5 cycles for good accuracy
case config.SampleTime <= 1:
smpTime = ADC_SMPR_1_5
case config.SampleTime <= 2:
smpTime = ADC_SMPR_3_5
case config.SampleTime <= 3:
smpTime = ADC_SMPR_7_5
case config.SampleTime <= 4:
smpTime = ADC_SMPR_12_5
case config.SampleTime <= 5:
smpTime = ADC_SMPR_19_5
case config.SampleTime <= 10:
smpTime = ADC_SMPR_39_5
case config.SampleTime <= 20:
smpTime = ADC_SMPR_79_5
default:
smpTime = ADC_SMPR_160_5
}
stm32.ADC.SetSMPR_SMP2(uint32(smpTime))
// Set SMPSEL bit for this channel to use SMP2
stm32.ADC.SMPR.SetBits(1 << (8 + ch))
}
}
// Get returns the current value of a ADC pin in the range 0..0xffff.
func (a ADC) Get() uint16 {
ch := a.getChannel()
// Wait until channel configuration is ready if needed
// (CCRDY indicates when CHSELR changes are applied)
for stm32.ADC.GetISR_CCRDY() != 0 {
stm32.ADC.SetISR_CCRDY(1) // Clear by writing 1
}
// Select the channel to convert using CHSELR
// CHSELR uses a bitfield where bit N = 1 enables channel N
stm32.ADC.CHSELR.Set(1 << ch)
// Wait for channel configuration ready
for stm32.ADC.GetISR_CCRDY() == 0 {
}
stm32.ADC.SetISR_CCRDY(1) // Clear flag
// Start conversion
stm32.ADC.SetCR_ADSTART(1)
// Wait for end of conversion
for stm32.ADC.GetISR_EOC() == 0 {
}
// Read the 12-bit result and scale to 16-bit
result := uint16(stm32.ADC.GetDR_DATA()) << 4
return result
}
// getChannel returns the ADC channel number for a given pin.
// STM32G0B1 ADC channel mapping:
// PA0-PA7: CH0-CH7
// PB0-PB2: CH8-CH10
// PB10-PB12: CH11-CH13 (some variants)
// PC4-PC5: CH17-CH18 (some variants)
func (a ADC) getChannel() uint8 {
switch a.Pin {
case PA0:
return 0
case PA1:
return 1
case PA2:
return 2
case PA3:
return 3
case PA4:
return 4
case PA5:
return 5
case PA6:
return 6
case PA7:
return 7
case PB0:
return 8
case PB1:
return 9
case PB2:
return 10
case PB10:
return 11
case PB11:
return 12
case PB12:
return 13
case PC4:
return 17
case PC5:
return 18
}
return 0
}
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build stm32 && !stm32f1 && !stm32l5 && !stm32wlx && !stm32g0 //go:build stm32 && !stm32f1 && !stm32l5 && !stm32wlx
package machine package machine
-3
View File
@@ -2,9 +2,6 @@
package machine package machine
// Flash support for STM32 chips, except for STM32L0 which have a different type
// of flash.
import ( import (
"device/stm32" "device/stm32"
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build stm32 && !stm32l4 && !stm32l5 && !stm32wlx && !stm32g0 //go:build stm32 && !stm32l4 && !stm32l5 && !stm32wlx
package machine package machine
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build stm32l5 || stm32f7 || stm32l4 || stm32l0 || stm32wlx || stm32g0 //go:build stm32l5 || stm32f7 || stm32l4 || stm32l0 || stm32wlx
package machine package machine
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build stm32 && !(stm32f103 || stm32l0x1 || stm32g0) //go:build stm32 && !(stm32f103 || stm32l0x1)
package machine package machine
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build stm32 && !stm32f7x2 && !stm32l5x2 && !stm32g0 //go:build stm32 && !stm32f7x2 && !stm32l5x2
package machine package machine
+2 -2
View File
@@ -1,8 +1,8 @@
//go:build stm32 && !stm32g0 //go:build stm32
package machine package machine
// Peripheral abstraction layer for UARTs on the stm32 family (except stm32g0). // Peripheral abstraction layer for UARTs on the stm32 family.
import ( import (
"device/stm32" "device/stm32"
-567
View File
@@ -1,567 +0,0 @@
//go:build stm32g0
package machine
// Peripheral abstraction layer for the stm32g0
import (
"device/stm32"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
)
const (
// CPU frequency for STM32G0 (64MHz via PLL: HSI16 / 1 * 8 / 2)
cpuFreq = 64000000
)
func CPUFrequency() uint32 {
return cpuFreq
}
var deviceIDAddr = []uintptr{0x1FFF7590, 0x1FFF7594, 0x1FFF7598}
// Internal use: configured speed of the APB1 and APB2 timers, this should be kept
// in sync with any changes to runtime package which configures the oscillators
// and clock frequencies
const APB1_TIM_FREQ = 64e6 // 64MHz (PLL: HSI16 / 1 * 8 / 2)
const APB2_TIM_FREQ = 64e6 // 64MHz (PLL: HSI16 / 1 * 8 / 2)
const (
PA0 = portA + 0
PA1 = portA + 1
PA2 = portA + 2
PA3 = portA + 3
PA4 = portA + 4
PA5 = portA + 5
PA6 = portA + 6
PA7 = portA + 7
PA8 = portA + 8
PA9 = portA + 9
PA10 = portA + 10
PA11 = portA + 11
PA12 = portA + 12
PA13 = portA + 13
PA14 = portA + 14
PA15 = portA + 15
PB0 = portB + 0
PB1 = portB + 1
PB2 = portB + 2
PB3 = portB + 3
PB4 = portB + 4
PB5 = portB + 5
PB6 = portB + 6
PB7 = portB + 7
PB8 = portB + 8
PB9 = portB + 9
PB10 = portB + 10
PB11 = portB + 11
PB12 = portB + 12
PB13 = portB + 13
PB14 = portB + 14
PB15 = portB + 15
PC0 = portC + 0
PC1 = portC + 1
PC2 = portC + 2
PC3 = portC + 3
PC4 = portC + 4
PC5 = portC + 5
PC6 = portC + 6
PC7 = portC + 7
PC8 = portC + 8
PC9 = portC + 9
PC10 = portC + 10
PC11 = portC + 11
PC12 = portC + 12
PC13 = portC + 13
PC14 = portC + 14
PC15 = portC + 15
PD0 = portD + 0
PD1 = portD + 1
PD2 = portD + 2
PD3 = portD + 3
PD4 = portD + 4
PD5 = portD + 5
PD6 = portD + 6
PD7 = portD + 7
PD8 = portD + 8
PD9 = portD + 9
PD10 = portD + 10
PD11 = portD + 11
PD12 = portD + 12
PD13 = portD + 13
PD14 = portD + 14
PD15 = portD + 15
PE0 = portE + 0
PE1 = portE + 1
PE2 = portE + 2
PE3 = portE + 3
PE4 = portE + 4
PE5 = portE + 5
PE6 = portE + 6
PE7 = portE + 7
PE8 = portE + 8
PE9 = portE + 9
PE10 = portE + 10
PE11 = portE + 11
PE12 = portE + 12
PE13 = portE + 13
PE14 = portE + 14
PE15 = portE + 15
PF0 = portF + 0
PF1 = portF + 1
PF2 = portF + 2
PF3 = portF + 3
PF4 = portF + 4
PF5 = portF + 5
PF6 = portF + 6
PF7 = portF + 7
PF8 = portF + 8
PF9 = portF + 9
PF10 = portF + 10
PF11 = portF + 11
PF12 = portF + 12
PF13 = portF + 13
PF14 = portF + 14
PF15 = portF + 15
)
func (p Pin) getPort() *stm32.GPIO_Type {
switch p / 16 {
case 0:
return stm32.GPIOA
case 1:
return stm32.GPIOB
case 2:
return stm32.GPIOC
case 3:
return stm32.GPIOD
case 4:
return stm32.GPIOE
case 5:
return stm32.GPIOF
default:
panic("machine: unknown port")
}
}
// enableClock enables the clock for this desired GPIO port.
func (p Pin) enableClock() {
switch p / 16 {
case 0:
stm32.RCC.SetIOPENR_GPIOAEN(1)
case 1:
stm32.RCC.SetIOPENR_GPIOBEN(1)
case 2:
stm32.RCC.SetIOPENR_GPIOCEN(1)
case 3:
stm32.RCC.SetIOPENR_GPIODEN(1)
case 4:
stm32.RCC.SetIOPENR_GPIOEEN(1)
case 5:
stm32.RCC.SetIOPENR_GPIOFEN(1)
default:
panic("machine: unknown port")
}
}
func (p Pin) registerInterrupt() interrupt.Interrupt {
pin := uint8(p) % 16
switch pin {
case 0:
return interrupt.New(stm32.IRQ_EXTI0_1, func(interrupt.Interrupt) { handlePinInterrupt(0) })
case 1:
return interrupt.New(stm32.IRQ_EXTI0_1, func(interrupt.Interrupt) { handlePinInterrupt(1) })
case 2:
return interrupt.New(stm32.IRQ_EXTI2_3, func(interrupt.Interrupt) { handlePinInterrupt(2) })
case 3:
return interrupt.New(stm32.IRQ_EXTI2_3, func(interrupt.Interrupt) { handlePinInterrupt(3) })
case 4:
return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(4) })
case 5:
return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(5) })
case 6:
return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(6) })
case 7:
return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(7) })
case 8:
return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(8) })
case 9:
return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(9) })
case 10:
return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(10) })
case 11:
return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(11) })
case 12:
return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(12) })
case 13:
return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(13) })
case 14:
return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(14) })
case 15:
return interrupt.New(stm32.IRQ_EXTI4_15, func(interrupt.Interrupt) { handlePinInterrupt(15) })
}
return interrupt.Interrupt{}
}
//---------- UART related types and code
// Configure the UART.
func (uart *UART) configurePins(config UARTConfig) {
// enable the alternate functions on the TX and RX pins
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.TxAltFuncSelector)
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector)
}
// UART baudrate calc based on the bus and clockspeed
func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 {
return CPUFrequency() / baudRate
}
// Register names vary by ST processor, these are for STM G0 family
func (uart *UART) setRegisters() {
uart.rxReg = &uart.Bus.RDR
uart.txReg = &uart.Bus.TDR
uart.statusReg = &uart.Bus.ISR_FIFO_ENABLED
uart.txEmptyFlag = stm32.USART_ISR_TXE
}
//---------- SPI related types and code
// SPI on the STM32G0 using MODER / alternate function pins
type SPI struct {
Bus *stm32.SPI_Type
AltFuncSelector uint8
}
func (spi *SPI) config8Bits() {
// Set rx threshold to 8-bits, so RXNE flag is set for 1 byte
spi.Bus.SetCR2_FRXTH(1)
}
// Set baud rate for SPI
func (spi *SPI) getBaudRate(config SPIConfig) uint32 {
var conf uint32
localFrequency := config.Frequency
// Default
if localFrequency == 0 {
localFrequency = 4e6
}
// set frequency dependent on PCLK prescaler
switch {
case localFrequency < 250000:
conf = stm32.SPI_CR1_BR_Div256
case localFrequency < 500000:
conf = stm32.SPI_CR1_BR_Div128
case localFrequency < 1000000:
conf = stm32.SPI_CR1_BR_Div64
case localFrequency < 2000000:
conf = stm32.SPI_CR1_BR_Div32
case localFrequency < 4000000:
conf = stm32.SPI_CR1_BR_Div16
case localFrequency < 8000000:
conf = stm32.SPI_CR1_BR_Div8
case localFrequency < 16000000:
conf = stm32.SPI_CR1_BR_Div4
case localFrequency < 32000000:
conf = stm32.SPI_CR1_BR_Div2
default:
// None of the specific baudrates were selected; choose the lowest speed
conf = stm32.SPI_CR1_BR_Div256
}
return conf << stm32.SPI_CR1_BR_Pos
}
// Configure SPI pins for input output and clock
func (spi *SPI) configurePins(config SPIConfig) {
config.SCK.ConfigureAltFunc(PinConfig{Mode: PinModeSPICLK}, spi.AltFuncSelector)
config.SDO.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDO}, spi.AltFuncSelector)
config.SDI.ConfigureAltFunc(PinConfig{Mode: PinModeSPISDI}, spi.AltFuncSelector)
}
//---------- I2C related types and code
// Gets the value for TIMINGR register
func (i2c *I2C) getFreqRange(br uint32) uint32 {
// These are 'magic' values calculated by STM32CubeMX
// for 64MHz PCLK1 (PLL: HSI16 / 1 * 8 / 2).
// TODO: Do calculations based on PCLK1
switch br {
case 10 * KHz:
return 0xF010F3FE // 64MHz, 10kHz I2C
case 100 * KHz:
return 0x30A0A7FB // 64MHz, 100kHz I2C (Standard mode)
case 400 * KHz:
return 0x10802D9B // 64MHz, 400kHz I2C (Fast mode)
case 500 * KHz:
return 0x00802172 // 64MHz, 500kHz I2C
default:
return 0
}
}
// Enable peripheral clock
func enableAltFuncClock(bus unsafe.Pointer) {
switch bus {
case unsafe.Pointer(stm32.PWR): // Power interface clock enable
stm32.RCC.SetAPBENR1_PWREN(1)
case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
stm32.RCC.SetAPBENR1_I2C1EN(1)
case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
stm32.RCC.SetAPBENR1_I2C2EN(1)
case unsafe.Pointer(stm32.USART2): // USART2 clock enable
stm32.RCC.SetAPBENR1_USART2EN(1)
case unsafe.Pointer(stm32.USART3): // USART3 clock enable
stm32.RCC.SetAPBENR1_USART3EN(1)
case unsafe.Pointer(stm32.USART4): // USART4 clock enable
stm32.RCC.SetAPBENR1_USART4EN(1)
case unsafe.Pointer(stm32.SPI2): // SPI2 clock enable
stm32.RCC.SetAPBENR1_SPI2EN(1)
case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
stm32.RCC.SetAPBENR1_WWDGEN(1)
case unsafe.Pointer(stm32.TIM2): // TIM2 clock enable
stm32.RCC.SetAPBENR1_TIM2EN(1)
case unsafe.Pointer(stm32.TIM3): // TIM3 clock enable
stm32.RCC.SetAPBENR1_TIM3EN(1)
case unsafe.Pointer(stm32.TIM6): // TIM6 clock enable
stm32.RCC.SetAPBENR1_TIM6EN(1)
case unsafe.Pointer(stm32.TIM7): // TIM7 clock enable
stm32.RCC.SetAPBENR1_TIM7EN(1)
case unsafe.Pointer(stm32.LPUART1): // LPUART1 clock enable
stm32.RCC.SetAPBENR1_LPUART1EN(1)
case unsafe.Pointer(stm32.TIM1): // TIM1 clock enable
stm32.RCC.SetAPBENR2_TIM1EN(1)
case unsafe.Pointer(stm32.SPI1): // SPI1 clock enable
stm32.RCC.SetAPBENR2_SPI1EN(1)
case unsafe.Pointer(stm32.USART1): // USART1 clock enable
stm32.RCC.SetAPBENR2_USART1EN(1)
case unsafe.Pointer(stm32.TIM14): // TIM14 clock enable
stm32.RCC.SetAPBENR2_TIM14EN(1)
case unsafe.Pointer(stm32.TIM15): // TIM15 clock enable
stm32.RCC.SetAPBENR2_TIM15EN(1)
case unsafe.Pointer(stm32.TIM16): // TIM16 clock enable
stm32.RCC.SetAPBENR2_TIM16EN(1)
case unsafe.Pointer(stm32.TIM17): // TIM17 clock enable
stm32.RCC.SetAPBENR2_TIM17EN(1)
case unsafe.Pointer(stm32.ADC): // ADC clock enable
stm32.RCC.SetAPBENR2_ADCEN(1)
case unsafe.Pointer(stm32.FDCAN1), unsafe.Pointer(stm32.FDCAN2): // FDCAN clock enable
stm32.RCC.SetAPBENR1_FDCANEN(1)
}
}
//---------- Timer related code
// Alternate function constants for STM32G0
const (
AF0_SYSTEM = 0
AF1_TIM1_TIM2_TIM3_LPTIM1 = 1
AF2_TIM1_TIM2_TIM3_TIM14_I2C2 = 2
AF3_USART5_USART6_LPUART2 = 3
AF3_FDCAN1_FDCAN2 = 3 // FDCAN on PC2/PC3/PC4/PC5, PD12/PD13/PD14/PD15
AF4_USART1_USART2_TIM14 = 4
AF5_SPI1_SPI2_TIM16_TIM17 = 5
AF6_SPI2_USART3_USART4_I2C1 = 6
AF7_USART1_USART2_COMP1_COMP2 = 7
AF8_I2C1_I2C2_UCPD1_UCPD2 = 8
AF9_SPI2_TIM14_TIM15 = 9
AF9_FDCAN1_FDCAN2 = 9 // FDCAN on PA11/PA12, PB8/PB9
)
var (
TIM1 = TIM{
EnableRegister: &stm32.RCC.APBENR2,
EnableFlag: stm32.RCC_APBENR2_TIM1EN,
Device: stm32.TIM1,
Channels: [4]TimerChannel{
{Pins: []PinFunction{{PA8, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}}},
{Pins: []PinFunction{{PA9, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}}},
{Pins: []PinFunction{{PA10, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}}},
{Pins: []PinFunction{{PA11, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}}},
},
busFreq: APB2_TIM_FREQ,
}
TIM2 = TIM{
EnableRegister: &stm32.RCC.APBENR1,
EnableFlag: stm32.RCC_APBENR1_TIM2EN,
Device: stm32.TIM2,
Channels: [4]TimerChannel{
{Pins: []PinFunction{{PA0, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}, {PA5, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}, {PA15, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}}},
{Pins: []PinFunction{{PA1, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}, {PB3, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}}},
{Pins: []PinFunction{{PA2, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}, {PB10, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}}},
{Pins: []PinFunction{{PA3, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}, {PB11, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}}},
},
busFreq: APB1_TIM_FREQ,
}
TIM3 = TIM{
EnableRegister: &stm32.RCC.APBENR1,
EnableFlag: stm32.RCC_APBENR1_TIM3EN,
Device: stm32.TIM3,
Channels: [4]TimerChannel{
{Pins: []PinFunction{{PA6, AF1_TIM1_TIM2_TIM3_LPTIM1}, {PB4, AF1_TIM1_TIM2_TIM3_LPTIM1}, {PC6, AF1_TIM1_TIM2_TIM3_LPTIM1}}},
{Pins: []PinFunction{{PA7, AF1_TIM1_TIM2_TIM3_LPTIM1}, {PB5, AF1_TIM1_TIM2_TIM3_LPTIM1}, {PC7, AF1_TIM1_TIM2_TIM3_LPTIM1}}},
{Pins: []PinFunction{{PB0, AF1_TIM1_TIM2_TIM3_LPTIM1}, {PC8, AF1_TIM1_TIM2_TIM3_LPTIM1}}},
{Pins: []PinFunction{{PB1, AF1_TIM1_TIM2_TIM3_LPTIM1}, {PC9, AF1_TIM1_TIM2_TIM3_LPTIM1}}},
},
busFreq: APB1_TIM_FREQ,
}
TIM6 = TIM{
EnableRegister: &stm32.RCC.APBENR1,
EnableFlag: stm32.RCC_APBENR1_TIM6EN,
Device: stm32.TIM6,
Channels: [4]TimerChannel{
{Pins: []PinFunction{}},
{Pins: []PinFunction{}},
{Pins: []PinFunction{}},
{Pins: []PinFunction{}},
},
busFreq: APB1_TIM_FREQ,
}
TIM7 = TIM{
EnableRegister: &stm32.RCC.APBENR1,
EnableFlag: stm32.RCC_APBENR1_TIM7EN,
Device: stm32.TIM7,
Channels: [4]TimerChannel{
{Pins: []PinFunction{}},
{Pins: []PinFunction{}},
{Pins: []PinFunction{}},
{Pins: []PinFunction{}},
},
busFreq: APB1_TIM_FREQ,
}
TIM14 = TIM{
EnableRegister: &stm32.RCC.APBENR2,
EnableFlag: stm32.RCC_APBENR2_TIM14EN,
Device: stm32.TIM14,
Channels: [4]TimerChannel{
{Pins: []PinFunction{{PA4, AF4_USART1_USART2_TIM14}, {PA7, AF4_USART1_USART2_TIM14}, {PB1, AF0_SYSTEM}}},
{Pins: []PinFunction{}},
{Pins: []PinFunction{}},
{Pins: []PinFunction{}},
},
busFreq: APB2_TIM_FREQ,
}
TIM15 = TIM{
EnableRegister: &stm32.RCC.APBENR2,
EnableFlag: stm32.RCC_APBENR2_TIM15EN,
Device: stm32.TIM15,
Channels: [4]TimerChannel{
{Pins: []PinFunction{{PA2, AF5_SPI1_SPI2_TIM16_TIM17}, {PB14, AF5_SPI1_SPI2_TIM16_TIM17}}},
{Pins: []PinFunction{{PA3, AF5_SPI1_SPI2_TIM16_TIM17}, {PB15, AF5_SPI1_SPI2_TIM16_TIM17}}},
{Pins: []PinFunction{}},
{Pins: []PinFunction{}},
},
busFreq: APB2_TIM_FREQ,
}
TIM16 = TIM{
EnableRegister: &stm32.RCC.APBENR2,
EnableFlag: stm32.RCC_APBENR2_TIM16EN,
Device: stm32.TIM16,
Channels: [4]TimerChannel{
{Pins: []PinFunction{{PA6, AF5_SPI1_SPI2_TIM16_TIM17}, {PB8, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}}},
{Pins: []PinFunction{}},
{Pins: []PinFunction{}},
{Pins: []PinFunction{}},
},
busFreq: APB2_TIM_FREQ,
}
TIM17 = TIM{
EnableRegister: &stm32.RCC.APBENR2,
EnableFlag: stm32.RCC_APBENR2_TIM17EN,
Device: stm32.TIM17,
Channels: [4]TimerChannel{
{Pins: []PinFunction{{PA7, AF5_SPI1_SPI2_TIM16_TIM17}, {PB9, AF2_TIM1_TIM2_TIM3_TIM14_I2C2}}},
{Pins: []PinFunction{}},
{Pins: []PinFunction{}},
{Pins: []PinFunction{}},
},
busFreq: APB2_TIM_FREQ,
}
)
func (t *TIM) registerUPInterrupt() interrupt.Interrupt {
switch t {
case &TIM1:
return interrupt.New(stm32.IRQ_TIM1_BRK_UP_TRG_COM, TIM1.handleUPInterrupt)
case &TIM2:
return interrupt.New(stm32.IRQ_TIM2, TIM2.handleUPInterrupt)
case &TIM3:
return interrupt.New(stm32.IRQ_TIM3_TIM4, TIM3.handleUPInterrupt)
case &TIM6:
return interrupt.New(stm32.IRQ_TIM6_DAC, TIM6.handleUPInterrupt)
case &TIM7:
return interrupt.New(stm32.IRQ_TIM7, TIM7.handleUPInterrupt)
case &TIM14:
return interrupt.New(stm32.IRQ_TIM14, TIM14.handleUPInterrupt)
case &TIM15:
return interrupt.New(stm32.IRQ_TIM15, TIM15.handleUPInterrupt)
case &TIM16:
return interrupt.New(stm32.IRQ_TIM16, TIM16.handleUPInterrupt)
case &TIM17:
return interrupt.New(stm32.IRQ_TIM17, TIM17.handleUPInterrupt)
}
return interrupt.Interrupt{}
}
func (t *TIM) registerOCInterrupt() interrupt.Interrupt {
switch t {
case &TIM1:
return interrupt.New(stm32.IRQ_TIM1_CC, TIM1.handleOCInterrupt)
case &TIM2:
return interrupt.New(stm32.IRQ_TIM2, TIM2.handleOCInterrupt)
case &TIM3:
return interrupt.New(stm32.IRQ_TIM3_TIM4, TIM3.handleOCInterrupt)
case &TIM6:
return interrupt.New(stm32.IRQ_TIM6_DAC, TIM6.handleOCInterrupt)
case &TIM7:
return interrupt.New(stm32.IRQ_TIM7, TIM7.handleOCInterrupt)
case &TIM14:
return interrupt.New(stm32.IRQ_TIM14, TIM14.handleOCInterrupt)
case &TIM15:
return interrupt.New(stm32.IRQ_TIM15, TIM15.handleOCInterrupt)
case &TIM16:
return interrupt.New(stm32.IRQ_TIM16, TIM16.handleOCInterrupt)
case &TIM17:
return interrupt.New(stm32.IRQ_TIM17, TIM17.handleOCInterrupt)
}
return interrupt.Interrupt{}
}
func (t *TIM) enableMainOutput() {
t.Device.SetBDTR_MOE(1)
}
type arrtype = uint32
type arrRegType = volatile.Register32
const (
ARR_MAX = 0x10000
PSC_MAX = 0x10000
)
func initRNG() {
// STM32G0B1 does not have a hardware RNG peripheral
// RNG is available on some other STM32G0 variants
}
-711
View File
@@ -1,711 +0,0 @@
//go:build stm32g0b1
package machine
import (
"device/stm32"
"errors"
"runtime/interrupt"
"unsafe"
)
// FDCAN Message RAM configuration
// STM32G0B1 SRAMCAN base address: 0x4000B400
// Each FDCAN instance has its own message RAM area
const (
sramcanBase = 0x4000B400
// Message RAM layout sizes (matching STM32 HAL)
sramcanFLSNbr = 28 // Max. Filter List Standard Number
sramcanFLENbr = 8 // Max. Filter List Extended Number
sramcanRF0Nbr = 3 // RX FIFO 0 Elements Number
sramcanRF1Nbr = 3 // RX FIFO 1 Elements Number
sramcanTEFNbr = 3 // TX Event FIFO Elements Number
sramcanTFQNbr = 3 // TX FIFO/Queue Elements Number
// Element sizes in bytes
sramcanFLSSize = 1 * 4 // Filter Standard Element Size
sramcanFLESize = 2 * 4 // Filter Extended Element Size
sramcanRF0Size = 18 * 4 // RX FIFO 0 Element Size (for 64-byte data)
sramcanRF1Size = 18 * 4 // RX FIFO 1 Element Size
sramcanTEFSize = 2 * 4 // TX Event FIFO Element Size
sramcanTFQSize = 18 * 4 // TX FIFO/Queue Element Size
// Start addresses (offsets from base)
sramcanFLSSA = 0
sramcanFLESA = sramcanFLSSA + (sramcanFLSNbr * sramcanFLSSize)
sramcanRF0SA = sramcanFLESA + (sramcanFLENbr * sramcanFLESize)
sramcanRF1SA = sramcanRF0SA + (sramcanRF0Nbr * sramcanRF0Size)
sramcanTEFSA = sramcanRF1SA + (sramcanRF1Nbr * sramcanRF1Size)
sramcanTFQSA = sramcanTEFSA + (sramcanTEFNbr * sramcanTEFSize)
sramcanSize = sramcanTFQSA + (sramcanTFQNbr * sramcanTFQSize)
)
// FDCAN element masks (for parsing message RAM)
const (
fdcanElementMaskSTDID = 0x1FFC0000 // Standard Identifier
fdcanElementMaskEXTID = 0x1FFFFFFF // Extended Identifier
fdcanElementMaskRTR = 0x20000000 // Remote Transmission Request
fdcanElementMaskXTD = 0x40000000 // Extended Identifier flag
fdcanElementMaskESI = 0x80000000 // Error State Indicator
fdcanElementMaskTS = 0x0000FFFF // Timestamp
fdcanElementMaskDLC = 0x000F0000 // Data Length Code
fdcanElementMaskBRS = 0x00100000 // Bit Rate Switch
fdcanElementMaskFDF = 0x00200000 // FD Format
fdcanElementMaskEFC = 0x00800000 // Event FIFO Control
fdcanElementMaskMM = 0xFF000000 // Message Marker
fdcanElementMaskFIDX = 0x7F000000 // Filter Index
fdcanElementMaskANMF = 0x80000000 // Accepted Non-matching Frame
)
// Interrupt flags
const (
FDCAN_IT_RX_FIFO0_NEW_MESSAGE = 0x00000001
FDCAN_IT_RX_FIFO0_FULL = 0x00000002
FDCAN_IT_RX_FIFO0_MSG_LOST = 0x00000004
FDCAN_IT_RX_FIFO1_NEW_MESSAGE = 0x00000010
FDCAN_IT_RX_FIFO1_FULL = 0x00000020
FDCAN_IT_RX_FIFO1_MSG_LOST = 0x00000040
FDCAN_IT_TX_COMPLETE = 0x00000200
FDCAN_IT_TX_ABORT_COMPLETE = 0x00000400
FDCAN_IT_TX_FIFO_EMPTY = 0x00000800
FDCAN_IT_BUS_OFF = 0x02000000
FDCAN_IT_ERROR_WARNING = 0x01000000
FDCAN_IT_ERROR_PASSIVE = 0x00800000
)
// FDCAN represents an FDCAN peripheral
type FDCAN struct {
Bus *stm32.FDCAN_Type
TxAltFuncSelect uint8
RxAltFuncSelect uint8
Interrupt interrupt.Interrupt
instance uint8
}
// FDCANTransferRate represents CAN bus transfer rates
type FDCANTransferRate uint32
const (
FDCANTransferRate125kbps FDCANTransferRate = 125000
FDCANTransferRate250kbps FDCANTransferRate = 250000
FDCANTransferRate500kbps FDCANTransferRate = 500000
FDCANTransferRate1000kbps FDCANTransferRate = 1000000
FDCANTransferRate2000kbps FDCANTransferRate = 2000000 // FD only
FDCANTransferRate4000kbps FDCANTransferRate = 4000000 // FD only
)
// FDCANMode represents the FDCAN operating mode
type FDCANMode uint8
const (
FDCANModeNormal FDCANMode = 0
FDCANModeBusMonitoring FDCANMode = 1
FDCANModeInternalLoopback FDCANMode = 2
FDCANModeExternalLoopback FDCANMode = 3
)
// FDCANConfig holds FDCAN configuration parameters
type FDCANConfig struct {
TransferRate FDCANTransferRate // Nominal bit rate (arbitration phase)
TransferRateFD FDCANTransferRate // Data bit rate (data phase), must be >= TransferRate
Mode FDCANMode
Tx Pin
Rx Pin
Standby Pin // Optional standby pin for CAN transceiver (set to NoPin if not used)
}
// FDCANTxBufferElement represents a transmit buffer element
type FDCANTxBufferElement struct {
ESI bool // Error State Indicator
XTD bool // Extended ID flag
RTR bool // Remote Transmission Request
ID uint32 // CAN identifier (11-bit or 29-bit)
MM uint8 // Message Marker
EFC bool // Event FIFO Control
FDF bool // FD Frame indicator
BRS bool // Bit Rate Switch
DLC uint8 // Data Length Code (0-15)
DB [64]byte // Data buffer
}
// FDCANRxBufferElement represents a receive buffer element
type FDCANRxBufferElement struct {
ESI bool // Error State Indicator
XTD bool // Extended ID flag
RTR bool // Remote Transmission Request
ID uint32 // CAN identifier
ANMF bool // Accepted Non-matching Frame
FIDX uint8 // Filter Index
FDF bool // FD Frame
BRS bool // Bit Rate Switch
DLC uint8 // Data Length Code
RXTS uint16 // RX Timestamp
DB [64]byte // Data buffer
}
// FDCANFilterConfig represents a filter configuration
type FDCANFilterConfig struct {
Index uint8 // Filter index (0-27 for standard, 0-7 for extended)
Type uint8 // 0=Range, 1=Dual, 2=Classic (ID/Mask)
Config uint8 // 0=Disable, 1=FIFO0, 2=FIFO1, 3=Reject
ID1 uint32 // First ID or filter
ID2 uint32 // Second ID or mask
IsExtendedID bool // true for 29-bit ID, false for 11-bit
}
var (
errFDCANInvalidTransferRate = errors.New("FDCAN: invalid TransferRate")
errFDCANInvalidTransferRateFD = errors.New("FDCAN: invalid TransferRateFD")
errFDCANTimeout = errors.New("FDCAN: timeout")
errFDCANTxFifoFull = errors.New("FDCAN: Tx FIFO full")
errFDCANRxFifoEmpty = errors.New("FDCAN: Rx FIFO empty")
errFDCANNotStarted = errors.New("FDCAN: not started")
)
// DLC to bytes lookup table
var dlcToBytes = [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64}
// Configure initializes the FDCAN peripheral
func (can *FDCAN) Configure(config FDCANConfig) error {
// Configure standby pin if specified (for CAN transceivers with standby control)
// Setting it low enables the transceiver
if config.Standby != NoPin {
config.Standby.Configure(PinConfig{Mode: PinOutput})
config.Standby.Low()
}
// Enable FDCAN clock
enableFDCANClock()
// Configure TX and RX pins
config.Tx.ConfigureAltFunc(PinConfig{Mode: PinOutput}, can.TxAltFuncSelect)
config.Rx.ConfigureAltFunc(PinConfig{Mode: PinInputFloating}, can.RxAltFuncSelect)
// Exit from sleep mode
can.Bus.SetCCCR_CSR(0)
// Wait for sleep mode exit
timeout := 10000
for can.Bus.GetCCCR_CSA() != 0 {
timeout--
if timeout == 0 {
return errFDCANTimeout
}
}
// Request initialization
can.Bus.SetCCCR_INIT(1)
// Wait for init mode
timeout = 10000
for can.Bus.GetCCCR_INIT() == 0 {
timeout--
if timeout == 0 {
return errFDCANTimeout
}
}
// Enable configuration change
can.Bus.SetCCCR_CCE(1)
// Configure clock divider (only for FDCAN1)
if can.Bus == stm32.FDCAN1 {
can.Bus.SetCKDIV_PDIV(0)
//can.Bus.CKDIV.Set(0) // No division
}
// Enable automatic retransmission
can.Bus.SetCCCR_DAR(0)
// Disable transmit pause
can.Bus.SetCCCR_TXP(0)
// Enable protocol exception handling
can.Bus.SetCCCR_PXHD(0)
// Enable FD mode with bit rate switching
can.Bus.SetCCCR_FDOE(1)
can.Bus.SetCCCR_BRSE(1)
// Configure operating mode
can.Bus.SetCCCR_TEST(0)
can.Bus.SetCCCR_MON(0)
can.Bus.SetCCCR_ASM(0)
can.Bus.SetTEST_LBCK(0)
switch config.Mode {
case FDCANModeBusMonitoring:
can.Bus.SetCCCR_MON(1)
case FDCANModeInternalLoopback:
can.Bus.SetCCCR_TEST(1)
can.Bus.SetCCCR_MON(1)
can.Bus.SetTEST_LBCK(1)
case FDCANModeExternalLoopback:
can.Bus.SetCCCR_TEST(1)
can.Bus.SetTEST_LBCK(1)
}
// Set nominal bit timing
// STM32G0 runs at 64MHz, FDCAN clock = PCLK = 64MHz
// Bit time = (1 + NTSEG1 + NTSEG2) * tq
// tq = (NBRP + 1) / fCAN_CLK
if config.TransferRate == 0 {
config.TransferRate = FDCANTransferRate500kbps
}
nbrp, ntseg1, ntseg2, nsjw, err := can.calculateNominalBitTiming(config.TransferRate)
if err != nil {
return err
}
can.Bus.NBTP.Set(((nsjw - 1) << 25) | ((nbrp - 1) << 16) | ((ntseg1 - 1) << 8) | (ntseg2 - 1))
// Set data bit timing (for FD mode)
if config.TransferRateFD == 0 {
config.TransferRateFD = FDCANTransferRate1000kbps
}
if config.TransferRateFD < config.TransferRate {
return errFDCANInvalidTransferRateFD
}
dbrp, dtseg1, dtseg2, dsjw, err := can.calculateDataBitTiming(config.TransferRateFD)
if err != nil {
return err
}
can.Bus.DBTP.Set(((dbrp - 1) << 16) | ((dtseg1 - 1) << 8) | ((dtseg2 - 1) << 4) | (dsjw - 1))
// Configure message RAM
can.configureMessageRAM()
return nil
}
// Start enables the FDCAN peripheral for communication
func (can *FDCAN) Start() error {
// Disable configuration change
can.Bus.SetCCCR_CCE(0)
// Exit initialization mode
can.Bus.SetCCCR_INIT(0)
// Wait for normal operation
timeout := 10000
for can.Bus.GetCCCR_INIT() != 0 {
timeout--
if timeout == 0 {
return errFDCANTimeout
}
}
return nil
}
// Stop disables the FDCAN peripheral
func (can *FDCAN) Stop() error {
// Request initialization
can.Bus.SetCCCR_INIT(1)
// Wait for init mode
timeout := 10000
for can.Bus.GetCCCR_INIT() == 0 {
timeout--
if timeout == 0 {
return errFDCANTimeout
}
}
// Enable configuration change
can.Bus.SetCCCR_CCE(1)
return nil
}
// TxFifoIsFull returns true if the TX FIFO is full
func (can *FDCAN) TxFifoIsFull() bool {
return (can.Bus.TXFQS.Get() & 0x00200000) != 0 // TFQF bit
}
// TxFifoFreeLevel returns the number of free TX FIFO elements
func (can *FDCAN) TxFifoFreeLevel() int {
return int(can.Bus.TXFQS.Get() & 0x07) // TFFL[2:0]
}
// RxFifoSize returns the number of messages in RX FIFO 0
func (can *FDCAN) RxFifoSize() int {
return int(can.Bus.RXF0S.Get() & 0x0F) // F0FL[3:0]
}
// RxFifoIsEmpty returns true if RX FIFO 0 is empty
func (can *FDCAN) RxFifoIsEmpty() bool {
return (can.Bus.RXF0S.Get() & 0x0F) == 0
}
// TxRaw transmits a CAN frame using the raw buffer element structure
func (can *FDCAN) TxRaw(e *FDCANTxBufferElement) error {
// Check if TX FIFO is full
if can.TxFifoIsFull() {
return errFDCANTxFifoFull
}
// Get put index
putIndex := (can.Bus.TXFQS.Get() >> 16) & 0x03 // TFQPI[1:0]
// Calculate TX buffer address
sramBase := can.getSRAMBase()
txAddress := sramBase + sramcanTFQSA + (uintptr(putIndex) * sramcanTFQSize)
// Build first word
var w1 uint32
id := e.ID
if !e.XTD {
// Standard ID - shift to bits [28:18]
id = (id & 0x7FF) << 18
}
w1 = id & 0x1FFFFFFF
if e.ESI {
w1 |= fdcanElementMaskESI
}
if e.XTD {
w1 |= fdcanElementMaskXTD
}
if e.RTR {
w1 |= fdcanElementMaskRTR
}
// Build second word
var w2 uint32
w2 = uint32(e.DLC) << 16
if e.FDF {
w2 |= fdcanElementMaskFDF
}
if e.BRS {
w2 |= fdcanElementMaskBRS
}
if e.EFC {
w2 |= fdcanElementMaskEFC
}
w2 |= uint32(e.MM) << 24
// Write to message RAM
*(*uint32)(unsafe.Pointer(txAddress)) = w1
*(*uint32)(unsafe.Pointer(txAddress + 4)) = w2
// Copy data bytes - must use 32-bit word access on Cortex-M0+
dataLen := dlcToBytes[e.DLC&0x0F]
numWords := (dataLen + 3) / 4
for w := byte(0); w < numWords; w++ {
var word uint32
baseIdx := w * 4
for b := byte(0); b < 4 && baseIdx+b < dataLen; b++ {
word |= uint32(e.DB[baseIdx+b]) << (b * 8)
}
*(*uint32)(unsafe.Pointer(txAddress + 8 + uintptr(w)*4)) = word
}
// Request transmission
can.Bus.TXBAR.Set(1 << putIndex)
return nil
}
// Tx transmits a CAN frame with the specified ID and data
func (can *FDCAN) Tx(id uint32, data []byte, isFD, isExtendedID bool) error {
length := byte(len(data))
if length > 64 {
length = 64
}
if !isFD && length > 8 {
length = 8
}
e := FDCANTxBufferElement{
ESI: false,
XTD: isExtendedID,
RTR: false,
ID: id,
MM: 0,
EFC: false,
FDF: isFD,
BRS: isFD,
DLC: FDCANLengthToDlc(length, isFD),
}
for i := byte(0); i < length; i++ {
e.DB[i] = data[i]
}
return can.TxRaw(&e)
}
// RxRaw receives a CAN frame into the raw buffer element structure
func (can *FDCAN) RxRaw(e *FDCANRxBufferElement) error {
if can.RxFifoIsEmpty() {
return errFDCANRxFifoEmpty
}
// Get get index
getIndex := (can.Bus.RXF0S.Get() >> 8) & 0x03 // F0GI[1:0]
// Calculate RX buffer address
sramBase := can.getSRAMBase()
rxAddress := sramBase + sramcanRF0SA + (uintptr(getIndex) * sramcanRF0Size)
// Read first word
w1 := *(*uint32)(unsafe.Pointer(rxAddress))
e.ESI = (w1 & fdcanElementMaskESI) != 0
e.XTD = (w1 & fdcanElementMaskXTD) != 0
e.RTR = (w1 & fdcanElementMaskRTR) != 0
if e.XTD {
e.ID = w1 & fdcanElementMaskEXTID
} else {
e.ID = (w1 & fdcanElementMaskSTDID) >> 18
}
// Read second word
w2 := *(*uint32)(unsafe.Pointer(rxAddress + 4))
e.RXTS = uint16(w2 & fdcanElementMaskTS)
e.DLC = uint8((w2 & fdcanElementMaskDLC) >> 16)
e.BRS = (w2 & fdcanElementMaskBRS) != 0
e.FDF = (w2 & fdcanElementMaskFDF) != 0
e.FIDX = uint8((w2 & fdcanElementMaskFIDX) >> 24)
e.ANMF = (w2 & fdcanElementMaskANMF) != 0
// Copy data bytes - must use 32-bit word access on Cortex-M0+
dataLen := dlcToBytes[e.DLC&0x0F]
numWords := (dataLen + 3) / 4
for w := byte(0); w < numWords; w++ {
word := *(*uint32)(unsafe.Pointer(rxAddress + 8 + uintptr(w)*4))
baseIdx := w * 4
for b := byte(0); b < 4 && baseIdx+b < dataLen; b++ {
e.DB[baseIdx+b] = byte(word >> (b * 8))
}
}
// Acknowledge the read
can.Bus.RXF0A.Set(uint32(getIndex))
return nil
}
// Rx receives a CAN frame and returns its components
func (can *FDCAN) Rx() (id uint32, dlc byte, data []byte, isFD, isExtendedID bool, err error) {
e := FDCANRxBufferElement{}
err = can.RxRaw(&e)
if err != nil {
return 0, 0, nil, false, false, err
}
length := FDCANDlcToLength(e.DLC, e.FDF)
return e.ID, length, e.DB[:length], e.FDF, e.XTD, nil
}
// SetInterrupt configures interrupt handling for the FDCAN peripheral
func (can *FDCAN) SetInterrupt(ie uint32, callback func(*FDCAN)) error {
if callback == nil {
can.Bus.IE.ClearBits(ie)
return nil
}
can.Bus.IE.SetBits(ie)
idx := can.instance
fdcanInstances[idx] = can
for i := uint(0); i < 32; i++ {
if ie&(1<<i) != 0 {
fdcanCallbacks[idx][i] = callback
}
}
can.Interrupt.Enable()
return nil
}
// ConfigureFilter configures a message filter
func (can *FDCAN) ConfigureFilter(config FDCANFilterConfig) error {
sramBase := can.getSRAMBase()
if config.IsExtendedID {
// Extended filter
if config.Index >= sramcanFLENbr {
return errors.New("FDCAN: filter index out of range")
}
filterAddr := sramBase + sramcanFLESA + (uintptr(config.Index) * sramcanFLESize)
// Build filter elements
w1 := (uint32(config.Config) << 29) | (config.ID1 & 0x1FFFFFFF)
w2 := (uint32(config.Type) << 30) | (config.ID2 & 0x1FFFFFFF)
*(*uint32)(unsafe.Pointer(filterAddr)) = w1
*(*uint32)(unsafe.Pointer(filterAddr + 4)) = w2
} else {
// Standard filter
if config.Index >= sramcanFLSNbr {
return errors.New("FDCAN: filter index out of range")
}
filterAddr := sramBase + sramcanFLSSA + (uintptr(config.Index) * sramcanFLSSize)
// Build filter element
w := (uint32(config.Type) << 30) |
(uint32(config.Config) << 27) |
((config.ID1 & 0x7FF) << 16) |
(config.ID2 & 0x7FF)
*(*uint32)(unsafe.Pointer(filterAddr)) = w
}
return nil
}
func (can *FDCAN) getSRAMBase() uintptr {
base := uintptr(sramcanBase)
if can.Bus == stm32.FDCAN2 {
base += sramcanSize
}
return base
}
func (can *FDCAN) configureMessageRAM() {
sramBase := can.getSRAMBase()
// Clear message RAM
for addr := sramBase; addr < sramBase+sramcanSize; addr += 4 {
*(*uint32)(unsafe.Pointer(addr)) = 0
}
// Configure filter counts (using RXGFC register)
// LSS = number of standard filters, LSE = number of extended filters
rxgfc := can.Bus.RXGFC.Get()
rxgfc &= ^uint32(0xFF000000) // Clear LSS and LSE
rxgfc |= (sramcanFLSNbr << 24) // Standard filters
rxgfc |= (sramcanFLENbr << 24) & 0xFF00 // Extended filters (shifted)
can.Bus.RXGFC.Set(rxgfc)
}
func (can *FDCAN) calculateNominalBitTiming(rate FDCANTransferRate) (brp, tseg1, tseg2, sjw uint32, err error) {
// STM32G0 FDCAN clock = 64MHz
// Target: 80% sample point
// Bit time = (1 + TSEG1 + TSEG2) time quanta
switch rate {
case FDCANTransferRate125kbps:
// 64MHz / 32 = 2MHz, 16 tq per bit = 125kbps
return 32, 13, 2, 4, nil
case FDCANTransferRate250kbps:
// 64MHz / 16 = 4MHz, 16 tq per bit = 250kbps
return 16, 13, 2, 4, nil
case FDCANTransferRate500kbps:
// 64MHz / 8 = 8MHz, 16 tq per bit = 500kbps
return 8, 13, 2, 4, nil
case FDCANTransferRate1000kbps:
// 64MHz / 4 = 16MHz, 16 tq per bit = 1Mbps
return 4, 13, 2, 4, nil
default:
return 0, 0, 0, 0, errFDCANInvalidTransferRate
}
}
func (can *FDCAN) calculateDataBitTiming(rate FDCANTransferRate) (brp, tseg1, tseg2, sjw uint32, err error) {
// STM32G0 FDCAN clock = 64MHz
// For data phase, we need higher bit rates
switch rate {
case FDCANTransferRate125kbps:
return 32, 13, 2, 4, nil
case FDCANTransferRate250kbps:
return 16, 13, 2, 4, nil
case FDCANTransferRate500kbps:
return 8, 13, 2, 4, nil
case FDCANTransferRate1000kbps:
return 4, 13, 2, 4, nil
case FDCANTransferRate2000kbps:
// 64MHz / 2 = 32MHz, 16 tq per bit = 2Mbps
return 2, 13, 2, 4, nil
case FDCANTransferRate4000kbps:
// 64MHz / 1 = 64MHz, 16 tq per bit = 4Mbps
return 1, 13, 2, 4, nil
default:
return 0, 0, 0, 0, errFDCANInvalidTransferRateFD
}
}
// FDCANDlcToLength converts a DLC value to actual byte length
func FDCANDlcToLength(dlc byte, isFD bool) byte {
if dlc > 15 {
dlc = 15
}
length := dlcToBytes[dlc]
if !isFD && length > 8 {
return 8
}
return length
}
// FDCANLengthToDlc converts a byte length to DLC value
func FDCANLengthToDlc(length byte, isFD bool) byte {
if !isFD {
if length > 8 {
return 8
}
return length
}
switch {
case length <= 8:
return length
case length <= 12:
return 9
case length <= 16:
return 10
case length <= 20:
return 11
case length <= 24:
return 12
case length <= 32:
return 13
case length <= 48:
return 14
default:
return 15
}
}
// Interrupt handling
var (
fdcanInstances [2]*FDCAN
fdcanCallbacks [2][32]func(*FDCAN)
)
func fdcanHandleInterrupt(idx int) {
if fdcanInstances[idx] == nil {
return
}
can := fdcanInstances[idx]
ir := can.Bus.IR.Get()
can.Bus.IR.Set(ir) // Clear interrupt flags
for i := uint(0); i < 32; i++ {
if ir&(1<<i) != 0 && fdcanCallbacks[idx][i] != nil {
fdcanCallbacks[idx][i](can)
}
}
}
// Data returns the received data as a slice
func (e *FDCANRxBufferElement) Data() []byte {
return e.DB[:FDCANDlcToLength(e.DLC, e.FDF)]
}
// Length returns the actual data length
func (e *FDCANRxBufferElement) Length() byte {
return FDCANDlcToLength(e.DLC, e.FDF)
}
// enableFDCANClock enables the FDCAN peripheral clock
func enableFDCANClock() {
// FDCAN clock is on APB1
stm32.RCC.SetAPBENR1_FDCANEN(1)
}
-92
View File
@@ -1,92 +0,0 @@
//go:build stm32g0
package machine
import (
"device/stm32"
)
// This variant of the GPIO input interrupt logic is for
// STM32G0 chips which use a different EXTI register structure
// with IMR1, RTSR1, FTSR1, and separate RPR1/FPR1 pending registers.
// Callbacks for pin interrupt events
var pinCallbacks [16]func(Pin)
// The pin currently associated with interrupt callback
// for a given slot.
var interruptPins [16]Pin
// SetInterrupt sets an interrupt to be executed when a particular pin changes
// state. The pin should already be configured as an input, including a pull up
// or down if no external pull is provided.
//
// This call will replace a previously set callback on this pin. You can pass a
// nil func to unset the pin change interrupt. If you do so, the change
// parameter is ignored and can be set to any value (such as 0).
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
port := uint32(uint8(p) / 16)
pin := uint8(p) % 16
enableEXTIConfigRegisters()
if callback == nil {
stm32.EXTI.IMR1.ClearBits(1 << pin)
pinCallbacks[pin] = nil
return nil
}
if pinCallbacks[pin] != nil {
// The pin was already configured.
// To properly re-configure a pin, unset it first and set a new
// configuration.
return ErrNoPinChangeChannel
}
// Set the callback now (before the interrupt is enabled) to avoid
// possible race condition
pinCallbacks[pin] = callback
interruptPins[pin] = p
crReg := getEXTIConfigRegister(pin)
shift := (pin & 0x3) * 4
crReg.ReplaceBits(port, 0xf, shift)
if (change & PinRising) != 0 {
stm32.EXTI.RTSR1.SetBits(1 << pin)
}
if (change & PinFalling) != 0 {
stm32.EXTI.FTSR1.SetBits(1 << pin)
}
stm32.EXTI.IMR1.SetBits(1 << pin)
intr := p.registerInterrupt()
intr.SetPriority(0)
intr.Enable()
return nil
}
func handlePinInterrupt(pin uint8) {
// STM32G0 has separate rising and falling pending registers
// Check both and clear the appropriate one
mask := uint32(1 << pin)
if stm32.EXTI.RPR1.HasBits(mask) {
// Writing 1 to the pending register clears the pending flag
stm32.EXTI.RPR1.Set(mask)
callback := pinCallbacks[pin]
if callback != nil {
callback(interruptPins[pin])
}
}
if stm32.EXTI.FPR1.HasBits(mask) {
// Writing 1 to the pending register clears the pending flag
stm32.EXTI.FPR1.Set(mask)
callback := pinCallbacks[pin]
if callback != nil {
callback(interruptPins[pin])
}
}
}
-99
View File
@@ -1,99 +0,0 @@
//go:build stm32g0
package machine
// SPI on STM32G0 uses 16-bit registers
import (
"device/stm32"
"runtime/volatile"
"unsafe"
)
// SPIConfig is used to store config info for SPI.
type SPIConfig struct {
Frequency uint32
SCK Pin
SDO Pin
SDI Pin
LSBFirst bool
Mode uint8
}
// Configure is intended to setup the STM32 SPI peripheral
func (spi *SPI) Configure(config SPIConfig) error {
// disable SPI interface before any configuration changes
spi.Bus.CR1.ClearBits(stm32.SPI_CR1_SPE)
// enable clock for SPI
enableAltFuncClock(unsafe.Pointer(spi.Bus))
// init pins - use defaults if not specified
if config.SCK == 0 && config.SDO == 0 && config.SDI == 0 {
config.SCK = SPI0_SCK_PIN
config.SDO = SPI0_SDO_PIN
config.SDI = SPI0_SDI_PIN
}
spi.configurePins(config)
// Get SPI baud rate divisor
conf := spi.getBaudRate(config)
// set polarity and phase on the SPI interface
switch config.Mode {
case Mode1:
conf |= stm32.SPI_CR1_CPHA
case Mode2:
conf |= stm32.SPI_CR1_CPOL
case Mode3:
conf |= stm32.SPI_CR1_CPOL | stm32.SPI_CR1_CPHA
}
// set bit transfer order
if config.LSBFirst {
conf |= stm32.SPI_CR1_LSBFIRST
}
// set SPI master
conf |= stm32.SPI_CR1_MSTR | stm32.SPI_CR1_SSI
// use software CS (GPIO) by default
conf |= stm32.SPI_CR1_SSM
// Set CR1 configuration WITHOUT enabling SPE yet
// (STM32G0 requires CR2 DS bits to be set before SPE is enabled)
spi.Bus.CR1.Set(uint16(conf))
// Series-specific configuration to set 8-bit transfer mode (must be done before SPE)
spi.config8Bits()
// Now enable SPI
spi.Bus.SetCR1_SPE(1)
return nil
}
// Transfer writes/reads a single byte using the SPI interface.
func (spi *SPI) Transfer(w byte) (byte, error) {
// STM32G0 requires 8-bit access to DR for 8-bit transfers
// Using 16-bit access causes data packing issues
dr := (*volatile.Register8)(unsafe.Pointer(&spi.Bus.DR))
// Write data to be transmitted to the SPI data register (8-bit access)
dr.Set(w)
// Wait until transmit complete
for !spi.Bus.SR.HasBits(stm32.SPI_SR_TXE) {
}
// Wait until receive complete
for !spi.Bus.SR.HasBits(stm32.SPI_SR_RXNE) {
}
// Wait until SPI is not busy
for spi.Bus.SR.HasBits(stm32.SPI_SR_BSY) {
}
// Return received data from SPI data register (8-bit access)
return dr.Get(), nil
}
-86
View File
@@ -1,86 +0,0 @@
//go:build stm32g0
package machine
// Peripheral abstraction layer for UARTs on the stm32g0 family.
import (
"device/stm32"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
)
// UART representation
type UART struct {
Buffer *RingBuffer
Bus *stm32.USART_Type
Interrupt interrupt.Interrupt
TxAltFuncSelector uint8
RxAltFuncSelector uint8
// Registers specific to the chip
rxReg *volatile.Register32
txReg *volatile.Register32
statusReg *volatile.Register32
txEmptyFlag uint32
}
// Configure the UART.
func (uart *UART) Configure(config UARTConfig) {
// Default baud rate to 115200.
if config.BaudRate == 0 {
config.BaudRate = 115200
}
// Set the GPIO pins to defaults if they're not set
if config.TX == 0 && config.RX == 0 {
config.TX = UART_TX_PIN
config.RX = UART_RX_PIN
}
// STM32 families have different, but compatible, registers for
// basic UART functions. For each family populate the registers
// into `uart`.
uart.setRegisters()
// Enable USART clock
enableAltFuncClock(unsafe.Pointer(uart.Bus))
uart.configurePins(config)
// Set baud rate
uart.SetBaudRate(config.BaudRate)
// Enable USART port, tx, rx and rx interrupts
// STM32G0 uses CR1_FIFO_ENABLED register
uart.Bus.CR1_FIFO_ENABLED.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()
}
// handleInterrupt should be called from the appropriate interrupt handler for
// this UART instance.
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
uart.Receive(byte((uart.rxReg.Get() & 0xFF)))
}
// SetBaudRate sets the communication speed for the UART. Defer to chip-specific
// routines for calculation
func (uart *UART) SetBaudRate(br uint32) {
divider := uart.getBaudRateDivisor(br)
uart.Bus.BRR.Set(divider)
}
// WriteByte writes a byte of data to the UART.
func (uart *UART) writeByte(c byte) error {
uart.txReg.Set(uint32(c))
for !uart.statusReg.HasBits(uart.txEmptyFlag) {
}
return nil
}
func (uart *UART) flush() {}
-173
View File
@@ -1,173 +0,0 @@
//go:build stm32g0
package machine
import (
"device/stm32"
"unsafe"
)
// WindowWatchdog provides access to the Window Watchdog (WWDG) peripheral.
// Unlike IWDG, WWDG must be refreshed within a specific window - not too early
// and not too late. This provides protection against both runaway code and
// code that gets stuck in a loop refreshing the watchdog.
var WindowWatchdog = &windowWatchdogImpl{}
// WindowWatchdogConfig holds configuration for the window watchdog timer.
// The timeout (in microseconds) before the watchdog fires.
// The valid range depends on System frequency.
// At 64MHz: ~64µs to ~524ms
type WindowWatchdogConfig struct {
TimeoutMicros uint32
// The window value as a percentage of timeout (0-100).
// Refresh must occur when counter is below this percentage of max.
// Default (0) sets window to 100% (no window restriction).
WindowPercent uint8
}
// WWDG prescaler values
const (
wwdgPrescaler1 = 0 // CK Counter Clock (PCLK/4096) / 1
wwdgPrescaler2 = 1 // CK Counter Clock (PCLK/4096) / 2
wwdgPrescaler4 = 2 // CK Counter Clock (PCLK/4096) / 4
wwdgPrescaler8 = 3 // CK Counter Clock (PCLK/4096) / 8
wwdgPrescaler16 = 4 // CK Counter Clock (PCLK/4096) / 16
wwdgPrescaler32 = 5 // CK Counter Clock (PCLK/4096) / 32
wwdgPrescaler64 = 6 // CK Counter Clock (PCLK/4096) / 64
wwdgPrescaler128 = 7 // CK Counter Clock (PCLK/4096) / 128
)
// WWDG counter limits
const (
wwdgCounterMin = 0x40 // Minimum counter value (T6 must be set)
wwdgCounterMax = 0x7F // Maximum counter value (7 bits)
wwdgWindowMax = 0x7F // Maximum window value
)
type windowWatchdogImpl struct {
counter uint8 // Configured counter reload value
prescaler uint8 // Configured prescaler
}
// Configure the window watchdog.
//
// This method should not be called after the watchdog is started.
// The WWDG cannot be disabled once started, except by a system reset.
//
// Timeout formula: t_WWDG = (1/PCLK) × 4096 × 2^WDGTB × (T[5:0] + 1)
// Where T[5:0] = counter value - 0x40
// Refer RM0444 Rev 6 861/1384
func (wd *windowWatchdogImpl) Configure(config WindowWatchdogConfig) error {
// Enable WWDG clock
enableAltFuncClock(unsafe.Pointer(stm32.WWDG))
// Calculate prescaler and counter value from timeout
// Base tick = PCLK / 4096
// With prescaler: tick = PCLK / (4096 * 2^prescaler)
// Timeout = tick * (counter - 0x3F)
pclk := CPUFrequency() // Assuming PCLK = CPU frequency (no APB prescaler)
baseTick := (4096 * 1000000) / pclk // Base tick in nanoseconds * 1000 for precision
timeout := config.TimeoutMicros
if timeout == 0 {
timeout = 10000 // Default 10ms
}
// Find the best prescaler and counter-combination
var bestPrescaler uint8
var bestCounter uint8
found := false
for prescaler := uint8(0); prescaler <= 7; prescaler++ {
// Tick duration in nanoseconds * 1000
tickNs := baseTick << prescaler
// Counter value needed (counter - 0x3F = timeout / tick)
// Rearranged: counter = (timeout * 1000 / tickNs) + 0x3F
counterVal := (uint32(timeout) * 1000000 / tickNs) + 0x3F
if counterVal >= wwdgCounterMin && counterVal <= wwdgCounterMax {
bestPrescaler = prescaler
bestCounter = uint8(counterVal)
found = true
break
}
}
if !found {
// Use maximum timeout
bestPrescaler = wwdgPrescaler128
bestCounter = wwdgCounterMax
}
wd.prescaler = bestPrescaler
wd.counter = bestCounter
// Calculate window value
windowVal := uint8(wwdgWindowMax)
if config.WindowPercent > 0 && config.WindowPercent < 100 {
// Window = 0x40 + ((counter - 0x40) * percent / 100)
counterRange := uint16(bestCounter) - wwdgCounterMin
windowOffset := (counterRange * uint16(config.WindowPercent)) / 100
windowVal = uint8(wwdgCounterMin + windowOffset)
}
stm32.WWDG.CFR.Set((uint32(bestPrescaler) << stm32.WWDG_CFR_WDGTB_Pos) | uint32(windowVal))
return nil
}
// Start enables the window watchdog.
// Once started, the WWDG cannot be disabled except by a system reset.
func (wd *windowWatchdogImpl) Start() error {
stm32.WWDG.CR.Set(uint32(wd.counter) | (1 << 7))
return nil
}
// Update refreshes the window watchdog counter.
// This must be called within the configured window to prevent a reset.
// Calling too early (counter > window) or too late (counter <= 0x3F) causes reset.
func (wd *windowWatchdogImpl) Update() {
stm32.WWDG.CR.Set(uint32(wd.counter) | (1 << 7))
}
// GetCounter returns the current WWDG counter value.
// Useful for timing refresh operations within the window.
func (wd *windowWatchdogImpl) GetCounter() uint8 {
return uint8(stm32.WWDG.CR.Get() & 0x7F)
}
// EnableEarlyWakeupInterrupt enables the Early Wakeup Interrupt (EWI).
// The EWI is triggered when the counter reaches 0x40, giving the application
// a chance to refresh the watchdog or perform cleanup before reset.
func (wd *windowWatchdogImpl) EnableEarlyWakeupInterrupt() {
stm32.WWDG.CFR.SetBits(stm32.WWDG_CFR_EWI)
}
// ClearEarlyWakeupFlag clears the Early Wakeup Interrupt flag.
// Must be called in the interrupt handler.
func (wd *windowWatchdogImpl) ClearEarlyWakeupFlag() {
stm32.WWDG.SR.Set(0) // Write 0 to clear EWIF
}
// IsEarlyWakeupFlagSet returns true if the Early Wakeup Interrupt flag is set.
func (wd *windowWatchdogImpl) IsEarlyWakeupFlagSet() bool {
return stm32.WWDG.SR.Get()&1 != 0
}
// GetMaxTimeout returns the maximum timeout in microseconds for the current PCLK.
// Max timeout = (1/PCLK) × 4096 × 128 × 64
// At 64MHz: ~524ms = 524288µs
func (wd *windowWatchdogImpl) GetMaxTimeout() uint32 {
pclk := uint64(CPUFrequency())
return uint32((uint64(4096) * 128 * 64 * 1000000) / pclk)
}
// GetMinTimeout returns the minimum timeout in microseconds for the current PCLK.
// Min timeout = (1/PCLK) × 4096 × 1 × 1
// At 64MHz: ~64µs
func (wd *windowWatchdogImpl) GetMinTimeout() uint32 {
pclk := uint64(CPUFrequency())
return uint32((uint64(4096) * 1000000) / pclk)
}
-26
View File
@@ -1,26 +0,0 @@
//go:build stm32g0
package machine
import (
"device/stm32"
"runtime/volatile"
)
func getEXTIConfigRegister(pin uint8) *volatile.Register32 {
switch (pin & 0xf) / 4 {
case 0:
return &stm32.EXTI.EXTICR1
case 1:
return &stm32.EXTI.EXTICR2
case 2:
return &stm32.EXTI.EXTICR3
case 3:
return &stm32.EXTI.EXTICR4
}
return nil
}
func enableEXTIConfigRegisters() {
// EXTI configuration is in the EXTI peripheral on STM32G0, no enable needed
}

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