mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-28 23:58:40 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 91b0bbaae6 |
@@ -16,15 +16,14 @@ jobs:
|
||||
name: build-macos
|
||||
strategy:
|
||||
matrix:
|
||||
# macos-14: arm64 (oldest supported version as of 18-11-2025)
|
||||
# macos-15-intel: amd64 (last intel version to be supported by github runners)
|
||||
# See https://github.com/actions/runner-images/issues/13046
|
||||
os: [macos-14, macos-15-intel]
|
||||
# macos-13: amd64 (oldest supported version as of 18-10-2024)
|
||||
# macos-14: arm64 (oldest arm64 version)
|
||||
os: [macos-13, macos-14]
|
||||
include:
|
||||
- os: macos-13
|
||||
goarch: amd64
|
||||
- os: macos-14
|
||||
goarch: arm64
|
||||
- os: macos-15-intel
|
||||
goarch: amd64
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Install Dependencies
|
||||
@@ -40,7 +39,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: '1.25.1'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
@@ -135,7 +134,7 @@ jobs:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: '1.25.1'
|
||||
cache: true
|
||||
- name: Build TinyGo (LLVM ${{ matrix.version }})
|
||||
run: go install -tags=llvm${{ matrix.version }}
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
sudo rm -rf /usr/local/share/boost
|
||||
df -h
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
@@ -66,3 +66,45 @@ jobs:
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
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
@@ -31,7 +31,7 @@ jobs:
|
||||
# We're not on a multi-user machine, so this is safe.
|
||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Extract TinyGo version
|
||||
@@ -131,13 +131,13 @@ jobs:
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Install wasmtime
|
||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||
@@ -164,7 +164,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install apt dependencies
|
||||
@@ -179,9 +179,9 @@ jobs:
|
||||
simavr \
|
||||
ninja-build
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
@@ -284,7 +284,7 @@ jobs:
|
||||
needs: build-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
- name: Get TinyGo version
|
||||
id: version
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
@@ -296,9 +296,9 @@ jobs:
|
||||
g++-${{ matrix.toolchain }} \
|
||||
libc6-dev-${{ matrix.libc }}-cross
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Restore LLVM source cache
|
||||
uses: actions/cache/restore@v4
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
target: tinygo-llvm-build
|
||||
context: .
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
# See: https://github.com/tinygo-org/tinygo/pull/4516#issuecomment-2416363668
|
||||
run: sudo apt-get remove llvm-18
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
- name: Pull musl, bdwgc
|
||||
run: |
|
||||
git submodule update --init lib/musl lib/bdwgc
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
run: |
|
||||
echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # fetch all history (no sparse checkout)
|
||||
submodules: true
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
run: |
|
||||
scoop install ninja binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Extract TinyGo version
|
||||
@@ -39,9 +39,9 @@ jobs:
|
||||
shell: bash
|
||||
run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT"
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Restore cached LLVM source
|
||||
uses: actions/cache/restore@v4
|
||||
@@ -143,11 +143,11 @@ jobs:
|
||||
run: |
|
||||
scoop install binaryen
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -173,11 +173,11 @@ jobs:
|
||||
maximum-size: 24GB
|
||||
disk-root: "C:"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -209,11 +209,11 @@ jobs:
|
||||
run: |
|
||||
scoop install binaryen && scoop install wasmtime@29.0.1
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.25.5'
|
||||
go-version: '1.25.0'
|
||||
cache: true
|
||||
- name: Download TinyGo build
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
url = https://github.com/WebAssembly/wasi-libc
|
||||
[submodule "lib/picolibc"]
|
||||
path = lib/picolibc
|
||||
url = https://github.com/picolibc/picolibc.git
|
||||
url = https://github.com/keith-packard/picolibc.git
|
||||
[submodule "lib/stm32-svd"]
|
||||
path = lib/stm32-svd
|
||||
url = https://github.com/tinygo-org/stm32-svd
|
||||
|
||||
-101
@@ -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
|
||||
---
|
||||
* **general**
|
||||
|
||||
+2
-8
@@ -363,7 +363,6 @@ TEST_PACKAGES_FAST = \
|
||||
path \
|
||||
reflect \
|
||||
sync \
|
||||
testing \
|
||||
testing/iotest \
|
||||
text/scanner \
|
||||
unicode \
|
||||
@@ -480,8 +479,7 @@ TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
||||
TEST_IOFS := false
|
||||
endif
|
||||
|
||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic'
|
||||
TEST_ADDITIONAL_FLAGS ?=
|
||||
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic|^Fuzz'
|
||||
|
||||
# Test known-working standard library packages.
|
||||
# TODO: parallelize, and only show failing tests (no implied -v flag).
|
||||
@@ -489,7 +487,7 @@ TEST_ADDITIONAL_FLAGS ?=
|
||||
tinygo-test:
|
||||
@# 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
|
||||
$(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
|
||||
@# requires a large stack-size. Hence, io/fs is only run conditionally.
|
||||
@# For more details, see the comments on issue #3143.
|
||||
@@ -622,8 +620,6 @@ smoketest: testchdir
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id
|
||||
@$(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
|
||||
ifneq ($(WASM), 0)
|
||||
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
|
||||
$(TINYGO) build -size short -o test.bin -target mch2022 examples/machinetest
|
||||
@$(MD5SUM) test.bin
|
||||
$(TINYGO) build -size short -o test.bin -target=xiao-esp32s3 examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
endif
|
||||
$(TINYGO) build -size short -o test.bin -target=esp-c3-32s-kit examples/blinky1
|
||||
@$(MD5SUM) test.bin
|
||||
|
||||
@@ -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.
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
See https://github.com/golang/go/blob/master/LICENSE for license information.
|
||||
Copyright (c) 2009-2024 The Go Authors. All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1042,7 +1042,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
||||
if err != nil {
|
||||
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
|
||||
// bootloader).
|
||||
result.Binary = filepath.Join(tmpdir, "main"+outext)
|
||||
|
||||
@@ -28,7 +28,6 @@ func TestClangAttributes(t *testing.T) {
|
||||
"cortex-m4",
|
||||
"cortex-m7",
|
||||
"esp32c3",
|
||||
"esp32s3",
|
||||
"fe310",
|
||||
"gameboy-advance",
|
||||
"k210",
|
||||
|
||||
+4
-7
@@ -33,13 +33,10 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if options.GoCompatibility {
|
||||
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax {
|
||||
// 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
|
||||
return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", minorMin, minorMax, gorootMajor, gorootMinor)
|
||||
}
|
||||
if gorootMajor != 1 || gorootMinor < minorMin || gorootMinor > minorMax {
|
||||
// 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
|
||||
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
|
||||
|
||||
+1
-2
@@ -100,12 +100,11 @@ func makeESPFirmwareImage(infile, outfile, format string) error {
|
||||
chip_id := map[string]uint16{
|
||||
"esp32": 0x0000,
|
||||
"esp32c3": 0x0005,
|
||||
"esp32s3": 0x0009,
|
||||
}[chip]
|
||||
|
||||
// Image header.
|
||||
switch chip {
|
||||
case "esp32", "esp32c3", "esp32s3":
|
||||
case "esp32", "esp32c3":
|
||||
// Header format:
|
||||
// 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
|
||||
|
||||
@@ -42,9 +42,9 @@ func TestBinarySize(t *testing.T) {
|
||||
// This is a small number of very diverse targets that we want to test.
|
||||
tests := []sizeTest{
|
||||
// microcontrollers
|
||||
{"hifive1b", "examples/echo", 3668, 280, 0, 2244},
|
||||
{"microbit", "examples/serial", 2694, 342, 8, 2248},
|
||||
{"wioterminal", "examples/pininterrupt", 6837, 1491, 120, 6888},
|
||||
{"hifive1b", "examples/echo", 3884, 280, 0, 2268},
|
||||
{"microbit", "examples/serial", 2852, 360, 8, 2272},
|
||||
{"wioterminal", "examples/pininterrupt", 7337, 1491, 116, 6912},
|
||||
|
||||
// TODO: also check wasm. Right now this is difficult, because
|
||||
// wasm binaries are run through wasm-opt and therefore the
|
||||
|
||||
@@ -226,7 +226,7 @@ func (c *Config) StackSize() uint64 {
|
||||
|
||||
// MaxStackAlloc returns the size of the maximum allocation to put on the stack vs heap.
|
||||
func (c *Config) MaxStackAlloc() uint64 {
|
||||
if c.StackSize() >= 16*1024 {
|
||||
if c.StackSize() > 32*1024 {
|
||||
return 1024
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ type Options struct {
|
||||
WITPackage string // pass through to wasm-tools component embed invocation
|
||||
WITWorld string // pass through to wasm-tools component embed -w option
|
||||
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.
|
||||
|
||||
+1
-1
@@ -245,7 +245,7 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
|
||||
// current insert position.
|
||||
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
||||
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.
|
||||
b.CreateCondBr(assert, faultBlock, nextBlock)
|
||||
|
||||
@@ -32,9 +32,6 @@ const (
|
||||
// Whether this is a full or partial Go parameter (int, slice, etc).
|
||||
// The extra context parameter is not a Go parameter.
|
||||
paramIsGoParam = 1 << iota
|
||||
|
||||
// Whether this is a readonly parameter (for example, a string pointer).
|
||||
paramIsReadonly
|
||||
)
|
||||
|
||||
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
|
||||
@@ -170,7 +167,6 @@ func (c *compilerContext) flattenAggregateType(t llvm.Type, name string, goType
|
||||
continue
|
||||
}
|
||||
suffix := strconv.Itoa(i)
|
||||
isString := false
|
||||
if goType != nil {
|
||||
// Try to come up with a good suffix for this struct field,
|
||||
// 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]
|
||||
case types.String:
|
||||
suffix = []string{"data", "len"}[i]
|
||||
isString = true
|
||||
}
|
||||
case *types.Signature:
|
||||
suffix = []string{"context", "funcptr"}[i]
|
||||
}
|
||||
}
|
||||
subInfos := c.flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
||||
if isString {
|
||||
subInfos[0].flags |= paramIsReadonly
|
||||
}
|
||||
paramInfos = append(paramInfos, subInfos...)
|
||||
}
|
||||
return paramInfos
|
||||
|
||||
+28
-132
@@ -152,12 +152,10 @@ type builder struct {
|
||||
llvmFnType llvm.Type
|
||||
llvmFn llvm.Value
|
||||
info functionInfo
|
||||
locals map[ssa.Value]llvm.Value // local variables
|
||||
blockInfo []blockInfo
|
||||
locals map[ssa.Value]llvm.Value // local variables
|
||||
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
|
||||
currentBlockInfo *blockInfo
|
||||
tarjanStack []uint
|
||||
tarjanIndex uint
|
||||
phis []phiNode
|
||||
deferPtr llvm.Value
|
||||
deferFrame llvm.Value
|
||||
@@ -189,22 +187,11 @@ func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *bu
|
||||
info: c.getFunctionInfo(f),
|
||||
locals: make(map[ssa.Value]llvm.Value),
|
||||
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 {
|
||||
callName string
|
||||
pos token.Pos
|
||||
@@ -1233,29 +1220,14 @@ func (b *builder) createFunctionStart(intrinsic bool) {
|
||||
// intrinsic (like an atomic operation). Create the entry block
|
||||
// manually.
|
||||
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 {
|
||||
blocks := b.fn.Blocks
|
||||
blockInfo := make([]blockInfo, len(blocks))
|
||||
for _, block := range b.fn.DomPreorder() {
|
||||
info := &blockInfo[block.Index]
|
||||
llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
|
||||
info.entry = llvmBlock
|
||||
info.exit = llvmBlock
|
||||
b.blockEntries[block] = llvmBlock
|
||||
b.blockExits[block] = llvmBlock
|
||||
}
|
||||
b.blockInfo = blockInfo
|
||||
// Normal functions have an entry block.
|
||||
entryBlock = blockInfo[0].entry
|
||||
entryBlock = b.blockEntries[b.fn.Blocks[0]]
|
||||
}
|
||||
b.SetInsertPointAtEnd(entryBlock)
|
||||
|
||||
@@ -1351,9 +1323,8 @@ func (b *builder) createFunction() {
|
||||
if b.DumpSSA {
|
||||
fmt.Printf("%d: %s:\n", block.Index, block.Comment)
|
||||
}
|
||||
b.SetInsertPointAtEnd(b.blockEntries[block])
|
||||
b.currentBlock = block
|
||||
b.currentBlockInfo = &b.blockInfo[block.Index]
|
||||
b.SetInsertPointAtEnd(b.currentBlockInfo.entry)
|
||||
for _, instr := range block.Instrs {
|
||||
if instr, ok := instr.(*ssa.DebugRef); ok {
|
||||
if !b.Debug {
|
||||
@@ -1413,7 +1384,7 @@ func (b *builder) createFunction() {
|
||||
block := phi.ssa.Block()
|
||||
for i, edge := range phi.ssa.Edges {
|
||||
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})
|
||||
}
|
||||
}
|
||||
@@ -1527,11 +1498,11 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
|
||||
case *ssa.If:
|
||||
cond := b.getValue(instr.Cond, getPos(instr))
|
||||
block := instr.Block()
|
||||
blockThen := b.blockInfo[block.Succs[0].Index].entry
|
||||
blockElse := b.blockInfo[block.Succs[1].Index].entry
|
||||
blockThen := b.blockEntries[block.Succs[0]]
|
||||
blockElse := b.blockEntries[block.Succs[1]]
|
||||
b.CreateCondBr(cond, blockThen, blockElse)
|
||||
case *ssa.Jump:
|
||||
blockJump := b.blockInfo[instr.Block().Succs[0].Index].entry
|
||||
blockJump := b.blockEntries[instr.Block().Succs[0]]
|
||||
b.CreateBr(blockJump)
|
||||
case *ssa.MapUpdate:
|
||||
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")
|
||||
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
||||
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, elemLayout}, "append.new")
|
||||
result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize}, "append.new")
|
||||
newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
|
||||
newLen := b.CreateExtractValue(result, 1, "append.newLen")
|
||||
newCap := b.CreateExtractValue(result, 2, "append.newCap")
|
||||
@@ -1682,41 +1652,13 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
||||
case "copy":
|
||||
dst := argValues[0]
|
||||
src := argValues[1]
|
||||
// Fetch the lengths.
|
||||
dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen")
|
||||
srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
|
||||
// Find the minimum of the lengths.
|
||||
minFuncName := "llvm.umin.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
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.
|
||||
dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
|
||||
srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
|
||||
elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
|
||||
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?
|
||||
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
|
||||
return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
|
||||
case "delete":
|
||||
m := argValues[0]
|
||||
key := argValues[1]
|
||||
@@ -1744,66 +1686,20 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
||||
return llvmLen, nil
|
||||
case "min", "max":
|
||||
// min and max builtins, added in Go 1.21.
|
||||
// Find the corresponding intrinsic name.
|
||||
ty := argTypes[0].Underlying().(*types.Basic)
|
||||
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
|
||||
if callName == "max" {
|
||||
tok = token.GTR
|
||||
}
|
||||
result := argValues[0]
|
||||
typ := argTypes[0]
|
||||
for _, arg := range argValues[1:] {
|
||||
cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result = b.CreateSelect(cmp, result, arg, "")
|
||||
}
|
||||
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."
|
||||
// We can simply reuse the existing binop comparison code, which has all
|
||||
// the edge cases figured out already.
|
||||
tok := token.LSS
|
||||
if callName == "max" {
|
||||
tok = token.GTR
|
||||
}
|
||||
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]
|
||||
typ := argTypes[0]
|
||||
for _, arg := range argValues[1:] {
|
||||
result = b.CreateCall(callType, llvmFn, []llvm.Value{result, arg}, "")
|
||||
cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result = b.CreateSelect(cmp, result, arg, "")
|
||||
}
|
||||
return result, nil
|
||||
case "panic":
|
||||
|
||||
+29
-99
@@ -100,7 +100,7 @@ func (b *builder) createLandingPad() {
|
||||
|
||||
// Continue at the 'recover' block, which returns to the parent in an
|
||||
// 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
|
||||
@@ -234,108 +234,41 @@ func (b *builder) createInvokeCheckpoint() {
|
||||
continueBB := b.insertBasicBlock("")
|
||||
b.CreateCondBr(isZero, continueBB, b.landingpad)
|
||||
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.
|
||||
// Use Tarjan's strongly connected components algorithm to search for cycles.
|
||||
// A one-node SCC is a cycle iff there is an edge from the node to itself.
|
||||
// A multi-node SCC is always a cycle.
|
||||
// https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
|
||||
func (b *builder) isInLoop() bool {
|
||||
if b.currentBlockInfo.tarjan.lowLink == 0 {
|
||||
b.strongConnect(b.currentBlock)
|
||||
}
|
||||
return b.currentBlockInfo.tarjan.cyclic
|
||||
}
|
||||
// isInLoop checks if there is a path from a basic block to itself.
|
||||
func isInLoop(start *ssa.BasicBlock) bool {
|
||||
// Use a breadth-first search to scan backwards through the block graph.
|
||||
queue := []*ssa.BasicBlock{start}
|
||||
checked := map[*ssa.BasicBlock]struct{}{}
|
||||
|
||||
func (b *builder) strongConnect(block *ssa.BasicBlock) {
|
||||
// Assign a new index.
|
||||
// Indices start from 1 so that 0 can be used as a sentinel.
|
||||
assignedIndex := b.tarjanIndex + 1
|
||||
b.tarjanIndex = assignedIndex
|
||||
for len(queue) > 0 {
|
||||
// pop a block off of the queue
|
||||
block := queue[len(queue)-1]
|
||||
queue = queue[:len(queue)-1]
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Update the lowLink index.
|
||||
// This always uses the min-of-lowlink instead of using index in the on-stack case.
|
||||
// This is done for two reasons:
|
||||
// 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 {
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
|
||||
if _, ok := checked[pred]; ok {
|
||||
// block already checked
|
||||
continue
|
||||
}
|
||||
|
||||
// add to queue and checked map
|
||||
queue = append(queue, pred)
|
||||
checked[pred] = struct{}{}
|
||||
}
|
||||
|
||||
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
|
||||
return false
|
||||
}
|
||||
|
||||
// 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.
|
||||
var alloca llvm.Value
|
||||
if instr.Block() != b.currentBlock {
|
||||
panic("block mismatch")
|
||||
}
|
||||
if !b.isInLoop() {
|
||||
if !isInLoop(instr.Block()) {
|
||||
// This can safely use a stack allocation.
|
||||
alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca")
|
||||
} else {
|
||||
|
||||
@@ -737,7 +737,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
prevBlock := b.GetInsertBlock()
|
||||
okBlock := b.insertBasicBlock("typeassert.ok")
|
||||
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)
|
||||
|
||||
// Retrieve the value from the interface if the type assert was
|
||||
|
||||
+8
-29
@@ -27,8 +27,6 @@ func (b *builder) defineIntrinsicFunction() {
|
||||
b.createStackSaveImpl()
|
||||
case name == "runtime.KeepAlive":
|
||||
b.createKeepAliveImpl()
|
||||
case name == "machine.keepAliveNoEscape":
|
||||
b.createMachineKeepAliveImpl()
|
||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||
b.createVolatileLoad()
|
||||
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.
|
||||
func (b *builder) createMemoryCopyImpl() {
|
||||
b.createFunctionStart(true)
|
||||
params := b.fn.Params[0:3]
|
||||
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())
|
||||
fnName := "llvm." + b.fn.Name() + ".p0.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
|
||||
llvmFn := b.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
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)
|
||||
}
|
||||
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
|
||||
@@ -151,20 +144,6 @@ func (b *builder) createAbiEscapeImpl() {
|
||||
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{
|
||||
"math.Ceil": "llvm.ceil.f64",
|
||||
"math.Exp": "llvm.exp.f64",
|
||||
|
||||
@@ -250,9 +250,6 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
||||
func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||
switch keyType := keyType.Underlying().(type) {
|
||||
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
|
||||
case *types.Pointer:
|
||||
return true
|
||||
|
||||
+6
-7
@@ -142,11 +142,6 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)
|
||||
llvmFn.AddAttributeAtIndex(i+1, nocapture)
|
||||
}
|
||||
if paramInfo.flags¶mIsReadonly != 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
|
||||
@@ -159,8 +154,6 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0))
|
||||
case "internal/abi.NoEscape":
|
||||
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":
|
||||
// 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.
|
||||
@@ -184,6 +177,12 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
||||
// be modified.
|
||||
llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 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":
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0))
|
||||
llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0))
|
||||
|
||||
@@ -74,14 +74,6 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||
return b.CreateCall(fnType, target, args, ""), nil
|
||||
|
||||
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.
|
||||
// Source: syscall(2) man page.
|
||||
args := []llvm.Value{}
|
||||
|
||||
-235
@@ -270,241 +270,6 @@ entry:
|
||||
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 #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" }
|
||||
|
||||
Vendored
-20
@@ -18,23 +18,3 @@ func deferMultiple() {
|
||||
}()
|
||||
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++ {
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -50,7 +50,7 @@ unsafe.String.throw: ; preds = %entry
|
||||
declare void @runtime.unsafeSlicePanic(ptr) #1
|
||||
|
||||
; 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:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3
|
||||
|
||||
Vendored
+30
-36
@@ -22,9 +22,6 @@ entry:
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i32 @llvm.smin.i32(i32, i32) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
@@ -56,9 +53,6 @@ entry:
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i8 @llvm.umin.i8(i8, i8) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
@@ -66,31 +60,24 @@ entry:
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i32 @llvm.umin.i32(i32, i32) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call float @llvm.minimum.f32(float %a, float %b)
|
||||
ret float %0
|
||||
%0 = fcmp olt float %a, %b
|
||||
%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
|
||||
define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call double @llvm.minimum.f64(double %a, double %b)
|
||||
ret double %0
|
||||
%0 = fcmp olt double %a, %b
|
||||
%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
|
||||
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:
|
||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||
@@ -104,7 +91,7 @@ entry:
|
||||
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
|
||||
define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||
@@ -113,9 +100,6 @@ entry:
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i32 @llvm.smax.i32(i32, i32) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
@@ -123,21 +107,16 @@ entry:
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
declare i32 @llvm.umax.i32(i32, i32) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call float @llvm.maximum.f32(float %a, float %b)
|
||||
ret float %0
|
||||
%0 = fcmp ogt float %a, %b
|
||||
%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
|
||||
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:
|
||||
%0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0
|
||||
%1 = insertvalue %runtime._string %0, i32 %a.len, 1
|
||||
@@ -160,7 +139,7 @@ entry:
|
||||
}
|
||||
|
||||
; 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
|
||||
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
|
||||
|
||||
; 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 #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 #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
||||
attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) }
|
||||
attributes #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
attributes #5 = { nounwind }
|
||||
|
||||
+28
-35
@@ -17,8 +17,8 @@ entry:
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), 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) #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) #9
|
||||
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 {
|
||||
entry:
|
||||
%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
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #2
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), 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) #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) #9
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -61,18 +61,18 @@ entry:
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 {
|
||||
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
|
||||
%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
|
||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 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
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, 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) #9
|
||||
%2 = load i32, ptr %n, align 4
|
||||
call void @runtime.printlock(ptr undef) #11
|
||||
call void @runtime.printint32(i32 %2, ptr undef) #11
|
||||
call void @runtime.printunlock(ptr undef) #11
|
||||
call void @runtime.printlock(ptr undef) #9
|
||||
call void @runtime.printint32(i32 %2, ptr undef) #9
|
||||
call void @runtime.printunlock(ptr undef) #9
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -102,14 +102,14 @@ declare void @runtime.printunlock(ptr) #2
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
|
||||
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
|
||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
store ptr %fn.context, ptr %1, align 4
|
||||
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||
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
|
||||
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, 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) #9
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ entry:
|
||||
%3 = load ptr, ptr %2, align 4
|
||||
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||
%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
|
||||
}
|
||||
|
||||
@@ -134,21 +134,16 @@ entry:
|
||||
; 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 {
|
||||
entry:
|
||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
||||
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #9
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
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
|
||||
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #1 {
|
||||
entry:
|
||||
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
||||
call void @runtime.chanClose(ptr %ch, ptr undef) #9
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -157,7 +152,7 @@ declare void @runtime.chanClose(ptr dereferenceable_or_null(36), ptr) #2
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 {
|
||||
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
|
||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
store ptr @"main$string", ptr %1, align 4
|
||||
@@ -165,15 +160,15 @@ entry:
|
||||
store i32 4, ptr %2, align 4
|
||||
%3 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
||||
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
|
||||
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
|
||||
%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) #9
|
||||
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
|
||||
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:
|
||||
%1 = load ptr, ptr %0, align 4
|
||||
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
@@ -182,7 +177,7 @@ entry:
|
||||
%5 = load i32, ptr %4, align 4
|
||||
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
||||
%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
|
||||
}
|
||||
|
||||
@@ -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 #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 #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||
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 #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 }
|
||||
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 = { 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 = { nounwind }
|
||||
|
||||
+34
-41
@@ -19,7 +19,7 @@ entry:
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ declare void @runtime.deadlock(ptr) #1
|
||||
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 {
|
||||
entry:
|
||||
%unpack.int = ptrtoint ptr %0 to i32
|
||||
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
|
||||
call void @runtime.deadlock(ptr undef) #11
|
||||
call void @main.regularFunction(i32 %unpack.int, ptr undef) #9
|
||||
call void @runtime.deadlock(ptr undef) #9
|
||||
unreachable
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unn
|
||||
entry:
|
||||
%unpack.int = ptrtoint ptr %0 to i32
|
||||
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
|
||||
}
|
||||
|
||||
@@ -64,21 +64,21 @@ entry:
|
||||
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%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
|
||||
call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, 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) #9
|
||||
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 @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #11
|
||||
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #11
|
||||
call void @runtime.trackPointer(ptr nonnull %0, 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) #9
|
||||
%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) #9
|
||||
store i32 5, ptr %0, align 4
|
||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 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
|
||||
call void @runtime.printlock(ptr undef) #11
|
||||
call void @runtime.printint32(i32 %2, ptr undef) #11
|
||||
call void @runtime.printunlock(ptr undef) #11
|
||||
call void @runtime.printlock(ptr undef) #9
|
||||
call void @runtime.printint32(i32 %2, ptr undef) #9
|
||||
call void @runtime.printunlock(ptr undef) #9
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ entry:
|
||||
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
%3 = load ptr, ptr %2, align 4
|
||||
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
|
||||
call void @runtime.deadlock(ptr undef) #11
|
||||
call void @runtime.deadlock(ptr undef) #9
|
||||
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 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #11
|
||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, 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) #9
|
||||
store i32 5, ptr %0, align 4
|
||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
store ptr %fn.context, ptr %1, align 4
|
||||
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||
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
|
||||
}
|
||||
|
||||
@@ -129,8 +129,8 @@ entry:
|
||||
%3 = load ptr, ptr %2, align 4
|
||||
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
|
||||
%5 = load ptr, ptr %4, align 4
|
||||
call void %5(i32 %1, ptr %3) #11
|
||||
call void @runtime.deadlock(ptr undef) #11
|
||||
call void %5(i32 %1, ptr %3) #9
|
||||
call void @runtime.deadlock(ptr undef) #9
|
||||
unreachable
|
||||
}
|
||||
|
||||
@@ -143,21 +143,16 @@ entry:
|
||||
; 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 {
|
||||
entry:
|
||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||
call void @llvm.memmove.p0.p0.i32(ptr align 1 %dst.data, ptr align 1 %src.data, i32 %copy.n, i1 false)
|
||||
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #9
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
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
|
||||
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(36) %ch, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
call void @runtime.chanClose(ptr %ch, ptr undef) #11
|
||||
call void @runtime.chanClose(ptr %ch, ptr undef) #9
|
||||
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 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #11
|
||||
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, 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) #9
|
||||
store ptr %itf.value, ptr %0, align 4
|
||||
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
store ptr @"main$string", ptr %1, align 4
|
||||
@@ -176,14 +171,14 @@ entry:
|
||||
store i32 4, ptr %2, align 4
|
||||
%3 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
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:
|
||||
%1 = load ptr, ptr %0, align 4
|
||||
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
|
||||
@@ -192,8 +187,8 @@ entry:
|
||||
%5 = load i32, ptr %4, align 4
|
||||
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12
|
||||
%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 @runtime.deadlock(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) #9
|
||||
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 #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 #7 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||
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 #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 }
|
||||
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 = { 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 = { nounwind }
|
||||
|
||||
Vendored
+36
-44
@@ -38,7 +38,7 @@ lookup.next: ; preds = %entry
|
||||
ret i32 %1
|
||||
|
||||
lookup.throw: ; preds = %entry
|
||||
call void @runtime.lookupPanic(ptr undef) #5
|
||||
call void @runtime.lookupPanic(ptr undef) #3
|
||||
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 {
|
||||
entry:
|
||||
%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
|
||||
call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, 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) #3
|
||||
store i32 1, ptr %varargs, align 4
|
||||
%0 = getelementptr inbounds nuw i8, ptr %varargs, i32 4
|
||||
store i32 2, ptr %0, align 4
|
||||
%1 = getelementptr inbounds nuw i8, ptr %varargs, i32 8
|
||||
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.newLen = extractvalue { ptr, i32, i32 } %append.new, 1
|
||||
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2
|
||||
%2 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0
|
||||
%3 = insertvalue { ptr, i32, i32 } %2, i32 %append.newLen, 1
|
||||
%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
|
||||
}
|
||||
|
||||
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
|
||||
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:
|
||||
%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.newLen = extractvalue { ptr, i32, i32 } %append.new, 1
|
||||
%append.newCap = extractvalue { ptr, i32, i32 } %append.new, 2
|
||||
%0 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0
|
||||
%1 = insertvalue { ptr, i32, i32 } %0, i32 %append.newLen, 1
|
||||
%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
|
||||
}
|
||||
|
||||
; 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 {
|
||||
entry:
|
||||
%copy.n = call i32 @llvm.umin.i32(i32 %dst.len, i32 %src.len)
|
||||
%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)
|
||||
%copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 4, ptr undef) #3
|
||||
ret i32 %copy.n
|
||||
}
|
||||
|
||||
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
|
||||
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
|
||||
declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
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
|
||||
|
||||
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
|
||||
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
||||
%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
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(ptr undef) #5
|
||||
call void @runtime.slicePanic(ptr undef) #3
|
||||
unreachable
|
||||
}
|
||||
|
||||
@@ -129,15 +123,15 @@ entry:
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%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
|
||||
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
||||
%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
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(ptr undef) #5
|
||||
call void @runtime.slicePanic(ptr undef) #3
|
||||
unreachable
|
||||
}
|
||||
|
||||
@@ -150,15 +144,15 @@ entry:
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%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
|
||||
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
||||
%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
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(ptr undef) #5
|
||||
call void @runtime.slicePanic(ptr undef) #3
|
||||
unreachable
|
||||
}
|
||||
|
||||
@@ -171,15 +165,15 @@ entry:
|
||||
|
||||
slice.next: ; preds = %entry
|
||||
%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
|
||||
%1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1
|
||||
%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
|
||||
|
||||
slice.throw: ; preds = %entry
|
||||
call void @runtime.slicePanic(ptr undef) #5
|
||||
call void @runtime.slicePanic(ptr undef) #3
|
||||
unreachable
|
||||
}
|
||||
|
||||
@@ -188,7 +182,7 @@ define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%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
|
||||
}
|
||||
|
||||
@@ -198,7 +192,7 @@ entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = trunc i64 %len to i32
|
||||
%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
|
||||
}
|
||||
|
||||
@@ -212,7 +206,7 @@ slicetoarray.next: ; preds = %entry
|
||||
ret ptr %s.data
|
||||
|
||||
slicetoarray.throw: ; preds = %entry
|
||||
call void @runtime.sliceToArrayPointerPanic(ptr undef) #5
|
||||
call void @runtime.sliceToArrayPointerPanic(ptr undef) #3
|
||||
unreachable
|
||||
}
|
||||
|
||||
@@ -222,8 +216,8 @@ declare void @runtime.sliceToArrayPointerPanic(ptr) #1
|
||||
define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%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
|
||||
call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, 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) #3
|
||||
br i1 false, label %slicetoarray.throw, label %slicetoarray.next
|
||||
|
||||
slicetoarray.next: ; preds = %entry
|
||||
@@ -248,11 +242,11 @@ unsafe.Slice.next: ; preds = %entry
|
||||
%5 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||
%6 = insertvalue { ptr, i32, i32 } %5, i32 %len, 1
|
||||
%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
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(ptr undef) #5
|
||||
call void @runtime.unsafeSlicePanic(ptr undef) #3
|
||||
unreachable
|
||||
}
|
||||
|
||||
@@ -272,11 +266,11 @@ unsafe.Slice.next: ; preds = %entry
|
||||
%4 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||
%5 = insertvalue { ptr, i32, i32 } %4, i32 %3, 1
|
||||
%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
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(ptr undef) #5
|
||||
call void @runtime.unsafeSlicePanic(ptr undef) #3
|
||||
unreachable
|
||||
}
|
||||
|
||||
@@ -296,11 +290,11 @@ unsafe.Slice.next: ; preds = %entry
|
||||
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
|
||||
%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
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(ptr undef) #5
|
||||
call void @runtime.unsafeSlicePanic(ptr undef) #3
|
||||
unreachable
|
||||
}
|
||||
|
||||
@@ -320,17 +314,15 @@ unsafe.Slice.next: ; preds = %entry
|
||||
%6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0
|
||||
%7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1
|
||||
%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
|
||||
|
||||
unsafe.Slice.throw: ; preds = %entry
|
||||
call void @runtime.unsafeSlicePanic(ptr undef) #5
|
||||
call void @runtime.unsafeSlicePanic(ptr undef) #3
|
||||
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 #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 #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
|
||||
attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
|
||||
attributes #5 = { nounwind }
|
||||
attributes #3 = { nounwind }
|
||||
|
||||
Vendored
+8
-8
@@ -31,13 +31,13 @@ entry:
|
||||
}
|
||||
|
||||
; 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:
|
||||
ret i32 %s.len
|
||||
}
|
||||
|
||||
; 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:
|
||||
%.not = icmp ult i32 %index, %s.len
|
||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||
@@ -55,16 +55,16 @@ lookup.throw: ; preds = %entry
|
||||
declare void @runtime.lookupPanic(ptr) #1
|
||||
|
||||
; 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:
|
||||
%0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3
|
||||
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
|
||||
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:
|
||||
%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
|
||||
@@ -72,16 +72,16 @@ entry:
|
||||
}
|
||||
|
||||
; 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:
|
||||
%0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3
|
||||
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
|
||||
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:
|
||||
%0 = zext i8 %x to i32
|
||||
%.not = icmp ugt i32 %s.len, %0
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
// Version of TinyGo.
|
||||
// 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
|
||||
// (like 0.30.0-dev-abcd012).
|
||||
|
||||
+11
-2
@@ -2,6 +2,7 @@ package interp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -10,17 +11,25 @@ import (
|
||||
)
|
||||
|
||||
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{
|
||||
"basic",
|
||||
"phi",
|
||||
"slice-copy",
|
||||
"consteval",
|
||||
"intrinsics",
|
||||
"copy",
|
||||
"interface",
|
||||
"revert",
|
||||
"alloc",
|
||||
} {
|
||||
name := name // make local to this closure
|
||||
if name == "slice-copy" && llvmVersion < 14 {
|
||||
continue
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest(t, "testdata/"+name)
|
||||
|
||||
+51
-22
@@ -312,28 +312,33 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
fmt.Fprintln(os.Stderr, indent+"runtime.alloc:", size, "->", ptr)
|
||||
}
|
||||
locals[inst.localIndex] = ptr
|
||||
case strings.HasPrefix(callFn.name, "llvm.umin."):
|
||||
locals[inst.localIndex] = makeLiteralInt(min(operands[1].Uint(r), operands[2].Uint(r)), inst.llvmInst.Type().IntTypeWidth())
|
||||
case strings.HasPrefix(callFn.name, "llvm.smin."):
|
||||
locals[inst.localIndex] = makeLiteralInt(uint64(min(operands[1].Int(r), operands[2].Int(r))), inst.llvmInst.Type().IntTypeWidth())
|
||||
case strings.HasPrefix(callFn.name, "llvm.umax."):
|
||||
locals[inst.localIndex] = makeLiteralInt(max(operands[1].Uint(r), operands[2].Uint(r)), inst.llvmInst.Type().IntTypeWidth())
|
||||
case strings.HasPrefix(callFn.name, "llvm.smax."):
|
||||
locals[inst.localIndex] = makeLiteralInt(uint64(max(operands[1].Int(r), operands[2].Int(r))), 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.
|
||||
if operands[4].Uint(r) != 0 {
|
||||
// This is a volatile copy/move.
|
||||
err := r.runAtRuntime(fn, inst, locals, &mem, indent)
|
||||
if err != nil {
|
||||
return nil, mem, err
|
||||
}
|
||||
continue
|
||||
case callFn.name == "runtime.sliceCopy":
|
||||
// sliceCopy implements the built-in copy function for slices.
|
||||
// It is implemented here so that it can be used even if the
|
||||
// runtime implementation is not available. Doing it this way
|
||||
// may also be faster.
|
||||
// Code:
|
||||
// func sliceCopy(dst, src unsafe.Pointer, dstLen, srcLen uintptr, elemSize uintptr) int {
|
||||
// n := srcLen
|
||||
// if n > dstLen {
|
||||
// n = dstLen
|
||||
// }
|
||||
// memmove(dst, src, n*elemSize)
|
||||
// return int(n)
|
||||
// }
|
||||
dstLen := operands[3].Uint(r)
|
||||
srcLen := operands[4].Uint(r)
|
||||
elemSize := operands[5].Uint(r)
|
||||
n := srcLen
|
||||
if n > dstLen {
|
||||
n = dstLen
|
||||
}
|
||||
nBytes := operands[3].Uint(r)
|
||||
if nBytes != 0 {
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"copy:", operands[1], operands[2], n)
|
||||
}
|
||||
if n != 0 {
|
||||
// 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
|
||||
// even though copying a nil slice is allowed.
|
||||
dst, err := operands[1].asPointer(r)
|
||||
@@ -358,10 +363,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
}
|
||||
continue
|
||||
}
|
||||
nBytes := uint32(n * elemSize)
|
||||
srcObj := mem.get(src.index())
|
||||
dstObj := mem.getWritable(dst.index())
|
||||
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
|
||||
// a //go:embed slice, which is not available at interp
|
||||
// time.
|
||||
@@ -374,10 +380,33 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
}
|
||||
dstBuf := dstObj.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
|
||||
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":
|
||||
// This function must be implemented manually as it is normally
|
||||
// implemented by the interface lowering pass.
|
||||
|
||||
Vendored
-68
@@ -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)
|
||||
Vendored
-27
@@ -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) }
|
||||
Vendored
-52
@@ -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)
|
||||
Vendored
-11
@@ -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
|
||||
}
|
||||
Vendored
+124
@@ -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
|
||||
}
|
||||
Vendored
+42
@@ -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
|
||||
}
|
||||
@@ -256,7 +256,6 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
|
||||
"reflect/": false,
|
||||
"runtime/": false,
|
||||
"sync/": true,
|
||||
"testing/": true,
|
||||
"tinygo/": false,
|
||||
"unique/": false,
|
||||
}
|
||||
|
||||
@@ -1635,7 +1635,6 @@ func main() {
|
||||
cpuprofile := flag.String("cpuprofile", "", "cpuprofile output")
|
||||
monitor := flag.Bool("monitor", false, "enable 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.
|
||||
printIR := flag.Bool("internal-printir", false, "print LLVM IR")
|
||||
@@ -1713,16 +1712,6 @@ func main() {
|
||||
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{
|
||||
GOOS: goenv.Get("GOOS"),
|
||||
GOARCH: goenv.Get("GOARCH"),
|
||||
@@ -1759,7 +1748,6 @@ func main() {
|
||||
Timeout: *timeout,
|
||||
WITPackage: witPackage,
|
||||
WITWorld: witWorld,
|
||||
GoCompatibility: *gocompatibility,
|
||||
}
|
||||
if *printCommands {
|
||||
options.PrintCommands = printCommand
|
||||
|
||||
@@ -222,7 +222,6 @@ func TestBuild(t *testing.T) {
|
||||
// to be sure.
|
||||
t.Parallel()
|
||||
options := optionsFromOSARCH("linux/mipsle/softfloat", sema)
|
||||
emuCheck(t, options)
|
||||
runTest("cgo/", options, t, nil, nil)
|
||||
})
|
||||
} else if runtime.GOOS == "windows" {
|
||||
|
||||
@@ -13,14 +13,12 @@ const (
|
||||
// 64-bit int => bits = 6
|
||||
sizeBits = 4 + unsafe.Sizeof(uintptr(0))/4
|
||||
|
||||
ptrAlign = unsafe.Alignof(uintptr(0))
|
||||
|
||||
sizeShift = sizeBits + 1
|
||||
|
||||
NoPtrs = Layout((0 << sizeShift) | (1 << 1) | 1)
|
||||
Pointer = Layout((1 << sizeShift) | ((unsafe.Sizeof(unsafe.Pointer(nil)) / ptrAlign) << 1) | 1)
|
||||
String = Layout((1 << sizeShift) | ((unsafe.Sizeof("") / ptrAlign) << 1) | 1)
|
||||
Slice = Layout((1 << sizeShift) | ((unsafe.Sizeof([]byte{}) / ptrAlign) << 1) | 1)
|
||||
NoPtrs = Layout(uintptr(0b0<<sizeShift) | uintptr(0b1<<1) | uintptr(1))
|
||||
Pointer = Layout(uintptr(0b1<<sizeShift) | uintptr(0b1<<1) | uintptr(1))
|
||||
String = Layout(uintptr(0b01<<sizeShift) | uintptr(0b10<<1) | uintptr(1))
|
||||
Slice = Layout(uintptr(0b001<<sizeShift) | uintptr(0b11<<1) | uintptr(1))
|
||||
)
|
||||
|
||||
func (l Layout) AsPtr() unsafe.Pointer { return unsafe.Pointer(l) }
|
||||
|
||||
@@ -86,64 +86,6 @@ func (v Value) Interface() interface{} {
|
||||
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
|
||||
// be subject to the isExported check.
|
||||
func valueInterfaceUnsafe(v Value) interface{} {
|
||||
@@ -1796,9 +1738,6 @@ func (e *ValueError) Error() string {
|
||||
//go:linkname memcpy runtime.memcpy
|
||||
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
|
||||
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
|
||||
|
||||
//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
|
||||
// dst has been filled or src has been exhausted.
|
||||
@@ -1837,10 +1779,7 @@ func Copy(dst, src Value) int {
|
||||
dst.checkRO()
|
||||
}
|
||||
|
||||
minLen := min(dstlen, srclen)
|
||||
elemSize := dst.typecode.elem().Size()
|
||||
memmove(dstbuf, srcbuf, minLen*elemSize)
|
||||
return int(minLen)
|
||||
return sliceCopy(dstbuf, srcbuf, dstlen, srclen, dst.typecode.elem().Size())
|
||||
}
|
||||
|
||||
func buflen(v Value) (unsafe.Pointer, uintptr) {
|
||||
@@ -1871,7 +1810,7 @@ func buflen(v Value) (unsafe.Pointer, uintptr) {
|
||||
}
|
||||
|
||||
//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
|
||||
func extendSlice(v Value, n int) sliceHeader {
|
||||
@@ -1884,10 +1823,7 @@ func extendSlice(v Value, n int) sliceHeader {
|
||||
old = *(*sliceHeader)(v.value)
|
||||
}
|
||||
|
||||
elem := v.typecode.elem()
|
||||
elemSize := elem.Size()
|
||||
elemLayout := elem.gcLayout()
|
||||
nbuf, nlen, ncap := sliceGrow(old.data, old.len, old.cap, old.len+uintptr(n), elemSize, elemLayout)
|
||||
nbuf, nlen, ncap := sliceGrow(old.data, old.len, old.cap, old.len+uintptr(n), v.typecode.elem().Size())
|
||||
|
||||
return sliceHeader{
|
||||
data: nbuf,
|
||||
@@ -1926,10 +1862,8 @@ func AppendSlice(s, t Value) Value {
|
||||
}
|
||||
sSlice := (*sliceHeader)(s.value)
|
||||
tSlice := (*sliceHeader)(t.value)
|
||||
elem := s.typecode.elem()
|
||||
elemSize := elem.Size()
|
||||
elemLayout := elem.gcLayout()
|
||||
ptr, len, cap := sliceAppend(sSlice.data, tSlice.data, sSlice.len, sSlice.cap, tSlice.len, elemSize, elemLayout)
|
||||
elemSize := s.typecode.elem().Size()
|
||||
ptr, len, cap := sliceAppend(sSlice.data, tSlice.data, sSlice.len, sSlice.cap, tSlice.len, elemSize)
|
||||
result := &sliceHeader{
|
||||
data: ptr,
|
||||
len: len,
|
||||
|
||||
@@ -108,10 +108,6 @@ func (*stackState) unwind()
|
||||
func (t *Task) Resume() {
|
||||
// The current task must be saved and restored because this can nest on WASM with JS.
|
||||
prevTask := currentTask
|
||||
if prevTask == nil {
|
||||
// Save the system stack pointer.
|
||||
saveStackPointer()
|
||||
}
|
||||
t.gcData.swap()
|
||||
currentTask = t
|
||||
if !t.state.launched {
|
||||
@@ -127,9 +123,6 @@ func (t *Task) Resume() {
|
||||
}
|
||||
}
|
||||
|
||||
//go:linkname saveStackPointer runtime.saveStackPointer
|
||||
func saveStackPointer()
|
||||
|
||||
//export tinygo_rewind
|
||||
func (*state) rewind()
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build scheduler.tasks && (esp32 || esp32s3)
|
||||
//go:build scheduler.tasks && esp32
|
||||
|
||||
package task
|
||||
|
||||
|
||||
@@ -120,21 +120,15 @@ int tinygo_task_start(uintptr_t fn, void *args, void *task, pthread_t *thread, u
|
||||
#endif
|
||||
pthread_attr_t attrs;
|
||||
pthread_attr_init(&attrs);
|
||||
pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
|
||||
pthread_attr_setstacksize(&attrs, stackSize);
|
||||
int result = pthread_create(thread, &attrs, &start_wrapper, &state);
|
||||
pthread_attr_destroy(&attrs);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Wait until the thread has been created and read all state_pass variables.
|
||||
#if __APPLE__
|
||||
dispatch_semaphore_wait(state.startlock, DISPATCH_TIME_FOREVER);
|
||||
dispatch_release(state.startlock);
|
||||
#else
|
||||
sem_wait(&state.startlock);
|
||||
sem_destroy(&state.startlock);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
|
||||
@@ -23,15 +23,16 @@ type state struct {
|
||||
// is needed to be able to scan the stack.
|
||||
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.
|
||||
QueueNext *Task
|
||||
|
||||
// Semaphore to pause/resume the thread atomically.
|
||||
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.
|
||||
@@ -95,9 +96,6 @@ func (t *Task) Resume() {
|
||||
t.state.pauseSem.Post()
|
||||
}
|
||||
|
||||
// otherGoroutines is the total number of live goroutines minus one.
|
||||
var otherGoroutines uint32
|
||||
|
||||
// Start a new OS thread.
|
||||
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||
t := &Task{}
|
||||
@@ -117,7 +115,6 @@ func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||
}
|
||||
t.state.QueueNext = activeTasks
|
||||
activeTasks = t
|
||||
otherGoroutines++
|
||||
activeTaskLock.Unlock()
|
||||
}
|
||||
|
||||
@@ -138,7 +135,6 @@ func taskExited(t *Task) {
|
||||
break
|
||||
}
|
||||
}
|
||||
otherGoroutines--
|
||||
activeTaskLock.Unlock()
|
||||
|
||||
// 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.
|
||||
var scanWaitGroup waitGroup
|
||||
|
||||
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
|
||||
)
|
||||
// Futex to wait on until all tasks have finished scanning the stack.
|
||||
// This is basically a sync.WaitGroup.
|
||||
var scanDoneFutex Futex
|
||||
|
||||
// GC scan phase. Because we need to stop the world while scanning, this kinda
|
||||
// needs to be done in the tasks package.
|
||||
@@ -192,71 +155,65 @@ const (
|
||||
func GCStopWorldAndScan() {
|
||||
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
|
||||
// in the stop-the-world phase.
|
||||
activeTaskLock.Lock()
|
||||
// Don't allow new goroutines to be started while pausing/resuming threads
|
||||
// in the stop-the-world phase.
|
||||
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.
|
||||
for t := activeTasks; t != nil; t = t.state.QueueNext {
|
||||
if t != current {
|
||||
tinygo_task_send_gc_signal(t.state.thread)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the threads to finish stopping.
|
||||
scanWaitGroup.wait()
|
||||
}
|
||||
|
||||
// Scan other thread stacks.
|
||||
// Pause all other threads.
|
||||
numOtherThreads := uint32(0)
|
||||
for t := activeTasks; t != nil; t = t.state.QueueNext {
|
||||
if t != current {
|
||||
markRoots(t.state.stackBottom, t.state.stackTop)
|
||||
numOtherThreads++
|
||||
tinygo_task_send_gc_signal(t.state.thread)
|
||||
}
|
||||
}
|
||||
|
||||
// Store the number of threads to wait for in the futex.
|
||||
// This is the equivalent of doing an initial wg.Add(numOtherThreads).
|
||||
scanDoneFutex.Store(numOtherThreads)
|
||||
|
||||
// Scan the current stack, and all current registers.
|
||||
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).
|
||||
gcScanGlobals()
|
||||
}
|
||||
|
||||
// After the GC is done scanning, resume all other threads.
|
||||
//
|
||||
// This must only be called after a GCStopWorldAndScan call.
|
||||
func GCResumeWorld() {
|
||||
// NOTE: This does not need to be atomic.
|
||||
if gcState.Load() == gcStateResumed {
|
||||
// This is already resumed.
|
||||
return
|
||||
current := Current()
|
||||
|
||||
// Wake each paused thread for the second time, so they will resume normal
|
||||
// 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.
|
||||
activeTaskLock.Unlock()
|
||||
}
|
||||
|
||||
//go:linkname markRoots runtime.markRoots
|
||||
func markRoots(start, end uintptr)
|
||||
|
||||
// Scan globals, implemented in the runtime package.
|
||||
func gcScanGlobals()
|
||||
|
||||
@@ -264,27 +221,34 @@ var stackScanLock PMutex
|
||||
|
||||
//export tinygo_task_gc_pause
|
||||
func tingyo_task_gc_pause(sig int32) {
|
||||
// Write the entrty stack pointer to the state.
|
||||
Current().state.stackBottom = uintptr(stacksave())
|
||||
// Wait until we get the signal to start scanning the stack.
|
||||
Current().state.gcSem.Wait()
|
||||
|
||||
// Notify the GC that we are stopped.
|
||||
scanWaitGroup.done()
|
||||
// Scan the thread stack.
|
||||
// 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.
|
||||
for gcState.Load() == gcStateStopped {
|
||||
gcState.Wait(gcStateStopped)
|
||||
// Equivalent of wg.Done(): subtract one from the futex and if the result is
|
||||
// 0 (meaning we were the last in the waitgroup), wake the waiting thread.
|
||||
n := uint32(1)
|
||||
if scanDoneFutex.Add(-n) == 0 {
|
||||
scanDoneFutex.Wake()
|
||||
}
|
||||
|
||||
// Notify the GC that we have resumed.
|
||||
scanWaitGroup.done()
|
||||
// Wait until we get the signal we can resume normally (after the mark phase
|
||||
// has finished).
|
||||
Current().state.gcSem.Wait()
|
||||
}
|
||||
|
||||
//go:export tinygo_scanCurrentStack
|
||||
func scanCurrentStack()
|
||||
|
||||
//go:linkname stacksave runtime.stacksave
|
||||
func stacksave() unsafe.Pointer
|
||||
|
||||
// Return the highest address of the current stack.
|
||||
func StackTop() uintptr {
|
||||
return Current().state.stackTop
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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,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
|
||||
|
||||
@@ -43,11 +43,6 @@ type BlockDevice interface {
|
||||
io.ReaderAt
|
||||
|
||||
// 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
|
||||
|
||||
// Size returns the number of bytes in this block device.
|
||||
|
||||
+1
-31
@@ -1,9 +1,6 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
)
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrTimeoutRNG = errors.New("machine: RNG Timeout")
|
||||
@@ -65,30 +62,3 @@ func (p Pin) Low() {
|
||||
type ADC struct {
|
||||
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)
|
||||
|
||||
@@ -337,7 +337,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
|
||||
|
||||
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||
sendUSBPacket(ep, data)
|
||||
sendUSBPacket(ep, data, 0)
|
||||
|
||||
// clear transfer complete flag
|
||||
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
|
||||
//
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte) {
|
||||
// Select the corresponding buffer.
|
||||
buffer := udd_ep_control_cache_buffer[:]
|
||||
if ep != 0 {
|
||||
buffer = udd_ep_in_cache_buffer[ep][:]
|
||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||
l := uint16(len(data))
|
||||
if 0 < maxsize && maxsize < l {
|
||||
l = maxsize
|
||||
}
|
||||
|
||||
// Copy the packet to the buffer.
|
||||
copy(buffer[:len(data)], data)
|
||||
|
||||
// Select the corresponding endpoint descriptor.
|
||||
endpoint := &usbEndpointDescriptors[ep].DeviceDescBank[1]
|
||||
|
||||
// Set the endpoint address.
|
||||
endpoint.ADDR.Set(uint32(uintptr(unsafe.Pointer(unsafe.SliceData(buffer)))))
|
||||
// Set endpoint address for sending data
|
||||
if ep == 0 {
|
||||
copy(udd_ep_control_cache_buffer[:], data[:l])
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_control_cache_buffer))))
|
||||
} else {
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data[:l])
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
}
|
||||
|
||||
// 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
|
||||
endpoint.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.ClearBits(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) {
|
||||
|
||||
@@ -340,7 +340,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
|
||||
|
||||
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||
sendUSBPacket(ep, data)
|
||||
sendUSBPacket(ep, data, 0)
|
||||
|
||||
// clear transfer complete flag
|
||||
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
|
||||
//
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte) {
|
||||
// Select the corresponding buffer.
|
||||
buffer := udd_ep_control_cache_buffer[:]
|
||||
if ep != 0 {
|
||||
buffer = udd_ep_in_cache_buffer[ep][:]
|
||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||
l := uint16(len(data))
|
||||
if 0 < maxsize && maxsize < l {
|
||||
l = maxsize
|
||||
}
|
||||
|
||||
// Copy the packet to the buffer.
|
||||
copy(buffer[:len(data)], data)
|
||||
|
||||
// Select the corresponding endpoint descriptor.
|
||||
endpoint := &usbEndpointDescriptors[ep].DeviceDescBank[1]
|
||||
|
||||
// Set the endpoint address.
|
||||
endpoint.ADDR.Set(uint32(uintptr(unsafe.Pointer(unsafe.SliceData(buffer)))))
|
||||
// Set endpoint address for sending data
|
||||
if ep == 0 {
|
||||
copy(udd_ep_control_cache_buffer[:], data[:l])
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_control_cache_buffer))))
|
||||
} else {
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data[:l])
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
}
|
||||
|
||||
// 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
|
||||
endpoint.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.ClearBits(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) {
|
||||
|
||||
@@ -519,10 +519,6 @@ type Serialer interface {
|
||||
RTS() bool
|
||||
}
|
||||
|
||||
func initUSB() {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
// USB Serial/JTAG Controller
|
||||
// See esp32-c3_technical_reference_manual_en.pdf
|
||||
// pg. 736
|
||||
|
||||
@@ -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, 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, 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
|
||||
@@ -3,9 +3,7 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/nrf"
|
||||
"errors"
|
||||
"internal/binary"
|
||||
"runtime/interrupt"
|
||||
"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.
|
||||
// This method assumes that the destination is already erased.
|
||||
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() {
|
||||
return 0, errFlashCannotWritePastEOF
|
||||
}
|
||||
@@ -393,35 +387,6 @@ func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
|
||||
address := FlashDataStart() + uintptr(off)
|
||||
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()
|
||||
|
||||
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
|
||||
// EraseBlockSize to map addresses to blocks.
|
||||
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())
|
||||
waitWhileFlashBusy()
|
||||
|
||||
@@ -516,52 +447,3 @@ func waitWhileFlashBusy() {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,5 +69,3 @@ const eraseBlockSizeValue = 4096
|
||||
func eraseBlockSize() int64 {
|
||||
return eraseBlockSizeValue
|
||||
}
|
||||
|
||||
const spiMaxBufferSize = 255 // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
|
||||
|
||||
@@ -90,5 +90,3 @@ const eraseBlockSizeValue = 4096
|
||||
func eraseBlockSize() int64 {
|
||||
return eraseBlockSizeValue
|
||||
}
|
||||
|
||||
const spiMaxBufferSize = 0xffff // from the datasheet: TXD.MAXCNT and RXD.MAXCNT
|
||||
|
||||
@@ -108,9 +108,3 @@ const eraseBlockSizeValue = 4096
|
||||
func eraseBlockSize() int64 {
|
||||
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}
|
||||
|
||||
@@ -256,7 +256,7 @@ func initEndpoint(ep, config uint32) {
|
||||
|
||||
// SendUSBInPacket sends a packet for USBHID (interrupt in / bulk in).
|
||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||
sendUSBPacket(ep, data)
|
||||
sendUSBPacket(ep, data, 0)
|
||||
|
||||
// clear transfer complete flag
|
||||
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
|
||||
//
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte) {
|
||||
// Select the corresponding buffer.
|
||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||
count := len(data)
|
||||
var buffer []byte
|
||||
if 0 < int(maxsize) && int(maxsize) < count {
|
||||
count = int(maxsize)
|
||||
}
|
||||
|
||||
if ep == 0 {
|
||||
buffer = udd_ep_control_cache_buffer[:]
|
||||
copy(udd_ep_control_cache_buffer[:], data[:count])
|
||||
if count > usb.EndpointPacketSize {
|
||||
// The packet must be sent in chunks.
|
||||
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
|
||||
count = usb.EndpointPacketSize
|
||||
}
|
||||
sendViaEPIn(
|
||||
ep,
|
||||
&udd_ep_control_cache_buffer[0],
|
||||
count,
|
||||
)
|
||||
} else {
|
||||
buffer = udd_ep_in_cache_buffer[ep][:]
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data[:count])
|
||||
sendViaEPIn(
|
||||
ep,
|
||||
&udd_ep_in_cache_buffer[ep][0],
|
||||
count,
|
||||
)
|
||||
}
|
||||
|
||||
// Copy the packet to the buffer.
|
||||
copy(buffer[:len(data)], data)
|
||||
|
||||
// Send the first chunk of the packet.
|
||||
sendViaEPIn(
|
||||
ep,
|
||||
&buffer[0],
|
||||
count,
|
||||
)
|
||||
}
|
||||
|
||||
func handleEndpointRx(ep uint32) []byte {
|
||||
|
||||
@@ -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)
|
||||
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)))
|
||||
|
||||
// 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 {
|
||||
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)))
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
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.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
|
||||
evt = I2CFinish
|
||||
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.
|
||||
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.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)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -12,8 +12,6 @@ func CPUFrequency() uint32 {
|
||||
return 64000000
|
||||
}
|
||||
|
||||
var adcVDDHPin = Pin(254) // special pin number for VDDH on the nrf52840
|
||||
|
||||
// InitADC initializes the registers needed for ADC.
|
||||
func InitADC() {
|
||||
// Enable ADC.
|
||||
@@ -28,7 +26,7 @@ func InitADC() {
|
||||
// Samples can be 1(default), 2, 4, 8, 16, 32, 64, 128, 256 samples
|
||||
func (a *ADC) Configure(config ADCConfig) {
|
||||
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_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.
|
||||
func (a *ADC) Get() uint16 {
|
||||
var adcPin uint32
|
||||
var pwmPin uint32
|
||||
var rawValue volatile.Register16
|
||||
|
||||
switch a.Pin {
|
||||
case 2:
|
||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput0
|
||||
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput0
|
||||
case 3:
|
||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput1
|
||||
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput1
|
||||
case 4:
|
||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput2
|
||||
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput2
|
||||
case 5:
|
||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput3
|
||||
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput3
|
||||
case 28:
|
||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput4
|
||||
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput4
|
||||
case 29:
|
||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput5
|
||||
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput5
|
||||
case 30:
|
||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput6
|
||||
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput6
|
||||
case 31:
|
||||
adcPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput7
|
||||
case adcVDDHPin:
|
||||
if Device == "nrf52840" {
|
||||
adcPin = 0x0D // VDDHDIV5 on the nrf52840
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput7
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Note: rawValue doesn't need to be kept alive for the GC, since the
|
||||
// volatile read later will force it to stay alive.
|
||||
nrf.SAADC.RESULT.PTR.Set(uint32(unsafeNoEscape(unsafe.Pointer(&rawValue))))
|
||||
nrf.SAADC.RESULT.PTR.Set(uint32(uintptr(unsafe.Pointer(&rawValue))))
|
||||
nrf.SAADC.RESULT.MAXCNT.Set(1) // One sample
|
||||
|
||||
// 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
|
||||
// dropped and if len(w) < len(r) extra 0 bytes will be sent.
|
||||
func (spi *SPI) Tx(w, r []byte) error {
|
||||
// Unfortunately the hardware (on the nrf52832) only supports a limited
|
||||
// amount of bytes in the buffers (depending on the chip), so if either w or
|
||||
// r is longer than that the transfer needs to be broken up in pieces.
|
||||
// Unfortunately the hardware (on the nrf52832) only supports up to 255
|
||||
// bytes in the buffers, so if either w or r is longer than that the
|
||||
// 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 {
|
||||
// Prepare the SPI transfer: set the DMA pointers and lengths.
|
||||
// read buffer
|
||||
nr := uint32(len(r))
|
||||
if nr > 0 {
|
||||
if nr > spiMaxBufferSize {
|
||||
nr = spiMaxBufferSize
|
||||
if nr > 255 {
|
||||
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:]
|
||||
}
|
||||
spi.Bus.RXD.MAXCNT.Set(nr)
|
||||
@@ -329,10 +322,10 @@ func (spi *SPI) Tx(w, r []byte) error {
|
||||
// write buffer
|
||||
nw := uint32(len(w))
|
||||
if nw > 0 {
|
||||
if nw > spiMaxBufferSize {
|
||||
nw = spiMaxBufferSize
|
||||
if nw > 255 {
|
||||
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:]
|
||||
}
|
||||
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).
|
||||
spi.Bus.TASKS_START.Set(1)
|
||||
for spi.Bus.EVENTS_END.Get() == 0 {
|
||||
gosched()
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,3 @@ package machine
|
||||
func GetRNG() (ret uint32, err error) {
|
||||
return getRNG()
|
||||
}
|
||||
|
||||
func isSoftDeviceEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@ import (
|
||||
|
||||
// avoid a heap allocation in GetRNG.
|
||||
var (
|
||||
bytesAvailable uint8
|
||||
buf [4]uint8
|
||||
softdeviceEnabled uint8
|
||||
bytesAvailable uint8
|
||||
buf [4]uint8
|
||||
|
||||
errNoSoftDeviceSupport = errors.New("rng: softdevice not supported on this device")
|
||||
errNotEnoughRandomData = errors.New("rng: not enough random data available")
|
||||
@@ -23,7 +24,9 @@ var (
|
||||
func GetRNG() (ret uint32, err error) {
|
||||
// First check whether the SoftDevice is 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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// This function is defined in the runtime, but we need it too.
|
||||
//
|
||||
//go:linkname isSoftDeviceEnabled runtime.isSoftDeviceEnabled
|
||||
func isSoftDeviceEnabled() bool
|
||||
|
||||
@@ -49,10 +49,15 @@ func machineInit() {
|
||||
bits := ^uint32(initDontReset)
|
||||
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()
|
||||
|
||||
// Peripheral clocks should now all be running
|
||||
unresetBlockWait(initUnreset)
|
||||
unresetBlockWait(RESETS_RESET_Msk)
|
||||
|
||||
// DBGPAUSE pauses the timer when a debugger is connected. This prevents
|
||||
// sleep functions from ever returning, so disable it.
|
||||
|
||||
@@ -128,7 +128,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
|
||||
const ackTimeout = 570
|
||||
|
||||
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.
|
||||
t := timer.timeElapsed()
|
||||
|
||||
@@ -131,7 +131,7 @@ func handleUSBSetAddress(setup usb.Setup) bool {
|
||||
const ackTimeout = 570
|
||||
|
||||
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.
|
||||
t := timer.timeElapsed()
|
||||
|
||||
@@ -15,8 +15,13 @@ const (
|
||||
_NUMIRQ = 32
|
||||
rp2350ExtraReg = 0
|
||||
RESETS_RESET_Msk = 0x01ffffff
|
||||
initUnreset = rp.RESETS_RESET_IO_BANK0 |
|
||||
rp.RESETS_RESET_TIMER
|
||||
initUnreset = rp.RESETS_RESET_ADC |
|
||||
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 |
|
||||
rp.RESETS_RESET_PADS_QSPI |
|
||||
rp.RESETS_RESET_PLL_USB |
|
||||
@@ -119,31 +124,6 @@ const (
|
||||
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.
|
||||
func (p Pin) Configure(config PinConfig) {
|
||||
if p == NoPin {
|
||||
|
||||
@@ -16,8 +16,12 @@ const (
|
||||
_NUMIRQ = 51
|
||||
notimpl = "rp2350: not implemented"
|
||||
RESETS_RESET_Msk = 0x1fffffff
|
||||
initUnreset = rp.RESETS_RESET_IO_BANK0 |
|
||||
rp.RESETS_RESET_TIMER0
|
||||
initUnreset = rp.RESETS_RESET_ADC |
|
||||
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 |
|
||||
rp.RESETS_RESET_SYSCFG |
|
||||
rp.RESETS_RESET_PLL_USB |
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/rp"
|
||||
|
||||
// Analog pins on RP2350a.
|
||||
const (
|
||||
ADC0 Pin = GPIO26
|
||||
@@ -14,28 +12,3 @@ const (
|
||||
// fifth ADC channel.
|
||||
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
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
package machine
|
||||
|
||||
import "device/rp"
|
||||
|
||||
// RP2350B has additional pins.
|
||||
|
||||
const (
|
||||
@@ -48,28 +46,3 @@ var (
|
||||
PWM10 = getPWMGroup(10)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@ var adcAref uint32
|
||||
|
||||
// InitADC resets the ADC peripheral.
|
||||
func InitADC() {
|
||||
// reset ADC controller
|
||||
resetBlock(rp.RESETS_RESET_ADC)
|
||||
unresetBlockWait(rp.RESETS_RESET_ADC)
|
||||
|
||||
rp.RESETS.RESET.SetBits(rp.RESETS_RESET_ADC)
|
||||
rp.RESETS.RESET.ClearBits(rp.RESETS_RESET_ADC)
|
||||
for !rp.RESETS.RESET_DONE.HasBits(rp.RESETS_RESET_ADC) {
|
||||
}
|
||||
// enable ADC
|
||||
rp.ADC.CS.Set(rp.ADC_CS_EN)
|
||||
adcAref = 3300
|
||||
|
||||
@@ -259,7 +259,10 @@ func (i2c *I2C) init(config I2CConfig) error {
|
||||
//go:inline
|
||||
func (i2c *I2C) reset() {
|
||||
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.
|
||||
@@ -273,7 +276,7 @@ func (i2c *I2C) deinit() (resetVal uint32) {
|
||||
resetVal = rp.RESETS_RESET_I2C1
|
||||
}
|
||||
// Perform I2C reset.
|
||||
resetBlock(resetVal)
|
||||
rp.RESETS.RESET.SetBits(resetVal)
|
||||
|
||||
return resetVal
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ func (p *pwmGroup) Period() uint64 {
|
||||
top := p.getWrap()
|
||||
phc := p.getPhaseCorrect()
|
||||
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.
|
||||
|
||||
@@ -108,7 +108,7 @@ func (spi *SPI) SetBaudRate(br uint32) error {
|
||||
var prescale, postdiv uint32
|
||||
freq := CPUFrequency()
|
||||
for prescale = 2; prescale < 255; prescale += 2 {
|
||||
if uint64(freq) < uint64((prescale+2)*256)*uint64(br) {
|
||||
if freq < (prescale+2)*256*br {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -165,9 +165,27 @@ func (spi *SPI) Configure(config SPIConfig) error {
|
||||
config.SDI = SPI1_SDI_PIN
|
||||
}
|
||||
}
|
||||
if err := spi.validPins(config); err != nil {
|
||||
return err
|
||||
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
|
||||
}
|
||||
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = defaultBaud
|
||||
}
|
||||
@@ -212,7 +230,10 @@ func (spi *SPI) setFormat(mode uint8) {
|
||||
//go:inline
|
||||
func (spi *SPI) reset() {
|
||||
resetVal := spi.deinit()
|
||||
unresetBlockWait(resetVal)
|
||||
rp.RESETS.RESET.ClearBits(resetVal)
|
||||
// Wait until reset is done.
|
||||
for !rp.RESETS.RESET_DONE.HasBits(resetVal) {
|
||||
}
|
||||
}
|
||||
|
||||
//go:inline
|
||||
@@ -224,7 +245,7 @@ func (spi *SPI) deinit() (resetVal uint32) {
|
||||
resetVal = rp.RESETS_RESET_SPI1
|
||||
}
|
||||
// Perform SPI reset.
|
||||
resetBlock(resetVal)
|
||||
rp.RESETS.RESET.SetBits(resetVal)
|
||||
return resetVal
|
||||
}
|
||||
|
||||
@@ -288,7 +309,7 @@ func (spi *SPI) tx(tx []byte) error {
|
||||
// - set data size to single bytes
|
||||
// - set the DREQ so that the DMA will fill the SPI FIFO as needed
|
||||
// - 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.TRANS_COUNT.Set(uint32(len(tx)))
|
||||
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 {
|
||||
}
|
||||
|
||||
// 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
|
||||
// overflown. We have to clean up this mess now.
|
||||
|
||||
|
||||
@@ -148,8 +148,10 @@ func initUART(uart *UART) {
|
||||
}
|
||||
|
||||
// reset UART
|
||||
resetBlock(resetVal)
|
||||
unresetBlockWait(resetVal)
|
||||
rp.RESETS.RESET.SetBits(resetVal)
|
||||
rp.RESETS.RESET.ClearBits(resetVal)
|
||||
for !rp.RESETS.RESET_DONE.HasBits(resetVal) {
|
||||
}
|
||||
}
|
||||
|
||||
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||
|
||||
@@ -72,15 +72,19 @@ func initEndpoint(ep, config uint32) {
|
||||
|
||||
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||
sendUSBPacket(ep, data)
|
||||
sendUSBPacket(ep, data, 0)
|
||||
return true
|
||||
}
|
||||
|
||||
// Prevent file size increases: https://github.com/tinygo-org/tinygo/pull/998
|
||||
//
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte) {
|
||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||
count := len(data)
|
||||
if 0 < int(maxsize) && int(maxsize) < count {
|
||||
count = int(maxsize)
|
||||
}
|
||||
|
||||
if ep == 0 {
|
||||
if count > usb.EndpointPacketSize {
|
||||
count = usb.EndpointPacketSize
|
||||
@@ -141,7 +145,7 @@ func setEPDataPID(ep uint32, dataOne bool) {
|
||||
}
|
||||
|
||||
func SendZlp() {
|
||||
sendUSBPacket(0, []byte{})
|
||||
sendUSBPacket(0, []byte{}, 0)
|
||||
}
|
||||
|
||||
func sendViaEPIn(ep uint32, data []byte, count int) {
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
//go:build stm32 && !stm32f1 && !stm32l5 && !stm32wlx && !stm32g0
|
||||
//go:build stm32 && !stm32f1 && !stm32l5 && !stm32wlx
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
package machine
|
||||
|
||||
// Flash support for STM32 chips, except for STM32L0 which have a different type
|
||||
// of flash.
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build stm32 && !stm32l4 && !stm32l5 && !stm32wlx && !stm32g0
|
||||
//go:build stm32 && !stm32l4 && !stm32l5 && !stm32wlx
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build stm32l5 || stm32f7 || stm32l4 || stm32l0 || stm32wlx || stm32g0
|
||||
//go:build stm32l5 || stm32f7 || stm32l4 || stm32l0 || stm32wlx
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build stm32 && !(stm32f103 || stm32l0x1 || stm32g0)
|
||||
//go:build stm32 && !(stm32f103 || stm32l0x1)
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build stm32 && !stm32f7x2 && !stm32l5x2 && !stm32g0
|
||||
//go:build stm32 && !stm32f7x2 && !stm32l5x2
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//go:build stm32 && !stm32g0
|
||||
//go:build stm32
|
||||
|
||||
package machine
|
||||
|
||||
// Peripheral abstraction layer for UARTs on the stm32 family (except stm32g0).
|
||||
// Peripheral abstraction layer for UARTs on the stm32 family.
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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() {}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user