mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-06 03:53:42 +00:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6645f412ae | |||
| 35a61ac8c5 | |||
| c1a4ed1489 | |||
| 140c82e012 | |||
| dc23ce73d6 | |||
| e039ce131c | |||
| d59c706e60 | |||
| 26d3645317 | |||
| 432ebe13ed | |||
| 16eefc59eb | |||
| fb1bf2e6bf | |||
| 0922e3e956 | |||
| bd61913973 | |||
| 2dbfdbae10 | |||
| 05029c4fb5 | |||
| a7b30639d8 | |||
| 4adc661197 | |||
| 311c071ae6 | |||
| f1fdfe2313 | |||
| 7c3dc05117 | |||
| e569dcfe38 | |||
| 20038817ae | |||
| 9ce73cfafa | |||
| 0033c23848 | |||
| 5c15a68d18 | |||
| 02384c9414 |
@@ -209,7 +209,7 @@ jobs:
|
|||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '22'
|
||||||
- name: Install wasmtime
|
- name: Install wasmtime
|
||||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v5
|
uses: actions/cache/restore@v5
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-20-windows-v4
|
key: llvm-build-20-windows-v5
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
|||||||
+16
-3
@@ -142,6 +142,9 @@ ifeq ($(OS),Windows_NT)
|
|||||||
|
|
||||||
# PIC needs to be disabled for libclang to work.
|
# PIC needs to be disabled for libclang to work.
|
||||||
LLVM_OPTION += -DLLVM_ENABLE_PIC=OFF
|
LLVM_OPTION += -DLLVM_ENABLE_PIC=OFF
|
||||||
|
# Statically link the C++ and GCC runtime into LLVM tools so they don't
|
||||||
|
# depend on MinGW DLLs that may not be on PATH when executed during the build.
|
||||||
|
LLVM_OPTION += '-DCMAKE_EXE_LINKER_FLAGS=-static-libgcc -static-libstdc++'
|
||||||
|
|
||||||
CGO_CPPFLAGS += -DCINDEX_NO_EXPORTS
|
CGO_CPPFLAGS += -DCINDEX_NO_EXPORTS
|
||||||
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
|
CGO_LDFLAGS += -static -static-libgcc -static-libstdc++
|
||||||
@@ -304,7 +307,7 @@ wasi-cm:
|
|||||||
rsync -rv --delete --exclude go.mod --exclude '*_test.go' --exclude '*_json.go' --exclude '*.md' --exclude LICENSE $(shell go list -m -f {{.Dir}} $(WASM_TOOLS_MODULE)/cm)/ ./src/internal/cm
|
rsync -rv --delete --exclude go.mod --exclude '*_test.go' --exclude '*_json.go' --exclude '*.md' --exclude LICENSE $(shell go list -m -f {{.Dir}} $(WASM_TOOLS_MODULE)/cm)/ ./src/internal/cm
|
||||||
|
|
||||||
# Check for Node.js used during WASM tests.
|
# Check for Node.js used during WASM tests.
|
||||||
MIN_NODEJS_VERSION=18
|
MIN_NODEJS_VERSION=22
|
||||||
|
|
||||||
.PHONY: check-nodejs-version
|
.PHONY: check-nodejs-version
|
||||||
check-nodejs-version:
|
check-nodejs-version:
|
||||||
@@ -410,6 +413,7 @@ TEST_PACKAGES_LINUX := \
|
|||||||
context \
|
context \
|
||||||
crypto/aes \
|
crypto/aes \
|
||||||
crypto/des \
|
crypto/des \
|
||||||
|
crypto/ecdh \
|
||||||
crypto/hmac \
|
crypto/hmac \
|
||||||
debug/dwarf \
|
debug/dwarf \
|
||||||
debug/plan9obj \
|
debug/plan9obj \
|
||||||
@@ -489,10 +493,12 @@ report-stdlib-tests-pass:
|
|||||||
ifeq ($(uname),Darwin)
|
ifeq ($(uname),Darwin)
|
||||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
|
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_DARWIN)
|
||||||
TEST_IOFS := true
|
TEST_IOFS := true
|
||||||
|
TEST_ENCODING_XML := true
|
||||||
endif
|
endif
|
||||||
ifeq ($(uname),Linux)
|
ifeq ($(uname),Linux)
|
||||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
|
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_LINUX)
|
||||||
TEST_IOFS := true
|
TEST_IOFS := true
|
||||||
|
TEST_ENCODING_XML := true
|
||||||
endif
|
endif
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
|
||||||
@@ -507,8 +513,11 @@ TEST_ADDITIONAL_FLAGS ?=
|
|||||||
.PHONY: tinygo-test
|
.PHONY: tinygo-test
|
||||||
tinygo-test:
|
tinygo-test:
|
||||||
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented.
|
@# TestExtraMethods: used by many crypto packages and uses reflect.Type.Method which is not implemented.
|
||||||
@# TestParseAndBytesRoundTrip/P256/Generic: relies on t.Skip() which is not implemented
|
@# TestParseAndBytesRoundTrip/P256/Generic: needs Goexit to run defers on wasm.
|
||||||
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(TEST_PACKAGES_HOST) $(TEST_PACKAGES_SLOW)
|
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) $(filter-out encoding/xml,$(TEST_PACKAGES_HOST)) $(TEST_PACKAGES_SLOW)
|
||||||
|
ifeq ($(TEST_ENCODING_XML),true)
|
||||||
|
$(TINYGO) test $(TEST_ADDITIONAL_FLAGS) $(TEST_SKIP_FLAG) -stack-size=16MB encoding/xml
|
||||||
|
endif
|
||||||
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also
|
@# io/fs requires os.ReadDir, not yet supported on windows or wasi. It also
|
||||||
@# requires a large stack-size. Hence, io/fs is only run conditionally.
|
@# requires a large stack-size. Hence, io/fs is only run conditionally.
|
||||||
@# For more details, see the comments on issue #3143.
|
@# For more details, see the comments on issue #3143.
|
||||||
@@ -753,6 +762,8 @@ endif
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=xiao examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=xiao examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=xiao-ble-plus examples/blinky1
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=rak4631 examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=rak4631 examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/dac
|
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/dac
|
||||||
@@ -900,6 +911,8 @@ ifneq ($(STM32), 0)
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=stm32l0x1 examples/serial
|
$(TINYGO) build -size short -o test.hex -target=stm32l0x1 examples/serial
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
|
$(TINYGO) build -size short -o test.hex -target=stm32u031 examples/empty
|
||||||
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/serial
|
$(TINYGO) build -size short -o test.hex -target=arduino-uno-q examples/serial
|
||||||
|
|||||||
+10
-10
@@ -14,6 +14,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"go/types"
|
"go/types"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
|
"maps"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -137,9 +138,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if _, ok := globalValues[pkgPath]; !ok {
|
if _, ok := globalValues[pkgPath]; !ok {
|
||||||
globalValues[pkgPath] = map[string]string{}
|
globalValues[pkgPath] = map[string]string{}
|
||||||
}
|
}
|
||||||
for k, v := range vals {
|
maps.Copy(globalValues[pkgPath], vals)
|
||||||
globalValues[pkgPath][k] = v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a libc dependency.
|
// Check for a libc dependency.
|
||||||
@@ -278,7 +277,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
|
|
||||||
var embedFileObjects []*compileJob
|
var embedFileObjects []*compileJob
|
||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
pkg := pkg // necessary to avoid a race condition
|
|
||||||
|
|
||||||
var undefinedGlobals []string
|
var undefinedGlobals []string
|
||||||
for name := range globalValues[pkg.Pkg.Path()] {
|
for name := range globalValues[pkg.Pkg.Path()] {
|
||||||
@@ -775,7 +773,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
// TODO: do this as part of building the package to be able to link the
|
// TODO: do this as part of building the package to be able to link the
|
||||||
// bitcode files together.
|
// bitcode files together.
|
||||||
for _, pkg := range lprogram.Sorted() {
|
for _, pkg := range lprogram.Sorted() {
|
||||||
pkg := pkg
|
|
||||||
for _, filename := range pkg.CFiles {
|
for _, filename := range pkg.CFiles {
|
||||||
abspath := filepath.Join(pkg.OriginalDir(), filename)
|
abspath := filepath.Join(pkg.OriginalDir(), filename)
|
||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
@@ -842,22 +839,25 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
}
|
}
|
||||||
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
||||||
ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features()) // needed for MIPS softfloat
|
ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features()) // needed for MIPS softfloat
|
||||||
if config.GOOS() == "windows" {
|
switch config.LinkerFlavor() {
|
||||||
// Options for the MinGW wrapper for the lld COFF linker.
|
case "coff":
|
||||||
|
// Options for driving ld.lld in PE/COFF mode.
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
"-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel),
|
"-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel),
|
||||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
||||||
} else if config.GOOS() == "darwin" {
|
case "darwin":
|
||||||
// Options for the ld64-compatible lld linker.
|
// Options for the ld64-compatible lld linker.
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
"--lto-O"+strconv.Itoa(speedLevel),
|
"--lto-O"+strconv.Itoa(speedLevel),
|
||||||
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
||||||
} else {
|
case "gnu":
|
||||||
// Options for the ELF linker.
|
// Options for the ELF linker.
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
"--lto-O"+strconv.Itoa(speedLevel),
|
"--lto-O"+strconv.Itoa(speedLevel),
|
||||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
||||||
)
|
)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown linker flavor: %s", config.LinkerFlavor())
|
||||||
}
|
}
|
||||||
if config.CodeModel() != "default" {
|
if config.CodeModel() != "default" {
|
||||||
ldflags = append(ldflags,
|
ldflags = append(ldflags,
|
||||||
@@ -914,7 +914,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run wasm-opt for wasm binaries
|
// Run wasm-opt for wasm binaries
|
||||||
if arch := strings.Split(config.Triple(), "-")[0]; arch == "wasm32" {
|
if arch, _, _ := strings.Cut(config.Triple(), "-"); arch == "wasm32" {
|
||||||
optLevel, _, _ := config.OptLevel()
|
optLevel, _, _ := config.OptLevel()
|
||||||
opt := "-" + optLevel
|
opt := "-" + optLevel
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
targetNames = append(targetNames, "esp32", "esp8266")
|
targetNames = append(targetNames, "esp32", "esp8266")
|
||||||
}
|
}
|
||||||
for _, targetName := range targetNames {
|
for _, targetName := range targetNames {
|
||||||
targetName := targetName
|
|
||||||
t.Run(targetName, func(t *testing.T) {
|
t.Run(targetName, func(t *testing.T) {
|
||||||
testClangAttributes(t, &compileopts.Options{Target: targetName})
|
testClangAttributes(t, &compileopts.Options{Target: targetName})
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-1
@@ -281,7 +281,7 @@ func parseDepFile(s string) ([]string, error) {
|
|||||||
s = strings.ReplaceAll(s, "\\\n", " ")
|
s = strings.ReplaceAll(s, "\\\n", " ")
|
||||||
|
|
||||||
// Only use the first line, which is expected to begin with "deps:".
|
// Only use the first line, which is expected to begin with "deps:".
|
||||||
line := strings.SplitN(s, "\n", 2)[0]
|
line, _, _ := strings.Cut(s, "\n")
|
||||||
if !strings.HasPrefix(line, "deps:") {
|
if !strings.HasPrefix(line, "deps:") {
|
||||||
return nil, errors.New("readDepFile: expected 'deps:' prefix")
|
return nil, errors.New("readDepFile: expected 'deps:' prefix")
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ import (
|
|||||||
var commands = map[string][]string{}
|
var commands = map[string][]string{}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
llvmMajor := strings.Split(llvm.Version, ".")[0]
|
llvmMajor, _, _ := strings.Cut(llvm.Version, ".")
|
||||||
commands["clang"] = []string{"clang-" + llvmMajor}
|
commands["clang"] = []string{"clang-" + llvmMajor}
|
||||||
commands["ld.lld"] = []string{"ld.lld-" + llvmMajor, "ld.lld"}
|
commands["ld.lld"] = []string{"ld.lld-" + llvmMajor, "ld.lld"}
|
||||||
commands["wasm-ld"] = []string{"wasm-ld-" + llvmMajor, "wasm-ld"}
|
commands["wasm-ld"] = []string{"wasm-ld-" + llvmMajor, "wasm-ld"}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJ
|
|||||||
return &compileJob{
|
return &compileJob{
|
||||||
description: "compile Darwin libSystem.dylib",
|
description: "compile Darwin libSystem.dylib",
|
||||||
run: func(job *compileJob) (err error) {
|
run: func(job *compileJob) (err error) {
|
||||||
arch := strings.Split(config.Triple(), "-")[0]
|
arch, _, _ := strings.Cut(config.Triple(), "-")
|
||||||
job.result = filepath.Join(tmpdir, "libSystem.dylib")
|
job.result = filepath.Join(tmpdir, "libSystem.dylib")
|
||||||
objpath := filepath.Join(tmpdir, "libSystem.o")
|
objpath := filepath.Join(tmpdir, "libSystem.o")
|
||||||
inpath := filepath.Join(goenv.Get("TINYGOROOT"), "lib/macos-minimal-sdk/src", arch, "libSystem.s")
|
inpath := filepath.Join(goenv.Get("TINYGOROOT"), "lib/macos-minimal-sdk/src", arch, "libSystem.s")
|
||||||
|
|||||||
+2
-2
@@ -195,11 +195,11 @@ type intHeap struct {
|
|||||||
sort.IntSlice
|
sort.IntSlice
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *intHeap) Push(x interface{}) {
|
func (h *intHeap) Push(x any) {
|
||||||
h.IntSlice = append(h.IntSlice, x.(int))
|
h.IntSlice = append(h.IntSlice, x.(int))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *intHeap) Pop() interface{} {
|
func (h *intHeap) Pop() any {
|
||||||
x := h.IntSlice[len(h.IntSlice)-1]
|
x := h.IntSlice[len(h.IntSlice)-1]
|
||||||
h.IntSlice = h.IntSlice[:len(h.IntSlice)-1]
|
h.IntSlice = h.IntSlice[:len(h.IntSlice)-1]
|
||||||
return x
|
return x
|
||||||
|
|||||||
@@ -232,7 +232,6 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
}
|
}
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
// Strip leading "../" parts off the path.
|
// Strip leading "../" parts off the path.
|
||||||
path := path
|
|
||||||
cleanpath := path
|
cleanpath := path
|
||||||
for strings.HasPrefix(cleanpath, "../") {
|
for strings.HasPrefix(cleanpath, "../") {
|
||||||
cleanpath = cleanpath[3:]
|
cleanpath = cleanpath[3:]
|
||||||
|
|||||||
+2
-2
@@ -28,8 +28,8 @@ func buildMuslAllTypes(arch, muslDir, outputBitsDir string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
lines := strings.Split(string(data), "\n")
|
lines := strings.SplitSeq(string(data), "\n")
|
||||||
for _, line := range lines {
|
for line := range lines {
|
||||||
if strings.HasPrefix(line, "TYPEDEF ") {
|
if strings.HasPrefix(line, "TYPEDEF ") {
|
||||||
matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line)
|
matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line)
|
||||||
value := matches[1]
|
value := matches[1]
|
||||||
|
|||||||
@@ -42,16 +42,15 @@ func TestBinarySize(t *testing.T) {
|
|||||||
// This is a small number of very diverse targets that we want to test.
|
// This is a small number of very diverse targets that we want to test.
|
||||||
tests := []sizeTest{
|
tests := []sizeTest{
|
||||||
// microcontrollers
|
// microcontrollers
|
||||||
{"hifive1b", "examples/echo", 3699, 297, 0, 2252},
|
{"hifive1b", "examples/echo", 3771, 309, 0, 2260},
|
||||||
{"microbit", "examples/serial", 2736, 356, 8, 2248},
|
{"microbit", "examples/serial", 2832, 368, 8, 2256},
|
||||||
{"wioterminal", "examples/pininterrupt", 7960, 1652, 132, 7480},
|
{"wioterminal", "examples/pininterrupt", 8065, 1663, 132, 7488},
|
||||||
|
|
||||||
// TODO: also check wasm. Right now this is difficult, because
|
// TODO: also check wasm. Right now this is difficult, because
|
||||||
// wasm binaries are run through wasm-opt and therefore the
|
// wasm binaries are run through wasm-opt and therefore the
|
||||||
// output varies by binaryen version.
|
// output varies by binaryen version.
|
||||||
}
|
}
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
tc := tc
|
|
||||||
t.Run(tc.target+"/"+tc.path, func(t *testing.T) {
|
t.Run(tc.target+"/"+tc.path, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -85,7 +84,6 @@ func TestSizeFull(t *testing.T) {
|
|||||||
pkgMatch := regexp.MustCompile(`^[a-z/]+$`) // example: "internal/task"
|
pkgMatch := regexp.MustCompile(`^[a-z/]+$`) // example: "internal/task"
|
||||||
|
|
||||||
for _, target := range tests {
|
for _, target := range tests {
|
||||||
target := target
|
|
||||||
t.Run(target, func(t *testing.T) {
|
t.Run(target, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -116,7 +116,7 @@ func parseLLDErrors(text string) error {
|
|||||||
// This can happen in some cases like with CGo and //go:linkname tricker.
|
// This can happen in some cases like with CGo and //go:linkname tricker.
|
||||||
if matches := regexp.MustCompile(`^ld.lld(-[0-9]+)?: error: undefined symbol: (.*)\n`).FindStringSubmatch(message); matches != nil {
|
if matches := regexp.MustCompile(`^ld.lld(-[0-9]+)?: error: undefined symbol: (.*)\n`).FindStringSubmatch(message); matches != nil {
|
||||||
symbolName := matches[2]
|
symbolName := matches[2]
|
||||||
for _, line := range strings.Split(message, "\n") {
|
for line := range strings.SplitSeq(message, "\n") {
|
||||||
matches := regexp.MustCompile(`referenced by .* \(((.*):([0-9]+))\)`).FindStringSubmatch(line)
|
matches := regexp.MustCompile(`referenced by .* \(((.*):([0-9]+))\)`).FindStringSubmatch(line)
|
||||||
if matches != nil {
|
if matches != nil {
|
||||||
parsedError = true
|
parsedError = true
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ func convertBinToUF2(input []byte, targetAddr uint32, uf2FamilyID string) ([]byt
|
|||||||
}
|
}
|
||||||
bl.SetNumBlocks(len(blocks))
|
bl.SetNumBlocks(len(blocks))
|
||||||
|
|
||||||
for i := 0; i < len(blocks); i++ {
|
for i := range blocks {
|
||||||
bl.SetBlockNo(i)
|
bl.SetBlockNo(i)
|
||||||
bl.SetData(blocks[i])
|
bl.SetData(blocks[i])
|
||||||
|
|
||||||
|
|||||||
+10
-8
@@ -40,7 +40,7 @@ type cgoPackage struct {
|
|||||||
tokenFiles map[string]*token.File
|
tokenFiles map[string]*token.File
|
||||||
definedGlobally map[string]ast.Node
|
definedGlobally map[string]ast.Node
|
||||||
noescapingFuncs map[string]*noescapingFunc // #cgo noescape lines
|
noescapingFuncs map[string]*noescapingFunc // #cgo noescape lines
|
||||||
anonDecls map[interface{}]string
|
anonDecls map[any]string
|
||||||
cflags []string // CFlags from #cgo lines
|
cflags []string // CFlags from #cgo lines
|
||||||
ldflags []string // LDFlags from #cgo lines
|
ldflags []string // LDFlags from #cgo lines
|
||||||
visitedFiles map[string][]byte
|
visitedFiles map[string][]byte
|
||||||
@@ -259,7 +259,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
tokenFiles: map[string]*token.File{},
|
tokenFiles: map[string]*token.File{},
|
||||||
definedGlobally: map[string]ast.Node{},
|
definedGlobally: map[string]ast.Node{},
|
||||||
noescapingFuncs: map[string]*noescapingFunc{},
|
noescapingFuncs: map[string]*noescapingFunc{},
|
||||||
anonDecls: map[interface{}]string{},
|
anonDecls: map[any]string{},
|
||||||
visitedFiles: map[string][]byte{},
|
visitedFiles: map[string][]byte{},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,7 +302,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
// Find `import "C"` C fragments in the file.
|
// Find `import "C"` C fragments in the file.
|
||||||
p.cgoHeaders = make([]string, len(files)) // combined CGo header fragment for each file
|
p.cgoHeaders = make([]string, len(files)) // combined CGo header fragment for each file
|
||||||
for i, f := range files {
|
for i, f := range files {
|
||||||
var cgoHeader string
|
var cgoHeader strings.Builder
|
||||||
for i := 0; i < len(f.Decls); i++ {
|
for i := 0; i < len(f.Decls); i++ {
|
||||||
decl := f.Decls[i]
|
decl := f.Decls[i]
|
||||||
genDecl, ok := decl.(*ast.GenDecl)
|
genDecl, ok := decl.(*ast.GenDecl)
|
||||||
@@ -337,7 +337,8 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
// Iterate through all parts of the CGo header. Note that every //
|
// Iterate through all parts of the CGo header. Note that every //
|
||||||
// line is a new comment.
|
// line is a new comment.
|
||||||
position := fset.Position(genDecl.Doc.Pos())
|
position := fset.Position(genDecl.Doc.Pos())
|
||||||
fragment := fmt.Sprintf("# %d %#v\n", position.Line, position.Filename)
|
var fragment strings.Builder
|
||||||
|
fragment.WriteString(fmt.Sprintf("# %d %#v\n", position.Line, position.Filename))
|
||||||
for _, comment := range genDecl.Doc.List {
|
for _, comment := range genDecl.Doc.List {
|
||||||
// Find all #cgo lines, extract and use their contents, and
|
// Find all #cgo lines, extract and use their contents, and
|
||||||
// replace the lines with spaces (to preserve locations).
|
// replace the lines with spaces (to preserve locations).
|
||||||
@@ -354,12 +355,13 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
} else { // comment
|
} else { // comment
|
||||||
c = " " + c[2:len(c)-2]
|
c = " " + c[2:len(c)-2]
|
||||||
}
|
}
|
||||||
fragment += c + "\n"
|
fragment.WriteString(c)
|
||||||
|
fragment.WriteByte('\n')
|
||||||
}
|
}
|
||||||
cgoHeader += fragment
|
cgoHeader.WriteString(fragment.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
p.cgoHeaders[i] = cgoHeader
|
p.cgoHeaders[i] = cgoHeader.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define CFlags that will be used while parsing the package.
|
// Define CFlags that will be used while parsing the package.
|
||||||
@@ -1217,7 +1219,7 @@ func getPos(node ast.Node) token.Pos {
|
|||||||
// getUnnamedDeclName creates a name (with the given prefix) for the given C
|
// getUnnamedDeclName creates a name (with the given prefix) for the given C
|
||||||
// declaration. This is used for structs, unions, and enums that are often
|
// declaration. This is used for structs, unions, and enums that are often
|
||||||
// defined without a name and used in a typedef.
|
// defined without a name and used in a typedef.
|
||||||
func (p *cgoPackage) getUnnamedDeclName(prefix string, itf interface{}) string {
|
func (p *cgoPackage) getUnnamedDeclName(prefix string, itf any) string {
|
||||||
if name, ok := p.anonDecls[itf]; ok {
|
if name, ok := p.anonDecls[itf]; ok {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ func TestCGo(t *testing.T) {
|
|||||||
"flags",
|
"flags",
|
||||||
"const",
|
"const",
|
||||||
} {
|
} {
|
||||||
name := name // avoid a race condition
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
// Read the AST in memory.
|
// Read the AST in memory.
|
||||||
path := filepath.Join("testdata", name+".go")
|
path := filepath.Join("testdata", name+".go")
|
||||||
|
|||||||
+3
-3
@@ -160,7 +160,7 @@ func (f *cgoFile) readNames(fragment string, cflags []string, filename string, c
|
|||||||
pos := f.getClangLocationPosition(location, unit)
|
pos := f.getClangLocationPosition(location, unit)
|
||||||
f.addError(pos, severity+": "+spelling)
|
f.addError(pos, severity+": "+spelling)
|
||||||
}
|
}
|
||||||
for i := 0; i < numDiagnostics; i++ {
|
for i := range numDiagnostics {
|
||||||
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
diagnostic := C.clang_getDiagnostic(unit, C.uint(i))
|
||||||
addDiagnostic(diagnostic)
|
addDiagnostic(diagnostic)
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) {
|
|||||||
Text: strings.Join(doc, "\n"),
|
Text: strings.Join(doc, "\n"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for i := 0; i < numArgs; i++ {
|
for i := range numArgs {
|
||||||
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
||||||
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
||||||
argType := C.clang_getArgType(cursorType, C.uint(i))
|
argType := C.clang_getArgType(cursorType, C.uint(i))
|
||||||
@@ -534,7 +534,7 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
|
|
||||||
// Get the precise location in the source code. Used for uniquely identifying
|
// Get the precise location in the source code. Used for uniquely identifying
|
||||||
// source locations.
|
// source locations.
|
||||||
func (f *cgoFile) getUniqueLocationID(pos token.Pos, cursor C.GoCXCursor) interface{} {
|
func (f *cgoFile) getUniqueLocationID(pos token.Pos, cursor C.GoCXCursor) any {
|
||||||
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
|
clangLocation := C.tinygo_clang_getCursorLocation(cursor)
|
||||||
var file C.CXFile
|
var file C.CXFile
|
||||||
var line C.unsigned
|
var line C.unsigned
|
||||||
|
|||||||
+4
-4
@@ -12,17 +12,17 @@ import "C"
|
|||||||
// C. It is useful if an API uses function pointers and you cannot pass a Go
|
// C. It is useful if an API uses function pointers and you cannot pass a Go
|
||||||
// pointer but only a C pointer.
|
// pointer but only a C pointer.
|
||||||
type refMap struct {
|
type refMap struct {
|
||||||
refs map[unsafe.Pointer]interface{}
|
refs map[unsafe.Pointer]any
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put stores a value in the map. It can later be retrieved using Get. It must
|
// Put stores a value in the map. It can later be retrieved using Get. It must
|
||||||
// be removed using Remove to avoid memory leaks.
|
// be removed using Remove to avoid memory leaks.
|
||||||
func (m *refMap) Put(v interface{}) unsafe.Pointer {
|
func (m *refMap) Put(v any) unsafe.Pointer {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
if m.refs == nil {
|
if m.refs == nil {
|
||||||
m.refs = make(map[unsafe.Pointer]interface{}, 1)
|
m.refs = make(map[unsafe.Pointer]any, 1)
|
||||||
}
|
}
|
||||||
ref := C.malloc(1)
|
ref := C.malloc(1)
|
||||||
m.refs[ref] = v
|
m.refs[ref] = v
|
||||||
@@ -31,7 +31,7 @@ func (m *refMap) Put(v interface{}) unsafe.Pointer {
|
|||||||
|
|
||||||
// Get returns a stored value previously inserted with Put. Use the same
|
// Get returns a stored value previously inserted with Put. Use the same
|
||||||
// reference as you got from Put.
|
// reference as you got from Put.
|
||||||
func (m *refMap) Get(ref unsafe.Pointer) interface{} {
|
func (m *refMap) Get(ref unsafe.Pointer) any {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
return m.refs[ref]
|
return m.refs[ref]
|
||||||
|
|||||||
+19
-8
@@ -8,6 +8,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -140,13 +141,7 @@ func (c *Config) GC() string {
|
|||||||
func (c *Config) NeedsStackObjects() bool {
|
func (c *Config) NeedsStackObjects() bool {
|
||||||
switch c.GC() {
|
switch c.GC() {
|
||||||
case "conservative", "custom", "precise", "boehm":
|
case "conservative", "custom", "precise", "boehm":
|
||||||
for _, tag := range c.BuildTags() {
|
return slices.Contains(c.BuildTags(), "tinygo.wasm")
|
||||||
if tag == "tinygo.wasm" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -245,7 +240,7 @@ func (c *Config) RP2040BootPatch() bool {
|
|||||||
// Return a canonicalized architecture name, so we don't have to deal with arm*
|
// Return a canonicalized architecture name, so we don't have to deal with arm*
|
||||||
// vs thumb* vs arm64.
|
// vs thumb* vs arm64.
|
||||||
func CanonicalArchName(triple string) string {
|
func CanonicalArchName(triple string) string {
|
||||||
arch := strings.Split(triple, "-")[0]
|
arch, _, _ := strings.Cut(triple, "-")
|
||||||
if arch == "arm64" {
|
if arch == "arm64" {
|
||||||
return "aarch64"
|
return "aarch64"
|
||||||
}
|
}
|
||||||
@@ -466,6 +461,22 @@ func (c *Config) LDFlags() []string {
|
|||||||
return ldflags
|
return ldflags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LinkerFlavor returns how the configured linker should be driven.
|
||||||
|
// Usually this is derived from GOOS, but targets may override it explicitly.
|
||||||
|
func (c *Config) LinkerFlavor() string {
|
||||||
|
if c.Target.LinkerFlavor != "" {
|
||||||
|
return c.Target.LinkerFlavor
|
||||||
|
}
|
||||||
|
switch c.GOOS() {
|
||||||
|
case "windows":
|
||||||
|
return "coff"
|
||||||
|
case "darwin":
|
||||||
|
return "darwin"
|
||||||
|
default:
|
||||||
|
return "gnu"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ExtraFiles returns the list of extra files to be built and linked with the
|
// ExtraFiles returns the list of extra files to be built and linked with the
|
||||||
// executable. This can include extra C and assembly files.
|
// executable. This can include extra C and assembly files.
|
||||||
func (c *Config) ExtraFiles() []string {
|
func (c *Config) ExtraFiles() []string {
|
||||||
|
|||||||
+8
-16
@@ -3,6 +3,7 @@ package compileopts
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -67,7 +68,7 @@ type Options struct {
|
|||||||
// Verify performs a validation on the given options, raising an error if options are not valid.
|
// Verify performs a validation on the given options, raising an error if options are not valid.
|
||||||
func (o *Options) Verify() error {
|
func (o *Options) Verify() error {
|
||||||
if o.BuildMode != "" {
|
if o.BuildMode != "" {
|
||||||
valid := isInArray(validBuildModeOptions, o.BuildMode)
|
valid := slices.Contains(validBuildModeOptions, o.BuildMode)
|
||||||
if !valid {
|
if !valid {
|
||||||
return fmt.Errorf(`invalid buildmode option '%s': valid values are %s`,
|
return fmt.Errorf(`invalid buildmode option '%s': valid values are %s`,
|
||||||
o.BuildMode,
|
o.BuildMode,
|
||||||
@@ -75,7 +76,7 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if o.GC != "" {
|
if o.GC != "" {
|
||||||
valid := isInArray(validGCOptions, o.GC)
|
valid := slices.Contains(validGCOptions, o.GC)
|
||||||
if !valid {
|
if !valid {
|
||||||
return fmt.Errorf(`invalid gc option '%s': valid values are %s`,
|
return fmt.Errorf(`invalid gc option '%s': valid values are %s`,
|
||||||
o.GC,
|
o.GC,
|
||||||
@@ -84,7 +85,7 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.Scheduler != "" {
|
if o.Scheduler != "" {
|
||||||
valid := isInArray(validSchedulerOptions, o.Scheduler)
|
valid := slices.Contains(validSchedulerOptions, o.Scheduler)
|
||||||
if !valid {
|
if !valid {
|
||||||
return fmt.Errorf(`invalid scheduler option '%s': valid values are %s`,
|
return fmt.Errorf(`invalid scheduler option '%s': valid values are %s`,
|
||||||
o.Scheduler,
|
o.Scheduler,
|
||||||
@@ -93,7 +94,7 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.Serial != "" {
|
if o.Serial != "" {
|
||||||
valid := isInArray(validSerialOptions, o.Serial)
|
valid := slices.Contains(validSerialOptions, o.Serial)
|
||||||
if !valid {
|
if !valid {
|
||||||
return fmt.Errorf(`invalid serial option '%s': valid values are %s`,
|
return fmt.Errorf(`invalid serial option '%s': valid values are %s`,
|
||||||
o.Serial,
|
o.Serial,
|
||||||
@@ -102,7 +103,7 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.PrintSizes != "" {
|
if o.PrintSizes != "" {
|
||||||
valid := isInArray(validPrintSizeOptions, o.PrintSizes)
|
valid := slices.Contains(validPrintSizeOptions, o.PrintSizes)
|
||||||
if !valid {
|
if !valid {
|
||||||
return fmt.Errorf(`invalid size option '%s': valid values are %s`,
|
return fmt.Errorf(`invalid size option '%s': valid values are %s`,
|
||||||
o.PrintSizes,
|
o.PrintSizes,
|
||||||
@@ -111,7 +112,7 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.PanicStrategy != "" {
|
if o.PanicStrategy != "" {
|
||||||
valid := isInArray(validPanicStrategyOptions, o.PanicStrategy)
|
valid := slices.Contains(validPanicStrategyOptions, o.PanicStrategy)
|
||||||
if !valid {
|
if !valid {
|
||||||
return fmt.Errorf(`invalid panic option '%s': valid values are %s`,
|
return fmt.Errorf(`invalid panic option '%s': valid values are %s`,
|
||||||
o.PanicStrategy,
|
o.PanicStrategy,
|
||||||
@@ -120,19 +121,10 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.Opt != "" {
|
if o.Opt != "" {
|
||||||
if !isInArray(validOptOptions, o.Opt) {
|
if !slices.Contains(validOptOptions, o.Opt) {
|
||||||
return fmt.Errorf("invalid -opt=%s: valid values are %s", o.Opt, strings.Join(validOptOptions, ", "))
|
return fmt.Errorf("invalid -opt=%s: valid values are %s", o.Opt, strings.Join(validOptOptions, ", "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isInArray(arr []string, item string) bool {
|
|
||||||
for _, i := range arr {
|
|
||||||
if i == item {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ type TargetSpec struct {
|
|||||||
Scheduler string `json:"scheduler,omitempty"`
|
Scheduler string `json:"scheduler,omitempty"`
|
||||||
Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none)
|
Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none)
|
||||||
Linker string `json:"linker,omitempty"`
|
Linker string `json:"linker,omitempty"`
|
||||||
RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt)
|
LinkerFlavor string `json:"linker-flavor,omitempty"` // how to drive the configured linker (for example: gnu, coff, darwin)
|
||||||
|
RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt)
|
||||||
Libc string `json:"libc,omitempty"`
|
Libc string `json:"libc,omitempty"`
|
||||||
AutoStackSize *bool `json:"automatic-stack-size,omitempty"` // Determine stack size automatically at compile time.
|
AutoStackSize *bool `json:"automatic-stack-size,omitempty"` // Determine stack size automatically at compile time.
|
||||||
DefaultStackSize uint64 `json:"default-stack-size,omitempty"` // Default stack size if the size couldn't be determined at compile time.
|
DefaultStackSize uint64 `json:"default-stack-size,omitempty"` // Default stack size if the size couldn't be determined at compile time.
|
||||||
|
|||||||
@@ -112,3 +112,52 @@ func TestOverrideProperties(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfigLinkerFlavor(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
target *TargetSpec
|
||||||
|
goos string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "default gnu",
|
||||||
|
target: &TargetSpec{},
|
||||||
|
goos: "linux",
|
||||||
|
want: "gnu",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default coff",
|
||||||
|
target: &TargetSpec{},
|
||||||
|
goos: "windows",
|
||||||
|
want: "coff",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default darwin",
|
||||||
|
target: &TargetSpec{},
|
||||||
|
goos: "darwin",
|
||||||
|
want: "darwin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "target override",
|
||||||
|
target: &TargetSpec{
|
||||||
|
LinkerFlavor: "coff",
|
||||||
|
},
|
||||||
|
goos: "linux",
|
||||||
|
want: "coff",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
tc.target.GOOS = tc.goos
|
||||||
|
config := &Config{
|
||||||
|
Options: &Options{},
|
||||||
|
Target: tc.target,
|
||||||
|
}
|
||||||
|
if got := config.LinkerFlavor(); got != tc.want {
|
||||||
|
t.Fatalf("LinkerFlavor() = %q, want %q", got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+8
-18
@@ -90,7 +90,8 @@ type compilerContext struct {
|
|||||||
astComments map[string]*ast.CommentGroup
|
astComments map[string]*ast.CommentGroup
|
||||||
embedGlobals map[string][]*loader.EmbedFile
|
embedGlobals map[string][]*loader.EmbedFile
|
||||||
pkg *types.Package
|
pkg *types.Package
|
||||||
packageDir string // directory for this package
|
loaderPkg *loader.Package // current package being compiled (for AST access)
|
||||||
|
packageDir string // directory for this package
|
||||||
runtimePkg *types.Package
|
runtimePkg *types.Package
|
||||||
localTypeNames typeutil.Map // *types.Named (synthetic local from generic instantiation) -> string
|
localTypeNames typeutil.Map // *types.Named (synthetic local from generic instantiation) -> string
|
||||||
}
|
}
|
||||||
@@ -168,7 +169,7 @@ type builder struct {
|
|||||||
dilocals map[*types.Var]llvm.Metadata
|
dilocals map[*types.Var]llvm.Metadata
|
||||||
initInlinedAt llvm.Metadata // fake inlinedAt position
|
initInlinedAt llvm.Metadata // fake inlinedAt position
|
||||||
initPseudoFuncs map[string]llvm.Metadata // fake "inlined" functions for proper init debug locations
|
initPseudoFuncs map[string]llvm.Metadata // fake "inlined" functions for proper init debug locations
|
||||||
allDeferFuncs []interface{}
|
allDeferFuncs []any
|
||||||
deferFuncs map[*ssa.Function]int
|
deferFuncs map[*ssa.Function]int
|
||||||
deferInvokeFuncs map[string]int
|
deferInvokeFuncs map[string]int
|
||||||
deferClosureFuncs map[*ssa.Function]int
|
deferClosureFuncs map[*ssa.Function]int
|
||||||
@@ -298,6 +299,7 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
|||||||
c.packageDir = pkg.OriginalDir()
|
c.packageDir = pkg.OriginalDir()
|
||||||
c.embedGlobals = pkg.EmbedGlobals
|
c.embedGlobals = pkg.EmbedGlobals
|
||||||
c.pkg = pkg.Pkg
|
c.pkg = pkg.Pkg
|
||||||
|
c.loaderPkg = pkg
|
||||||
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
||||||
c.program = ssaPkg.Prog
|
c.program = ssaPkg.Prog
|
||||||
|
|
||||||
@@ -1354,15 +1356,6 @@ func (b *builder) createFunctionStart(intrinsic bool) {
|
|||||||
// diagnostic.
|
// diagnostic.
|
||||||
func (b *builder) createFunction() {
|
func (b *builder) createFunction() {
|
||||||
b.createFunctionStart(false)
|
b.createFunctionStart(false)
|
||||||
// createFunctionStart returns early (leaving blockInfo unset) when it hits
|
|
||||||
// an error such as a redeclared symbol. Don't plow into the block loop and
|
|
||||||
// panic on an empty blockInfo — the error was already recorded.
|
|
||||||
if b.blockInfo == nil {
|
|
||||||
// TINYGO-DEBUG: surface the redeclaration collision.
|
|
||||||
fmt.Printf("TINYGO-DEBUG skipping fn (redeclared/errored): %s pkg=%s\n",
|
|
||||||
b.fn.RelString(nil), b.fn.Pkg.Pkg.Path())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill blocks with instructions.
|
// Fill blocks with instructions.
|
||||||
for _, block := range b.fn.DomPreorder() {
|
for _, block := range b.fn.DomPreorder() {
|
||||||
@@ -2163,13 +2156,10 @@ func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
|
|||||||
if elementSize == 0 {
|
if elementSize == 0 {
|
||||||
elementSize = 1
|
elementSize = 1
|
||||||
}
|
}
|
||||||
maxSize := maxPointerValue / elementSize
|
maxSize := min(
|
||||||
|
// len(slice) is an int. Make sure the length remains small enough to fit in
|
||||||
// len(slice) is an int. Make sure the length remains small enough to fit in
|
// an int.
|
||||||
// an int.
|
maxPointerValue/elementSize, maxIntegerValue)
|
||||||
if maxSize > maxIntegerValue {
|
|
||||||
maxSize = maxIntegerValue
|
|
||||||
}
|
|
||||||
|
|
||||||
return maxSize
|
return maxSize
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,9 +188,9 @@ func TestCompilerErrors(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
errorsFileString := strings.ReplaceAll(string(errorsFile), "\r\n", "\n")
|
errorsFileString := strings.ReplaceAll(string(errorsFile), "\r\n", "\n")
|
||||||
for _, line := range strings.Split(errorsFileString, "\n") {
|
for line := range strings.SplitSeq(errorsFileString, "\n") {
|
||||||
if strings.HasPrefix(line, "// ERROR: ") {
|
if after, ok := strings.CutPrefix(line, "// ERROR: "); ok {
|
||||||
expectedErrors = append(expectedErrors, strings.TrimPrefix(line, "// ERROR: "))
|
expectedErrors = append(expectedErrors, after)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-7
@@ -116,8 +116,8 @@ func (b *builder) createLandingPad() {
|
|||||||
func (b *builder) createCheckpoint(ptr llvm.Value) llvm.Value {
|
func (b *builder) createCheckpoint(ptr llvm.Value) llvm.Value {
|
||||||
// Construct inline assembly equivalents of setjmp.
|
// Construct inline assembly equivalents of setjmp.
|
||||||
// The assembly works as follows:
|
// The assembly works as follows:
|
||||||
// * All registers (both callee-saved and caller saved) are clobbered
|
// * Registers are either clobbered or, on 386, saved for longjmp to
|
||||||
// after the inline assembly returns.
|
// restore if the ABI requires them to survive calls.
|
||||||
// * The assembly stores the address just past the end of the assembly
|
// * The assembly stores the address just past the end of the assembly
|
||||||
// into the jump buffer.
|
// into the jump buffer.
|
||||||
// * The return value (eax, rax, r0, etc) is set to zero in the inline
|
// * The return value (eax, rax, r0, etc) is set to zero in the inline
|
||||||
@@ -130,8 +130,12 @@ func (b *builder) createCheckpoint(ptr llvm.Value) llvm.Value {
|
|||||||
asmString = `
|
asmString = `
|
||||||
xorl %eax, %eax
|
xorl %eax, %eax
|
||||||
movl $$1f, 4(%ebx)
|
movl $$1f, 4(%ebx)
|
||||||
|
movl %ebx, 8(%ebx)
|
||||||
|
movl %esi, 12(%ebx)
|
||||||
|
movl %edi, 16(%ebx)
|
||||||
|
movl %ebp, 20(%ebx)
|
||||||
1:`
|
1:`
|
||||||
constraints = "={eax},{ebx},~{ebx},~{ecx},~{edx},~{esi},~{edi},~{ebp},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
constraints = "={eax},{ebx},~{ecx},~{edx},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
|
||||||
// This doesn't include the floating point stack because TinyGo uses
|
// This doesn't include the floating point stack because TinyGo uses
|
||||||
// newer floating point instructions.
|
// newer floating point instructions.
|
||||||
case "x86_64":
|
case "x86_64":
|
||||||
@@ -669,8 +673,8 @@ func (b *builder) createRunDefers() {
|
|||||||
fn := callback.Fn.(*ssa.Function)
|
fn := callback.Fn.(*ssa.Function)
|
||||||
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
|
||||||
params := fn.Signature.Params()
|
params := fn.Signature.Params()
|
||||||
for i := 0; i < params.Len(); i++ {
|
for v := range params.Variables() {
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
valueTypes = append(valueTypes, b.getLLVMType(v.Type()))
|
||||||
}
|
}
|
||||||
valueTypes = append(valueTypes, b.dataPtrType) // closure
|
valueTypes = append(valueTypes, b.dataPtrType) // closure
|
||||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
@@ -695,8 +699,8 @@ func (b *builder) createRunDefers() {
|
|||||||
|
|
||||||
//Get signature from call results
|
//Get signature from call results
|
||||||
params := callback.Type().Underlying().(*types.Signature).Params()
|
params := callback.Type().Underlying().(*types.Signature).Params()
|
||||||
for i := 0; i < params.Len(); i++ {
|
for v := range params.Variables() {
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
valueTypes = append(valueTypes, b.getLLVMType(v.Type()))
|
||||||
}
|
}
|
||||||
|
|
||||||
deferredCallType := b.ctx.StructType(valueTypes, false)
|
deferredCallType := b.ctx.StructType(valueTypes, false)
|
||||||
|
|||||||
+2
-2
@@ -81,8 +81,8 @@ func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
|
|||||||
paramTypes = append(paramTypes, info.llvmType)
|
paramTypes = append(paramTypes, info.llvmType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < typ.Params().Len(); i++ {
|
for v := range typ.Params().Variables() {
|
||||||
subType := c.getLLVMType(typ.Params().At(i).Type())
|
subType := c.getLLVMType(v.Type())
|
||||||
for _, info := range c.expandFormalParamType(subType, "", nil) {
|
for _, info := range c.expandFormalParamType(subType, "", nil) {
|
||||||
paramTypes = append(paramTypes, info.llvmType)
|
paramTypes = append(paramTypes, info.llvmType)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-8
@@ -5,6 +5,7 @@ package compiler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
@@ -88,7 +89,7 @@ func (b *builder) trackValue(value llvm.Value) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
numElements := typ.StructElementTypesCount()
|
numElements := typ.StructElementTypesCount()
|
||||||
for i := 0; i < numElements; i++ {
|
for i := range numElements {
|
||||||
subValue := b.CreateExtractValue(value, i, "")
|
subValue := b.CreateExtractValue(value, i, "")
|
||||||
b.trackValue(subValue)
|
b.trackValue(subValue)
|
||||||
}
|
}
|
||||||
@@ -97,7 +98,7 @@ func (b *builder) trackValue(value llvm.Value) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
numElements := typ.ArrayLength()
|
numElements := typ.ArrayLength()
|
||||||
for i := 0; i < numElements; i++ {
|
for i := range numElements {
|
||||||
subValue := b.CreateExtractValue(value, i, "")
|
subValue := b.CreateExtractValue(value, i, "")
|
||||||
b.trackValue(subValue)
|
b.trackValue(subValue)
|
||||||
}
|
}
|
||||||
@@ -118,12 +119,7 @@ func typeHasPointers(t llvm.Type) bool {
|
|||||||
case llvm.PointerTypeKind:
|
case llvm.PointerTypeKind:
|
||||||
return true
|
return true
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
for _, subType := range t.StructElementTypes() {
|
return slices.ContainsFunc(t.StructElementTypes(), typeHasPointers)
|
||||||
if typeHasPointers(subType) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
case llvm.ArrayTypeKind:
|
case llvm.ArrayTypeKind:
|
||||||
if t.ArrayLength() == 0 {
|
if t.ArrayLength() == 0 {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ func (b *builder) createGo(instr *ssa.Go) {
|
|||||||
funcType = b.getLLVMFunctionType(instr.Call.Value.Type().Underlying().(*types.Signature))
|
funcType = b.getLLVMFunctionType(instr.Call.Value.Type().Underlying().(*types.Signature))
|
||||||
params = append(params, context, funcPtr)
|
params = append(params, context, funcPtr)
|
||||||
hasContext = true
|
hasContext = true
|
||||||
prefix = b.fn.RelString(nil)
|
prefix = b.getFunctionInfo(b.fn).linkName
|
||||||
}
|
}
|
||||||
|
|
||||||
paramBundle := b.emitPointerPack(params)
|
paramBundle := b.emitPointerPack(params)
|
||||||
@@ -139,7 +139,7 @@ func (b *builder) createWasmExport() {
|
|||||||
// Declare the exported function.
|
// Declare the exported function.
|
||||||
paramTypes := b.llvmFnType.ParamTypes()
|
paramTypes := b.llvmFnType.ParamTypes()
|
||||||
exportedFnType := llvm.FunctionType(b.llvmFnType.ReturnType(), paramTypes[:len(paramTypes)-1], false)
|
exportedFnType := llvm.FunctionType(b.llvmFnType.ReturnType(), paramTypes[:len(paramTypes)-1], false)
|
||||||
exportedFn := llvm.AddFunction(b.mod, b.fn.RelString(nil)+suffix, exportedFnType)
|
exportedFn := llvm.AddFunction(b.mod, b.getFunctionInfo(b.fn).linkName+suffix, exportedFnType)
|
||||||
b.addStandardAttributes(exportedFn)
|
b.addStandardAttributes(exportedFn)
|
||||||
llvmutil.AppendToGlobal(b.mod, "llvm.used", exportedFn)
|
llvmutil.AppendToGlobal(b.mod, "llvm.used", exportedFn)
|
||||||
exportedFn.AddFunctionAttr(b.ctx.CreateStringAttribute("wasm-export-name", b.info.wasmExport))
|
exportedFn.AddFunctionAttr(b.ctx.CreateStringAttribute("wasm-export-name", b.info.wasmExport))
|
||||||
@@ -414,7 +414,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
|
|||||||
// Extract parameters from the state object, and call the function
|
// Extract parameters from the state object, and call the function
|
||||||
// that's being wrapped.
|
// that's being wrapped.
|
||||||
var callParams []llvm.Value
|
var callParams []llvm.Value
|
||||||
for i := 0; i < numParams; i++ {
|
for i := range numParams {
|
||||||
gep := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
|
gep := b.CreateInBoundsGEP(stateStruct, statePtr, []llvm.Value{
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
llvm.ConstInt(b.ctx.Int32Type(), 0, false),
|
||||||
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false),
|
||||||
|
|||||||
+12
-10
@@ -146,13 +146,14 @@ func (b *builder) emitSVCall(args []ssa.Value, pos token.Pos) (llvm.Value, error
|
|||||||
llvmArgs := []llvm.Value{}
|
llvmArgs := []llvm.Value{}
|
||||||
argTypes := []llvm.Type{}
|
argTypes := []llvm.Type{}
|
||||||
asm := "svc #" + strconv.FormatUint(num, 10)
|
asm := "svc #" + strconv.FormatUint(num, 10)
|
||||||
constraints := "={r0}"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={r0}")
|
||||||
for i, arg := range args[1:] {
|
for i, arg := range args[1:] {
|
||||||
arg = arg.(*ssa.MakeInterface).X
|
arg = arg.(*ssa.MakeInterface).X
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
constraints += ",0"
|
constraints.WriteString(",0")
|
||||||
} else {
|
} else {
|
||||||
constraints += ",{r" + strconv.Itoa(i) + "}"
|
constraints.WriteString(",{r" + strconv.Itoa(i) + "}")
|
||||||
}
|
}
|
||||||
llvmValue := b.getValue(arg, pos)
|
llvmValue := b.getValue(arg, pos)
|
||||||
llvmArgs = append(llvmArgs, llvmValue)
|
llvmArgs = append(llvmArgs, llvmValue)
|
||||||
@@ -161,9 +162,9 @@ func (b *builder) emitSVCall(args []ssa.Value, pos token.Pos) (llvm.Value, error
|
|||||||
// Implement the ARM calling convention by marking r1-r3 as
|
// Implement the ARM calling convention by marking r1-r3 as
|
||||||
// clobbered. r0 is used as an output register so doesn't have to be
|
// clobbered. r0 is used as an output register so doesn't have to be
|
||||||
// marked as clobbered.
|
// marked as clobbered.
|
||||||
constraints += ",~{r1},~{r2},~{r3}"
|
constraints.WriteString(",~{r1},~{r2},~{r3}")
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
|
target := llvm.InlineAsm(fnType, asm, constraints.String(), true, false, 0, false)
|
||||||
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,13 +185,14 @@ func (b *builder) emitSV64Call(args []ssa.Value, pos token.Pos) (llvm.Value, err
|
|||||||
llvmArgs := []llvm.Value{}
|
llvmArgs := []llvm.Value{}
|
||||||
argTypes := []llvm.Type{}
|
argTypes := []llvm.Type{}
|
||||||
asm := "svc #" + strconv.FormatUint(num, 10)
|
asm := "svc #" + strconv.FormatUint(num, 10)
|
||||||
constraints := "={x0}"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={x0}")
|
||||||
for i, arg := range args[1:] {
|
for i, arg := range args[1:] {
|
||||||
arg = arg.(*ssa.MakeInterface).X
|
arg = arg.(*ssa.MakeInterface).X
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
constraints += ",0"
|
constraints.WriteString(",0")
|
||||||
} else {
|
} else {
|
||||||
constraints += ",{x" + strconv.Itoa(i) + "}"
|
constraints.WriteString(",{x" + strconv.Itoa(i) + "}")
|
||||||
}
|
}
|
||||||
llvmValue := b.getValue(arg, pos)
|
llvmValue := b.getValue(arg, pos)
|
||||||
llvmArgs = append(llvmArgs, llvmValue)
|
llvmArgs = append(llvmArgs, llvmValue)
|
||||||
@@ -199,9 +201,9 @@ func (b *builder) emitSV64Call(args []ssa.Value, pos token.Pos) (llvm.Value, err
|
|||||||
// Implement the ARM64 calling convention by marking x1-x7 as
|
// Implement the ARM64 calling convention by marking x1-x7 as
|
||||||
// clobbered. x0 is used as an output register so doesn't have to be
|
// clobbered. x0 is used as an output register so doesn't have to be
|
||||||
// marked as clobbered.
|
// marked as clobbered.
|
||||||
constraints += ",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}"
|
constraints.WriteString(",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}")
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0, false)
|
target := llvm.InlineAsm(fnType, asm, constraints.String(), true, false, 0, false)
|
||||||
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
return b.CreateCall(fnType, target, llvmArgs, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+42
-42
@@ -145,8 +145,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
// For a non-interface type, it returns the number of exported methods.
|
// For a non-interface type, it returns the number of exported methods.
|
||||||
// For an interface type, it returns the number of exported and unexported methods.
|
// For an interface type, it returns the number of exported and unexported methods.
|
||||||
var numMethods int
|
var numMethods int
|
||||||
for i := 0; i < ms.Len(); i++ {
|
for method := range ms.Methods() {
|
||||||
if isInterface || ms.At(i).Obj().Exported() {
|
if isInterface || method.Obj().Exported() {
|
||||||
numMethods++
|
numMethods++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,8 +193,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
}
|
}
|
||||||
// Compute the method set value for types that support methods.
|
// Compute the method set value for types that support methods.
|
||||||
var methods []*types.Func
|
var methods []*types.Func
|
||||||
for i := 0; i < ms.Len(); i++ {
|
for method := range ms.Methods() {
|
||||||
methods = append(methods, ms.At(i).Obj().(*types.Func))
|
methods = append(methods, method.Obj().(*types.Func))
|
||||||
}
|
}
|
||||||
methodSetType := types.NewStruct([]*types.Var{
|
methodSetType := types.NewStruct([]*types.Var{
|
||||||
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
|
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
|
||||||
@@ -490,10 +490,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
c.getTypeMethodSet(typ),
|
c.getTypeMethodSet(typ),
|
||||||
}, typeFields...)
|
}, typeFields...)
|
||||||
}
|
}
|
||||||
alignment := c.targetData.TypeAllocSize(c.dataPtrType)
|
alignment := max(c.targetData.TypeAllocSize(c.dataPtrType), 4)
|
||||||
if alignment < 4 {
|
|
||||||
alignment = 4
|
|
||||||
}
|
|
||||||
globalValue := c.ctx.ConstStruct(typeFields, false)
|
globalValue := c.ctx.ConstStruct(typeFields, false)
|
||||||
global.SetInitializer(globalValue)
|
global.SetInitializer(globalValue)
|
||||||
if isLocal {
|
if isLocal {
|
||||||
@@ -783,12 +780,12 @@ func (c *compilerContext) scanLocalTypes(ssaPkg *ssa.Package) {
|
|||||||
walk(m, false)
|
walk(m, false)
|
||||||
case *ssa.Type:
|
case *ssa.Type:
|
||||||
mset := c.program.MethodSets.MethodSet(m.Type())
|
mset := c.program.MethodSets.MethodSet(m.Type())
|
||||||
for i := 0; i < mset.Len(); i++ {
|
for method := range mset.Methods() {
|
||||||
walk(c.program.MethodValue(mset.At(i)), false)
|
walk(c.program.MethodValue(method), false)
|
||||||
}
|
}
|
||||||
pmset := c.program.MethodSets.MethodSet(types.NewPointer(m.Type()))
|
pmset := c.program.MethodSets.MethodSet(types.NewPointer(m.Type()))
|
||||||
for i := 0; i < pmset.Len(); i++ {
|
for method := range pmset.Methods() {
|
||||||
walk(c.program.MethodValue(pmset.At(i)), false)
|
walk(c.program.MethodValue(method), false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -846,8 +843,8 @@ func (c *compilerContext) registerSyntheticLocalTypes(fn *ssa.Function) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
targs := t.TypeArgs()
|
targs := t.TypeArgs()
|
||||||
for i := 0; i < targs.Len(); i++ {
|
for t := range targs.Types() {
|
||||||
visit(targs.At(i))
|
visit(t)
|
||||||
}
|
}
|
||||||
visit(t.Underlying())
|
visit(t.Underlying())
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
@@ -862,23 +859,23 @@ func (c *compilerContext) registerSyntheticLocalTypes(fn *ssa.Function) {
|
|||||||
visit(t.Key())
|
visit(t.Key())
|
||||||
visit(t.Elem())
|
visit(t.Elem())
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
for i := 0; i < t.NumFields(); i++ {
|
for field := range t.Fields() {
|
||||||
visit(t.Field(i).Type())
|
visit(field.Type())
|
||||||
}
|
}
|
||||||
case *types.Signature:
|
case *types.Signature:
|
||||||
if p := t.Params(); p != nil {
|
if p := t.Params(); p != nil {
|
||||||
for i := 0; i < p.Len(); i++ {
|
for v := range p.Variables() {
|
||||||
visit(p.At(i).Type())
|
visit(v.Type())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if r := t.Results(); r != nil {
|
if r := t.Results(); r != nil {
|
||||||
for i := 0; i < r.Len(); i++ {
|
for v := range r.Variables() {
|
||||||
visit(r.At(i).Type())
|
visit(v.Type())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *types.Tuple:
|
case *types.Tuple:
|
||||||
for i := 0; i < t.Len(); i++ {
|
for v := range t.Variables() {
|
||||||
visit(t.At(i).Type())
|
visit(v.Type())
|
||||||
}
|
}
|
||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
// A synthetic local type can be reachable only through a
|
// A synthetic local type can be reachable only through a
|
||||||
@@ -887,8 +884,8 @@ func (c *compilerContext) registerSyntheticLocalTypes(fn *ssa.Function) {
|
|||||||
// the interface's identifier, and the seen map breaks
|
// the interface's identifier, and the seen map breaks
|
||||||
// cycles formed by methods that mention the interface
|
// cycles formed by methods that mention the interface
|
||||||
// itself.
|
// itself.
|
||||||
for i := 0; i < t.NumMethods(); i++ {
|
for method := range t.Methods() {
|
||||||
visit(t.Method(i).Type())
|
visit(method.Type())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -939,8 +936,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
|||||||
|
|
||||||
// Create method set.
|
// Create method set.
|
||||||
var signatures, wrappers []llvm.Value
|
var signatures, wrappers []llvm.Value
|
||||||
for i := 0; i < ms.Len(); i++ {
|
for method := range ms.Methods() {
|
||||||
method := ms.At(i)
|
|
||||||
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
|
signatureGlobal := c.getMethodSignature(method.Obj().(*types.Func))
|
||||||
signatures = append(signatures, signatureGlobal)
|
signatures = append(signatures, signatureGlobal)
|
||||||
fn := c.program.MethodValue(method)
|
fn := c.program.MethodValue(method)
|
||||||
@@ -1189,8 +1185,8 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
|
|||||||
if llvmFn.IsNil() {
|
if llvmFn.IsNil() {
|
||||||
sig := instr.Method.Type().(*types.Signature)
|
sig := instr.Method.Type().(*types.Signature)
|
||||||
var paramTuple []*types.Var
|
var paramTuple []*types.Var
|
||||||
for i := 0; i < sig.Params().Len(); i++ {
|
for v := range sig.Params().Variables() {
|
||||||
paramTuple = append(paramTuple, sig.Params().At(i))
|
paramTuple = append(paramTuple, v)
|
||||||
}
|
}
|
||||||
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer]))
|
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.UnsafePointer]))
|
||||||
llvmFnType := c.getLLVMFunctionType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
|
llvmFnType := c.getLLVMFunctionType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false))
|
||||||
@@ -1307,34 +1303,38 @@ func methodSignature(method *types.Func) string {
|
|||||||
// () string
|
// () string
|
||||||
// (string, int) (int, error)
|
// (string, int) (int, error)
|
||||||
func signature(sig *types.Signature) string {
|
func signature(sig *types.Signature) string {
|
||||||
s := ""
|
var s strings.Builder
|
||||||
if sig.Params().Len() == 0 {
|
if sig.Params().Len() == 0 {
|
||||||
s += "()"
|
s.WriteString("()")
|
||||||
} else {
|
} else {
|
||||||
s += "("
|
s.WriteString("(")
|
||||||
for i := 0; i < sig.Params().Len(); i++ {
|
i := 0
|
||||||
|
for v := range sig.Params().Variables() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
s += ", "
|
s.WriteString(", ")
|
||||||
}
|
}
|
||||||
s += typestring(sig.Params().At(i).Type())
|
s.WriteString(typestring(v.Type()))
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
s += ")"
|
s.WriteString(")")
|
||||||
}
|
}
|
||||||
if sig.Results().Len() == 0 {
|
if sig.Results().Len() == 0 {
|
||||||
// keep as-is
|
// keep as-is
|
||||||
} else if sig.Results().Len() == 1 {
|
} else if sig.Results().Len() == 1 {
|
||||||
s += " " + typestring(sig.Results().At(0).Type())
|
s.WriteString(" " + typestring(sig.Results().At(0).Type()))
|
||||||
} else {
|
} else {
|
||||||
s += " ("
|
s.WriteString(" (")
|
||||||
for i := 0; i < sig.Results().Len(); i++ {
|
i := 0
|
||||||
|
for v := range sig.Results().Variables() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
s += ", "
|
s.WriteString(", ")
|
||||||
}
|
}
|
||||||
s += typestring(sig.Results().At(i).Type())
|
s.WriteString(typestring(v.Type()))
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
s += ")"
|
s.WriteString(")")
|
||||||
}
|
}
|
||||||
return s
|
return s.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// typestring returns a stable (human-readable) type string for the given type
|
// typestring returns a stable (human-readable) type string for the given type
|
||||||
|
|||||||
+3
-3
@@ -206,7 +206,7 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
|
|||||||
globalType := llvm.ArrayType(elementType, len(buf))
|
globalType := llvm.ArrayType(elementType, len(buf))
|
||||||
global := llvm.AddGlobal(c.mod, globalType, name)
|
global := llvm.AddGlobal(c.mod, globalType, name)
|
||||||
value := llvm.Undef(globalType)
|
value := llvm.Undef(globalType)
|
||||||
for i := 0; i < len(buf); i++ {
|
for i := range buf {
|
||||||
ch := uint64(buf[i])
|
ch := uint64(buf[i])
|
||||||
value = c.builder.CreateInsertValue(value, llvm.ConstInt(elementType, ch, false), i, "")
|
value = c.builder.CreateInsertValue(value, llvm.ConstInt(elementType, ch, false), i, "")
|
||||||
}
|
}
|
||||||
@@ -390,7 +390,7 @@ func (c *compilerContext) buildPointerBitmap(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
elementSize /= ptrAlign
|
elementSize /= ptrAlign
|
||||||
for i := 0; i < len; i++ {
|
for i := range len {
|
||||||
c.buildPointerBitmap(
|
c.buildPointerBitmap(
|
||||||
dst,
|
dst,
|
||||||
ptrAlign,
|
ptrAlign,
|
||||||
@@ -417,7 +417,7 @@ func (c *compilerContext) archFamily() string {
|
|||||||
// features string is not one for an ARM architecture.
|
// features string is not one for an ARM architecture.
|
||||||
func (c *compilerContext) isThumb() bool {
|
func (c *compilerContext) isThumb() bool {
|
||||||
var isThumb, isNotThumb bool
|
var isThumb, isNotThumb bool
|
||||||
for _, feature := range strings.Split(c.Features, ",") {
|
for feature := range strings.SplitSeq(c.Features, ",") {
|
||||||
if feature == "+thumb-mode" {
|
if feature == "+thumb-mode" {
|
||||||
isThumb = true
|
isThumb = true
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -7,6 +7,7 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
|
"strings"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -293,14 +294,15 @@ func hashmapCanonicalTypeName(t types.Type) string {
|
|||||||
}
|
}
|
||||||
return t.String()
|
return t.String()
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
s := "struct{"
|
var s strings.Builder
|
||||||
|
s.WriteString("struct{")
|
||||||
for i := 0; i < t.NumFields(); i++ {
|
for i := 0; i < t.NumFields(); i++ {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
s += "; "
|
s.WriteString("; ")
|
||||||
}
|
}
|
||||||
s += hashmapCanonicalTypeName(t.Field(i).Type())
|
s.WriteString(hashmapCanonicalTypeName(t.Field(i).Type()))
|
||||||
}
|
}
|
||||||
return s + "}"
|
return s.String() + "}"
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
return fmt.Sprintf("[%d]%s", t.Len(), hashmapCanonicalTypeName(t.Elem()))
|
return fmt.Sprintf("[%d]%s", t.Len(), hashmapCanonicalTypeName(t.Elem()))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -29,8 +29,7 @@ func (s *stdSizes) Alignof(T types.Type) int64 {
|
|||||||
// is the largest of the values unsafe.Alignof(x.f) for each
|
// is the largest of the values unsafe.Alignof(x.f) for each
|
||||||
// field f of x, but at least 1."
|
// field f of x, but at least 1."
|
||||||
max := int64(1)
|
max := int64(1)
|
||||||
for i := 0; i < t.NumFields(); i++ {
|
for f := range t.Fields() {
|
||||||
f := t.Field(i)
|
|
||||||
if a := s.Alignof(f.Type()); a > max {
|
if a := s.Alignof(f.Type()); a > max {
|
||||||
max = a
|
max = a
|
||||||
}
|
}
|
||||||
|
|||||||
+129
-25
@@ -8,6 +8,8 @@ import (
|
|||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -85,8 +87,8 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
|
|||||||
retType = c.getLLVMType(fn.Signature.Results().At(0).Type())
|
retType = c.getLLVMType(fn.Signature.Results().At(0).Type())
|
||||||
} else {
|
} else {
|
||||||
results := make([]llvm.Type, 0, fn.Signature.Results().Len())
|
results := make([]llvm.Type, 0, fn.Signature.Results().Len())
|
||||||
for i := 0; i < fn.Signature.Results().Len(); i++ {
|
for v := range fn.Signature.Results().Variables() {
|
||||||
results = append(results, c.getLLVMType(fn.Signature.Results().At(i).Type()))
|
results = append(results, c.getLLVMType(v.Type()))
|
||||||
}
|
}
|
||||||
retType = c.ctx.StructType(results, false)
|
retType = c.ctx.StructType(results, false)
|
||||||
}
|
}
|
||||||
@@ -322,6 +324,12 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
|
|||||||
linkName: f.RelString(nil),
|
linkName: f.RelString(nil),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RelString is not unique for local type arguments, so add a suffix
|
||||||
|
// when needed.
|
||||||
|
if suffix := c.localTypeArgsSuffix(f); suffix != "" {
|
||||||
|
info.linkName += suffix
|
||||||
|
}
|
||||||
|
|
||||||
// Check for a few runtime functions that are treated specially.
|
// Check for a few runtime functions that are treated specially.
|
||||||
if info.linkName == "runtime.wasmEntryReactor" && c.BuildMode == "c-shared" {
|
if info.linkName == "runtime.wasmEntryReactor" && c.BuildMode == "c-shared" {
|
||||||
info.linkName = "_initialize"
|
info.linkName = "_initialize"
|
||||||
@@ -346,6 +354,42 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
|
|||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *compilerContext) localTypeArgsSuffix(f *ssa.Function) string {
|
||||||
|
typeArgs := f.TypeArgs()
|
||||||
|
if len(typeArgs) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var hasLocal bool
|
||||||
|
parts := make([]string, len(typeArgs))
|
||||||
|
for i, ta := range typeArgs {
|
||||||
|
name, isLocal := c.getTypeCodeName(ta)
|
||||||
|
if isLocal {
|
||||||
|
hasLocal = true
|
||||||
|
}
|
||||||
|
// A function-local type alias (e.g. `type F = float64` inside a
|
||||||
|
// function body) is invisible to getTypeCodeName because it calls
|
||||||
|
// types.Unalias first. Two callers that use distinct aliases with
|
||||||
|
// the same name (e.g. Go 1.27's internal/strconv.ftoa32 and ftoa64
|
||||||
|
// both declare a local `type F = ...`) then produce identical
|
||||||
|
// RelStrings for their shortFloat[F] instantiations and collide on
|
||||||
|
// mod.NamedFunction. Treat these aliases as local so the suffix
|
||||||
|
// disambiguates them.
|
||||||
|
if alias, ok := ta.(*types.Alias); ok {
|
||||||
|
if obj := alias.Obj(); obj.Pkg() != nil && obj.Parent() != obj.Pkg().Scope() {
|
||||||
|
hasLocal = true
|
||||||
|
pos := c.program.Fset.PositionFor(obj.Pos(), false)
|
||||||
|
parts[i] = fmt.Sprintf("%s$alias:%s:%d:%d", name, filepath.Base(pos.Filename), pos.Line, pos.Column)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parts[i] = name
|
||||||
|
}
|
||||||
|
if !hasLocal {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return "$localtype:" + strings.Join(parts, ",")
|
||||||
|
}
|
||||||
|
|
||||||
// parsePragmas is used by getFunctionInfo to parse function pragmas such as
|
// parsePragmas is used by getFunctionInfo to parse function pragmas such as
|
||||||
// //export or //go:noinline.
|
// //export or //go:noinline.
|
||||||
func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
||||||
@@ -372,6 +416,51 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also scan file-level //go:linkname directives. These appear as
|
||||||
|
// free-standing comments in *ast.File.Comments (not attached to any
|
||||||
|
// declaration), and are used by modern golang.org/x/sys/unix and others.
|
||||||
|
// Function-attached directives (above) take precedence — we only add
|
||||||
|
// file-level ones if no doc-comment linkname was found for this function.
|
||||||
|
//
|
||||||
|
// TODO: the hasUnsafeImport gate enforced downstream (see the
|
||||||
|
// //go:linkname case below) is package-level. gc enforces it per
|
||||||
|
// file, on the file containing the directive. For file-level
|
||||||
|
// linknames this is more important than for function-attached ones,
|
||||||
|
// because the directive can live in a file separate from the
|
||||||
|
// function. A stricter implementation would check whether the file
|
||||||
|
// returned by fileForFunc imports "unsafe", not whether any file in
|
||||||
|
// the package does.
|
||||||
|
hasFunctionLinkname := false
|
||||||
|
for _, comment := range pragmas {
|
||||||
|
if strings.HasPrefix(comment.Text, "//go:linkname ") {
|
||||||
|
parts := strings.Fields(comment.Text)
|
||||||
|
if len(parts) == 3 && parts[1] == f.Name() {
|
||||||
|
hasFunctionLinkname = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasFunctionLinkname {
|
||||||
|
if file := c.fileForFunc(f); file != nil {
|
||||||
|
for _, group := range file.Comments {
|
||||||
|
// Skip the function's own doc comment — already handled above.
|
||||||
|
if decl, ok := syntax.(*ast.FuncDecl); ok && group == decl.Doc {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, comment := range group.List {
|
||||||
|
if !strings.HasPrefix(comment.Text, "//go:linkname ") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.Fields(comment.Text)
|
||||||
|
if len(parts) != 3 || parts[1] != f.Name() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pragmas = append(pragmas, comment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Parse each pragma.
|
// Parse each pragma.
|
||||||
for _, comment := range pragmas {
|
for _, comment := range pragmas {
|
||||||
parts := strings.Fields(comment.Text)
|
parts := strings.Fields(comment.Text)
|
||||||
@@ -389,7 +478,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
info.wasmName = info.linkName
|
info.wasmName = info.linkName
|
||||||
info.exported = true
|
info.exported = true
|
||||||
case "//go:interrupt":
|
case "//go:interrupt":
|
||||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
if slices.Contains(f.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||||
info.interrupt = true
|
info.interrupt = true
|
||||||
}
|
}
|
||||||
case "//go:wasm-module":
|
case "//go:wasm-module":
|
||||||
@@ -451,14 +540,14 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
// This is a slightly looser requirement than what gc uses: gc
|
// This is a slightly looser requirement than what gc uses: gc
|
||||||
// requires the file to import "unsafe", not the package as a
|
// requires the file to import "unsafe", not the package as a
|
||||||
// whole.
|
// whole.
|
||||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
if slices.Contains(f.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||||
info.linkName = parts[2]
|
info.linkName = parts[2]
|
||||||
}
|
}
|
||||||
case "//go:section":
|
case "//go:section":
|
||||||
// Only enable go:section when the package imports "unsafe".
|
// Only enable go:section when the package imports "unsafe".
|
||||||
// go:section also implies go:noinline since inlining could
|
// go:section also implies go:noinline since inlining could
|
||||||
// move the code to a different section than that requested.
|
// move the code to a different section than that requested.
|
||||||
if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) {
|
if len(parts) == 2 && slices.Contains(f.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||||
info.section = parts[1]
|
info.section = parts[1]
|
||||||
info.inline = inlineNone
|
info.inline = inlineNone
|
||||||
}
|
}
|
||||||
@@ -467,7 +556,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
|
|||||||
// runtime functions.
|
// runtime functions.
|
||||||
// This is somewhat dangerous and thus only imported in packages
|
// This is somewhat dangerous and thus only imported in packages
|
||||||
// that import unsafe.
|
// that import unsafe.
|
||||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
if slices.Contains(f.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||||
info.nobounds = true
|
info.nobounds = true
|
||||||
}
|
}
|
||||||
case "//go:noescape":
|
case "//go:noescape":
|
||||||
@@ -567,8 +656,8 @@ func (c *compilerContext) isValidWasmType(typ types.Type, site wasmSite) bool {
|
|||||||
hasHostLayout = false // package structs added in go1.23
|
hasHostLayout = false // package structs added in go1.23
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < typ.NumFields(); i++ {
|
for field := range typ.Fields() {
|
||||||
ftyp := typ.Field(i).Type()
|
ftyp := field.Type()
|
||||||
if types.Unalias(ftyp).String() == "structs.HostLayout" {
|
if types.Unalias(ftyp).String() == "structs.HostLayout" {
|
||||||
hasHostLayout = true
|
hasHostLayout = true
|
||||||
continue
|
continue
|
||||||
@@ -600,8 +689,8 @@ func getParams(sig *types.Signature) []*types.Var {
|
|||||||
if sig.Recv() != nil {
|
if sig.Recv() != nil {
|
||||||
params = append(params, sig.Recv())
|
params = append(params, sig.Recv())
|
||||||
}
|
}
|
||||||
for i := 0; i < sig.Params().Len(); i++ {
|
for v := range sig.Params().Variables() {
|
||||||
params = append(params, sig.Params().At(i))
|
params = append(params, v)
|
||||||
}
|
}
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
@@ -666,6 +755,34 @@ type globalInfo struct {
|
|||||||
section string // go:section
|
section string // go:section
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fileForFunc returns the *ast.File that contains the declaration of f, or
|
||||||
|
// nil if it cannot be determined. File-level pragmas are only consulted for
|
||||||
|
// functions in the package currently being compiled — functions imported from
|
||||||
|
// other packages have their file-level pragmas processed when those packages
|
||||||
|
// are compiled.
|
||||||
|
func (c *compilerContext) fileForFunc(f *ssa.Function) *ast.File {
|
||||||
|
if c.loaderPkg == nil || f.Pkg == nil || f.Pkg.Pkg != c.loaderPkg.Pkg {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
syntax := f.Syntax()
|
||||||
|
if f.Origin() != nil {
|
||||||
|
syntax = f.Origin().Syntax()
|
||||||
|
}
|
||||||
|
if syntax == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pos := syntax.Pos()
|
||||||
|
if !pos.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, file := range c.loaderPkg.Files {
|
||||||
|
if file.FileStart <= pos && pos < file.FileEnd {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// loadASTComments loads comments on globals from the AST, for use later in the
|
// loadASTComments loads comments on globals from the AST, for use later in the
|
||||||
// program. In particular, they are required for //go:extern pragmas on globals.
|
// program. In particular, they are required for //go:extern pragmas on globals.
|
||||||
func (c *compilerContext) loadASTComments(pkg *loader.Package) {
|
func (c *compilerContext) loadASTComments(pkg *loader.Package) {
|
||||||
@@ -704,10 +821,7 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
|||||||
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
||||||
|
|
||||||
// Set alignment from the //go:align comment.
|
// Set alignment from the //go:align comment.
|
||||||
alignment := c.targetData.ABITypeAlignment(llvmType)
|
alignment := max(info.align, c.targetData.ABITypeAlignment(llvmType))
|
||||||
if info.align > alignment {
|
|
||||||
alignment = info.align
|
|
||||||
}
|
|
||||||
if alignment <= 0 || alignment&(alignment-1) != 0 {
|
if alignment <= 0 || alignment&(alignment-1) != 0 {
|
||||||
// Check for power-of-two (or 0).
|
// Check for power-of-two (or 0).
|
||||||
// See: https://stackoverflow.com/a/108360
|
// See: https://stackoverflow.com/a/108360
|
||||||
@@ -781,7 +895,7 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup, c *compilerContext,
|
|||||||
// This is a slightly looser requirement than what gc uses: gc
|
// This is a slightly looser requirement than what gc uses: gc
|
||||||
// requires the file to import "unsafe", not the package as a
|
// requires the file to import "unsafe", not the package as a
|
||||||
// whole.
|
// whole.
|
||||||
if hasUnsafeImport(g.Pkg.Pkg) {
|
if slices.Contains(g.Pkg.Pkg.Imports(), types.Unsafe) {
|
||||||
info.linkName = parts[2]
|
info.linkName = parts[2]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -797,13 +911,3 @@ func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
|||||||
}
|
}
|
||||||
return methods
|
return methods
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true if this package imports "unsafe", false otherwise.
|
|
||||||
func hasUnsafeImport(pkg *types.Package) bool {
|
|
||||||
for _, imp := range pkg.Imports() {
|
|
||||||
if imp == types.Unsafe {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
+32
-27
@@ -26,24 +26,25 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
argTypes := []llvm.Type{b.uintptrType}
|
argTypes := []llvm.Type{b.uintptrType}
|
||||||
// Constraints will look something like:
|
// Constraints will look something like:
|
||||||
// "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}"
|
// "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}"
|
||||||
constraints := "={rax},0"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={rax},0")
|
||||||
for i, arg := range call.Args[1:] {
|
for i, arg := range call.Args[1:] {
|
||||||
constraints += "," + [...]string{
|
constraints.WriteString("," + [...]string{
|
||||||
"{rdi}",
|
"{rdi}",
|
||||||
"{rsi}",
|
"{rsi}",
|
||||||
"{rdx}",
|
"{rdx}",
|
||||||
"{r10}",
|
"{r10}",
|
||||||
"{r8}",
|
"{r8}",
|
||||||
"{r9}",
|
"{r9}",
|
||||||
}[i]
|
}[i])
|
||||||
llvmValue := b.getValue(arg, getPos(call))
|
llvmValue := b.getValue(arg, getPos(call))
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
// rcx and r11 are clobbered by the syscall, so make sure they are not used
|
// rcx and r11 are clobbered by the syscall, so make sure they are not used
|
||||||
constraints += ",~{rcx},~{r11}"
|
constraints.WriteString(",~{rcx},~{r11}")
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel, false)
|
target := llvm.InlineAsm(fnType, "syscall", constraints.String(), true, false, llvm.InlineAsmDialectIntel, false)
|
||||||
return b.CreateCall(fnType, target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
|
|
||||||
case b.GOARCH == "386" && b.GOOS == "linux":
|
case b.GOARCH == "386" && b.GOOS == "linux":
|
||||||
@@ -55,22 +56,23 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
argTypes := []llvm.Type{b.uintptrType}
|
argTypes := []llvm.Type{b.uintptrType}
|
||||||
// Constraints will look something like:
|
// Constraints will look something like:
|
||||||
// "={eax},0,{ebx},{ecx},{edx},{esi},{edi},{ebp}"
|
// "={eax},0,{ebx},{ecx},{edx},{esi},{edi},{ebp}"
|
||||||
constraints := "={eax},0"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={eax},0")
|
||||||
for i, arg := range call.Args[1:] {
|
for i, arg := range call.Args[1:] {
|
||||||
constraints += "," + [...]string{
|
constraints.WriteString("," + [...]string{
|
||||||
"{ebx}",
|
"{ebx}",
|
||||||
"{ecx}",
|
"{ecx}",
|
||||||
"{edx}",
|
"{edx}",
|
||||||
"{esi}",
|
"{esi}",
|
||||||
"{edi}",
|
"{edi}",
|
||||||
"{ebp}",
|
"{ebp}",
|
||||||
}[i]
|
}[i])
|
||||||
llvmValue := b.getValue(arg, getPos(call))
|
llvmValue := b.getValue(arg, getPos(call))
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel, false)
|
target := llvm.InlineAsm(fnType, "int 0x80", constraints.String(), true, false, llvm.InlineAsmDialectIntel, false)
|
||||||
return b.CreateCall(fnType, target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
|
|
||||||
case b.GOARCH == "arm" && b.GOOS == "linux":
|
case b.GOARCH == "arm" && b.GOOS == "linux":
|
||||||
@@ -88,9 +90,10 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
argTypes := []llvm.Type{}
|
argTypes := []llvm.Type{}
|
||||||
// Constraints will look something like:
|
// Constraints will look something like:
|
||||||
// ={r0},0,{r1},{r2},{r7},~{r3}
|
// ={r0},0,{r1},{r2},{r7},~{r3}
|
||||||
constraints := "={r0}"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={r0}")
|
||||||
for i, arg := range call.Args[1:] {
|
for i, arg := range call.Args[1:] {
|
||||||
constraints += "," + [...]string{
|
constraints.WriteString("," + [...]string{
|
||||||
"0", // tie to output
|
"0", // tie to output
|
||||||
"{r1}",
|
"{r1}",
|
||||||
"{r2}",
|
"{r2}",
|
||||||
@@ -98,20 +101,20 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
"{r4}",
|
"{r4}",
|
||||||
"{r5}",
|
"{r5}",
|
||||||
"{r6}",
|
"{r6}",
|
||||||
}[i]
|
}[i])
|
||||||
llvmValue := b.getValue(arg, getPos(call))
|
llvmValue := b.getValue(arg, getPos(call))
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
args = append(args, num)
|
args = append(args, num)
|
||||||
argTypes = append(argTypes, b.uintptrType)
|
argTypes = append(argTypes, b.uintptrType)
|
||||||
constraints += ",{r7}" // syscall number
|
constraints.WriteString(",{r7}") // syscall number
|
||||||
for i := len(call.Args) - 1; i < 4; i++ {
|
for i := len(call.Args) - 1; i < 4; i++ {
|
||||||
// r0-r3 get clobbered after the syscall returns
|
// r0-r3 get clobbered after the syscall returns
|
||||||
constraints += ",~{r" + strconv.Itoa(i) + "}"
|
constraints.WriteString(",~{r" + strconv.Itoa(i) + "}")
|
||||||
}
|
}
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
|
target := llvm.InlineAsm(fnType, "svc #0", constraints.String(), true, false, 0, false)
|
||||||
return b.CreateCall(fnType, target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
|
|
||||||
case b.GOARCH == "arm64" && b.GOOS == "linux":
|
case b.GOARCH == "arm64" && b.GOOS == "linux":
|
||||||
@@ -120,31 +123,32 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
argTypes := []llvm.Type{}
|
argTypes := []llvm.Type{}
|
||||||
// Constraints will look something like:
|
// Constraints will look something like:
|
||||||
// ={x0},0,{x1},{x2},{x8},~{x3},~{x4},~{x5},~{x6},~{x7},~{x16},~{x17}
|
// ={x0},0,{x1},{x2},{x8},~{x3},~{x4},~{x5},~{x6},~{x7},~{x16},~{x17}
|
||||||
constraints := "={x0}"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={x0}")
|
||||||
for i, arg := range call.Args[1:] {
|
for i, arg := range call.Args[1:] {
|
||||||
constraints += "," + [...]string{
|
constraints.WriteString("," + [...]string{
|
||||||
"0", // tie to output
|
"0", // tie to output
|
||||||
"{x1}",
|
"{x1}",
|
||||||
"{x2}",
|
"{x2}",
|
||||||
"{x3}",
|
"{x3}",
|
||||||
"{x4}",
|
"{x4}",
|
||||||
"{x5}",
|
"{x5}",
|
||||||
}[i]
|
}[i])
|
||||||
llvmValue := b.getValue(arg, getPos(call))
|
llvmValue := b.getValue(arg, getPos(call))
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
args = append(args, num)
|
args = append(args, num)
|
||||||
argTypes = append(argTypes, b.uintptrType)
|
argTypes = append(argTypes, b.uintptrType)
|
||||||
constraints += ",{x8}" // syscall number
|
constraints.WriteString(",{x8}") // syscall number
|
||||||
for i := len(call.Args) - 1; i < 8; i++ {
|
for i := len(call.Args) - 1; i < 8; i++ {
|
||||||
// x0-x7 may get clobbered during the syscall following the aarch64
|
// x0-x7 may get clobbered during the syscall following the aarch64
|
||||||
// calling convention.
|
// calling convention.
|
||||||
constraints += ",~{x" + strconv.Itoa(i) + "}"
|
constraints.WriteString(",~{x" + strconv.Itoa(i) + "}")
|
||||||
}
|
}
|
||||||
constraints += ",~{x16},~{x17}" // scratch registers
|
constraints.WriteString(",~{x16},~{x17}") // scratch registers
|
||||||
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
|
target := llvm.InlineAsm(fnType, "svc #0", constraints.String(), true, false, 0, false)
|
||||||
return b.CreateCall(fnType, target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
|
|
||||||
case (b.GOARCH == "mips" || b.GOARCH == "mipsle") && b.GOOS == "linux":
|
case (b.GOARCH == "mips" || b.GOARCH == "mipsle") && b.GOOS == "linux":
|
||||||
@@ -163,7 +167,8 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
// faster and smaller code.
|
// faster and smaller code.
|
||||||
args := []llvm.Value{num}
|
args := []llvm.Value{num}
|
||||||
argTypes := []llvm.Type{b.uintptrType}
|
argTypes := []llvm.Type{b.uintptrType}
|
||||||
constraints := "={$2},={$7},0"
|
var constraints strings.Builder
|
||||||
|
constraints.WriteString("={$2},={$7},0")
|
||||||
syscallParams := call.Args[1:]
|
syscallParams := call.Args[1:]
|
||||||
if len(syscallParams) > 7 {
|
if len(syscallParams) > 7 {
|
||||||
// There is one syscall that uses 7 parameters: sync_file_range.
|
// There is one syscall that uses 7 parameters: sync_file_range.
|
||||||
@@ -172,7 +177,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
syscallParams = syscallParams[:7]
|
syscallParams = syscallParams[:7]
|
||||||
}
|
}
|
||||||
for i, arg := range syscallParams {
|
for i, arg := range syscallParams {
|
||||||
constraints += "," + [...]string{
|
constraints.WriteString("," + [...]string{
|
||||||
"{$4}", // arg1
|
"{$4}", // arg1
|
||||||
"{$5}", // arg2
|
"{$5}", // arg2
|
||||||
"{$6}", // arg3
|
"{$6}", // arg3
|
||||||
@@ -180,7 +185,7 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
"r", // arg5 on the stack
|
"r", // arg5 on the stack
|
||||||
"r", // arg6 on the stack
|
"r", // arg6 on the stack
|
||||||
"r", // arg7 on the stack
|
"r", // arg7 on the stack
|
||||||
}[i]
|
}[i])
|
||||||
llvmValue := b.getValue(arg, getPos(call))
|
llvmValue := b.getValue(arg, getPos(call))
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
@@ -221,10 +226,10 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
"addu $$sp, $$sp, 32\n" +
|
"addu $$sp, $$sp, 32\n" +
|
||||||
".set at\n"
|
".set at\n"
|
||||||
}
|
}
|
||||||
constraints += ",~{$3},~{$4},~{$5},~{$6},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$24},~{$25},~{hi},~{lo},~{memory}"
|
constraints.WriteString(",~{$3},~{$4},~{$5},~{$6},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$24},~{$25},~{hi},~{lo},~{memory}")
|
||||||
returnType := b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType}, false)
|
returnType := b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType}, false)
|
||||||
fnType := llvm.FunctionType(returnType, argTypes, false)
|
fnType := llvm.FunctionType(returnType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, asm, constraints, true, true, 0, false)
|
target := llvm.InlineAsm(fnType, asm, constraints.String(), true, true, 0, false)
|
||||||
call := b.CreateCall(fnType, target, args, "")
|
call := b.CreateCall(fnType, target, args, "")
|
||||||
resultCode := b.CreateExtractValue(call, 0, "") // r2
|
resultCode := b.CreateExtractValue(call, 0, "") // r2
|
||||||
errorFlag := b.CreateExtractValue(call, 1, "") // r7
|
errorFlag := b.CreateExtractValue(call, 1, "") // r7
|
||||||
|
|||||||
Vendored
+28
@@ -120,3 +120,31 @@ func stillEscapes(a *int, b []int, c chan int, d *[0]byte) {
|
|||||||
func doesHeapAlloc() *int {
|
func doesHeapAlloc() *int {
|
||||||
return new(int)
|
return new(int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Define a function in a different package using a file-level go:linkname.
|
||||||
|
// (Same as withLinkageName1, but with the //go:linkname directive detached
|
||||||
|
// from the function declaration — see https://github.com/tinygo-org/tinygo/issues/4395)
|
||||||
|
func withFileLevelLinkageName1() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import a function from a different package using a file-level go:linkname.
|
||||||
|
// (Same as withLinkageName2, but with the //go:linkname directive detached
|
||||||
|
// from the function declaration.)
|
||||||
|
func withFileLevelLinkageName2()
|
||||||
|
|
||||||
|
//go:linkname withFileLevelLinkageName1 somepkg.someFileLevelFunction1
|
||||||
|
//go:linkname withFileLevelLinkageName2 somepkg.someFileLevelFunction2
|
||||||
|
|
||||||
|
// File-level linkname directives can also appear between two function
|
||||||
|
// declarations, in which case Go's AST attaches them as the doc comment
|
||||||
|
// of the following function — even when the directive's localname refers
|
||||||
|
// to a different function. Exercise that case: the directive below names
|
||||||
|
// withAdjacentLinkageName, but Go will attach it to
|
||||||
|
// sentinelAfterAdjacentLinkname's Doc. The file-level scan must find it
|
||||||
|
// by walking comment groups regardless of which decl they're attached to.
|
||||||
|
func withAdjacentLinkageName() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname withAdjacentLinkageName somepkg.someAdjacentFunction
|
||||||
|
func sentinelAfterAdjacentLinkname() {
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+20
@@ -102,6 +102,26 @@ entry:
|
|||||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||||
declare noalias nonnull ptr @runtime.alloc_noheap(i32, ptr, ptr) #9
|
declare noalias nonnull ptr @runtime.alloc_noheap(i32, ptr, ptr) #9
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @somepkg.someFileLevelFunction1(ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @somepkg.someFileLevelFunction2(ptr) #0
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @somepkg.someAdjacentFunction(ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden void @main.sentinelAfterAdjacentLinkname(ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="extern_func" }
|
attributes #2 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "wasm-export-name"="extern_func" }
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ func TestCorpus(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, repo := range repos {
|
for _, repo := range repos {
|
||||||
repo := repo
|
|
||||||
name := repo.Repo
|
name := repo.Repo
|
||||||
if repo.Tags != "" {
|
if repo.Tags != "" {
|
||||||
name += "(" + strings.ReplaceAll(repo.Tags, " ", "-") + ")"
|
name += "(" + strings.ReplaceAll(repo.Tags, " ", "-") + ")"
|
||||||
@@ -132,7 +131,6 @@ func TestCorpus(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, dir := range repo.Subdirs {
|
for _, dir := range repo.Subdirs {
|
||||||
dir := dir
|
|
||||||
t.Run(dir.Pkg, func(t *testing.T) {
|
t.Run(dir.Pkg, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -116,10 +116,7 @@ func Diff(oldName string, old []byte, newName string, new []byte) []byte {
|
|||||||
|
|
||||||
// End chunk with common lines for context.
|
// End chunk with common lines for context.
|
||||||
if len(ctext) > 0 {
|
if len(ctext) > 0 {
|
||||||
n := end.x - start.x
|
n := min(end.x-start.x, C)
|
||||||
if n > C {
|
|
||||||
n = C
|
|
||||||
}
|
|
||||||
for _, s := range x[start.x : start.x+n] {
|
for _, s := range x[start.x : start.x+n] {
|
||||||
ctext = append(ctext, " "+s)
|
ctext = append(ctext, " "+s)
|
||||||
count.x++
|
count.x++
|
||||||
@@ -234,7 +231,7 @@ func tgs(x, y []string) []pair {
|
|||||||
for i := range T {
|
for i := range T {
|
||||||
T[i] = n + 1
|
T[i] = n + 1
|
||||||
}
|
}
|
||||||
for i := 0; i < n; i++ {
|
for i := range n {
|
||||||
k := sort.Search(n, func(k int) bool {
|
k := sort.Search(n, func(k int) bool {
|
||||||
return T[k] >= J[i]
|
return T[k] >= J[i]
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-1
@@ -136,7 +136,7 @@ func readErrorMessages(t *testing.T, file string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var errors []string
|
var errors []string
|
||||||
for _, line := range strings.Split(string(data), "\n") {
|
for line := range strings.SplitSeq(string(data), "\n") {
|
||||||
if strings.HasPrefix(line, "// ERROR: ") {
|
if strings.HasPrefix(line, "// ERROR: ") {
|
||||||
errors = append(errors, strings.TrimRight(line[len("// ERROR: "):], "\r\n"))
|
errors = append(errors, strings.TrimRight(line[len("// ERROR: "):], "\r\n"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,6 +151,8 @@ func Get(name string) string {
|
|||||||
panic("could not find cache dir: " + err.Error())
|
panic("could not find cache dir: " + err.Error())
|
||||||
}
|
}
|
||||||
return filepath.Join(dir, "tinygo")
|
return filepath.Join(dir, "tinygo")
|
||||||
|
case "CGO_CFLAGS":
|
||||||
|
return os.Getenv("CGO_CFLAGS")
|
||||||
case "CGO_ENABLED":
|
case "CGO_ENABLED":
|
||||||
// Always enable CGo. It is required by a number of targets, including
|
// Always enable CGo. It is required by a number of targets, including
|
||||||
// macOS and the rp2040.
|
// macOS and the rp2040.
|
||||||
|
|||||||
+1
-1
@@ -151,7 +151,7 @@ func (r *runner) compileFunction(llvmFn llvm.Value) *function {
|
|||||||
case llvm.PHI:
|
case llvm.PHI:
|
||||||
inst.name = llvmInst.Name()
|
inst.name = llvmInst.Name()
|
||||||
incomingCount := inst.llvmInst.IncomingCount()
|
incomingCount := inst.llvmInst.IncomingCount()
|
||||||
for i := 0; i < incomingCount; i++ {
|
for i := range incomingCount {
|
||||||
incomingBB := inst.llvmInst.IncomingBlock(i)
|
incomingBB := inst.llvmInst.IncomingBlock(i)
|
||||||
incomingValue := inst.llvmInst.IncomingValue(i)
|
incomingValue := inst.llvmInst.IncomingValue(i)
|
||||||
inst.operands = append(inst.operands,
|
inst.operands = append(inst.operands,
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ func TestInterp(t *testing.T) {
|
|||||||
"alloc",
|
"alloc",
|
||||||
"slicedata",
|
"slicedata",
|
||||||
} {
|
} {
|
||||||
name := name // make local to this closure
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTest(t, "testdata/"+name)
|
runTest(t, "testdata/"+name)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -470,7 +471,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
|||||||
// should be returned.
|
// should be returned.
|
||||||
numMethods := int(r.builder.CreateExtractValue(methodSet, 0, "").ZExtValue())
|
numMethods := int(r.builder.CreateExtractValue(methodSet, 0, "").ZExtValue())
|
||||||
var method llvm.Value
|
var method llvm.Value
|
||||||
for i := 0; i < numMethods; i++ {
|
for i := range numMethods {
|
||||||
methodSignatureAgg := r.builder.CreateExtractValue(methodSet, 1, "")
|
methodSignatureAgg := r.builder.CreateExtractValue(methodSet, 1, "")
|
||||||
methodSignature := r.builder.CreateExtractValue(methodSignatureAgg, i, "")
|
methodSignature := r.builder.CreateExtractValue(methodSignatureAgg, i, "")
|
||||||
if methodSignature == signature {
|
if methodSignature == signature {
|
||||||
@@ -907,7 +908,7 @@ func (r *runner) interpretICmp(lhs, rhs value, predicate llvm.IntPredicate) bool
|
|||||||
func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, mem *memoryView, indent string) *Error {
|
func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, mem *memoryView, indent string) *Error {
|
||||||
numOperands := inst.llvmInst.OperandsCount()
|
numOperands := inst.llvmInst.OperandsCount()
|
||||||
operands := make([]llvm.Value, numOperands)
|
operands := make([]llvm.Value, numOperands)
|
||||||
for i := 0; i < numOperands; i++ {
|
for i := range numOperands {
|
||||||
operand := inst.llvmInst.Operand(i)
|
operand := inst.llvmInst.Operand(i)
|
||||||
if !operand.IsAInstruction().IsNil() || !operand.IsAArgument().IsNil() {
|
if !operand.IsAInstruction().IsNil() || !operand.IsAArgument().IsNil() {
|
||||||
var err error
|
var err error
|
||||||
@@ -985,9 +986,9 @@ func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, me
|
|||||||
mem.instructions = append(mem.instructions, agg)
|
mem.instructions = append(mem.instructions, agg)
|
||||||
}
|
}
|
||||||
result = operands[1]
|
result = operands[1]
|
||||||
for i := len(indices) - 1; i >= 0; i-- {
|
for i, indice := range slices.Backward(indices) {
|
||||||
agg := aggregates[i]
|
agg := aggregates[i]
|
||||||
result = r.builder.CreateInsertValue(agg, result, int(indices[i]), inst.name+".insertvalue"+strconv.Itoa(i))
|
result = r.builder.CreateInsertValue(agg, result, int(indice), inst.name+".insertvalue"+strconv.Itoa(i))
|
||||||
if i != 0 { // don't add last result to mem.instructions as it will be done at the end already
|
if i != 0 { // don't add last result to mem.instructions as it will be done at the end already
|
||||||
mem.instructions = append(mem.instructions, result)
|
mem.instructions = append(mem.instructions, result)
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-14
@@ -18,8 +18,10 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -80,9 +82,7 @@ func (mv *memoryView) extend(sub memoryView) {
|
|||||||
if mv.objects == nil && len(sub.objects) != 0 {
|
if mv.objects == nil && len(sub.objects) != 0 {
|
||||||
mv.objects = make(map[uint32]object)
|
mv.objects = make(map[uint32]object)
|
||||||
}
|
}
|
||||||
for key, value := range sub.objects {
|
maps.Copy(mv.objects, sub.objects)
|
||||||
mv.objects[key] = value
|
|
||||||
}
|
|
||||||
mv.instructions = append(mv.instructions, sub.instructions...)
|
mv.instructions = append(mv.instructions, sub.instructions...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,8 +90,8 @@ func (mv *memoryView) extend(sub memoryView) {
|
|||||||
// created in this memoryView. Do not reuse this memoryView.
|
// created in this memoryView. Do not reuse this memoryView.
|
||||||
func (mv *memoryView) revert() {
|
func (mv *memoryView) revert() {
|
||||||
// Erase instructions in reverse order.
|
// Erase instructions in reverse order.
|
||||||
for i := len(mv.instructions) - 1; i >= 0; i-- {
|
for _, llvmInst := range slices.Backward(mv.instructions) {
|
||||||
llvmInst := mv.instructions[i]
|
|
||||||
if llvmInst.IsAInstruction().IsNil() {
|
if llvmInst.IsAInstruction().IsNil() {
|
||||||
// The IR builder will try to create constant versions of
|
// The IR builder will try to create constant versions of
|
||||||
// instructions whenever possible. If it does this, it's not an
|
// instructions whenever possible. If it does this, it's not an
|
||||||
@@ -172,7 +172,7 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
numOperands := inst.OperandsCount()
|
numOperands := inst.OperandsCount()
|
||||||
for i := 0; i < numOperands; i++ {
|
for i := range numOperands {
|
||||||
// Using mark '2' (which means read/write access)
|
// Using mark '2' (which means read/write access)
|
||||||
// because this might be a store instruction.
|
// because this might be a store instruction.
|
||||||
err := mv.markExternal(inst.Operand(i), 2)
|
err := mv.markExternal(inst.Operand(i), 2)
|
||||||
@@ -215,7 +215,7 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
|
|||||||
// need any marking.
|
// need any marking.
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
numElements := llvmType.StructElementTypesCount()
|
numElements := llvmType.StructElementTypesCount()
|
||||||
for i := 0; i < numElements; i++ {
|
for i := range numElements {
|
||||||
element := mv.r.builder.CreateExtractValue(llvmValue, i, "")
|
element := mv.r.builder.CreateExtractValue(llvmValue, i, "")
|
||||||
err := mv.markExternal(element, mark)
|
err := mv.markExternal(element, mark)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -224,7 +224,7 @@ func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
|
|||||||
}
|
}
|
||||||
case llvm.ArrayTypeKind:
|
case llvm.ArrayTypeKind:
|
||||||
numElements := llvmType.ArrayLength()
|
numElements := llvmType.ArrayLength()
|
||||||
for i := 0; i < numElements; i++ {
|
for i := range numElements {
|
||||||
element := mv.r.builder.CreateExtractValue(llvmValue, i, "")
|
element := mv.r.builder.CreateExtractValue(llvmValue, i, "")
|
||||||
err := mv.markExternal(element, mark)
|
err := mv.markExternal(element, mark)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -366,7 +366,7 @@ func (mv *memoryView) store(v value, p pointerValue) bool {
|
|||||||
buffer := obj.buffer.asRawValue(mv.r)
|
buffer := obj.buffer.asRawValue(mv.r)
|
||||||
obj.buffer = buffer
|
obj.buffer = buffer
|
||||||
v := v.asRawValue(mv.r)
|
v := v.asRawValue(mv.r)
|
||||||
for i := uint32(0); i < valueLen; i++ {
|
for i := range valueLen {
|
||||||
buffer.buf[p.offset()+i] = v.buf[i]
|
buffer.buf[p.offset()+i] = v.buf[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,7 +392,7 @@ type value interface {
|
|||||||
// literalValue contains simple integer values that don't need to be stored in a
|
// literalValue contains simple integer values that don't need to be stored in a
|
||||||
// buffer.
|
// buffer.
|
||||||
type literalValue struct {
|
type literalValue struct {
|
||||||
value interface{}
|
value any
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a literalValue given the number of bits.
|
// Make a literalValue given the number of bits.
|
||||||
@@ -1015,7 +1015,7 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
for i := uint32(0); i < ptrSize; i++ {
|
for i := range ptrSize {
|
||||||
v.buf[i] = ptr.pointer
|
v.buf[i] = ptr.pointer
|
||||||
}
|
}
|
||||||
} else if !llvmValue.IsAConstantExpr().IsNil() {
|
} else if !llvmValue.IsAConstantExpr().IsNil() {
|
||||||
@@ -1056,7 +1056,7 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
ptrValue.pointer += totalOffset
|
ptrValue.pointer += totalOffset
|
||||||
for i := uint32(0); i < ptrSize; i++ {
|
for i := range ptrSize {
|
||||||
v.buf[i] = ptrValue.pointer
|
v.buf[i] = ptrValue.pointer
|
||||||
}
|
}
|
||||||
case llvm.ICmp:
|
case llvm.ICmp:
|
||||||
@@ -1113,7 +1113,7 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
|
|||||||
}
|
}
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
numElements := llvmType.StructElementTypesCount()
|
numElements := llvmType.StructElementTypesCount()
|
||||||
for i := 0; i < numElements; i++ {
|
for i := range numElements {
|
||||||
offset := r.targetData.ElementOffset(llvmType, i)
|
offset := r.targetData.ElementOffset(llvmType, i)
|
||||||
field := rawValue{
|
field := rawValue{
|
||||||
buf: v.buf[offset:],
|
buf: v.buf[offset:],
|
||||||
@@ -1124,7 +1124,7 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
|
|||||||
numElements := llvmType.ArrayLength()
|
numElements := llvmType.ArrayLength()
|
||||||
childType := llvmType.ElementType()
|
childType := llvmType.ElementType()
|
||||||
childTypeSize := r.targetData.TypeAllocSize(childType)
|
childTypeSize := r.targetData.TypeAllocSize(childType)
|
||||||
for i := 0; i < numElements; i++ {
|
for i := range numElements {
|
||||||
offset := i * int(childTypeSize)
|
offset := i * int(childTypeSize)
|
||||||
field := rawValue{
|
field := rawValue{
|
||||||
buf: v.buf[offset:],
|
buf: v.buf[offset:],
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/google/shlex"
|
||||||
"github.com/tinygo-org/tinygo/cgo"
|
"github.com/tinygo-org/tinygo/cgo"
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
"github.com/tinygo-org/tinygo/goenv"
|
"github.com/tinygo-org/tinygo/goenv"
|
||||||
@@ -506,6 +507,11 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
var initialCFlags []string
|
var initialCFlags []string
|
||||||
initialCFlags = append(initialCFlags, p.program.config.CFlags(true)...)
|
initialCFlags = append(initialCFlags, p.program.config.CFlags(true)...)
|
||||||
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
||||||
|
cgoCFlags, err := shlex.Split(goenv.Get("CGO_CFLAGS"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to split CGO_CFLAGS: %w", err)
|
||||||
|
}
|
||||||
|
initialCFlags = append(initialCFlags, cgoCFlags...)
|
||||||
generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags, p.program.config.GOOS())
|
generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags, p.program.config.GOOS())
|
||||||
p.CFlags = append(initialCFlags, cflags...)
|
p.CFlags = append(initialCFlags, cflags...)
|
||||||
p.CGoHeaders = headerCode
|
p.CGoHeaders = headerCode
|
||||||
|
|||||||
@@ -786,15 +786,15 @@ func Debug(debugger, pkgName string, ocdOutput bool, options *compileopts.Option
|
|||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
daemon.Process.Signal(os.Interrupt)
|
daemon.Process.Signal(os.Interrupt)
|
||||||
var stopped uint32
|
var stopped atomic.Uint32
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(time.Millisecond * 100)
|
time.Sleep(time.Millisecond * 100)
|
||||||
if atomic.LoadUint32(&stopped) == 0 {
|
if stopped.Load() == 0 {
|
||||||
daemon.Process.Kill()
|
daemon.Process.Kill()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
daemon.Wait()
|
daemon.Wait()
|
||||||
atomic.StoreUint32(&stopped, 1)
|
stopped.Store(1)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1046,7 +1046,7 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
|
|||||||
|
|
||||||
func touchSerialPortAt1200bps(port string) (err error) {
|
func touchSerialPortAt1200bps(port string) (err error) {
|
||||||
retryCount := 3
|
retryCount := 3
|
||||||
for i := 0; i < retryCount; i++ {
|
for range retryCount {
|
||||||
// Open port
|
// Open port
|
||||||
p, e := serial.Open(port, &serial.Mode{BaudRate: 1200})
|
p, e := serial.Open(port, &serial.Mode{BaudRate: 1200})
|
||||||
if e != nil {
|
if e != nil {
|
||||||
@@ -1244,7 +1244,7 @@ func findFATMounts(options *compileopts.Options) ([]mountPoint, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not list mount points: %w", err)
|
return nil, fmt.Errorf("could not list mount points: %w", err)
|
||||||
}
|
}
|
||||||
for _, line := range strings.Split(string(tab), "\n") {
|
for line := range strings.SplitSeq(string(tab), "\n") {
|
||||||
fields := strings.Fields(line)
|
fields := strings.Fields(line)
|
||||||
if len(fields) <= 2 {
|
if len(fields) <= 2 {
|
||||||
continue
|
continue
|
||||||
@@ -1273,7 +1273,7 @@ func findFATMounts(options *compileopts.Options) ([]mountPoint, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract data to convert to a []mountPoint slice.
|
// Extract data to convert to a []mountPoint slice.
|
||||||
for _, line := range strings.Split(out.String(), "\n") {
|
for line := range strings.SplitSeq(out.String(), "\n") {
|
||||||
words := strings.Fields(line)
|
words := strings.Fields(line)
|
||||||
if len(words) < 3 {
|
if len(words) < 3 {
|
||||||
continue
|
continue
|
||||||
@@ -1685,18 +1685,18 @@ func (m globalValuesFlag) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m globalValuesFlag) Set(value string) error {
|
func (m globalValuesFlag) Set(value string) error {
|
||||||
equalsIndex := strings.IndexByte(value, '=')
|
before, after, ok := strings.Cut(value, "=")
|
||||||
if equalsIndex < 0 {
|
if !ok {
|
||||||
return errors.New("expected format pkgpath.Var=value")
|
return errors.New("expected format pkgpath.Var=value")
|
||||||
}
|
}
|
||||||
pathAndName := value[:equalsIndex]
|
pathAndName := before
|
||||||
pointIndex := strings.LastIndexByte(pathAndName, '.')
|
pointIndex := strings.LastIndexByte(pathAndName, '.')
|
||||||
if pointIndex < 0 {
|
if pointIndex < 0 {
|
||||||
return errors.New("expected format pkgpath.Var=value")
|
return errors.New("expected format pkgpath.Var=value")
|
||||||
}
|
}
|
||||||
path := pathAndName[:pointIndex]
|
path := pathAndName[:pointIndex]
|
||||||
name := pathAndName[pointIndex+1:]
|
name := pathAndName[pointIndex+1:]
|
||||||
stringValue := value[equalsIndex+1:]
|
stringValue := after
|
||||||
if m[path] == nil {
|
if m[path] == nil {
|
||||||
m[path] = make(map[string]string)
|
m[path] = make(map[string]string)
|
||||||
}
|
}
|
||||||
@@ -2054,7 +2054,6 @@ func main() {
|
|||||||
// This uses an additional semaphore to reduce the memory usage.
|
// This uses an additional semaphore to reduce the memory usage.
|
||||||
testSema := make(chan struct{}, cap(options.Semaphore))
|
testSema := make(chan struct{}, cap(options.Semaphore))
|
||||||
for i, pkgName := range explicitPkgNames {
|
for i, pkgName := range explicitPkgNames {
|
||||||
pkgName := pkgName
|
|
||||||
buf := &bufs[i]
|
buf := &bufs[i]
|
||||||
testSema <- struct{}{}
|
testSema <- struct{}{}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|||||||
+94
-12
@@ -245,6 +245,21 @@ func TestBuild(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestTimerStopResetRace checks that stopping or resetting a timer while its
|
||||||
|
// callback is running behaves correctly. It reaches into the runtime timer
|
||||||
|
// hooks via //go:linkname and relies on the threads scheduler, which is only
|
||||||
|
// used on these hosts.
|
||||||
|
func TestTimerStopResetRace(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin", "linux":
|
||||||
|
default:
|
||||||
|
t.Skipf("host GOOS %s does not use the threads scheduler", runtime.GOOS)
|
||||||
|
}
|
||||||
|
runTest("timer_stop_reset_race.go", optionsFromTarget("", sema), t, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
||||||
emuCheck(t, options)
|
emuCheck(t, options)
|
||||||
|
|
||||||
@@ -383,7 +398,7 @@ func emuCheck(t *testing.T, options compileopts.Options) {
|
|||||||
t.Fatal("failed to load target spec:", err)
|
t.Fatal("failed to load target spec:", err)
|
||||||
}
|
}
|
||||||
if spec.Emulator != "" {
|
if spec.Emulator != "" {
|
||||||
emulatorCommand := strings.SplitN(spec.Emulator, " ", 2)[0]
|
emulatorCommand, _, _ := strings.Cut(spec.Emulator, " ")
|
||||||
_, err := exec.LookPath(emulatorCommand)
|
_, err := exec.LookPath(emulatorCommand)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, exec.ErrNotFound) {
|
if errors.Is(err, exec.ErrNotFound) {
|
||||||
@@ -458,6 +473,12 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
options.Directory = path
|
options.Directory = path
|
||||||
pkgName = "."
|
pkgName = "."
|
||||||
}
|
}
|
||||||
|
isWebAssembly := strings.HasPrefix(options.Target, "wasi") ||
|
||||||
|
strings.HasPrefix(options.Target, "wasm") ||
|
||||||
|
strings.HasPrefix(options.GOARCH, "wasm")
|
||||||
|
if name == "testing.go" && isWebAssembly {
|
||||||
|
expectedOutputPath = TESTDATA + "/testing-wasm.txt"
|
||||||
|
}
|
||||||
|
|
||||||
config, err := builder.NewConfig(&options)
|
config, err := builder.NewConfig(&options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -467,12 +488,18 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
// Build the test binary.
|
// Build the test binary.
|
||||||
stdout := &bytes.Buffer{}
|
stdout := &bytes.Buffer{}
|
||||||
_, err = buildAndRun(pkgName, config, stdout, cmdArgs, environmentVars, 2*time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
_, err = buildAndRun(pkgName, config, stdout, cmdArgs, environmentVars, 2*time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
||||||
|
if config.EmulatorName() == "simavr" {
|
||||||
|
// simavr before v1.8 wrote firmware output to stderr and loader logs
|
||||||
|
// to stdout, but PR #490 swapped these streams:
|
||||||
|
// https://github.com/buserror/simavr/pull/490
|
||||||
|
cmd.Stdout = stdout
|
||||||
|
}
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w := &bytes.Buffer{}
|
w := &bytes.Buffer{}
|
||||||
diagnostics.CreateDiagnostics(err).WriteTo(w, "")
|
diagnostics.CreateDiagnostics(err).WriteTo(w, "")
|
||||||
for _, line := range strings.Split(strings.TrimRight(w.String(), "\n"), "\n") {
|
for line := range strings.SplitSeq(strings.TrimRight(w.String(), "\n"), "\n") {
|
||||||
t.Log(line)
|
t.Log(line)
|
||||||
}
|
}
|
||||||
if stdout.Len() != 0 {
|
if stdout.Len() != 0 {
|
||||||
@@ -484,11 +511,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
|
|
||||||
actual := stdout.Bytes()
|
actual := stdout.Bytes()
|
||||||
if config.EmulatorName() == "simavr" {
|
if config.EmulatorName() == "simavr" {
|
||||||
// Strip simavr log formatting.
|
actual = cleanSimAVRTestOutput(actual)
|
||||||
actual = bytes.Replace(actual, []byte{0x1b, '[', '3', '2', 'm'}, nil, -1)
|
|
||||||
actual = bytes.Replace(actual, []byte{0x1b, '[', '0', 'm'}, nil, -1)
|
|
||||||
actual = bytes.Replace(actual, []byte{'.', '.', '\n'}, []byte{'\n'}, -1)
|
|
||||||
actual = bytes.Replace(actual, []byte{'\n', '.', '\n'}, []byte{'\n', '\n'}, -1)
|
|
||||||
}
|
}
|
||||||
if name == "testing.go" {
|
if name == "testing.go" {
|
||||||
// Strip actual time.
|
// Strip actual time.
|
||||||
@@ -515,6 +538,28 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cleanSimAVRTestOutput(output []byte) []byte {
|
||||||
|
output = bytes.ReplaceAll(output, []byte{0x1b, '[', '3', '2', 'm'}, nil)
|
||||||
|
output = bytes.ReplaceAll(output, []byte{0x1b, '[', '0', 'm'}, nil)
|
||||||
|
output = bytes.ReplaceAll(output, []byte{'.', '.', '\n'}, []byte{'\n'})
|
||||||
|
output = bytes.ReplaceAll(output, []byte{'\n', '.', '\n'}, []byte{'\n', '\n'})
|
||||||
|
|
||||||
|
var cleaned []byte
|
||||||
|
for _, line := range bytes.SplitAfter(output, []byte{'\n'}) {
|
||||||
|
trimmedLine := bytes.TrimRight(line, "\r\n")
|
||||||
|
if simavrLoadTextLogPattern.Match(trimmedLine) || simavrLoadBytesLogPattern.Match(trimmedLine) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cleaned = append(cleaned, line...)
|
||||||
|
}
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
simavrLoadTextLogPattern = regexp.MustCompile(`^Loaded [0-9]+ \.[A-Za-z0-9_]+( at address 0x[0-9a-fA-F]+)?$`)
|
||||||
|
simavrLoadBytesLogPattern = regexp.MustCompile(`^Loaded [0-9]+ bytes of [A-Za-z]+ data at (0x)?[0-9a-fA-F]+$`)
|
||||||
|
)
|
||||||
|
|
||||||
// Test WebAssembly files for certain properties.
|
// Test WebAssembly files for certain properties.
|
||||||
func TestWebAssembly(t *testing.T) {
|
func TestWebAssembly(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
@@ -530,7 +575,6 @@ func TestWebAssembly(t *testing.T) {
|
|||||||
{name: "panic-default", target: "wasip1", imports: []string{"wasi_snapshot_preview1.fd_write", "wasi_snapshot_preview1.random_get"}},
|
{name: "panic-default", target: "wasip1", imports: []string{"wasi_snapshot_preview1.fd_write", "wasi_snapshot_preview1.random_get"}},
|
||||||
{name: "panic-trap", target: "wasm-unknown", panicStrategy: "trap", imports: []string{}},
|
{name: "panic-trap", target: "wasm-unknown", panicStrategy: "trap", imports: []string{}},
|
||||||
} {
|
} {
|
||||||
tc := tc
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
tmpdir := t.TempDir()
|
tmpdir := t.TempDir()
|
||||||
@@ -652,7 +696,6 @@ func TestWasmExport(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
tc := tc
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -822,7 +865,6 @@ func TestWasmExportJS(t *testing.T) {
|
|||||||
{name: "c-shared", buildMode: "c-shared"},
|
{name: "c-shared", buildMode: "c-shared"},
|
||||||
}
|
}
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
tc := tc
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
// Build the wasm binary.
|
// Build the wasm binary.
|
||||||
@@ -870,7 +912,6 @@ func TestWasmExit(t *testing.T) {
|
|||||||
{name: "exit-1-sleep", output: "slept\nexit code: 1\n"},
|
{name: "exit-1-sleep", output: "slept\nexit code: 1\n"},
|
||||||
}
|
}
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
tc := tc
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
options := optionsFromTarget("wasm", sema)
|
options := optionsFromTarget("wasm", sema)
|
||||||
@@ -913,6 +954,48 @@ func checkOutputData(t *testing.T, expectedOutput, actual []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGoexitCrash(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
options := optionsFromTarget("", sema)
|
||||||
|
config, err := builder.NewConfig(&options)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"main", "all goroutines are asleep - deadlock!"},
|
||||||
|
{"deadlock", "all goroutines are asleep - deadlock!"},
|
||||||
|
{"exit", "all goroutines are asleep - deadlock!"},
|
||||||
|
{"main-other", "all goroutines are asleep - deadlock!"},
|
||||||
|
{"in-panic", "all goroutines are asleep - deadlock!"},
|
||||||
|
{"panic", "panic: panic after Goexit"},
|
||||||
|
{"recovered-panic", "all goroutines are asleep - deadlock!"},
|
||||||
|
{"recover-before-panic", "all goroutines are asleep - deadlock!"},
|
||||||
|
{"recover-before-panic-loop", "all goroutines are asleep - deadlock!"},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
_, err = buildAndRun("testdata/goexit.go", config, output, []string{tc.name}, nil, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error {
|
||||||
|
cmd.Stdout = nil
|
||||||
|
cmd.Stderr = nil
|
||||||
|
data, err := cmd.CombinedOutput()
|
||||||
|
output.Write(data)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("program unexpectedly exited successfully")
|
||||||
|
}
|
||||||
|
if !strings.Contains(output.String(), tc.want) {
|
||||||
|
t.Fatalf("output does not contain %q:\n%s", tc.want, output.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTest(t *testing.T) {
|
func TestTest(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -945,7 +1028,6 @@ func TestTest(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
for _, targ := range targs {
|
for _, targ := range targs {
|
||||||
targ := targ
|
|
||||||
t.Run(targ.name, func(t *testing.T) {
|
t.Run(targ.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32s3 || tkey || (tinygo.riscv32 && virt)
|
//go:build nrf || (stm32 && !(stm32f103 || stm32l0x1 || stm32u0)) || (sam && atsamd51) || (sam && atsame5x) || esp32c3 || esp32s3 || tkey || (tinygo.riscv32 && virt)
|
||||||
|
|
||||||
// If you update the above build constraint, you'll probably also need to update
|
// If you update the above build constraint, you'll probably also need to update
|
||||||
// src/runtime/rand_hwrng.go.
|
// src/runtime/rand_hwrng.go.
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
// TINYGO: cipher suite IDs and the exported CipherSuites/InsecureCipherSuites
|
||||||
|
// descriptors, copied verbatim from the Go official implementation. TinyGo has
|
||||||
|
// no software handshake, so only these declarations (no selection machinery)
|
||||||
|
// are provided to satisfy callers such as google.golang.org/grpc/credentials
|
||||||
|
// and github.com/pion/dtls that reference the cipher-suite API surface.
|
||||||
|
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package tls
|
||||||
|
|
||||||
|
// A list of cipher suite IDs that are, or have been, implemented by this
|
||||||
|
// package.
|
||||||
|
//
|
||||||
|
// See https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
|
||||||
|
const (
|
||||||
|
// TLS 1.0 - 1.2 cipher suites.
|
||||||
|
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
|
||||||
|
TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
|
||||||
|
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
|
||||||
|
TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
|
||||||
|
TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c
|
||||||
|
TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c
|
||||||
|
TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d
|
||||||
|
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a
|
||||||
|
TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
|
||||||
|
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
|
||||||
|
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
|
||||||
|
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023
|
||||||
|
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027
|
||||||
|
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
|
||||||
|
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030
|
||||||
|
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c
|
||||||
|
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8
|
||||||
|
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9
|
||||||
|
|
||||||
|
// TLS 1.3 cipher suites.
|
||||||
|
TLS_AES_128_GCM_SHA256 uint16 = 0x1301
|
||||||
|
TLS_AES_256_GCM_SHA384 uint16 = 0x1302
|
||||||
|
TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303
|
||||||
|
|
||||||
|
// TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator
|
||||||
|
// that the client is doing version fallback. See RFC 7507.
|
||||||
|
TLS_FALLBACK_SCSV uint16 = 0x5600
|
||||||
|
|
||||||
|
// Legacy names for the corresponding cipher suites with the correct _SHA256
|
||||||
|
// suffix, retained for backward compatibility.
|
||||||
|
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
|
||||||
|
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
supportedUpToTLS12 = []uint16{VersionTLS10, VersionTLS11, VersionTLS12}
|
||||||
|
supportedOnlyTLS12 = []uint16{VersionTLS12}
|
||||||
|
supportedOnlyTLS13 = []uint16{VersionTLS13}
|
||||||
|
)
|
||||||
|
|
||||||
|
// CipherSuites returns a list of cipher suites currently implemented by this
|
||||||
|
// package, excluding those with security issues, which are returned by
|
||||||
|
// InsecureCipherSuites.
|
||||||
|
func CipherSuites() []*CipherSuite {
|
||||||
|
return []*CipherSuite{
|
||||||
|
{TLS_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256", supportedOnlyTLS13, false},
|
||||||
|
{TLS_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384", supportedOnlyTLS13, false},
|
||||||
|
{TLS_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256", supportedOnlyTLS13, false},
|
||||||
|
|
||||||
|
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
||||||
|
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
||||||
|
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
||||||
|
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
||||||
|
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
||||||
|
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
||||||
|
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
||||||
|
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
||||||
|
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
|
||||||
|
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsecureCipherSuites returns a list of cipher suites currently implemented by
|
||||||
|
// this package and which have security issues.
|
||||||
|
//
|
||||||
|
// Most applications should not use the cipher suites in this list, and should
|
||||||
|
// only use those returned by CipherSuites.
|
||||||
|
func InsecureCipherSuites() []*CipherSuite {
|
||||||
|
// This list includes legacy RSA kex, RC4, CBC_SHA256, and 3DES cipher
|
||||||
|
// suites. See cipherSuitesPreferenceOrder for details.
|
||||||
|
return []*CipherSuite{
|
||||||
|
{TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
||||||
|
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
|
||||||
|
{TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, true},
|
||||||
|
{TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, true},
|
||||||
|
{TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
||||||
|
{TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, true},
|
||||||
|
{TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, true},
|
||||||
|
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
||||||
|
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
||||||
|
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
|
||||||
|
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
||||||
|
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
||||||
|
}
|
||||||
|
}
|
||||||
+121
-2
@@ -55,6 +55,16 @@ func VersionName(version uint16) string {
|
|||||||
// only supports Elliptic Curve based groups. See RFC 8446, Section 4.2.7.
|
// only supports Elliptic Curve based groups. See RFC 8446, Section 4.2.7.
|
||||||
type CurveID uint16
|
type CurveID uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
CurveP256 CurveID = 23
|
||||||
|
CurveP384 CurveID = 24
|
||||||
|
CurveP521 CurveID = 25
|
||||||
|
X25519 CurveID = 29
|
||||||
|
X25519MLKEM768 CurveID = 4588
|
||||||
|
SecP256r1MLKEM768 CurveID = 4587
|
||||||
|
SecP384r1MLKEM1024 CurveID = 4589
|
||||||
|
)
|
||||||
|
|
||||||
// CipherSuiteName returns the standard name for the passed cipher suite ID
|
// CipherSuiteName returns the standard name for the passed cipher suite ID
|
||||||
//
|
//
|
||||||
// Not Implemented.
|
// Not Implemented.
|
||||||
@@ -68,14 +78,42 @@ type ConnectionState struct {
|
|||||||
//
|
//
|
||||||
// Minimum (empty) fields for fortio.org/log http logging and others
|
// Minimum (empty) fields for fortio.org/log http logging and others
|
||||||
// to compile and run.
|
// to compile and run.
|
||||||
PeerCertificates []*x509.Certificate
|
Version uint16
|
||||||
CipherSuite uint16
|
ServerName string
|
||||||
|
PeerCertificates []*x509.Certificate
|
||||||
|
CipherSuite uint16
|
||||||
|
NegotiatedProtocol string
|
||||||
|
NegotiatedProtocolIsMutual bool // Deprecated: this value is always true.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientAuthType declares the policy the server will follow for
|
// ClientAuthType declares the policy the server will follow for
|
||||||
// TLS Client Authentication.
|
// TLS Client Authentication.
|
||||||
type ClientAuthType int
|
type ClientAuthType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NoClientCert indicates that no client certificate should be requested
|
||||||
|
// during the handshake, and if any certificates are sent they will not
|
||||||
|
// be verified.
|
||||||
|
NoClientCert ClientAuthType = iota
|
||||||
|
// RequestClientCert indicates that a client certificate should be requested
|
||||||
|
// during the handshake, but does not require that the client send any
|
||||||
|
// certificates.
|
||||||
|
RequestClientCert
|
||||||
|
// RequireAnyClientCert indicates that a client certificate should be requested
|
||||||
|
// during the handshake, and that at least one certificate is required to be
|
||||||
|
// sent by the client, but that certificate is not required to be valid.
|
||||||
|
RequireAnyClientCert
|
||||||
|
// VerifyClientCertIfGiven indicates that a client certificate should be requested
|
||||||
|
// during the handshake, but does not require that the client sends a
|
||||||
|
// certificate. If the client does send a certificate it is required to be
|
||||||
|
// valid.
|
||||||
|
VerifyClientCertIfGiven
|
||||||
|
// RequireAndVerifyClientCert indicates that a client certificate should be requested
|
||||||
|
// during the handshake, and that at least one valid certificate is required
|
||||||
|
// to be sent by the client.
|
||||||
|
RequireAndVerifyClientCert
|
||||||
|
)
|
||||||
|
|
||||||
// ClientSessionCache is a cache of ClientSessionState objects that can be used
|
// ClientSessionCache is a cache of ClientSessionState objects that can be used
|
||||||
// by a client to resume a TLS session with a given server. ClientSessionCache
|
// by a client to resume a TLS session with a given server. ClientSessionCache
|
||||||
// implementations should expect to be called concurrently from different
|
// implementations should expect to be called concurrently from different
|
||||||
@@ -100,6 +138,45 @@ type ClientSessionCache interface {
|
|||||||
// RFC 8446, Section 4.2.3.
|
// RFC 8446, Section 4.2.3.
|
||||||
type SignatureScheme uint16
|
type SignatureScheme uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RSASSA-PKCS1-v1_5 algorithms.
|
||||||
|
PKCS1WithSHA256 SignatureScheme = 0x0401
|
||||||
|
PKCS1WithSHA384 SignatureScheme = 0x0501
|
||||||
|
PKCS1WithSHA512 SignatureScheme = 0x0601
|
||||||
|
|
||||||
|
// RSASSA-PSS algorithms with public key OID rsaEncryption.
|
||||||
|
PSSWithSHA256 SignatureScheme = 0x0804
|
||||||
|
PSSWithSHA384 SignatureScheme = 0x0805
|
||||||
|
PSSWithSHA512 SignatureScheme = 0x0806
|
||||||
|
|
||||||
|
// ECDSA algorithms. Only constrained to a specific curve in TLS 1.3.
|
||||||
|
ECDSAWithP256AndSHA256 SignatureScheme = 0x0403
|
||||||
|
ECDSAWithP384AndSHA384 SignatureScheme = 0x0503
|
||||||
|
ECDSAWithP521AndSHA512 SignatureScheme = 0x0603
|
||||||
|
|
||||||
|
// EdDSA algorithms.
|
||||||
|
Ed25519 SignatureScheme = 0x0807
|
||||||
|
|
||||||
|
// Legacy signature and hash algorithms for TLS 1.2.
|
||||||
|
PKCS1WithSHA1 SignatureScheme = 0x0201
|
||||||
|
ECDSAWithSHA1 SignatureScheme = 0x0203
|
||||||
|
)
|
||||||
|
|
||||||
|
// CipherSuite is a TLS cipher suite. Note that most functions in this package
|
||||||
|
// accept and expose cipher suite IDs instead of this type.
|
||||||
|
type CipherSuite struct {
|
||||||
|
ID uint16
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Supported versions is the list of TLS protocol versions that can
|
||||||
|
// negotiate this cipher suite.
|
||||||
|
SupportedVersions []uint16
|
||||||
|
|
||||||
|
// Insecure is true if the cipher suite has known security issues
|
||||||
|
// due to its primitives, design, or implementation.
|
||||||
|
Insecure bool
|
||||||
|
}
|
||||||
|
|
||||||
// ClientHelloInfo contains information from a ClientHello message in order to
|
// ClientHelloInfo contains information from a ClientHello message in order to
|
||||||
// guide application logic in the GetCertificate and GetConfigForClient callbacks.
|
// guide application logic in the GetCertificate and GetConfigForClient callbacks.
|
||||||
type ClientHelloInfo struct {
|
type ClientHelloInfo struct {
|
||||||
@@ -459,6 +536,48 @@ type Config struct {
|
|||||||
autoSessionTicketKeys []ticketKey
|
autoSessionTicketKeys []ticketKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clone returns a shallow clone of c or nil if c is nil. It is safe to clone a
|
||||||
|
// Config that is being used concurrently by a TLS client or server.
|
||||||
|
func (c *Config) Clone() *Config {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c.mutex.RLock()
|
||||||
|
defer c.mutex.RUnlock()
|
||||||
|
return &Config{
|
||||||
|
Rand: c.Rand,
|
||||||
|
Time: c.Time,
|
||||||
|
Certificates: c.Certificates,
|
||||||
|
NameToCertificate: c.NameToCertificate,
|
||||||
|
GetCertificate: c.GetCertificate,
|
||||||
|
GetClientCertificate: c.GetClientCertificate,
|
||||||
|
GetConfigForClient: c.GetConfigForClient,
|
||||||
|
VerifyPeerCertificate: c.VerifyPeerCertificate,
|
||||||
|
VerifyConnection: c.VerifyConnection,
|
||||||
|
RootCAs: c.RootCAs,
|
||||||
|
NextProtos: c.NextProtos,
|
||||||
|
ServerName: c.ServerName,
|
||||||
|
ClientAuth: c.ClientAuth,
|
||||||
|
ClientCAs: c.ClientCAs,
|
||||||
|
InsecureSkipVerify: c.InsecureSkipVerify,
|
||||||
|
CipherSuites: c.CipherSuites,
|
||||||
|
PreferServerCipherSuites: c.PreferServerCipherSuites,
|
||||||
|
SessionTicketsDisabled: c.SessionTicketsDisabled,
|
||||||
|
SessionTicketKey: c.SessionTicketKey,
|
||||||
|
ClientSessionCache: c.ClientSessionCache,
|
||||||
|
UnwrapSession: c.UnwrapSession,
|
||||||
|
WrapSession: c.WrapSession,
|
||||||
|
MinVersion: c.MinVersion,
|
||||||
|
MaxVersion: c.MaxVersion,
|
||||||
|
CurvePreferences: c.CurvePreferences,
|
||||||
|
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
|
||||||
|
Renegotiation: c.Renegotiation,
|
||||||
|
KeyLogWriter: c.KeyLogWriter,
|
||||||
|
sessionTicketKeys: c.sessionTicketKeys,
|
||||||
|
autoSessionTicketKeys: c.autoSessionTicketKeys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ticketKey is the internal representation of a session ticket key.
|
// ticketKey is the internal representation of a session ticket key.
|
||||||
type ticketKey struct {
|
type ticketKey struct {
|
||||||
aesKey [16]byte
|
aesKey [16]byte
|
||||||
|
|||||||
+42
-3
@@ -20,13 +20,52 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Conn represents a secured connection. TINYGO: the actual TLS handshake and
|
||||||
|
// record layer are offloaded to the network device (see net.TLSConn), so Conn
|
||||||
|
// is a thin wrapper over an underlying net.Conn that exists to satisfy callers
|
||||||
|
// (e.g. google.golang.org/grpc/credentials) which expect the *tls.Conn API
|
||||||
|
// shape — ConnectionState/Handshake — that this package does not implement in
|
||||||
|
// software.
|
||||||
|
type Conn struct {
|
||||||
|
net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectionState returns basic TLS details about the connection. TINYGO:
|
||||||
|
// empty; TLS is offloaded to the network device.
|
||||||
|
func (c *Conn) ConnectionState() ConnectionState {
|
||||||
|
return ConnectionState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handshake runs the client or server handshake protocol if it has not yet been
|
||||||
|
// run. TINYGO: no-op; the handshake is performed by the network device.
|
||||||
|
func (c *Conn) Handshake() error {
|
||||||
|
return c.HandshakeContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandshakeContext is the context-aware variant of Handshake. TINYGO: no-op.
|
||||||
|
func (c *Conn) HandshakeContext(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetConn returns the underlying connection that is wrapped by c.
|
||||||
|
func (c *Conn) NetConn() net.Conn {
|
||||||
|
return c.Conn
|
||||||
|
}
|
||||||
|
|
||||||
// Client returns a new TLS client side connection
|
// Client returns a new TLS client side connection
|
||||||
// using conn as the underlying transport.
|
// using conn as the underlying transport.
|
||||||
// The config cannot be nil: users must set either ServerName or
|
// The config cannot be nil: users must set either ServerName or
|
||||||
// InsecureSkipVerify in the config.
|
// InsecureSkipVerify in the config.
|
||||||
func Client(conn net.Conn, config *Config) *net.TLSConn {
|
func Client(conn net.Conn, config *Config) *Conn {
|
||||||
panic("tls.Client() not implemented")
|
return &Conn{Conn: conn}
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
// Server returns a new TLS server side connection
|
||||||
|
// using conn as the underlying transport.
|
||||||
|
// The configuration config must be non-nil and must include
|
||||||
|
// at least one certificate or else set GetCertificate.
|
||||||
|
func Server(conn net.Conn, config *Config) *Conn {
|
||||||
|
return &Conn{Conn: conn}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A listener implements a network listener (net.Listener) for TLS connections.
|
// A listener implements a network listener (net.Listener) for TLS connections.
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
//go:build amd64
|
||||||
|
|
||||||
|
package amd64
|
||||||
|
|
||||||
|
const (
|
||||||
|
CPUIDTimeStampCounter = 0x15
|
||||||
|
CPUIDProcessorFrequency = 0x16
|
||||||
|
)
|
||||||
|
|
||||||
|
type CPUExtendedFamily uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
CPUFamilyIntelCore CPUExtendedFamily = 6
|
||||||
|
)
|
||||||
|
|
||||||
|
//export asmPause
|
||||||
|
func AsmPause()
|
||||||
|
|
||||||
|
//export asmReadRdtsc
|
||||||
|
func AsmReadRdtsc() uint64
|
||||||
|
|
||||||
|
//export asmCpuid
|
||||||
|
func AsmCpuid(index uint32, registerEax *uint32, registerEbx *uint32, registerEcx *uint32) int
|
||||||
|
|
||||||
|
var maxCpuidIndex uint32
|
||||||
|
var stdVendorName0 uint32
|
||||||
|
var stdCpuid1Eax uint32
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
AsmCpuid(0, &maxCpuidIndex, &stdVendorName0, nil)
|
||||||
|
AsmCpuid(1, &stdCpuid1Eax, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExtendedCPUFamily() CPUExtendedFamily {
|
||||||
|
family := CPUExtendedFamily((stdCpuid1Eax >> 8) & 0x0f)
|
||||||
|
family += CPUExtendedFamily((stdCpuid1Eax >> 20) & 0xff)
|
||||||
|
return family
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIntel() bool {
|
||||||
|
return stdVendorName0 == 0x756e6547
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIntelFamilyCore() bool {
|
||||||
|
return isIntel() && getExtendedCPUFamily() == CPUFamilyIntelCore
|
||||||
|
}
|
||||||
|
|
||||||
|
func InternalGetPerformanceCounterFrequency() uint64 {
|
||||||
|
if maxCpuidIndex >= CPUIDTimeStampCounter {
|
||||||
|
return cpuidCoreClockCalculateTSCFrequency()
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func cpuidCoreClockCalculateTSCFrequency() uint64 {
|
||||||
|
var eax uint32
|
||||||
|
var ebx uint32
|
||||||
|
var ecx uint32
|
||||||
|
|
||||||
|
AsmCpuid(CPUIDTimeStampCounter, &eax, &ebx, &ecx)
|
||||||
|
if eax == 0 || ebx == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
coreCrystalFrequency := uint64(ecx)
|
||||||
|
if coreCrystalFrequency == 0 {
|
||||||
|
if !isIntelFamilyCore() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
coreCrystalFrequency = 24000000
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((coreCrystalFrequency * uint64(ebx)) + (uint64(eax) / 2)) / uint64(eax)
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
.section .text
|
||||||
|
|
||||||
|
.global asmPause
|
||||||
|
asmPause:
|
||||||
|
pause
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global asmReadRdtsc
|
||||||
|
asmReadRdtsc:
|
||||||
|
rdtsc
|
||||||
|
shlq $0x20, %rdx
|
||||||
|
orq %rdx, %rax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global asmCpuid
|
||||||
|
asmCpuid:
|
||||||
|
pushq %rbx
|
||||||
|
|
||||||
|
mov %ecx, %eax
|
||||||
|
pushq %rax
|
||||||
|
|
||||||
|
pushq %rdx
|
||||||
|
cpuid
|
||||||
|
|
||||||
|
test %r9, %r9
|
||||||
|
jz .SkipEcx
|
||||||
|
mov %ecx, (%r9)
|
||||||
|
.SkipEcx:
|
||||||
|
popq %rcx
|
||||||
|
jrcxz .SkipEax
|
||||||
|
mov %eax, (%rcx)
|
||||||
|
.SkipEax:
|
||||||
|
mov %r8, %rcx
|
||||||
|
jrcxz .SkipEbx
|
||||||
|
mov %ebx, (%rcx)
|
||||||
|
.SkipEbx:
|
||||||
|
popq %rax
|
||||||
|
popq %rbx
|
||||||
|
ret
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
//go:build i386 || amd64
|
||||||
|
|
||||||
|
package uefi
|
||||||
|
|
||||||
|
import "device/amd64"
|
||||||
|
|
||||||
|
func Ticks() uint64 {
|
||||||
|
return amd64.AsmReadRdtsc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func CpuPause() {
|
||||||
|
amd64.AsmPause()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTSCFrequency() uint64 {
|
||||||
|
return amd64.InternalGetPerformanceCounterFrequency()
|
||||||
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
.section .text
|
||||||
|
|
||||||
|
.global uefiCall0
|
||||||
|
uefiCall0:
|
||||||
|
pushq %rbp
|
||||||
|
movq %rsp, %rbp
|
||||||
|
subq $0x20, %rsp
|
||||||
|
callq *%rcx
|
||||||
|
movq %rbp, %rsp
|
||||||
|
popq %rbp
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global uefiCall1
|
||||||
|
uefiCall1:
|
||||||
|
pushq %rbp
|
||||||
|
movq %rsp, %rbp
|
||||||
|
subq $0x20, %rsp
|
||||||
|
movq %rcx, %rax
|
||||||
|
movq %rdx, %rcx
|
||||||
|
callq *%rax
|
||||||
|
movq %rbp, %rsp
|
||||||
|
popq %rbp
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global uefiCall2
|
||||||
|
uefiCall2:
|
||||||
|
pushq %rbp
|
||||||
|
movq %rsp, %rbp
|
||||||
|
subq $0x20, %rsp
|
||||||
|
movq %rcx, %rax
|
||||||
|
movq %rdx, %rcx
|
||||||
|
movq %r8, %rdx
|
||||||
|
callq *%rax
|
||||||
|
movq %rbp, %rsp
|
||||||
|
popq %rbp
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global uefiCall3
|
||||||
|
uefiCall3:
|
||||||
|
pushq %rbp
|
||||||
|
movq %rsp, %rbp
|
||||||
|
subq $0x20, %rsp
|
||||||
|
movq %rcx, %rax
|
||||||
|
movq %rdx, %rcx
|
||||||
|
movq %r8, %rdx
|
||||||
|
movq %r9, %r8
|
||||||
|
callq *%rax
|
||||||
|
movq %rbp, %rsp
|
||||||
|
popq %rbp
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global uefiCall4
|
||||||
|
uefiCall4:
|
||||||
|
pushq %rbp
|
||||||
|
movq %rsp, %rbp
|
||||||
|
subq $0x20, %rsp
|
||||||
|
movq %rcx, %rax
|
||||||
|
movq %rdx, %rcx
|
||||||
|
movq %r8, %rdx
|
||||||
|
movq %r9, %r8
|
||||||
|
movq 0x30(%rbp), %r9
|
||||||
|
callq *%rax
|
||||||
|
movq %rbp, %rsp
|
||||||
|
popq %rbp
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global uefiCall5
|
||||||
|
uefiCall5:
|
||||||
|
pushq %rbp
|
||||||
|
movq %rsp, %rbp
|
||||||
|
subq $0x30, %rsp
|
||||||
|
movq %rcx, %rax
|
||||||
|
movq %rdx, %rcx
|
||||||
|
movq %r8, %rdx
|
||||||
|
movq %r9, %r8
|
||||||
|
movq 0x30(%rbp), %r9
|
||||||
|
movq 0x38(%rbp), %r10
|
||||||
|
movq %r10, 0x20(%rsp)
|
||||||
|
callq *%rax
|
||||||
|
movq %rbp, %rsp
|
||||||
|
popq %rbp
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global uefiCall6
|
||||||
|
uefiCall6:
|
||||||
|
pushq %rbp
|
||||||
|
movq %rsp, %rbp
|
||||||
|
subq $0x30, %rsp
|
||||||
|
movq %rcx, %rax
|
||||||
|
movq %rdx, %rcx
|
||||||
|
movq %r8, %rdx
|
||||||
|
movq %r9, %r8
|
||||||
|
movq 0x30(%rbp), %r9
|
||||||
|
movq 0x38(%rbp), %r10
|
||||||
|
movq %r10, 0x20(%rsp)
|
||||||
|
movq 0x40(%rbp), %r10
|
||||||
|
movq %r10, 0x28(%rsp)
|
||||||
|
callq *%rax
|
||||||
|
movq %rbp, %rsp
|
||||||
|
popq %rbp
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global uefiCall7
|
||||||
|
uefiCall7:
|
||||||
|
pushq %rbp
|
||||||
|
movq %rsp, %rbp
|
||||||
|
subq $0x40, %rsp
|
||||||
|
movq %rcx, %rax
|
||||||
|
movq %rdx, %rcx
|
||||||
|
movq %r8, %rdx
|
||||||
|
movq %r9, %r8
|
||||||
|
movq 0x30(%rbp), %r9
|
||||||
|
movq 0x38(%rbp), %r10
|
||||||
|
movq %r10, 0x20(%rsp)
|
||||||
|
movq 0x40(%rbp), %r10
|
||||||
|
movq %r10, 0x28(%rsp)
|
||||||
|
movq 0x48(%rbp), %r10
|
||||||
|
movq %r10, 0x30(%rsp)
|
||||||
|
callq *%rax
|
||||||
|
movq %rbp, %rsp
|
||||||
|
popq %rbp
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global uefiCall8
|
||||||
|
uefiCall8:
|
||||||
|
pushq %rbp
|
||||||
|
movq %rsp, %rbp
|
||||||
|
subq $0x40, %rsp
|
||||||
|
movq %rcx, %rax
|
||||||
|
movq %rdx, %rcx
|
||||||
|
movq %r8, %rdx
|
||||||
|
movq %r9, %r8
|
||||||
|
movq 0x30(%rbp), %r9
|
||||||
|
movq 0x38(%rbp), %r10
|
||||||
|
movq %r10, 0x20(%rsp)
|
||||||
|
movq 0x40(%rbp), %r10
|
||||||
|
movq %r10, 0x28(%rsp)
|
||||||
|
movq 0x48(%rbp), %r10
|
||||||
|
movq %r10, 0x30(%rsp)
|
||||||
|
movq 0x50(%rbp), %r10
|
||||||
|
movq %r10, 0x38(%rsp)
|
||||||
|
callq *%rax
|
||||||
|
movq %rbp, %rsp
|
||||||
|
popq %rbp
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global uefiCall9
|
||||||
|
uefiCall9:
|
||||||
|
pushq %rbp
|
||||||
|
movq %rsp, %rbp
|
||||||
|
subq $0x50, %rsp
|
||||||
|
movq %rcx, %rax
|
||||||
|
movq %rdx, %rcx
|
||||||
|
movq %r8, %rdx
|
||||||
|
movq %r9, %r8
|
||||||
|
movq 0x30(%rbp), %r9
|
||||||
|
movq 0x38(%rbp), %r10
|
||||||
|
movq %r10, 0x20(%rsp)
|
||||||
|
movq 0x40(%rbp), %r10
|
||||||
|
movq %r10, 0x28(%rsp)
|
||||||
|
movq 0x48(%rbp), %r10
|
||||||
|
movq %r10, 0x30(%rsp)
|
||||||
|
movq 0x50(%rbp), %r10
|
||||||
|
movq %r10, 0x38(%rsp)
|
||||||
|
movq 0x58(%rbp), %r10
|
||||||
|
movq %r10, 0x40(%rsp)
|
||||||
|
callq *%rax
|
||||||
|
movq %rbp, %rsp
|
||||||
|
popq %rbp
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global uefiCall10
|
||||||
|
uefiCall10:
|
||||||
|
pushq %rbp
|
||||||
|
movq %rsp, %rbp
|
||||||
|
subq $0x50, %rsp
|
||||||
|
movq %rcx, %rax
|
||||||
|
movq %rdx, %rcx
|
||||||
|
movq %r8, %rdx
|
||||||
|
movq %r9, %r8
|
||||||
|
movq 0x30(%rbp), %r9
|
||||||
|
movq 0x38(%rbp), %r10
|
||||||
|
movq %r10, 0x20(%rsp)
|
||||||
|
movq 0x40(%rbp), %r10
|
||||||
|
movq %r10, 0x28(%rsp)
|
||||||
|
movq 0x48(%rbp), %r10
|
||||||
|
movq %r10, 0x30(%rsp)
|
||||||
|
movq 0x50(%rbp), %r10
|
||||||
|
movq %r10, 0x38(%rsp)
|
||||||
|
movq 0x58(%rbp), %r10
|
||||||
|
movq %r10, 0x40(%rsp)
|
||||||
|
movq 0x60(%rbp), %r10
|
||||||
|
movq %r10, 0x48(%rsp)
|
||||||
|
callq *%rax
|
||||||
|
movq %rbp, %rsp
|
||||||
|
popq %rbp
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global ___chkstk_ms
|
||||||
|
___chkstk_ms:
|
||||||
|
ret
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package uefi
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//export uefiCall0
|
||||||
|
func UefiCall0(fn uintptr) EFI_STATUS
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:export uefiCall1
|
||||||
|
func UefiCall1(fn uintptr, a uintptr) EFI_STATUS
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:export uefiCall2
|
||||||
|
func UefiCall2(fn uintptr, a uintptr, b uintptr) EFI_STATUS
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:export uefiCall3
|
||||||
|
func UefiCall3(fn uintptr, a uintptr, b uintptr, c uintptr) EFI_STATUS
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:export uefiCall4
|
||||||
|
func UefiCall4(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr) EFI_STATUS
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:export uefiCall5
|
||||||
|
func UefiCall5(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr) EFI_STATUS
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:export uefiCall6
|
||||||
|
func UefiCall6(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr) EFI_STATUS
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:export uefiCall7
|
||||||
|
func UefiCall7(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr, g uintptr) EFI_STATUS
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:export uefiCall8
|
||||||
|
func UefiCall8(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr, g uintptr, h uintptr) EFI_STATUS
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:export uefiCall9
|
||||||
|
func UefiCall9(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr, g uintptr, h uintptr, i uintptr) EFI_STATUS
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:export uefiCall10
|
||||||
|
func UefiCall10(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr, g uintptr, h uintptr, i uintptr, j uintptr) EFI_STATUS
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package uefi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StringToCHAR16 converts a Go string to a UTF-16 code unit slice.
|
||||||
|
func StringToCHAR16(s string) []CHAR16 {
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
encoded := utf16.Encode([]rune(s))
|
||||||
|
out := make([]CHAR16, len(encoded))
|
||||||
|
for i, r := range encoded {
|
||||||
|
out[i] = CHAR16(r)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToCHAR16Z converts a Go string to a NUL-terminated UTF-16 code unit slice.
|
||||||
|
func StringToCHAR16Z(s string) []CHAR16 {
|
||||||
|
out := StringToCHAR16(s)
|
||||||
|
return append(out, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesToCHAR16 converts UTF-8 text bytes to a UTF-16 code unit slice.
|
||||||
|
func BytesToCHAR16(b []byte) []CHAR16 {
|
||||||
|
return StringToCHAR16(string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesToCHAR16Z converts UTF-8 text bytes to a NUL-terminated UTF-16 code unit slice.
|
||||||
|
func BytesToCHAR16Z(b []byte) []CHAR16 {
|
||||||
|
return StringToCHAR16Z(string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHAR16ToString converts a UTF-16 code unit slice to a Go string.
|
||||||
|
func CHAR16ToString(input []CHAR16) string {
|
||||||
|
if len(input) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
units := make([]uint16, len(input))
|
||||||
|
for i, c := range input {
|
||||||
|
units[i] = uint16(c)
|
||||||
|
}
|
||||||
|
return string(utf16.Decode(units))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHAR16ToBytes converts a UTF-16 code unit slice to UTF-8 text bytes.
|
||||||
|
func CHAR16ToBytes(input []CHAR16) []byte {
|
||||||
|
return []byte(CHAR16ToString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHAR16PtrToString converts a NUL-terminated UTF-16 string pointer to a Go string.
|
||||||
|
func CHAR16PtrToString(input *CHAR16) string {
|
||||||
|
if input == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr := uintptr(unsafe.Pointer(input))
|
||||||
|
length := 0
|
||||||
|
for *(*CHAR16)(unsafe.Pointer(ptr)) != 0 {
|
||||||
|
length++
|
||||||
|
ptr += 2
|
||||||
|
}
|
||||||
|
return CHAR16PtrLenToString(input, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHAR16PtrLenToString converts a UTF-16 string pointer with a known code unit count to a Go string.
|
||||||
|
func CHAR16PtrLenToString(input *CHAR16, length int) string {
|
||||||
|
if input == nil || length <= 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return CHAR16ToString(unsafe.Slice(input, length))
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
//go:build uefi
|
||||||
|
|
||||||
|
package uefi
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
var calibrateMutex sync.Mutex
|
||||||
|
var calculatedFrequency uint64
|
||||||
|
|
||||||
|
func TicksFrequency() uint64 {
|
||||||
|
frequency := getTSCFrequency()
|
||||||
|
if frequency > 0 {
|
||||||
|
return frequency
|
||||||
|
}
|
||||||
|
|
||||||
|
calibrateMutex.Lock()
|
||||||
|
defer calibrateMutex.Unlock()
|
||||||
|
if calculatedFrequency > 0 {
|
||||||
|
return calculatedFrequency
|
||||||
|
}
|
||||||
|
|
||||||
|
var event EFI_EVENT
|
||||||
|
var index UINTN
|
||||||
|
if BS().CreateEvent(EVT_TIMER, TPL_CALLBACK, nil, nil, &event) != EFI_SUCCESS {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
defer BS().CloseEvent(event)
|
||||||
|
|
||||||
|
start := Ticks()
|
||||||
|
if BS().SetTimer(event, TimerPeriodic, 250*10000) != EFI_SUCCESS {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if BS().WaitForEvent(1, &event, &index) != EFI_SUCCESS {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
calculatedFrequency = (Ticks() - start) * 4
|
||||||
|
return calculatedFrequency
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package uefi
|
||||||
|
|
||||||
|
type UINTN uintptr
|
||||||
|
type EFI_STATUS UINTN
|
||||||
|
type EFI_TPL UINTN
|
||||||
|
type EFI_HANDLE uintptr
|
||||||
|
type EFI_EVENT uintptr
|
||||||
|
type EFI_PHYSICAL_ADDRESS uint64
|
||||||
|
|
||||||
|
type CHAR16 uint16
|
||||||
|
type BOOLEAN bool
|
||||||
|
type VOID byte
|
||||||
|
|
||||||
|
type EFI_GUID struct {
|
||||||
|
Data1 uint32
|
||||||
|
Data2 uint16
|
||||||
|
Data3 uint16
|
||||||
|
Data4 [8]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type EFI_TABLE_HEADER struct {
|
||||||
|
Signature uint64
|
||||||
|
Revision uint32
|
||||||
|
HeaderSize uint32
|
||||||
|
CRC32 uint32
|
||||||
|
Reserved uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type EFI_ALLOCATE_TYPE int
|
||||||
|
|
||||||
|
const (
|
||||||
|
AllocateAnyPages EFI_ALLOCATE_TYPE = iota
|
||||||
|
AllocateMaxAddress
|
||||||
|
AllocateAddress
|
||||||
|
)
|
||||||
|
|
||||||
|
type EFI_MEMORY_TYPE int
|
||||||
|
|
||||||
|
const (
|
||||||
|
EfiReservedMemoryType EFI_MEMORY_TYPE = iota
|
||||||
|
EfiLoaderCode
|
||||||
|
EfiLoaderData
|
||||||
|
EfiBootServicesCode
|
||||||
|
EfiBootServicesData
|
||||||
|
EfiRuntimeServicesCode
|
||||||
|
EfiRuntimeServicesData
|
||||||
|
EfiConventionalMemory
|
||||||
|
)
|
||||||
|
|
||||||
|
type EVENT_TYPE uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
EVT_TIMER EVENT_TYPE = 0x80000000
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TPL_CALLBACK EFI_TPL = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
type EFI_TIMER_DELAY int
|
||||||
|
|
||||||
|
const (
|
||||||
|
TimerCancel EFI_TIMER_DELAY = iota
|
||||||
|
TimerPeriodic
|
||||||
|
TimerRelative
|
||||||
|
)
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package uefi
|
||||||
|
|
||||||
|
const (
|
||||||
|
uintnSize = 32 << (^uintptr(0) >> 63)
|
||||||
|
errorMask = 1 << uintptr(uintnSize-1)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EFI_SUCCESS EFI_STATUS = 0
|
||||||
|
EFI_LOAD_ERROR EFI_STATUS = errorMask | 1
|
||||||
|
EFI_INVALID_PARAMETER EFI_STATUS = errorMask | 2
|
||||||
|
EFI_UNSUPPORTED EFI_STATUS = errorMask | 3
|
||||||
|
EFI_BAD_BUFFER_SIZE EFI_STATUS = errorMask | 4
|
||||||
|
EFI_BUFFER_TOO_SMALL EFI_STATUS = errorMask | 5
|
||||||
|
EFI_NOT_READY EFI_STATUS = errorMask | 6
|
||||||
|
EFI_DEVICE_ERROR EFI_STATUS = errorMask | 7
|
||||||
|
EFI_WRITE_PROTECTED EFI_STATUS = errorMask | 8
|
||||||
|
EFI_OUT_OF_RESOURCES EFI_STATUS = errorMask | 9
|
||||||
|
EFI_VOLUME_CORRUPTED EFI_STATUS = errorMask | 10
|
||||||
|
EFI_VOLUME_FULL EFI_STATUS = errorMask | 11
|
||||||
|
EFI_NO_MEDIA EFI_STATUS = errorMask | 12
|
||||||
|
EFI_MEDIA_CHANGED EFI_STATUS = errorMask | 13
|
||||||
|
EFI_NOT_FOUND EFI_STATUS = errorMask | 14
|
||||||
|
EFI_ACCESS_DENIED EFI_STATUS = errorMask | 15
|
||||||
|
EFI_NO_RESPONSE EFI_STATUS = errorMask | 16
|
||||||
|
EFI_NO_MAPPING EFI_STATUS = errorMask | 17
|
||||||
|
EFI_TIMEOUT EFI_STATUS = errorMask | 18
|
||||||
|
EFI_NOT_STARTED EFI_STATUS = errorMask | 19
|
||||||
|
EFI_ALREADY_STARTED EFI_STATUS = errorMask | 20
|
||||||
|
EFI_ABORTED EFI_STATUS = errorMask | 21
|
||||||
|
EFI_ICMP_ERROR EFI_STATUS = errorMask | 22
|
||||||
|
EFI_TFTP_ERROR EFI_STATUS = errorMask | 23
|
||||||
|
EFI_PROTOCOL_ERROR EFI_STATUS = errorMask | 24
|
||||||
|
EFI_INCOMPATIBLE_VERSION EFI_STATUS = errorMask | 25
|
||||||
|
EFI_SECURITY_VIOLATION EFI_STATUS = errorMask | 26
|
||||||
|
EFI_CRC_ERROR EFI_STATUS = errorMask | 27
|
||||||
|
EFI_END_OF_MEDIA EFI_STATUS = errorMask | 28
|
||||||
|
EFI_END_OF_FILE EFI_STATUS = errorMask | 31
|
||||||
|
EFI_INVALID_LANGUAGE EFI_STATUS = errorMask | 32
|
||||||
|
EFI_COMPROMISED_DATA EFI_STATUS = errorMask | 33
|
||||||
|
EFI_IP_ADDRESS_CONFLICT EFI_STATUS = errorMask | 34
|
||||||
|
EFI_HTTP_ERROR EFI_STATUS = errorMask | 35
|
||||||
|
)
|
||||||
|
|
||||||
|
var errMap = map[EFI_STATUS]*Error{}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrLoadError = newError(EFI_LOAD_ERROR, "image failed to load")
|
||||||
|
ErrInvalidParameter = newError(EFI_INVALID_PARAMETER, "a parameter was incorrect")
|
||||||
|
ErrUnsupported = newError(EFI_UNSUPPORTED, "operation not supported")
|
||||||
|
ErrBadBufferSize = newError(EFI_BAD_BUFFER_SIZE, "buffer size incorrect for request")
|
||||||
|
ErrBufferTooSmall = newError(EFI_BUFFER_TOO_SMALL, "buffer too small; size returned in parameter")
|
||||||
|
ErrNotReady = newError(EFI_NOT_READY, "no data pending")
|
||||||
|
ErrDeviceError = newError(EFI_DEVICE_ERROR, "physical device reported an error")
|
||||||
|
ErrWriteProtected = newError(EFI_WRITE_PROTECTED, "device is write-protected")
|
||||||
|
ErrOutOfResources = newError(EFI_OUT_OF_RESOURCES, "out of resources")
|
||||||
|
ErrVolumeCorrupted = newError(EFI_VOLUME_CORRUPTED, "filesystem inconsistency detected")
|
||||||
|
ErrVolumeFull = newError(EFI_VOLUME_FULL, "no more space on filesystem")
|
||||||
|
ErrNoMedia = newError(EFI_NO_MEDIA, "device contains no medium")
|
||||||
|
ErrMediaChanged = newError(EFI_MEDIA_CHANGED, "medium changed since last access")
|
||||||
|
ErrNotFound = newError(EFI_NOT_FOUND, "item not found")
|
||||||
|
ErrAccessDenied = newError(EFI_ACCESS_DENIED, "access denied")
|
||||||
|
ErrNoResponse = newError(EFI_NO_RESPONSE, "server not found or no response")
|
||||||
|
ErrNoMapping = newError(EFI_NO_MAPPING, "no device mapping exists")
|
||||||
|
ErrTimeout = newError(EFI_TIMEOUT, "timeout expired")
|
||||||
|
ErrNotStarted = newError(EFI_NOT_STARTED, "protocol not started")
|
||||||
|
ErrAlreadyStarted = newError(EFI_ALREADY_STARTED, "protocol already started")
|
||||||
|
ErrAborted = newError(EFI_ABORTED, "operation aborted")
|
||||||
|
ErrICMPError = newError(EFI_ICMP_ERROR, "ICMP error during network operation")
|
||||||
|
ErrTFTPError = newError(EFI_TFTP_ERROR, "TFTP error during network operation")
|
||||||
|
ErrProtocolError = newError(EFI_PROTOCOL_ERROR, "protocol error during network operation")
|
||||||
|
ErrIncompatibleVersion = newError(EFI_INCOMPATIBLE_VERSION, "requested version incompatible")
|
||||||
|
ErrSecurityViolation = newError(EFI_SECURITY_VIOLATION, "security violation")
|
||||||
|
ErrCRCError = newError(EFI_CRC_ERROR, "CRC error detected")
|
||||||
|
ErrEndOfMedia = newError(EFI_END_OF_MEDIA, "beginning or end of media reached")
|
||||||
|
ErrEndOfFile = newError(EFI_END_OF_FILE, "end of file reached")
|
||||||
|
ErrInvalidLanguage = newError(EFI_INVALID_LANGUAGE, "invalid language specified")
|
||||||
|
ErrCompromisedData = newError(EFI_COMPROMISED_DATA, "data security status unknown or compromised")
|
||||||
|
ErrIPAddressConflict = newError(EFI_IP_ADDRESS_CONFLICT, "IP address conflict detected")
|
||||||
|
ErrHTTPError = newError(EFI_HTTP_ERROR, "HTTP error during network operation")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
code EFI_STATUS
|
||||||
|
msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newError(code EFI_STATUS, msg string) *Error {
|
||||||
|
err := &Error{code: code, msg: msg}
|
||||||
|
errMap[code] = err
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
return e.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Status() EFI_STATUS {
|
||||||
|
return e.code
|
||||||
|
}
|
||||||
|
|
||||||
|
func StatusError(status EFI_STATUS) *Error {
|
||||||
|
if status == EFI_SUCCESS {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err, ok := errMap[status]
|
||||||
|
if !ok {
|
||||||
|
return newError(status, "unknown EFI error")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package uefi
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
func booleanArg(v BOOLEAN) uintptr {
|
||||||
|
if v {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type EFI_SIMPLE_TEXT_OUTPUT_MODE struct {
|
||||||
|
MaxMode int32
|
||||||
|
Mode int32
|
||||||
|
Attribute int32
|
||||||
|
CursorColumn int32
|
||||||
|
CursorRow int32
|
||||||
|
CursorVisible BOOLEAN
|
||||||
|
}
|
||||||
|
|
||||||
|
type EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL struct {
|
||||||
|
reset uintptr
|
||||||
|
outputString uintptr
|
||||||
|
testString uintptr
|
||||||
|
queryMode uintptr
|
||||||
|
setMode uintptr
|
||||||
|
setAttribute uintptr
|
||||||
|
clearScreen uintptr
|
||||||
|
setCursorPosition uintptr
|
||||||
|
enableCursor uintptr
|
||||||
|
Mode *EFI_SIMPLE_TEXT_OUTPUT_MODE
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) Reset(extendedVerification BOOLEAN) EFI_STATUS {
|
||||||
|
return UefiCall2(p.reset, uintptr(unsafe.Pointer(p)), booleanArg(extendedVerification))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) OutputString(s *CHAR16) EFI_STATUS {
|
||||||
|
return UefiCall2(p.outputString, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(s)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) TestString(s *CHAR16) EFI_STATUS {
|
||||||
|
return UefiCall2(p.testString, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(s)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) QueryMode(modeNumber UINTN, columns *UINTN, rows *UINTN) EFI_STATUS {
|
||||||
|
return UefiCall4(
|
||||||
|
p.queryMode,
|
||||||
|
uintptr(unsafe.Pointer(p)),
|
||||||
|
uintptr(modeNumber),
|
||||||
|
uintptr(unsafe.Pointer(columns)),
|
||||||
|
uintptr(unsafe.Pointer(rows)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) SetMode(modeNumber UINTN) EFI_STATUS {
|
||||||
|
return UefiCall2(p.setMode, uintptr(unsafe.Pointer(p)), uintptr(modeNumber))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) SetAttribute(attribute UINTN) EFI_STATUS {
|
||||||
|
return UefiCall2(p.setAttribute, uintptr(unsafe.Pointer(p)), uintptr(attribute))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) ClearScreen() EFI_STATUS {
|
||||||
|
return UefiCall1(p.clearScreen, uintptr(unsafe.Pointer(p)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) SetCursorPosition(column UINTN, row UINTN) EFI_STATUS {
|
||||||
|
return UefiCall3(p.setCursorPosition, uintptr(unsafe.Pointer(p)), uintptr(column), uintptr(row))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) EnableCursor(visible BOOLEAN) EFI_STATUS {
|
||||||
|
return UefiCall2(p.enableCursor, uintptr(unsafe.Pointer(p)), booleanArg(visible))
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package uefi
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
type EFI_RUNTIME_SERVICES struct {
|
||||||
|
Hdr EFI_TABLE_HEADER
|
||||||
|
getTime uintptr
|
||||||
|
setTime uintptr
|
||||||
|
getWakeupTime uintptr
|
||||||
|
setWakeupTime uintptr
|
||||||
|
setVirtualAddressMap uintptr
|
||||||
|
convertPointer uintptr
|
||||||
|
getVariable uintptr
|
||||||
|
getNextVariableName uintptr
|
||||||
|
setVariable uintptr
|
||||||
|
getNextHighMonoCount uintptr
|
||||||
|
resetSystem uintptr
|
||||||
|
updateCapsule uintptr
|
||||||
|
queryCapsuleCaps uintptr
|
||||||
|
queryVariableInfo uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type EFI_BOOT_SERVICES struct {
|
||||||
|
Hdr EFI_TABLE_HEADER
|
||||||
|
raiseTPL uintptr
|
||||||
|
restoreTPL uintptr
|
||||||
|
allocatePages uintptr
|
||||||
|
freePages uintptr
|
||||||
|
getMemoryMap uintptr
|
||||||
|
allocatePool uintptr
|
||||||
|
freePool uintptr
|
||||||
|
createEvent uintptr
|
||||||
|
setTimer uintptr
|
||||||
|
waitForEvent uintptr
|
||||||
|
signalEvent uintptr
|
||||||
|
closeEvent uintptr
|
||||||
|
checkEvent uintptr
|
||||||
|
installProtocolInterface uintptr
|
||||||
|
reinstallProtocolIFace uintptr
|
||||||
|
uninstallProtocolIFace uintptr
|
||||||
|
handleProtocol uintptr
|
||||||
|
reserved *VOID
|
||||||
|
registerProtocolNotify uintptr
|
||||||
|
locateHandle uintptr
|
||||||
|
locateDevicePath uintptr
|
||||||
|
installConfigurationTable uintptr
|
||||||
|
loadImage uintptr
|
||||||
|
startImage uintptr
|
||||||
|
exit uintptr
|
||||||
|
unloadImage uintptr
|
||||||
|
exitBootServices uintptr
|
||||||
|
getNextMonotonicCount uintptr
|
||||||
|
stall uintptr
|
||||||
|
setWatchdogTimer uintptr
|
||||||
|
connectController uintptr
|
||||||
|
disconnectController uintptr
|
||||||
|
openProtocol uintptr
|
||||||
|
closeProtocol uintptr
|
||||||
|
openProtocolInformation uintptr
|
||||||
|
protocolsPerHandle uintptr
|
||||||
|
locateHandleBuffer uintptr
|
||||||
|
locateProtocol uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_BOOT_SERVICES) AllocatePages(typ EFI_ALLOCATE_TYPE, memoryType EFI_MEMORY_TYPE, pages UINTN, memory *EFI_PHYSICAL_ADDRESS) EFI_STATUS {
|
||||||
|
return UefiCall4(p.allocatePages, uintptr(typ), uintptr(memoryType), uintptr(pages), uintptr(unsafe.Pointer(memory)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_BOOT_SERVICES) FreePages(memory EFI_PHYSICAL_ADDRESS, pages UINTN) EFI_STATUS {
|
||||||
|
return UefiCall2(p.freePages, uintptr(memory), uintptr(pages))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_BOOT_SERVICES) CreateEvent(typ EVENT_TYPE, notifyTPL EFI_TPL, notifyFunction unsafe.Pointer, notifyContext unsafe.Pointer, event *EFI_EVENT) EFI_STATUS {
|
||||||
|
return UefiCall5(p.createEvent, uintptr(typ), uintptr(notifyTPL), uintptr(notifyFunction), uintptr(notifyContext), uintptr(unsafe.Pointer(event)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_BOOT_SERVICES) SetTimer(event EFI_EVENT, typ EFI_TIMER_DELAY, triggerTime uint64) EFI_STATUS {
|
||||||
|
return UefiCall3(p.setTimer, uintptr(event), uintptr(typ), uintptr(triggerTime))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_BOOT_SERVICES) WaitForEvent(numberOfEvents UINTN, event *EFI_EVENT, index *UINTN) EFI_STATUS {
|
||||||
|
return UefiCall3(p.waitForEvent, uintptr(numberOfEvents), uintptr(unsafe.Pointer(event)), uintptr(unsafe.Pointer(index)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_BOOT_SERVICES) CloseEvent(event EFI_EVENT) EFI_STATUS {
|
||||||
|
return UefiCall1(p.closeEvent, uintptr(event))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_BOOT_SERVICES) CheckEvent(event EFI_EVENT) EFI_STATUS {
|
||||||
|
return UefiCall1(p.checkEvent, uintptr(event))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_BOOT_SERVICES) HandleProtocol(handle EFI_HANDLE, protocol *EFI_GUID, iface unsafe.Pointer) EFI_STATUS {
|
||||||
|
return UefiCall3(p.handleProtocol, uintptr(handle), uintptr(unsafe.Pointer(protocol)), uintptr(iface))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_BOOT_SERVICES) LocateProtocol(protocol *EFI_GUID, registration *VOID, iface unsafe.Pointer) EFI_STATUS {
|
||||||
|
return UefiCall3(p.locateProtocol, uintptr(unsafe.Pointer(protocol)), uintptr(unsafe.Pointer(registration)), uintptr(iface))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_BOOT_SERVICES) Exit(imageHandle EFI_HANDLE, exitStatus EFI_STATUS, exitDataSize UINTN, exitData *CHAR16) EFI_STATUS {
|
||||||
|
return UefiCall4(p.exit, uintptr(imageHandle), uintptr(exitStatus), uintptr(exitDataSize), uintptr(unsafe.Pointer(exitData)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *EFI_BOOT_SERVICES) SetWatchdogTimer(timeout UINTN, watchdogCode uint64, dataSize UINTN, watchdogData *CHAR16) EFI_STATUS {
|
||||||
|
return UefiCall4(p.setWatchdogTimer, uintptr(timeout), uintptr(watchdogCode), uintptr(dataSize), uintptr(unsafe.Pointer(watchdogData)))
|
||||||
|
}
|
||||||
|
|
||||||
|
type EFI_SYSTEM_TABLE struct {
|
||||||
|
Hdr EFI_TABLE_HEADER
|
||||||
|
FirmwareVendor *CHAR16
|
||||||
|
FirmwareRevision uint32
|
||||||
|
ConsoleInHandle EFI_HANDLE
|
||||||
|
ConIn *VOID
|
||||||
|
ConsoleOutHandle EFI_HANDLE
|
||||||
|
ConOut *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
|
||||||
|
StandardErrorHandle EFI_HANDLE
|
||||||
|
StdErr *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
|
||||||
|
RuntimeServices *EFI_RUNTIME_SERVICES
|
||||||
|
BootServices *EFI_BOOT_SERVICES
|
||||||
|
NumberOfTableEntries UINTN
|
||||||
|
ConfigurationTable *VOID
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package uefi
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var errNilTextOutputProtocol = errors.New("uefi: nil simple text output protocol")
|
||||||
|
|
||||||
|
type TextOutput struct {
|
||||||
|
proto *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTextOutput(proto *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) *TextOutput {
|
||||||
|
return &TextOutput{proto: proto}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConsoleOut() *TextOutput {
|
||||||
|
return NewTextOutput(ST().ConOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
func StandardError() *TextOutput {
|
||||||
|
return NewTextOutput(ST().StdErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *TextOutput) Write(p []byte) (int, error) {
|
||||||
|
if w == nil || w.proto == nil {
|
||||||
|
return 0, errNilTextOutputProtocol
|
||||||
|
}
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := StringToCHAR16Z(string(p))
|
||||||
|
status := w.proto.OutputString(&buf[0])
|
||||||
|
if status != EFI_SUCCESS {
|
||||||
|
return 0, StatusError(status)
|
||||||
|
}
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *TextOutput) WriteString(s string) (int, error) {
|
||||||
|
return w.Write([]byte(s))
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
//go:build uefi
|
||||||
|
|
||||||
|
package uefi
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
var systemTable *EFI_SYSTEM_TABLE
|
||||||
|
var imageHandle uintptr
|
||||||
|
|
||||||
|
//go:nobounds
|
||||||
|
func Init(argImageHandle uintptr, argSystemTable uintptr) {
|
||||||
|
systemTable = (*EFI_SYSTEM_TABLE)(unsafe.Pointer(argSystemTable))
|
||||||
|
imageHandle = argImageHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
func ST() *EFI_SYSTEM_TABLE {
|
||||||
|
return systemTable
|
||||||
|
}
|
||||||
|
|
||||||
|
func BS() *EFI_BOOT_SERVICES {
|
||||||
|
if systemTable == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return systemTable.BootServices
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetImageHandle() EFI_HANDLE {
|
||||||
|
return EFI_HANDLE(imageHandle)
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ func main() {
|
|||||||
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||||
|
|
||||||
kb := keyboard.Port()
|
kb := keyboard.Port()
|
||||||
|
machine.USBDev.Configure(machine.UARTConfig{}) // no-op if already init'd by serial.usb
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !button.Get() {
|
if !button.Get() {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func main() {
|
|||||||
func escapesToHeap() {
|
func escapesToHeap() {
|
||||||
n := rand.Intn(100)
|
n := rand.Intn(100)
|
||||||
println("Doing ", n, " iterations")
|
println("Doing ", n, " iterations")
|
||||||
for i := 0; i < n; i++ {
|
for i := range n {
|
||||||
s := make([]byte, i)
|
s := make([]byte, i)
|
||||||
_ = append(s, 42)
|
_ = append(s, 42)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ func main() {
|
|||||||
printItf(Number(3))
|
printItf(Number(3))
|
||||||
s := Stringer(thing)
|
s := Stringer(thing)
|
||||||
println("Stringer.String():", s.String())
|
println("Stringer.String():", s.String())
|
||||||
var itf interface{} = s
|
var itf any = s
|
||||||
println("Stringer.(*Thing).String():", itf.(Stringer).String())
|
println("Stringer.(*Thing).String():", itf.(Stringer).String())
|
||||||
|
|
||||||
// unusual calls
|
// unusual calls
|
||||||
@@ -124,7 +124,7 @@ func strlen(s string) int {
|
|||||||
return len(s)
|
return len(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printItf(val interface{}) {
|
func printItf(val any) {
|
||||||
switch val := val.(type) {
|
switch val := val.(type) {
|
||||||
case Doubler:
|
case Doubler:
|
||||||
println("is Doubler:", val.Double())
|
println("is Doubler:", val.Double())
|
||||||
|
|||||||
@@ -6,8 +6,85 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Disk geometry.
|
||||||
|
const (
|
||||||
|
sectorSize = 512
|
||||||
|
diskSectors = 128 // 64 KB total
|
||||||
|
)
|
||||||
|
|
||||||
|
var diskData [diskSectors * sectorSize]byte
|
||||||
|
|
||||||
|
type ramDisk struct{}
|
||||||
|
|
||||||
|
func (r *ramDisk) ReadAt(p []byte, off int64) (int, error) {
|
||||||
|
return copy(p, diskData[off:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ramDisk) WriteAt(p []byte, off int64) (int, error) {
|
||||||
|
return copy(diskData[off:], p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ramDisk) Size() int64 { return int64(diskSectors * sectorSize) }
|
||||||
|
func (r *ramDisk) WriteBlockSize() int64 { return sectorSize }
|
||||||
|
func (r *ramDisk) EraseBlockSize() int64 { return sectorSize }
|
||||||
|
func (r *ramDisk) EraseBlocks(start, len int64) error { return nil }
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
formatFAT12(diskData[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatFAT12 writes a minimal FAT12 volume boot record and FAT tables so the
|
||||||
|
// host OS can mount the disk without reformatting.
|
||||||
|
func formatFAT12(d []byte) {
|
||||||
|
// --- Sector 0: Volume Boot Record ---
|
||||||
|
s := d[0:]
|
||||||
|
s[0] = 0xEB
|
||||||
|
s[1] = 0x3C
|
||||||
|
s[2] = 0x90 // short JMP + NOP
|
||||||
|
copy(s[3:11], "MSDOS5.0")
|
||||||
|
// BPB fields (little-endian)
|
||||||
|
s[11] = 0x00
|
||||||
|
s[12] = 0x02 // bytesPerSector = 512
|
||||||
|
s[13] = 0x01 // sectorsPerCluster = 1
|
||||||
|
s[14] = 0x01
|
||||||
|
s[15] = 0x00 // reservedSectors = 1
|
||||||
|
s[16] = 0x02 // numFATs = 2
|
||||||
|
s[17] = 0x20
|
||||||
|
s[18] = 0x00 // rootEntryCount = 32
|
||||||
|
s[19] = 0x80
|
||||||
|
s[20] = 0x00 // totalSectors16 = 128
|
||||||
|
s[21] = 0xF8 // mediaType = fixed disk
|
||||||
|
s[22] = 0x01
|
||||||
|
s[23] = 0x00 // sectorsPerFAT = 1
|
||||||
|
s[24] = 0x80
|
||||||
|
s[25] = 0x00 // sectorsPerTrack = 128
|
||||||
|
s[26] = 0x01
|
||||||
|
s[27] = 0x00 // numHeads = 1
|
||||||
|
// hiddenSectors[28:32] = 0
|
||||||
|
// totalSectors32[32:36] = 0
|
||||||
|
s[38] = 0x29 // extBootSig
|
||||||
|
s[39] = 0x47
|
||||||
|
s[40] = 0x4F
|
||||||
|
s[41] = 0x30
|
||||||
|
s[42] = 0x31 // volumeID "GO01"
|
||||||
|
copy(s[43:54], "TINYGO ") // volumeLabel (11 bytes)
|
||||||
|
copy(s[54:62], "FAT12 ") // fsType
|
||||||
|
s[510] = 0x55
|
||||||
|
s[511] = 0xAA // boot sector signature
|
||||||
|
|
||||||
|
// --- Sector 1: FAT1 ---
|
||||||
|
// Entry 0 = 0xFF8 (media byte), entry 1 = 0xFFF (EOC); all others = free.
|
||||||
|
d[512] = 0xF8
|
||||||
|
d[513] = 0xFF
|
||||||
|
d[514] = 0xFF
|
||||||
|
|
||||||
|
// --- Sector 2: FAT2 (identical copy) ---
|
||||||
|
copy(d[1024:1027], d[512:515])
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
msc.Port(machine.Flash)
|
msc.Port(&ramDisk{})
|
||||||
|
machine.USBDev.Configure(machine.UARTConfig{})
|
||||||
|
|
||||||
for {
|
for {
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
|
|||||||
@@ -45,11 +45,8 @@ func Compare(a, b []byte) int {
|
|||||||
// This function was copied from the Go 1.23 source tree (with runtime_cmpstring
|
// This function was copied from the Go 1.23 source tree (with runtime_cmpstring
|
||||||
// manually inlined).
|
// manually inlined).
|
||||||
func CompareString(a, b string) int {
|
func CompareString(a, b string) int {
|
||||||
l := len(a)
|
l := min(len(b), len(a))
|
||||||
if len(b) < l {
|
for i := range l {
|
||||||
l = len(b)
|
|
||||||
}
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
c1, c2 := a[i], b[i]
|
c1, c2 := a[i], b[i]
|
||||||
if c1 < c2 {
|
if c1 < c2 {
|
||||||
return -1
|
return -1
|
||||||
@@ -170,7 +167,7 @@ const PrimeRK = 16777619
|
|||||||
// This function was removed in Go 1.22.
|
// This function was removed in Go 1.22.
|
||||||
func HashStrBytes(sep []byte) (uint32, uint32) {
|
func HashStrBytes(sep []byte) (uint32, uint32) {
|
||||||
hash := uint32(0)
|
hash := uint32(0)
|
||||||
for i := 0; i < len(sep); i++ {
|
for i := range sep {
|
||||||
hash = hash*PrimeRK + uint32(sep[i])
|
hash = hash*PrimeRK + uint32(sep[i])
|
||||||
}
|
}
|
||||||
var pow, sq uint32 = 1, PrimeRK
|
var pow, sq uint32 = 1, PrimeRK
|
||||||
@@ -249,7 +246,7 @@ func IndexRabinKarpBytes(s, sep []byte) int {
|
|||||||
hashsep, pow := HashStrBytes(sep)
|
hashsep, pow := HashStrBytes(sep)
|
||||||
n := len(sep)
|
n := len(sep)
|
||||||
var h uint32
|
var h uint32
|
||||||
for i := 0; i < n; i++ {
|
for i := range n {
|
||||||
h = h*PrimeRK + uint32(s[i])
|
h = h*PrimeRK + uint32(s[i])
|
||||||
}
|
}
|
||||||
if h == hashsep && Equal(s[:n], sep) {
|
if h == hashsep && Equal(s[:n], sep) {
|
||||||
@@ -276,7 +273,7 @@ func IndexRabinKarp[T string | []byte](s, sep T) int {
|
|||||||
hashss, pow := HashStr(sep)
|
hashss, pow := HashStr(sep)
|
||||||
n := len(sep)
|
n := len(sep)
|
||||||
var h uint32
|
var h uint32
|
||||||
for i := 0; i < n; i++ {
|
for i := range n {
|
||||||
h = h*PrimeRK + uint32(s[i])
|
h = h*PrimeRK + uint32(s[i])
|
||||||
}
|
}
|
||||||
if h == hashss && string(s[:n]) == string(sep) {
|
if h == hashss && string(s[:n]) == string(sep) {
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ func CaseUnmarshaler[T ~uint8 | ~uint16 | ~uint32](cases []string) func(v *T, te
|
|||||||
return &emptyTextError{}
|
return &emptyTextError{}
|
||||||
}
|
}
|
||||||
s := string(text)
|
s := string(text)
|
||||||
for i := 0; i < len(cases); i++ {
|
for i, c := range cases {
|
||||||
if cases[i] == s {
|
if c == s {
|
||||||
*v = T(i)
|
*v = T(i)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ type stackState struct {
|
|||||||
// The new goroutine is immediately started.
|
// The new goroutine is immediately started.
|
||||||
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||||
t := &Task{}
|
t := &Task{}
|
||||||
|
addLiveTask(t)
|
||||||
t.state.initialize(fn, args, stackSize)
|
t.state.initialize(fn, args, stackSize)
|
||||||
scheduleTask(t)
|
scheduleTask(t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
//go:build scheduler.tasks || scheduler.asyncify || scheduler.cores
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import "sync/atomic"
|
||||||
|
|
||||||
|
var (
|
||||||
|
mainTask *Task
|
||||||
|
liveTasks uint32
|
||||||
|
mainExitedByGoexit uint32
|
||||||
|
)
|
||||||
|
|
||||||
|
func addLiveTask(t *Task) {
|
||||||
|
if mainTask == nil {
|
||||||
|
mainTask = t
|
||||||
|
}
|
||||||
|
atomic.AddUint32(&liveTasks, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit exits the current task because runtime.Goexit was called.
|
||||||
|
func Exit() {
|
||||||
|
exit(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exit(goexit bool) {
|
||||||
|
t := Current()
|
||||||
|
remaining := atomic.AddUint32(&liveTasks, ^uint32(0))
|
||||||
|
if t == mainTask {
|
||||||
|
if goexit {
|
||||||
|
if remaining == 0 {
|
||||||
|
runtimePanic("all goroutines are asleep - deadlock!")
|
||||||
|
}
|
||||||
|
atomic.StoreUint32(&mainExitedByGoexit, 1)
|
||||||
|
}
|
||||||
|
} else if atomic.LoadUint32(&mainExitedByGoexit) != 0 && remaining == 0 {
|
||||||
|
runtimePanic("all goroutines are asleep - deadlock!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: explicitly free the stack after switching back to the scheduler.
|
||||||
|
Pause()
|
||||||
|
runtimePanic("unreachable")
|
||||||
|
}
|
||||||
@@ -33,8 +33,7 @@ type state struct {
|
|||||||
|
|
||||||
//export tinygo_task_exit
|
//export tinygo_task_exit
|
||||||
func taskExit() {
|
func taskExit() {
|
||||||
// TODO: explicitly free the stack after switching back to the scheduler.
|
exit(false)
|
||||||
Pause()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize the state and prepare to call the specified function with the specified argument bundle.
|
// initialize the state and prepare to call the specified function with the specified argument bundle.
|
||||||
@@ -73,6 +72,7 @@ var startTask [0]uint8
|
|||||||
// The new goroutine is scheduled to run later.
|
// The new goroutine is scheduled to run later.
|
||||||
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||||
t := &Task{}
|
t := &Task{}
|
||||||
|
addLiveTask(t)
|
||||||
t.state.initialize(fn, args, stackSize)
|
t.state.initialize(fn, args, stackSize)
|
||||||
scheduleTask(t)
|
scheduleTask(t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,6 +145,11 @@ void* tinygo_task_current(void) {
|
|||||||
return current_task;
|
return current_task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exit the current thread.
|
||||||
|
void tinygo_task_exit(void) {
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// Send a signal to cause the task to pause for the GC mark phase.
|
// Send a signal to cause the task to pause for the GC mark phase.
|
||||||
void tinygo_task_send_gc_signal(pthread_t thread) {
|
void tinygo_task_send_gc_signal(pthread_t thread) {
|
||||||
pthread_kill(thread, taskPauseSignal);
|
pthread_kill(thread, taskPauseSignal);
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ var mainTask Task
|
|||||||
|
|
||||||
// Queue of tasks (see QueueNext) that currently exist in the program.
|
// Queue of tasks (see QueueNext) that currently exist in the program.
|
||||||
var activeTasks = &mainTask
|
var activeTasks = &mainTask
|
||||||
|
var activeTaskCount uint32 = 1
|
||||||
var activeTaskLock PMutex
|
var activeTaskLock PMutex
|
||||||
|
var mainExitedByGoexit bool
|
||||||
|
|
||||||
func OnSystemStack() bool {
|
func OnSystemStack() bool {
|
||||||
runtimePanic("todo: task.OnSystemStack")
|
runtimePanic("todo: task.OnSystemStack")
|
||||||
@@ -95,9 +97,6 @@ func (t *Task) Resume() {
|
|||||||
t.state.pauseSem.Post()
|
t.state.pauseSem.Post()
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherGoroutines is the total number of live goroutines minus one.
|
|
||||||
var otherGoroutines uint32
|
|
||||||
|
|
||||||
// Start a new OS thread.
|
// Start a new OS thread.
|
||||||
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||||
t := &Task{}
|
t := &Task{}
|
||||||
@@ -117,7 +116,7 @@ func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
|||||||
}
|
}
|
||||||
t.state.QueueNext = activeTasks
|
t.state.QueueNext = activeTasks
|
||||||
activeTasks = t
|
activeTasks = t
|
||||||
otherGoroutines++
|
activeTaskCount++
|
||||||
activeTaskLock.Unlock()
|
activeTaskLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +126,12 @@ func taskExited(t *Task) {
|
|||||||
println("*** exit:", t.state.id)
|
println("*** exit:", t.state.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if exit(t) {
|
||||||
|
runtimePanic("all goroutines are asleep - deadlock!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func exit(t *Task) bool {
|
||||||
// Remove from the queue.
|
// Remove from the queue.
|
||||||
// TODO: this can be made more efficient by using a doubly linked list.
|
// TODO: this can be made more efficient by using a doubly linked list.
|
||||||
activeTaskLock.Lock()
|
activeTaskLock.Lock()
|
||||||
@@ -135,16 +140,46 @@ func taskExited(t *Task) {
|
|||||||
if *q == t {
|
if *q == t {
|
||||||
*q = t.state.QueueNext
|
*q = t.state.QueueNext
|
||||||
found = true
|
found = true
|
||||||
|
activeTaskCount--
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
otherGoroutines--
|
deadlocked := mainExitedByGoexit && activeTaskCount == 0
|
||||||
activeTaskLock.Unlock()
|
activeTaskLock.Unlock()
|
||||||
|
|
||||||
// Sanity check.
|
// Sanity check.
|
||||||
if !found {
|
if !found {
|
||||||
runtimePanic("taskExited failed")
|
runtimePanic("taskExited failed")
|
||||||
}
|
}
|
||||||
|
return deadlocked
|
||||||
|
}
|
||||||
|
|
||||||
|
func otherTasks(current *Task) uint32 {
|
||||||
|
if activeTaskCount == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return activeTaskCount - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit exits the current task. If this is the main task and there are no other
|
||||||
|
// goroutines, it reports a deadlock.
|
||||||
|
func Exit() {
|
||||||
|
t := Current()
|
||||||
|
if t == &mainTask {
|
||||||
|
activeTaskLock.Lock()
|
||||||
|
noOtherTasks := otherTasks(t) == 0
|
||||||
|
if !noOtherTasks {
|
||||||
|
mainExitedByGoexit = true
|
||||||
|
}
|
||||||
|
activeTaskLock.Unlock()
|
||||||
|
if noOtherTasks {
|
||||||
|
runtimePanic("all goroutines are asleep - deadlock!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if exit(t) {
|
||||||
|
runtimePanic("all goroutines are asleep - deadlock!")
|
||||||
|
}
|
||||||
|
tinygo_task_exit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// scanWaitGroup is used to wait on until all threads have finished the current state transition.
|
// scanWaitGroup is used to wait on until all threads have finished the current state transition.
|
||||||
@@ -206,7 +241,7 @@ func GCStopWorldAndScan() {
|
|||||||
gcState.Store(gcStateStopped)
|
gcState.Store(gcStateStopped)
|
||||||
|
|
||||||
// Set the number of threads to wait for.
|
// Set the number of threads to wait for.
|
||||||
scanWaitGroup = initWaitGroup(otherGoroutines)
|
scanWaitGroup = initWaitGroup(otherTasks(current))
|
||||||
|
|
||||||
// Pause all other threads.
|
// Pause all other threads.
|
||||||
for t := activeTasks; t != nil; t = t.state.QueueNext {
|
for t := activeTasks; t != nil; t = t.state.QueueNext {
|
||||||
@@ -242,7 +277,7 @@ func GCResumeWorld() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the wait group to track resume progress.
|
// Set the wait group to track resume progress.
|
||||||
scanWaitGroup = initWaitGroup(otherGoroutines)
|
scanWaitGroup = initWaitGroup(otherTasks(Current()))
|
||||||
|
|
||||||
// Set the state to resumed.
|
// Set the state to resumed.
|
||||||
gcState.Store(gcStateResumed)
|
gcState.Store(gcStateResumed)
|
||||||
@@ -304,6 +339,9 @@ func tinygo_task_init(t *Task, thread *threadID, numCPU *int32)
|
|||||||
//go:linkname tinygo_task_start tinygo_task_start
|
//go:linkname tinygo_task_start tinygo_task_start
|
||||||
func tinygo_task_start(fn uintptr, args unsafe.Pointer, t *Task, thread *threadID, stackTop *uintptr, stackSize uintptr) int32
|
func tinygo_task_start(fn uintptr, args unsafe.Pointer, t *Task, thread *threadID, stackTop *uintptr, stackSize uintptr) int32
|
||||||
|
|
||||||
|
//go:linkname tinygo_task_exit tinygo_task_exit
|
||||||
|
func tinygo_task_exit()
|
||||||
|
|
||||||
// Pause the thread by sending it a signal.
|
// Pause the thread by sending it a signal.
|
||||||
//
|
//
|
||||||
//export tinygo_task_send_gc_signal
|
//export tinygo_task_send_gc_signal
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
//go:build xiao_ble_plus
|
||||||
|
|
||||||
|
// This file contains the pin mappings for the Seeed XIAO BLE nRF52840 [Plus, Sense Plus] boards.
|
||||||
|
//
|
||||||
|
// Seeed XIAO BLE is an ultra-small size, ultra-low power Bluetooth development board based on the Nordic nRF52840.
|
||||||
|
// It features an onboard Bluetooth antenna, onboard battery charging chip, and 21*17.5mm thumb size, which makes it ideal for IoT projects.
|
||||||
|
//
|
||||||
|
// Seeed XIAO BLE nRF52840 Sense is a tiny Bluetooth LE development board designed for IoT and AI applications.
|
||||||
|
// It features an onboard antenna, 6 Dof IMU, microphone, all of which make it an ideal board to run AI using TinyML and TensorFlow Lite.
|
||||||
|
//
|
||||||
|
// SoftDevice (s140v7) is pre-flashed on this board already.
|
||||||
|
// See https://github.com/tinygo-org/bluetooth
|
||||||
|
//
|
||||||
|
// - https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html
|
||||||
|
// - https://www.seeedstudio.com/Seeed-XIAO-BLE-Sense-nRF52840-p-5253.html
|
||||||
|
// - https://www.seeedstudio.com/Seeed-Studio-XIAO-nRF52840-Sense-Plus-p-6360.html
|
||||||
|
//
|
||||||
|
// - https://wiki.seeedstudio.com/XIAO_BLE/
|
||||||
|
// - https://github.com/Seeed-Studio/ArduinoCore-mbed/tree/master/variants/SEEED_XIAO_NRF52840_SENSE
|
||||||
|
package machine
|
||||||
|
|
||||||
|
const HasLowFrequencyCrystal = true
|
||||||
|
|
||||||
|
// Digital Pins
|
||||||
|
const (
|
||||||
|
D0 Pin = P0_02
|
||||||
|
D1 Pin = P0_03
|
||||||
|
D2 Pin = P0_28
|
||||||
|
D3 Pin = P0_29
|
||||||
|
D4 Pin = P0_04
|
||||||
|
D5 Pin = P0_05
|
||||||
|
D6 Pin = P1_11
|
||||||
|
D7 Pin = P1_12
|
||||||
|
D8 Pin = P1_13
|
||||||
|
D9 Pin = P1_14
|
||||||
|
D10 Pin = P1_15
|
||||||
|
D11 Pin = P0_15
|
||||||
|
D12 Pin = P0_19
|
||||||
|
D13 Pin = P1_01
|
||||||
|
D14 Pin = P0_09
|
||||||
|
D15 Pin = P0_10
|
||||||
|
D16 Pin = P0_31
|
||||||
|
D17 Pin = P1_03
|
||||||
|
D18 Pin = P1_05
|
||||||
|
D19 Pin = P1_07
|
||||||
|
)
|
||||||
|
|
||||||
|
// Analog pins
|
||||||
|
const (
|
||||||
|
A0 Pin = P0_02
|
||||||
|
A1 Pin = P0_03
|
||||||
|
A2 Pin = P0_28
|
||||||
|
A3 Pin = P0_29
|
||||||
|
A4 Pin = P0_04
|
||||||
|
A5 Pin = P0_05
|
||||||
|
)
|
||||||
|
|
||||||
|
// Onboard LEDs
|
||||||
|
const (
|
||||||
|
LED = LED_CHG
|
||||||
|
LED1 = LED_RED
|
||||||
|
LED2 = LED_GREEN
|
||||||
|
LED3 = LED_BLUE
|
||||||
|
LED_CHG = P0_17
|
||||||
|
LED_RED = P0_26
|
||||||
|
LED_GREEN = P0_30
|
||||||
|
LED_BLUE = P0_06
|
||||||
|
)
|
||||||
|
|
||||||
|
// UART pins
|
||||||
|
const (
|
||||||
|
UART_RX_PIN = P1_12
|
||||||
|
UART_TX_PIN = P1_11
|
||||||
|
UART1_RX_PIN = P0_09
|
||||||
|
UART1_TX_PIN = P0_10
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2C pins
|
||||||
|
const (
|
||||||
|
// Defaults to internal
|
||||||
|
SDA_PIN = SDA1_PIN
|
||||||
|
SCL_PIN = SCL1_PIN
|
||||||
|
|
||||||
|
// I2C0 (external) pins
|
||||||
|
SDA0_PIN = P0_04
|
||||||
|
SCL0_PIN = P0_05
|
||||||
|
|
||||||
|
// I2C1 (internal) pins
|
||||||
|
SDA1_PIN = P0_07
|
||||||
|
SCL1_PIN = P0_27
|
||||||
|
)
|
||||||
|
|
||||||
|
// I2S pins
|
||||||
|
const (
|
||||||
|
I2S_SCK_PIN = P0_19
|
||||||
|
I2S_WS_PIN = P1_01
|
||||||
|
I2S_SD_PIN = P0_15
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPI pins
|
||||||
|
const (
|
||||||
|
SPI0_SCK_PIN = P1_13
|
||||||
|
SPI0_SDO_PIN = P1_14
|
||||||
|
SPI0_SDI_PIN = P1_15
|
||||||
|
)
|
||||||
|
|
||||||
|
// Peripherals
|
||||||
|
const (
|
||||||
|
LSM_PWR = P1_08 // IMU (LSM6DS3TR) power
|
||||||
|
LSM_INT = P0_11 // IMU (LSM6DS3TR) interrupt
|
||||||
|
|
||||||
|
MIC_PWR = P1_10 // Microphone (MSM261D3526H1CPM) power
|
||||||
|
MIC_CLK = P1_00
|
||||||
|
MIC_DIN = P0_16
|
||||||
|
)
|
||||||
|
|
||||||
|
// NFC pins
|
||||||
|
const (
|
||||||
|
NFC1_PIN = P0_09
|
||||||
|
NFC2_PIN = P0_10
|
||||||
|
)
|
||||||
|
|
||||||
|
// USB CDC identifiers
|
||||||
|
const (
|
||||||
|
usb_STRING_PRODUCT = "XIAO nRF52840 Sense"
|
||||||
|
usb_STRING_MANUFACTURER = "Seeed"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
usb_VID uint16 = 0x2886
|
||||||
|
usb_PID uint16 = 0x8045
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultUART = UART0
|
||||||
|
)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build esp32c3 || nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l0 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350
|
//go:build esp32c3 || nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32f7 || stm32l0 || stm32l4 || stm32wlx || atsamd21 || atsamd51 || atsame5x || rp2040 || rp2350
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,392 @@
|
|||||||
|
//go:build esp32c6
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/esp"
|
||||||
|
"errors"
|
||||||
|
"runtime/volatile"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// newRegI2C returns the regI2C configured for ESP32-C6: hostID=0, drefInit=1.
|
||||||
|
// I2C_SAR_ADC_HOSTID = 0 per soc/esp32c6/include/soc/regi2c_saradc.h.
|
||||||
|
func newRegI2C() regI2C { return regI2C{hostID: 0, drefInit: 1} }
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ADC attenuation values for ESP32-C6 APB_SARADC.
|
||||||
|
// 0 dB : ~0 .. 1.1 V
|
||||||
|
// 11 dB : ~0 .. 3.3 V (matches typical VDD)
|
||||||
|
atten0dB = 0
|
||||||
|
atten11dB = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitADC initialises the APB_SARADC peripheral on ESP32-C6.
|
||||||
|
// On C6 the clock/reset gating moved to PCR (not SYSTEM as on C3), and the
|
||||||
|
// SARADC CLKM divider configuration also lives in PCR.
|
||||||
|
func InitADC() {
|
||||||
|
// Reset and enable the SARADC bus clock via PCR.
|
||||||
|
esp.PCR.SetSARADC_CONF_SARADC_RST_EN(1)
|
||||||
|
esp.PCR.SetSARADC_CONF_SARADC_CLK_EN(1)
|
||||||
|
esp.PCR.SetSARADC_CONF_SARADC_RST_EN(0)
|
||||||
|
|
||||||
|
// Select clock source 2 (PLL_F80M), divider = 1, no fractional.
|
||||||
|
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_SEL(2)
|
||||||
|
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_DIV_NUM(1)
|
||||||
|
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_DIV_B(0)
|
||||||
|
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_DIV_A(0)
|
||||||
|
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_EN(1)
|
||||||
|
|
||||||
|
// Power up the SAR ADC and configure FSM timing (same register layout as C3).
|
||||||
|
esp.APB_SARADC.SetCTRL_SARADC_XPD_SAR_FORCE(1)
|
||||||
|
esp.APB_SARADC.SetFSM_WAIT_SARADC_XPD_WAIT(8)
|
||||||
|
esp.APB_SARADC.SetFSM_WAIT_SARADC_RSTB_WAIT(8)
|
||||||
|
esp.APB_SARADC.SetFSM_WAIT_SARADC_STANDBY_WAIT(100)
|
||||||
|
|
||||||
|
adcSelfCalibrate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ESP32-C6 ADC pin mapping: ADC1 = GPIO0–GPIO6 (ch 0–6). There is no ADC2.
|
||||||
|
// (The machine_esp32c6.go file defines ADC0..ADC6 as GPIO0..GPIO6.)
|
||||||
|
func (a ADC) Configure(config ADCConfig) error {
|
||||||
|
if a.Pin > 6 {
|
||||||
|
return errors.New("invalid ADC pin for ESP32-C6")
|
||||||
|
}
|
||||||
|
a.Pin.Configure(PinConfig{Mode: PinAnalog})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get performs a single ADC1 conversion and returns a 16-bit value.
|
||||||
|
// The raw 12-bit result (0..4095) is left-shifted by 4 to fill 16 bits.
|
||||||
|
func (a ADC) Get() uint16 {
|
||||||
|
if a.Pin > 6 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB)
|
||||||
|
esp.APB_SARADC.SetINT_CLR_APB_SARADC1_DONE_INT_CLR(1)
|
||||||
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
||||||
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(uint32(a.Pin))
|
||||||
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(1)
|
||||||
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1)
|
||||||
|
for esp.APB_SARADC.GetINT_RAW_APB_SARADC1_DONE_INT_RAW() == 0 {
|
||||||
|
}
|
||||||
|
raw := esp.APB_SARADC.GetSAR1DATA_STATUS_APB_SARADC1_DATA()
|
||||||
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
||||||
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(0)
|
||||||
|
return uint16(raw&0xfff) << 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── regI2C: internal I2C-bus (LP_I2C_ANA_MST) for SAR ADC calibration ───────
|
||||||
|
//
|
||||||
|
// On ESP32-C6 the "REGI2C" master moved from the embedded SENS/APB_SARADC
|
||||||
|
// controller (0x6000_E000) used on C3/S3 to the dedicated LP_I2C_ANA_MST
|
||||||
|
// peripheral at 0x600b_2400. The SAR ADC block address and register layout
|
||||||
|
// (DREF, ENCAL_GND, INIT_CODE) remain identical to C3.
|
||||||
|
//
|
||||||
|
// LP_I2C_ANA_MST.I2C0_CTRL bit layout (25-bit command field):
|
||||||
|
// [7:0] = slave block address (0x69 for I2C_SAR_ADC)
|
||||||
|
// [15:8] = register address within the block
|
||||||
|
// [23:16]= write data (8 bits)
|
||||||
|
// [24] = WR_CNTL: 0=read, 1=write
|
||||||
|
// [25] = BUSY (read-only, set by hardware while processing)
|
||||||
|
//
|
||||||
|
// Source: components/esp_rom/patches/esp_rom_regi2c_esp32c6.c in esp-idf
|
||||||
|
|
||||||
|
// regI2C wraps the internal I2C bus used for SAR ADC calibration registers.
|
||||||
|
// Fields hold chip-specific parameters.
|
||||||
|
type regI2C struct {
|
||||||
|
// hostID is the I2C_SAR_ADC_HOSTID (0 for ESP32-C6, matching regi2c_saradc.h).
|
||||||
|
hostID uint8
|
||||||
|
// drefInit is the DREF reference value written during calibrationInit (1 for C6).
|
||||||
|
drefInit uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAR ADC I2C register layout — identical to ESP32-C3 / ESP32-S3.
|
||||||
|
// Source: soc/esp32c6/include/soc/regi2c_saradc.h
|
||||||
|
const (
|
||||||
|
i2cSarADC = uint8(0x69)
|
||||||
|
|
||||||
|
adc1DrefAddr = uint8(0x2)
|
||||||
|
adc1DrefMSB = uint8(6)
|
||||||
|
adc1DrefLSB = uint8(4)
|
||||||
|
adc2DrefAddr = uint8(0x5)
|
||||||
|
adc2DrefMSB = uint8(6)
|
||||||
|
adc2DrefLSB = uint8(4)
|
||||||
|
|
||||||
|
adc1EncalGndAddr = uint8(0x7)
|
||||||
|
adc1EncalGndMSB = uint8(5)
|
||||||
|
adc1EncalGndLSB = uint8(5)
|
||||||
|
adc2EncalGndAddr = uint8(0x7)
|
||||||
|
adc2EncalGndMSB = uint8(7)
|
||||||
|
adc2EncalGndLSB = uint8(7)
|
||||||
|
|
||||||
|
adc1InitCodeHighAddr = uint8(0x1)
|
||||||
|
adc1InitCodeHighMSB = uint8(3)
|
||||||
|
adc1InitCodeHighLSB = uint8(0)
|
||||||
|
adc1InitCodeLowAddr = uint8(0x0)
|
||||||
|
adc1InitCodeLowMSB = uint8(7)
|
||||||
|
adc1InitCodeLowLSB = uint8(0)
|
||||||
|
adc2InitCodeHighAddr = uint8(0x4)
|
||||||
|
adc2InitCodeHighMSB = uint8(3)
|
||||||
|
adc2InitCodeHighLSB = uint8(0)
|
||||||
|
adc2InitCodeLowAddr = uint8(0x3)
|
||||||
|
adc2InitCodeLowMSB = uint8(7)
|
||||||
|
adc2InitCodeLowLSB = uint8(0)
|
||||||
|
|
||||||
|
// adcCalOffsetRange is the binary search upper bound (12-bit full scale).
|
||||||
|
adcCalOffsetRange = uint32(4096)
|
||||||
|
// adcCalMaxIterations caps binary search iterations.
|
||||||
|
adcCalMaxIterations = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
// LP_I2C_ANA_MST I2C0_CTRL bit-field shifts (see file header comment).
|
||||||
|
const (
|
||||||
|
c6SlaveIDShift = 0 // bits [7:0]
|
||||||
|
c6AddrShift = 8 // bits [15:8]
|
||||||
|
c6DataShift = 16 // bits [23:16]
|
||||||
|
c6WrCntlShift = 24 // bit [24]
|
||||||
|
c6BusyBit = uint32(1 << 25)
|
||||||
|
|
||||||
|
// c6SarI2CDeviceEn is BIT(7) in LP_I2C_ANA_MST.DEVICE_EN for I2C_SAR_ADC (0x69).
|
||||||
|
c6SarI2CDeviceEn = uint32(1 << 7)
|
||||||
|
)
|
||||||
|
|
||||||
|
// ANA_CONFIG / ANA_CONFIG2 register addresses and bits for the internal SAR I2C
|
||||||
|
// domain on ESP32-C6. These differ from C3's SENS block (0x6000_E044/048).
|
||||||
|
// Source: soc/esp32c6/include/soc/regi2c_defs.h
|
||||||
|
const (
|
||||||
|
c6AnaConfigReg = uintptr(0x600AF81C) // clear ANA_I2C_SAR_FORCE_PD (bit 18)
|
||||||
|
c6AnaConfig2Reg = uintptr(0x600AF820) // set ANA_I2C_SAR_FORCE_PU (bit 16)
|
||||||
|
c6SarForcePD = uint32(1 << 18)
|
||||||
|
c6SarForcePU = uint32(1 << 16)
|
||||||
|
)
|
||||||
|
|
||||||
|
// sarEnable powers up the internal SAR I2C domain and enables the LP_I2C_ANA_MST
|
||||||
|
// clock and SAR slave device before any regI2C access.
|
||||||
|
// Matches regi2c_ctrl_ll_i2c_saradc_enable() + regi2c_enable_block(REGI2C_SAR_I2C).
|
||||||
|
func (r regI2C) sarEnable() {
|
||||||
|
cfg := (*volatile.Register32)(unsafe.Pointer(c6AnaConfigReg))
|
||||||
|
cfg2 := (*volatile.Register32)(unsafe.Pointer(c6AnaConfig2Reg))
|
||||||
|
cfg.Set(cfg.Get() &^ c6SarForcePD)
|
||||||
|
cfg2.Set(cfg2.Get() | c6SarForcePU)
|
||||||
|
|
||||||
|
// Enable the LP_I2C_ANA_MST master clock (MODEM_LPCON.CLK_CONF bit 2).
|
||||||
|
esp.MODEM_LPCON.SetCLK_CONF_CLK_I2C_MST_EN(1)
|
||||||
|
// Enable the master's own clock gate (LP_I2C_ANA_MST.DATE bit 28).
|
||||||
|
esp.LP_I2C_ANA_MST.SetDATE_LP_I2C_ANA_MAST_I2C_MAT_CLK_EN(1)
|
||||||
|
// Enable the SAR ADC slave device (DEVICE_EN bit 7).
|
||||||
|
dev := esp.LP_I2C_ANA_MST.GetDEVICE_EN_LP_I2C_ANA_MAST_I2C_DEVICE_EN()
|
||||||
|
esp.LP_I2C_ANA_MST.SetDEVICE_EN_LP_I2C_ANA_MAST_I2C_DEVICE_EN(dev | c6SarI2CDeviceEn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeMask implements the REGI2C_WRITE_MASK macro for ESP32-C6 via LP_I2C_ANA_MST.
|
||||||
|
// It reads the current byte at regAddr, updates the [msb:lsb] bitfield, and writes
|
||||||
|
// it back. Matches esp_rom_regi2c_write_mask() in esp_rom_regi2c_esp32c6.c.
|
||||||
|
func (r regI2C) writeMask(regAddr, msb, lsb, data uint8) {
|
||||||
|
ctrl := &esp.LP_I2C_ANA_MST.I2C0_CTRL
|
||||||
|
rdata := &esp.LP_I2C_ANA_MST.I2C0_DATA
|
||||||
|
|
||||||
|
// Issue a read command: slave_id | (reg_addr << 8), no WR_CNTL bit.
|
||||||
|
readCmd := (uint32(i2cSarADC) << c6SlaveIDShift) | (uint32(regAddr) << c6AddrShift)
|
||||||
|
volatile.StoreUint32(&ctrl.Reg, readCmd)
|
||||||
|
for volatile.LoadUint32(&ctrl.Reg)&c6BusyBit != 0 {
|
||||||
|
}
|
||||||
|
cur := volatile.LoadUint32(&rdata.Reg) & 0xFF
|
||||||
|
|
||||||
|
// Modify the [msb:lsb] bitfield.
|
||||||
|
mask := uint32(1<<(msb-lsb+1)-1) << lsb
|
||||||
|
cur &^= mask
|
||||||
|
cur |= uint32(data&(1<<(msb-lsb+1)-1)) << lsb
|
||||||
|
|
||||||
|
// Issue a write command: slave_id | (reg_addr<<8) | WR_CNTL | (data<<16).
|
||||||
|
writeCmd := (uint32(i2cSarADC) << c6SlaveIDShift) |
|
||||||
|
(uint32(regAddr) << c6AddrShift) |
|
||||||
|
(uint32(1) << c6WrCntlShift) |
|
||||||
|
((cur & 0xFF) << c6DataShift)
|
||||||
|
volatile.StoreUint32(&ctrl.Reg, writeCmd)
|
||||||
|
for volatile.LoadUint32(&ctrl.Reg)&c6BusyBit != 0 {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calibrationInit sets the DREF reference for the selected ADC unit.
|
||||||
|
func (r regI2C) calibrationInit(adcN uint8) {
|
||||||
|
if adcN == 0 {
|
||||||
|
r.writeMask(adc1DrefAddr, adc1DrefMSB, adc1DrefLSB, r.drefInit)
|
||||||
|
} else {
|
||||||
|
r.writeMask(adc2DrefAddr, adc2DrefMSB, adc2DrefLSB, r.drefInit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calibrationPrepare enables ENCAL_GND so the ADC input is shorted to ground.
|
||||||
|
func (r regI2C) calibrationPrepare(adcN uint8) {
|
||||||
|
if adcN == 0 {
|
||||||
|
r.writeMask(adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 1)
|
||||||
|
} else {
|
||||||
|
r.writeMask(adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calibrationFinish clears ENCAL_GND to reconnect the ADC input to the pad.
|
||||||
|
func (r regI2C) calibrationFinish(adcN uint8) {
|
||||||
|
if adcN == 0 {
|
||||||
|
r.writeMask(adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 0)
|
||||||
|
} else {
|
||||||
|
r.writeMask(adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCalibrationParam writes the INIT_CODE (offset trim) for the selected ADC unit.
|
||||||
|
func (r regI2C) setCalibrationParam(adcN uint8, param uint32) {
|
||||||
|
msb := uint8(param >> 8)
|
||||||
|
lsb := uint8(param & 0xFF)
|
||||||
|
if adcN == 0 {
|
||||||
|
r.writeMask(adc1InitCodeHighAddr, adc1InitCodeHighMSB, adc1InitCodeHighLSB, msb)
|
||||||
|
r.writeMask(adc1InitCodeLowAddr, adc1InitCodeLowMSB, adc1InitCodeLowLSB, lsb)
|
||||||
|
} else {
|
||||||
|
r.writeMask(adc2InitCodeHighAddr, adc2InitCodeHighMSB, adc2InitCodeHighLSB, msb)
|
||||||
|
r.writeMask(adc2InitCodeLowAddr, adc2InitCodeLowMSB, adc2InitCodeLowLSB, lsb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calibrateBinarySearch runs the ADC self-calibration binary search loop.
|
||||||
|
// It performs 'iterations' rounds, drops the min/max outliers, and returns
|
||||||
|
// the rounded mean of the remaining values. Matches adc_hal_self_calibration().
|
||||||
|
func (r regI2C) calibrateBinarySearch(adcN uint8, iterations int, readADC func() uint32) uint32 {
|
||||||
|
if iterations > adcCalMaxIterations {
|
||||||
|
iterations = adcCalMaxIterations
|
||||||
|
}
|
||||||
|
var codeList [adcCalMaxIterations]uint32
|
||||||
|
var codeSum uint32
|
||||||
|
|
||||||
|
for rpt := 0; rpt < iterations; rpt++ {
|
||||||
|
codeH := adcCalOffsetRange
|
||||||
|
codeL := uint32(0)
|
||||||
|
chkCode := (codeH + codeL) / 2
|
||||||
|
r.setCalibrationParam(adcN, chkCode)
|
||||||
|
selfCal := readADC()
|
||||||
|
|
||||||
|
for codeH-codeL > 1 {
|
||||||
|
if selfCal == 0 {
|
||||||
|
codeH = chkCode
|
||||||
|
} else {
|
||||||
|
codeL = chkCode
|
||||||
|
}
|
||||||
|
chkCode = (codeH + codeL) / 2
|
||||||
|
r.setCalibrationParam(adcN, chkCode)
|
||||||
|
selfCal = readADC()
|
||||||
|
if codeH-codeL == 1 {
|
||||||
|
chkCode++
|
||||||
|
r.setCalibrationParam(adcN, chkCode)
|
||||||
|
selfCal = readADC()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
codeList[rpt] = chkCode
|
||||||
|
codeSum += chkCode
|
||||||
|
}
|
||||||
|
|
||||||
|
codeMin := codeList[0]
|
||||||
|
codeMax := codeList[0]
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
if codeList[i] < codeMin {
|
||||||
|
codeMin = codeList[i]
|
||||||
|
}
|
||||||
|
if codeList[i] > codeMax {
|
||||||
|
codeMax = codeList[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remaining := codeSum - codeMax - codeMin
|
||||||
|
divisor := uint32(iterations - 2)
|
||||||
|
finalCode := remaining / divisor
|
||||||
|
if remaining%divisor >= 4 {
|
||||||
|
finalCode++
|
||||||
|
}
|
||||||
|
return finalCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Self-calibration ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const (
|
||||||
|
adcCalTimesC6 = 15
|
||||||
|
adcCalRtcMagicC6 = uint32(0xADC1C601) // magic distinguishes C6 from C3
|
||||||
|
adcCalInitMinC6 = uint32(1000)
|
||||||
|
adcCalInitMaxC6 = uint32(4096)
|
||||||
|
)
|
||||||
|
|
||||||
|
// adcSelfCalibrate runs a self-calibration for ADC1 (the only ADC unit on C6).
|
||||||
|
// The calibration code is cached in LP_AON scratch registers to survive sleep.
|
||||||
|
// eFuse calibration is not used: the fields are often unprogrammed.
|
||||||
|
func adcSelfCalibrate() {
|
||||||
|
reg := newRegI2C()
|
||||||
|
reg.sarEnable()
|
||||||
|
|
||||||
|
var adc1Code uint32
|
||||||
|
if saved, ok := c6RestoreFromLP(); ok {
|
||||||
|
adc1Code = saved
|
||||||
|
} else {
|
||||||
|
c6CalSetupADC1()
|
||||||
|
reg.calibrationInit(0)
|
||||||
|
reg.calibrationPrepare(0)
|
||||||
|
adc1Code = reg.calibrateBinarySearch(0, adcCalTimesC6, readADC1)
|
||||||
|
if adc1Code < adcCalInitMinC6 {
|
||||||
|
adc1Code = adcCalInitMinC6
|
||||||
|
}
|
||||||
|
if adc1Code > adcCalInitMaxC6 {
|
||||||
|
adc1Code = adcCalInitMaxC6
|
||||||
|
}
|
||||||
|
c6SaveToLP(adc1Code)
|
||||||
|
reg.calibrationFinish(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
c6ApplyADC1Code(reg, adc1Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// c6CalSetupADC1 configures APB_SARADC for oneshot ADC1 ch0 with fixed attenuation.
|
||||||
|
func c6CalSetupADC1() {
|
||||||
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB)
|
||||||
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(0)
|
||||||
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readADC1 performs a single ADC1 conversion and returns the raw 12-bit result.
|
||||||
|
func readADC1() uint32 {
|
||||||
|
esp.APB_SARADC.SetINT_CLR_APB_SARADC1_DONE_INT_CLR(1)
|
||||||
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
||||||
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1)
|
||||||
|
for esp.APB_SARADC.GetINT_RAW_APB_SARADC1_DONE_INT_RAW() == 0 {
|
||||||
|
}
|
||||||
|
raw := esp.APB_SARADC.GetSAR1DATA_STATUS_APB_SARADC1_DATA() & 0xfff
|
||||||
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
||||||
|
return uint32(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// c6RestoreFromLP reads the saved calibration code from LP_AON scratch registers.
|
||||||
|
// On C6, LP_AON replaces the C3's RTC_CNTL for scratch storage.
|
||||||
|
func c6RestoreFromLP() (uint32, bool) {
|
||||||
|
if esp.LP_AON.GetSTORE0() != adcCalRtcMagicC6 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
code := esp.LP_AON.GetSTORE1()
|
||||||
|
if code < adcCalInitMinC6 || code > adcCalInitMaxC6 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return code, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// c6SaveToLP stores the calibration code in LP_AON scratch registers.
|
||||||
|
func c6SaveToLP(code uint32) {
|
||||||
|
if code < adcCalInitMinC6 || code > adcCalInitMaxC6 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
esp.LP_AON.SetSTORE0(adcCalRtcMagicC6)
|
||||||
|
esp.LP_AON.SetSTORE1(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// c6ApplyADC1Code sets ADC1 init code and finishes calibration.
|
||||||
|
// ESP32-C6 has no ADC2 so only ADC1 (adcN=0) needs to be configured.
|
||||||
|
func c6ApplyADC1Code(reg regI2C, code uint32) {
|
||||||
|
c6CalSetupADC1()
|
||||||
|
reg.calibrationInit(0)
|
||||||
|
reg.calibrationPrepare(0)
|
||||||
|
reg.setCalibrationParam(0, code)
|
||||||
|
reg.calibrationFinish(0)
|
||||||
|
}
|
||||||
@@ -470,3 +470,41 @@ func initADCClock() {
|
|||||||
esp.APB_SARADC.SetCTRL_SARADC_SAR_CLK_DIV(1)
|
esp.APB_SARADC.SetCTRL_SARADC_SAR_CLK_DIV(1)
|
||||||
esp.APB_SARADC.SetCLKM_CONF_CLK_EN(1)
|
esp.APB_SARADC.SetCLKM_CONF_CLK_EN(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadTemperature reads the on-chip temperature sensor (TSENS) and returns
|
||||||
|
// a value in millicelsius (°C × 1000). Uses the default measurement range
|
||||||
|
// (offset = 0, approximately −10 °C to 80 °C, ±3 °C accuracy).
|
||||||
|
//
|
||||||
|
// The conversion uses the same formula as ESP-IDF with no eFuse calibration:
|
||||||
|
//
|
||||||
|
// T = (0.4386 × raw − 20.52) °C
|
||||||
|
func ReadTemperature() int32 {
|
||||||
|
// Enable TSENS peripheral clock.
|
||||||
|
esp.SENS.SetSAR_PERI_CLK_GATE_CONF_TSENS_CLK_EN(1)
|
||||||
|
|
||||||
|
// Set clock divider to default (6).
|
||||||
|
esp.SENS.SetSAR_TSENS_CTRL_SAR_TSENS_CLK_DIV(6)
|
||||||
|
|
||||||
|
// Power up the temperature sensor.
|
||||||
|
esp.SENS.SetSAR_TSENS_CTRL_SAR_TSENS_POWER_UP_FORCE(1)
|
||||||
|
esp.SENS.SetSAR_TSENS_CTRL2_SAR_TSENS_XPD_FORCE(1)
|
||||||
|
esp.SENS.SetSAR_TSENS_CTRL_SAR_TSENS_POWER_UP(1)
|
||||||
|
|
||||||
|
// Trigger a conversion.
|
||||||
|
esp.SENS.SetSAR_TSENS_CTRL_SAR_TSENS_DUMP_OUT(1)
|
||||||
|
|
||||||
|
// Wait for data ready.
|
||||||
|
for esp.SENS.GetSAR_TSENS_CTRL_SAR_TSENS_READY() == 0 {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the 8-bit raw value.
|
||||||
|
raw := int32(esp.SENS.GetSAR_TSENS_CTRL_SAR_TSENS_OUT())
|
||||||
|
|
||||||
|
// Stop the conversion.
|
||||||
|
esp.SENS.SetSAR_TSENS_CTRL_SAR_TSENS_DUMP_OUT(0)
|
||||||
|
|
||||||
|
// Convert to millicelsius using the ESP-IDF integer formula (offset=0):
|
||||||
|
// T_celsius = (4386 * raw - 205200) / 10000
|
||||||
|
// T_millicelsius = (4386 * raw - 205200) / 10
|
||||||
|
return (4386*raw - 205200) / 10
|
||||||
|
}
|
||||||
|
|||||||
@@ -86,6 +86,12 @@ func (p Pin) PortMaskClear() (*uint32, uint32) {
|
|||||||
return &port.BSRR.Reg, 1 << (pin + 16)
|
return &port.BSRR.Reg, 1 << (pin + 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnterBootloader resets the chip into the bootloader.
|
||||||
|
// This is currently a stub for STM32, required to satisfy machine.EnterBootloader
|
||||||
|
// called by machine/usb/cdc.
|
||||||
|
func EnterBootloader() {
|
||||||
|
}
|
||||||
|
|
||||||
var deviceID [12]byte
|
var deviceID [12]byte
|
||||||
|
|
||||||
// DeviceID returns an identifier that is unique within
|
// DeviceID returns an identifier that is unique within
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build stm32 && !stm32f1 && !stm32l5 && !stm32wlx && !stm32g0 && !stm32u5
|
//go:build stm32 && !stm32f1 && !stm32l5 && !stm32wlx && !stm32g0 && !stm32u5 && !stm32u0
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build stm32 && !stm32l4 && !stm32l5 && !stm32wlx && !stm32g0 && !stm32u5
|
//go:build stm32 && !stm32l4 && !stm32l5 && !stm32wlx && !stm32g0 && !stm32u5 && !stm32u0
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build stm32l5 || stm32f7 || stm32l4 || stm32l0 || stm32wlx || stm32g0 || stm32u5
|
//go:build stm32l5 || stm32f7 || stm32l4 || stm32l0 || stm32wlx || stm32g0 || stm32u0 || stm32u5
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,828 @@
|
|||||||
|
//go:build stm32f4 || stm32f7
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/stm32"
|
||||||
|
"machine/usb"
|
||||||
|
"runtime/interrupt"
|
||||||
|
"runtime/volatile"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NumberOfUSBEndpoints is sized to cover TinyGo's full endpoint index space
|
||||||
|
// (0=control, 1=CDC ACM, 2=CDC OUT, 3=CDC IN, 4=HID IN, 5=HID OUT, 6=MIDI IN, 7=MIDI OUT).
|
||||||
|
// Physical OTG FS hardware has 4 IN + 4 OUT endpoints (0–3).
|
||||||
|
const NumberOfUSBEndpoints = 8
|
||||||
|
|
||||||
|
// Default USB identifiers; board files with USB support should override these.
|
||||||
|
const (
|
||||||
|
usb_VID = uint16(0x239A)
|
||||||
|
usb_PID = uint16(0x0001)
|
||||||
|
usb_STRING_MANUFACTURER = "TinyGo"
|
||||||
|
usb_STRING_PRODUCT = "STM32 USB Device"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OTG FS register blocks.
|
||||||
|
var (
|
||||||
|
otgDevice = (*usbDeviceRegs)(unsafe.Pointer(stm32.OTG_FS_DEVICE))
|
||||||
|
otgPower = (*usbPowerRegs)(unsafe.Pointer(stm32.OTG_FS_PWRCLK))
|
||||||
|
)
|
||||||
|
|
||||||
|
// usbDeviceRegs represents the USB device-mode control block at base+0x800.
|
||||||
|
type usbDeviceRegs struct {
|
||||||
|
DCFG volatile.Register32 // 0x800
|
||||||
|
DCTL volatile.Register32 // 0x804
|
||||||
|
DSTS volatile.Register32 // 0x808
|
||||||
|
_ uint32 // 0x80C
|
||||||
|
DIEPMSK volatile.Register32 // 0x810
|
||||||
|
DOEPMSK volatile.Register32 // 0x814
|
||||||
|
DAINT volatile.Register32 // 0x818
|
||||||
|
DAINTMSK volatile.Register32 // 0x81C
|
||||||
|
_ [5]uint32 // 0x820 - 0x830
|
||||||
|
DIEPEMPMSK volatile.Register32 // 0x834
|
||||||
|
}
|
||||||
|
|
||||||
|
// usbInEndpointRegs represents the registers for a single IN endpoint at 0x900 + ep*0x20.
|
||||||
|
type usbInEndpointRegs struct {
|
||||||
|
CTL volatile.Register32 // 0x00
|
||||||
|
_ uint32 // 0x04
|
||||||
|
INT volatile.Register32 // 0x08
|
||||||
|
_ uint32 // 0x0C
|
||||||
|
TSIZ volatile.Register32 // 0x10
|
||||||
|
_ uint32 // 0x14
|
||||||
|
TXFST volatile.Register32 // 0x18
|
||||||
|
_ uint32 // 0x1C
|
||||||
|
}
|
||||||
|
|
||||||
|
// usbOutEndpointRegs represents the registers for a single OUT endpoint at 0xB00 + ep*0x20.
|
||||||
|
type usbOutEndpointRegs struct {
|
||||||
|
CTL volatile.Register32 // 0x00
|
||||||
|
_ uint32 // 0x04
|
||||||
|
INT volatile.Register32 // 0x08
|
||||||
|
_ uint32 // 0x0C
|
||||||
|
TSIZ volatile.Register32 // 0x10
|
||||||
|
_ [3]uint32 // 0x14 - 0x1C
|
||||||
|
}
|
||||||
|
|
||||||
|
// usbPowerRegs represents the power and clock gating block at base+0xE00.
|
||||||
|
type usbPowerRegs struct {
|
||||||
|
PCGCCTL volatile.Register32 // 0xE00
|
||||||
|
}
|
||||||
|
|
||||||
|
// otgInEP returns the IN endpoint registers for physical endpoint ep.
|
||||||
|
func otgInEP(ep uint32) *usbInEndpointRegs {
|
||||||
|
return (*usbInEndpointRegs)(unsafe.Pointer(uintptr(unsafe.Pointer(stm32.OTG_FS_GLOBAL)) + 0x900 + uintptr(ep)*0x20))
|
||||||
|
}
|
||||||
|
|
||||||
|
// otgOutEP returns the OUT endpoint registers for physical endpoint ep.
|
||||||
|
func otgOutEP(ep uint32) *usbOutEndpointRegs {
|
||||||
|
return (*usbOutEndpointRegs)(unsafe.Pointer(uintptr(unsafe.Pointer(stm32.OTG_FS_GLOBAL)) + 0xB00 + uintptr(ep)*0x20))
|
||||||
|
}
|
||||||
|
|
||||||
|
// otgDFIFO returns a volatile pointer to the data FIFO for physical endpoint ep.
|
||||||
|
// DFIFO[ep] is located at 0x50001000 + ep*0x1000.
|
||||||
|
func otgDFIFO(ep uint32) *volatile.Register32 {
|
||||||
|
return (*volatile.Register32)(unsafe.Pointer(uintptr(unsafe.Pointer(stm32.OTG_FS_GLOBAL)) + 0x1000 + uintptr(ep)*0x1000))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DCFG bit positions and masks.
|
||||||
|
const (
|
||||||
|
dcfgDSPD = uint32(0x3) // FS PHY speed (bits [1:0] = 0b11)
|
||||||
|
dcfgDAD_Pos = uint32(4) // device address field start bit
|
||||||
|
dcfgDAD_Msk = uint32(0x7F << 4)
|
||||||
|
)
|
||||||
|
|
||||||
|
// DCTL bits.
|
||||||
|
const (
|
||||||
|
dctlSDIS = uint32(1 << 1) // soft disconnect
|
||||||
|
dctlSGINAK = uint32(1 << 7) // set global IN NAK
|
||||||
|
dctlCGINAK = uint32(1 << 8) // clear global IN NAK
|
||||||
|
dctlSGONAK = uint32(1 << 9) // set global OUT NAK
|
||||||
|
dctlCGONAK = uint32(1 << 10) // clear global OUT NAK
|
||||||
|
)
|
||||||
|
|
||||||
|
// DSTS bits.
|
||||||
|
const (
|
||||||
|
dstsSUSPSTS = uint32(1 << 0)
|
||||||
|
dstsENUMSPD_Pos = uint32(1)
|
||||||
|
dstsENUMSPD_Msk = uint32(0x3 << 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
// DIEPCTLn / DOEPCTLn bits (shared between IN and OUT endpoint control registers).
|
||||||
|
const (
|
||||||
|
depctlEPENA = uint32(1 << 31) // endpoint enable
|
||||||
|
depctlEPDIS = uint32(1 << 30) // endpoint disable
|
||||||
|
depctlSD0PID = uint32(1 << 28) // set DATA0 PID
|
||||||
|
depctlSNAK = uint32(1 << 27) // set NAK
|
||||||
|
depctlCNAK = uint32(1 << 26) // clear NAK
|
||||||
|
depctlSTALL = uint32(1 << 21) // STALL handshake
|
||||||
|
depctlETYP_Pos = uint32(18) // endpoint type field start
|
||||||
|
depctlUSBAEP = uint32(1 << 15) // USB active endpoint
|
||||||
|
depctlTXFNUM_Pos = uint32(22) // TX FIFO number field start (IN eps only)
|
||||||
|
depctlMPSIZ_Pos = uint32(0) // max packet size field start
|
||||||
|
)
|
||||||
|
|
||||||
|
// EP0 max packet size encoding in DIEPCTL0 / DOEPCTL0 bits [1:0].
|
||||||
|
const ep0Mps64 = uint32(0x0) // 64 bytes (FS default)
|
||||||
|
|
||||||
|
// DIEPINTn / DOEPINTn bits.
|
||||||
|
const (
|
||||||
|
depintXFRC = uint32(1 << 0) // transfer complete
|
||||||
|
depintSTUP = uint32(1 << 3) // SETUP phase done (DOEPINTn)
|
||||||
|
)
|
||||||
|
|
||||||
|
// GRXSTSP_Device PKTSTS field values.
|
||||||
|
const (
|
||||||
|
rxPktstsGNAK = uint32(0x1) // global OUT NAK
|
||||||
|
rxPktstsOUTData = uint32(0x2) // OUT data packet received
|
||||||
|
rxPktstsOUTDone = uint32(0x3) // OUT transfer complete
|
||||||
|
rxPktstsSetupDone = uint32(0x4) // SETUP transaction complete
|
||||||
|
rxPktstsSetupData = uint32(0x6) // SETUP data packet received (always 8 bytes)
|
||||||
|
)
|
||||||
|
|
||||||
|
// GINTSTS / GINTMSK bits (values taken from stm32f405 SVD constants).
|
||||||
|
const (
|
||||||
|
gintRXFLVL = uint32(stm32.USB_OTG_FS_GINTSTS_RXFLVL) // 0x10
|
||||||
|
gintUSBRST = uint32(stm32.USB_OTG_FS_GINTSTS_USBRST) // 0x1000
|
||||||
|
gintENUMDNE = uint32(stm32.USB_OTG_FS_GINTSTS_ENUMDNE) // 0x2000
|
||||||
|
gintUSBSUSP = uint32(stm32.USB_OTG_FS_GINTSTS_USBSUSP) // 0x800
|
||||||
|
gintWKUPINT = uint32(stm32.USB_OTG_FS_GINTSTS_WKUPINT) // 0x80000000
|
||||||
|
gintIEPINT = uint32(stm32.USB_OTG_FS_GINTSTS_IEPINT) // 0x40000
|
||||||
|
gintOEPINT = uint32(stm32.USB_OTG_FS_GINTSTS_OEPINT) // 0x80000
|
||||||
|
)
|
||||||
|
|
||||||
|
// GRSTCTL bits.
|
||||||
|
const (
|
||||||
|
grstCSRST = uint32(stm32.USB_OTG_FS_GRSTCTL_CSRST) // core soft reset
|
||||||
|
grstRXFFLSH = uint32(stm32.USB_OTG_FS_GRSTCTL_RXFFLSH) // RX FIFO flush
|
||||||
|
grstTXFFLSH = uint32(stm32.USB_OTG_FS_GRSTCTL_TXFFLSH) // TX FIFO flush
|
||||||
|
grstTXFNUM_Pos = uint32(stm32.USB_OTG_FS_GRSTCTL_TXFNUM_Pos)
|
||||||
|
grstAHBIDL = uint32(stm32.USB_OTG_FS_GRSTCTL_AHBIDL) // AHB master idle
|
||||||
|
)
|
||||||
|
|
||||||
|
// FIFO size layout in 32-bit words (total budget = 320 words).
|
||||||
|
const (
|
||||||
|
rxFIFODepth = uint32(128) // shared RX FIFO
|
||||||
|
ep0TxFIFODepth = uint32(16) // EP0 TX FIFO
|
||||||
|
ep1TxFIFODepth = uint32(64) // EP1 TX FIFO
|
||||||
|
ep2TxFIFODepth = uint32(64) // EP2 TX FIFO
|
||||||
|
ep3TxFIFODepth = uint32(48) // EP3 TX FIFO
|
||||||
|
|
||||||
|
ep0TxFIFOStart = rxFIFODepth
|
||||||
|
ep1TxFIFOStart = ep0TxFIFOStart + ep0TxFIFODepth
|
||||||
|
ep2TxFIFOStart = ep1TxFIFOStart + ep1TxFIFODepth
|
||||||
|
ep3TxFIFOStart = ep2TxFIFOStart + ep2TxFIFODepth
|
||||||
|
)
|
||||||
|
|
||||||
|
// Driver state.
|
||||||
|
var (
|
||||||
|
// sendOnEP0DATADONE tracks multi-chunk EP0 IN transfers.
|
||||||
|
sendOnEP0DATADONE struct {
|
||||||
|
ptr *byte
|
||||||
|
count int
|
||||||
|
offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
// usbSetupBuf holds the 8-byte SETUP packet from the RX FIFO.
|
||||||
|
usbSetupBuf [8]byte
|
||||||
|
|
||||||
|
// usbRxBufLen tracks the byte count of the most recently received OUT packet
|
||||||
|
// per physical endpoint (index 0–3).
|
||||||
|
usbRxBufLen [4]uint32
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configure initialises the OTG FS USB peripheral in device mode.
|
||||||
|
// The config parameter is unused (present for interface compatibility).
|
||||||
|
func (dev *USBDevice) Configure(config UARTConfig) {
|
||||||
|
if dev.initcomplete {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 1. Enable peripheral clocks ----------------------------------------
|
||||||
|
|
||||||
|
// GPIOA clock (PA11 = D-, PA12 = D+)
|
||||||
|
stm32.RCC.AHB1ENR.SetBits(stm32.RCC_AHB1ENR_GPIOAEN)
|
||||||
|
// OTG FS peripheral clock
|
||||||
|
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_OTGFSEN)
|
||||||
|
|
||||||
|
// ---- 2. Configure GPIO pins (PA11 D-, PA12 D+) as AF, very high speed ----
|
||||||
|
|
||||||
|
for _, pin := range [2]Pin{PA11, PA12} {
|
||||||
|
pos := uint8(pin%16) * 2
|
||||||
|
port := pin.getPort()
|
||||||
|
port.MODER.ReplaceBits(gpioModeAlternate, gpioModeMask, pos)
|
||||||
|
port.OSPEEDR.ReplaceBits(gpioOutputSpeedVeryHigh, gpioOutputSpeedMask, pos)
|
||||||
|
port.PUPDR.ReplaceBits(gpioPullFloating, gpioPullMask, pos)
|
||||||
|
// OTYPER remains 0 (push-pull)
|
||||||
|
pin.SetAltFunc(10) // AF10 = OTG FS on both F4 and F7
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 3. OTG core reset --------------------------------------------------
|
||||||
|
|
||||||
|
// Wait for AHB master idle before core reset.
|
||||||
|
for stm32.OTG_FS_GLOBAL.GRSTCTL.Get()&grstAHBIDL == 0 {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Core soft reset
|
||||||
|
stm32.OTG_FS_GLOBAL.GRSTCTL.SetBits(grstCSRST)
|
||||||
|
for stm32.OTG_FS_GLOBAL.GRSTCTL.HasBits(grstCSRST) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for AHB idle again after reset
|
||||||
|
for stm32.OTG_FS_GLOBAL.GRSTCTL.Get()&grstAHBIDL == 0 {
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 4. Force device mode, set turnaround time --------------------------
|
||||||
|
|
||||||
|
gusbcfg := stm32.OTG_FS_GLOBAL.GUSBCFG.Get()
|
||||||
|
gusbcfg &^= stm32.USB_OTG_FS_GUSBCFG_FHMOD |
|
||||||
|
stm32.USB_OTG_FS_GUSBCFG_FDMOD |
|
||||||
|
stm32.USB_OTG_FS_GUSBCFG_TRDT_Msk
|
||||||
|
gusbcfg |= stm32.USB_OTG_FS_GUSBCFG_FDMOD |
|
||||||
|
(9 << stm32.USB_OTG_FS_GUSBCFG_TRDT_Pos) // turnaround time = 9 for 216MHz HCLK
|
||||||
|
stm32.OTG_FS_GLOBAL.GUSBCFG.Set(gusbcfg)
|
||||||
|
|
||||||
|
// ---- 5. PHY / VBUS configuration (platform-specific) --------------------
|
||||||
|
|
||||||
|
initOTGFSPHY()
|
||||||
|
|
||||||
|
// ---- 6. Enable PHY clock, soft-disconnect before further init -----------
|
||||||
|
|
||||||
|
// Clear stop-clock / stop-phy-clock bits so the PHY clock runs.
|
||||||
|
// If a bootloader left these set, USB would hang silently.
|
||||||
|
otgPower.PCGCCTL.Set(0)
|
||||||
|
|
||||||
|
// Soft-disconnect now (after CSRST reset DCTL to its default connected state).
|
||||||
|
otgDevice.DCTL.SetBits(dctlSDIS)
|
||||||
|
|
||||||
|
// ---- 7. Configure data FIFOs --------------------------------------------
|
||||||
|
|
||||||
|
// RX FIFO (shared for all OUT + SETUP packets)
|
||||||
|
stm32.OTG_FS_GLOBAL.GRXFSIZ.Set(rxFIFODepth)
|
||||||
|
|
||||||
|
// EP0 TX FIFO: start = rxFIFODepth, depth = ep0TxFIFODepth
|
||||||
|
stm32.OTG_FS_GLOBAL.DIEPTXF0.Set(
|
||||||
|
(ep0TxFIFODepth << 16) | ep0TxFIFOStart,
|
||||||
|
)
|
||||||
|
|
||||||
|
// EP1–3 TX FIFOs
|
||||||
|
stm32.OTG_FS_GLOBAL.DIEPTXF1.Set(
|
||||||
|
(ep1TxFIFODepth << 16) | ep1TxFIFOStart,
|
||||||
|
)
|
||||||
|
stm32.OTG_FS_GLOBAL.DIEPTXF2.Set(
|
||||||
|
(ep2TxFIFODepth << 16) | ep2TxFIFOStart,
|
||||||
|
)
|
||||||
|
stm32.OTG_FS_GLOBAL.DIEPTXF3.Set(
|
||||||
|
(ep3TxFIFODepth << 16) | ep3TxFIFOStart,
|
||||||
|
)
|
||||||
|
|
||||||
|
// ---- 8. Flush FIFOs ----------------------------------------------------
|
||||||
|
|
||||||
|
flushRxFIFO()
|
||||||
|
flushTxFIFO(0x10) // flush all TX FIFOs (TXFNUM = 0x10 = all)
|
||||||
|
|
||||||
|
// ---- 9. Configure device: full-speed, no SOF output --------------------
|
||||||
|
|
||||||
|
otgDevice.DCFG.Set(dcfgDSPD) // FS PHY speed
|
||||||
|
|
||||||
|
// Clear any stale interrupts
|
||||||
|
stm32.OTG_FS_GLOBAL.GINTSTS.Set(0xFFFFFFFF)
|
||||||
|
|
||||||
|
// ---- 10. Enable interrupts ----------------------------------------------
|
||||||
|
|
||||||
|
stm32.OTG_FS_GLOBAL.GINTMSK.Set(
|
||||||
|
gintUSBRST | gintENUMDNE | gintRXFLVL | gintIEPINT | gintOEPINT |
|
||||||
|
gintUSBSUSP | gintWKUPINT,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enable device-level IN and OUT endpoint interrupt masks
|
||||||
|
otgDevice.DIEPMSK.Set(depintXFRC)
|
||||||
|
otgDevice.DOEPMSK.Set(depintXFRC | depintSTUP)
|
||||||
|
|
||||||
|
// Enable global interrupt
|
||||||
|
stm32.OTG_FS_GLOBAL.GAHBCFG.SetBits(stm32.USB_OTG_FS_GAHBCFG_GINT)
|
||||||
|
|
||||||
|
// ---- 11. Register and enable NVIC interrupt -----------------------------
|
||||||
|
|
||||||
|
intr := interrupt.New(stm32.IRQ_OTG_FS, handleUSBIRQ)
|
||||||
|
intr.SetPriority(0) // Highest priority
|
||||||
|
intr.Enable()
|
||||||
|
|
||||||
|
// ---- 12. Connect to host (clear soft-disconnect) -----------------------
|
||||||
|
|
||||||
|
otgDevice.DCTL.ClearBits(dctlSDIS)
|
||||||
|
|
||||||
|
dev.initcomplete = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleUSBIRQ is the OTG FS interrupt handler, dispatching on GINTSTS bits.
|
||||||
|
func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||||
|
status := stm32.OTG_FS_GLOBAL.GINTSTS.Get() &
|
||||||
|
stm32.OTG_FS_GLOBAL.GINTMSK.Get()
|
||||||
|
|
||||||
|
if status&gintUSBSUSP != 0 {
|
||||||
|
stm32.OTG_FS_GLOBAL.GINTSTS.Set(gintUSBSUSP)
|
||||||
|
// Stop PHY clock during suspend. Only STPPCLK (bit 0); do NOT set
|
||||||
|
// GATEHCLK (bit 1) — that gates the AHB bus, which prevents the ISR
|
||||||
|
// from reading GINTSTS when WKUPINT fires.
|
||||||
|
otgPower.PCGCCTL.SetBits(1) // STPPCLK
|
||||||
|
}
|
||||||
|
|
||||||
|
if status&gintWKUPINT != 0 {
|
||||||
|
stm32.OTG_FS_GLOBAL.GINTSTS.Set(gintWKUPINT)
|
||||||
|
// Restart clocks before any endpoint activity can resume.
|
||||||
|
otgPower.PCGCCTL.ClearBits(1 | 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if status&gintUSBRST != 0 {
|
||||||
|
stm32.OTG_FS_GLOBAL.GINTSTS.Set(gintUSBRST)
|
||||||
|
otgPower.PCGCCTL.ClearBits(1 | 2) // ensure clocks running after reset-from-suspend
|
||||||
|
handleUSBReset()
|
||||||
|
}
|
||||||
|
|
||||||
|
if status&gintRXFLVL != 0 {
|
||||||
|
// RXFLVL is level-triggered; drain entire FIFO in a loop.
|
||||||
|
stm32.OTG_FS_GLOBAL.GINTMSK.ClearBits(gintRXFLVL)
|
||||||
|
handleRxFIFO()
|
||||||
|
stm32.OTG_FS_GLOBAL.GINTMSK.SetBits(gintRXFLVL)
|
||||||
|
}
|
||||||
|
|
||||||
|
if status&gintENUMDNE != 0 {
|
||||||
|
stm32.OTG_FS_GLOBAL.GINTSTS.Set(gintENUMDNE)
|
||||||
|
handleEnumDone()
|
||||||
|
}
|
||||||
|
|
||||||
|
if status&gintIEPINT != 0 {
|
||||||
|
handleInEndpoints()
|
||||||
|
}
|
||||||
|
|
||||||
|
if status&gintOEPINT != 0 {
|
||||||
|
handleOutEndpoints()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleUSBReset is called on USB bus reset (USBRST interrupt).
|
||||||
|
func handleUSBReset() {
|
||||||
|
// Set NAK on all OUT endpoints.
|
||||||
|
for ep := uint32(0); ep < 4; ep++ {
|
||||||
|
otgOutEP(ep).CTL.SetBits(depctlSNAK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush RX and all TX FIFOs.
|
||||||
|
flushRxFIFO()
|
||||||
|
flushTxFIFO(0x10)
|
||||||
|
|
||||||
|
// Clear all endpoint interrupts.
|
||||||
|
otgDevice.DAINT.Set(0xFFFFFFFF)
|
||||||
|
otgDevice.DAINTMSK.Set(0)
|
||||||
|
|
||||||
|
// Enable EP0 IN and OUT interrupt sources.
|
||||||
|
otgDevice.DAINTMSK.Set((1 << 0) | (1 << 16)) // DIEP0 + DOEP0
|
||||||
|
|
||||||
|
// Re-arm EP0 OUT for up to 3 back-to-back SETUP packets.
|
||||||
|
armEP0Out()
|
||||||
|
|
||||||
|
// Signal upper layer: device is no longer configured.
|
||||||
|
usbConfiguration = 0
|
||||||
|
USBDev.InitEndpointComplete = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleEnumDone is called after USB enumeration speed is detected (ENUMDNE).
|
||||||
|
func handleEnumDone() {
|
||||||
|
// Activate EP0 (max packet 64, type control, TX FIFO 0).
|
||||||
|
ep0Ctl := ep0Mps64 | depctlUSBAEP | (0 << depctlETYP_Pos) // control type
|
||||||
|
otgInEP(0).CTL.SetBits(ep0Ctl)
|
||||||
|
otgOutEP(0).CTL.SetBits(ep0Mps64 | depctlUSBAEP)
|
||||||
|
|
||||||
|
// Clear global IN NAK so EP0 IN can send.
|
||||||
|
otgDevice.DCTL.SetBits(dctlCGINAK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleRxFIFO drains the RX FIFO completely, processing each pop via GRXSTSP.
|
||||||
|
// RXFLVL is level-triggered, so this must loop until the FIFO is empty.
|
||||||
|
func handleRxFIFO() {
|
||||||
|
for stm32.OTG_FS_GLOBAL.GINTSTS.HasBits(gintRXFLVL) {
|
||||||
|
status := stm32.OTG_FS_GLOBAL.GRXSTSP_Device.Get()
|
||||||
|
|
||||||
|
ep := status & stm32.USB_OTG_FS_GRXSTSP_Device_EPNUM_Msk
|
||||||
|
bcnt := (status & stm32.USB_OTG_FS_GRXSTSP_Device_BCNT_Msk) >>
|
||||||
|
stm32.USB_OTG_FS_GRXSTSP_Device_BCNT_Pos
|
||||||
|
pktsts := (status >> stm32.USB_OTG_FS_GRXSTSP_Device_PKTSTS_Pos) & 0xF
|
||||||
|
|
||||||
|
pep := ep // GRXSTSP.EPNUM is already a physical endpoint (0–3)
|
||||||
|
|
||||||
|
switch pktsts {
|
||||||
|
case rxPktstsSetupData:
|
||||||
|
// 8-byte SETUP packet: read exactly 2 words from DFIFO[0].
|
||||||
|
w0 := otgDFIFO(0).Get()
|
||||||
|
w1 := otgDFIFO(0).Get()
|
||||||
|
usbSetupBuf[0] = byte(w0)
|
||||||
|
usbSetupBuf[1] = byte(w0 >> 8)
|
||||||
|
usbSetupBuf[2] = byte(w0 >> 16)
|
||||||
|
usbSetupBuf[3] = byte(w0 >> 24)
|
||||||
|
usbSetupBuf[4] = byte(w1)
|
||||||
|
usbSetupBuf[5] = byte(w1 >> 8)
|
||||||
|
usbSetupBuf[6] = byte(w1 >> 16)
|
||||||
|
usbSetupBuf[7] = byte(w1 >> 24)
|
||||||
|
|
||||||
|
case rxPktstsSetupDone:
|
||||||
|
// SETUP transaction complete: process the buffered SETUP packet.
|
||||||
|
setup := usb.Setup{
|
||||||
|
BmRequestType: usbSetupBuf[0],
|
||||||
|
BRequest: usbSetupBuf[1],
|
||||||
|
WValueL: usbSetupBuf[2],
|
||||||
|
WValueH: usbSetupBuf[3],
|
||||||
|
WIndex: uint16(usbSetupBuf[4]) | (uint16(usbSetupBuf[5]) << 8),
|
||||||
|
WLength: uint16(usbSetupBuf[6]) | (uint16(usbSetupBuf[7]) << 8),
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := false
|
||||||
|
if setup.BmRequestType&usb.REQUEST_TYPE == usb.REQUEST_STANDARD {
|
||||||
|
ok = handleStandardSetup(setup)
|
||||||
|
} else {
|
||||||
|
if setup.WIndex < uint16(len(usbSetupHandler)) &&
|
||||||
|
usbSetupHandler[setup.WIndex] != nil {
|
||||||
|
ok = usbSetupHandler[setup.WIndex](setup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
// Stall EP0 IN and OUT on unrecognised requests.
|
||||||
|
otgInEP(0).CTL.SetBits(depctlSTALL)
|
||||||
|
otgOutEP(0).CTL.SetBits(depctlSTALL)
|
||||||
|
}
|
||||||
|
// Re-arm EP0 OUT for the next SETUP.
|
||||||
|
armEP0Out()
|
||||||
|
|
||||||
|
case rxPktstsOUTData:
|
||||||
|
// OUT data: read bcnt bytes from DFIFO[pep] into cache buffer.
|
||||||
|
if bcnt > 0 && pep < 4 {
|
||||||
|
readFIFO(pep, bcnt)
|
||||||
|
usbRxBufLen[pep] = bcnt
|
||||||
|
}
|
||||||
|
|
||||||
|
case rxPktstsOUTDone:
|
||||||
|
// OUT transfer complete: nothing to do here; handled in handleOutEndpoints.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readFIFO reads bcnt bytes from the shared RX FIFO (DFIFO 0) into udd_ep_out_cache_buffer[ep].
|
||||||
|
func readFIFO(ep, bcnt uint32) {
|
||||||
|
buf := udd_ep_out_cache_buffer[ep][:]
|
||||||
|
words := (bcnt + 3) / 4
|
||||||
|
for i := uint32(0); i < words; i++ {
|
||||||
|
w := otgDFIFO(0).Get() // Always read from FIFO 0
|
||||||
|
b := i * 4
|
||||||
|
buf[b] = byte(w)
|
||||||
|
if b+1 < bcnt {
|
||||||
|
buf[b+1] = byte(w >> 8)
|
||||||
|
}
|
||||||
|
if b+2 < bcnt {
|
||||||
|
buf[b+2] = byte(w >> 16)
|
||||||
|
}
|
||||||
|
if b+3 < bcnt {
|
||||||
|
buf[b+3] = byte(w >> 24)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleInEndpoints handles IEPINT: checks each active IN endpoint for XFRC.
|
||||||
|
func handleInEndpoints() {
|
||||||
|
daint := otgDevice.DAINT.Get() & 0x0000FFFF // lower 16 bits = IN EPs
|
||||||
|
daintmsk := otgDevice.DAINTMSK.Get() & 0x0000FFFF
|
||||||
|
active := daint & daintmsk
|
||||||
|
|
||||||
|
for ep := uint32(0); ep < 4; ep++ {
|
||||||
|
if active&(1<<ep) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
diep := otgInEP(ep)
|
||||||
|
diepint := diep.INT.Get()
|
||||||
|
diepintmsk := otgDevice.DIEPMSK.Get()
|
||||||
|
fired := diepint & diepintmsk
|
||||||
|
|
||||||
|
if fired&depintXFRC != 0 {
|
||||||
|
// Clear XFRC.
|
||||||
|
diep.INT.Set(depintXFRC)
|
||||||
|
|
||||||
|
if ep == 0 {
|
||||||
|
// EP0 IN transfer complete.
|
||||||
|
if sendOnEP0DATADONE.ptr != nil {
|
||||||
|
// More data to send.
|
||||||
|
ptr := sendOnEP0DATADONE.ptr
|
||||||
|
count := sendOnEP0DATADONE.count
|
||||||
|
if count > usb.EndpointPacketSize {
|
||||||
|
sendOnEP0DATADONE.offset += usb.EndpointPacketSize
|
||||||
|
sendOnEP0DATADONE.ptr = &udd_ep_control_cache_buffer[sendOnEP0DATADONE.offset]
|
||||||
|
count = usb.EndpointPacketSize
|
||||||
|
}
|
||||||
|
sendOnEP0DATADONE.count -= count
|
||||||
|
sendViaEPIn(0, ptr, count)
|
||||||
|
if sendOnEP0DATADONE.count == 0 {
|
||||||
|
sendOnEP0DATADONE.ptr = nil
|
||||||
|
sendOnEP0DATADONE.offset = 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// All EP0 IN data sent; arm EP0 OUT for the status ZLP from host.
|
||||||
|
armEP0Out()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Non-EP0 IN: find the virtual endpoint(s) mapped to this physical EP
|
||||||
|
// and call the registered TX handler. Multiple virtual EPs may share
|
||||||
|
// a physical EP (e.g., HID_IN=4 and CDC_IN=3 both → physical 1 or 3).
|
||||||
|
for vep := uint32(0); vep < NumberOfUSBEndpoints; vep++ {
|
||||||
|
if vep == ep && usbTxHandler[vep] != nil {
|
||||||
|
usbTxHandler[vep]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleOutEndpoints handles OEPINT: checks each active OUT endpoint for STUP / XFRC.
|
||||||
|
func handleOutEndpoints() {
|
||||||
|
daint := otgDevice.DAINT.Get() >> 16 // upper 16 bits = OUT EPs
|
||||||
|
daintmsk := otgDevice.DAINTMSK.Get() >> 16
|
||||||
|
active := daint & daintmsk
|
||||||
|
|
||||||
|
for ep := uint32(0); ep < 4; ep++ {
|
||||||
|
if active&(1<<ep) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
doep := otgOutEP(ep)
|
||||||
|
doepint := doep.INT.Get()
|
||||||
|
doepintmsk := otgDevice.DOEPMSK.Get()
|
||||||
|
fired := doepint & doepintmsk
|
||||||
|
|
||||||
|
if fired&depintSTUP != 0 {
|
||||||
|
// EP0 SETUP phase done (already processed in handleRxFIFO).
|
||||||
|
doep.INT.Set(depintSTUP)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fired&depintXFRC != 0 {
|
||||||
|
doep.INT.Set(depintXFRC)
|
||||||
|
if ep > 0 {
|
||||||
|
buf := handleEndpointRx(ep)
|
||||||
|
// Find the virtual endpoint(s) mapped to this physical EP and call the RX handler.
|
||||||
|
for vep := uint32(0); vep < NumberOfUSBEndpoints; vep++ {
|
||||||
|
if vep == ep && usbRxHandler[vep] != nil {
|
||||||
|
if usbRxHandler[vep](buf) {
|
||||||
|
AckUsbOutTransfer(ep)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initEndpoint configures a USB endpoint for the given type and direction.
|
||||||
|
// MPS is hardcoded to 64 bytes; the caller (usb.go) does not pass a descriptor.
|
||||||
|
func initEndpoint(ep, config uint32) {
|
||||||
|
pep := ep
|
||||||
|
if pep == 0 {
|
||||||
|
return // EP0 is always active; configured in handleEnumDone
|
||||||
|
}
|
||||||
|
|
||||||
|
txFIFONum := pep // TX FIFO number matches physical EP
|
||||||
|
|
||||||
|
switch config {
|
||||||
|
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn:
|
||||||
|
ctl := (64 << depctlMPSIZ_Pos) | depctlUSBAEP |
|
||||||
|
(txFIFONum << depctlTXFNUM_Pos) |
|
||||||
|
(3 << depctlETYP_Pos) | // interrupt type
|
||||||
|
depctlSD0PID
|
||||||
|
otgInEP(pep).CTL.Set(ctl)
|
||||||
|
otgDevice.DAINTMSK.SetBits(1 << pep)
|
||||||
|
|
||||||
|
case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn:
|
||||||
|
ctl := (64 << depctlMPSIZ_Pos) | depctlUSBAEP |
|
||||||
|
(txFIFONum << depctlTXFNUM_Pos) |
|
||||||
|
(2 << depctlETYP_Pos) | // bulk type
|
||||||
|
depctlSD0PID
|
||||||
|
otgInEP(pep).CTL.Set(ctl)
|
||||||
|
otgDevice.DAINTMSK.SetBits(1 << pep)
|
||||||
|
|
||||||
|
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut:
|
||||||
|
ctl := uint32(64) | depctlUSBAEP | depctlSD0PID |
|
||||||
|
(3 << depctlETYP_Pos) // interrupt type
|
||||||
|
otgOutEP(pep).CTL.Set(ctl)
|
||||||
|
otgOutEP(pep).TSIZ.Set((1 << 19) | 64)
|
||||||
|
otgOutEP(pep).CTL.SetBits(depctlEPENA | depctlCNAK)
|
||||||
|
otgDevice.DAINTMSK.SetBits(1 << (pep + 16))
|
||||||
|
|
||||||
|
case usb.ENDPOINT_TYPE_BULK | usb.EndpointOut:
|
||||||
|
ctl := uint32(64) | depctlUSBAEP | depctlSD0PID |
|
||||||
|
(2 << depctlETYP_Pos) // bulk type
|
||||||
|
otgOutEP(pep).CTL.Set(ctl)
|
||||||
|
otgOutEP(pep).TSIZ.Set((1 << 19) | 64)
|
||||||
|
otgOutEP(pep).CTL.SetBits(depctlEPENA | depctlCNAK)
|
||||||
|
otgDevice.DAINTMSK.SetBits(1 << (pep + 16))
|
||||||
|
|
||||||
|
case usb.ENDPOINT_TYPE_CONTROL:
|
||||||
|
// EP0 activated in handleEnumDone.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendUSBInPacket sends data on a USB IN endpoint (interrupt or bulk).
|
||||||
|
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||||
|
sendUSBPacket(ep, data)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendUSBPacket copies data into the endpoint cache buffer then initiates the transfer.
|
||||||
|
//
|
||||||
|
//go:noinline
|
||||||
|
func sendUSBPacket(ep uint32, data []byte) {
|
||||||
|
count := len(data)
|
||||||
|
var buf []byte
|
||||||
|
if ep == 0 {
|
||||||
|
buf = udd_ep_control_cache_buffer[:]
|
||||||
|
if count > usb.EndpointPacketSize {
|
||||||
|
// Large response: queue continuation via sendOnEP0DATADONE.
|
||||||
|
sendOnEP0DATADONE.offset = usb.EndpointPacketSize
|
||||||
|
sendOnEP0DATADONE.ptr = &udd_ep_control_cache_buffer[usb.EndpointPacketSize]
|
||||||
|
sendOnEP0DATADONE.count = count - usb.EndpointPacketSize
|
||||||
|
count = usb.EndpointPacketSize
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pep := ep
|
||||||
|
buf = udd_ep_in_cache_buffer[pep][:]
|
||||||
|
}
|
||||||
|
copy(buf[:len(data)], data)
|
||||||
|
sendViaEPIn(ep, &buf[0], count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendViaEPIn arms the IN endpoint and writes count bytes from ptr into the TX FIFO.
|
||||||
|
func sendViaEPIn(ep uint32, ptr *byte, count int) {
|
||||||
|
pep := ep
|
||||||
|
diep := otgInEP(pep)
|
||||||
|
|
||||||
|
// Verify TX FIFO has enough space before writing.
|
||||||
|
// DTXFSTS[15:0] = INEPTFSAV: available words. Stall if insufficient.
|
||||||
|
if count > 0 {
|
||||||
|
need := uint32((count + 3) / 4)
|
||||||
|
avail := diep.TXFST.Get() & 0xFFFF
|
||||||
|
if avail < need {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Program transfer size: 1 packet, count bytes.
|
||||||
|
diep.TSIZ.Set(
|
||||||
|
uint32(count) | (1 << 19), // XFRSIZ = count, PKTCNT = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enable endpoint and clear NAK (starts transfer).
|
||||||
|
diep.CTL.SetBits(depctlEPENA | depctlCNAK)
|
||||||
|
|
||||||
|
// Write bytes to FIFO in 32-bit words (last word padded if needed).
|
||||||
|
fifo := otgDFIFO(pep)
|
||||||
|
data := unsafe.Slice(ptr, count)
|
||||||
|
words := (count + 3) / 4
|
||||||
|
for i := 0; i < words; i++ {
|
||||||
|
b := i * 4
|
||||||
|
var w uint32
|
||||||
|
w = uint32(data[b])
|
||||||
|
if b+1 < count {
|
||||||
|
w |= uint32(data[b+1]) << 8
|
||||||
|
}
|
||||||
|
if b+2 < count {
|
||||||
|
w |= uint32(data[b+2]) << 16
|
||||||
|
}
|
||||||
|
if b+3 < count {
|
||||||
|
w |= uint32(data[b+3]) << 24
|
||||||
|
}
|
||||||
|
fifo.Set(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendZlp sends a zero-length packet on EP0 IN (status stage for OUT control transfers).
|
||||||
|
func SendZlp() {
|
||||||
|
// PKTCNT=1, XFRSIZ=0
|
||||||
|
otgInEP(0).TSIZ.Set(1 << 19)
|
||||||
|
otgInEP(0).CTL.SetBits(depctlEPENA | depctlCNAK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleEndpointRx returns the bytes received on the given physical endpoint.
|
||||||
|
func handleEndpointRx(ep uint32) []byte {
|
||||||
|
pep := ep
|
||||||
|
return udd_ep_out_cache_buffer[pep][:usbRxBufLen[pep]]
|
||||||
|
}
|
||||||
|
|
||||||
|
// AckUsbOutTransfer re-arms the OUT endpoint to receive the next packet.
|
||||||
|
func AckUsbOutTransfer(ep uint32) {
|
||||||
|
pep := ep
|
||||||
|
usbRxBufLen[pep] = 0
|
||||||
|
doep := otgOutEP(pep)
|
||||||
|
doep.TSIZ.Set(
|
||||||
|
(1 << 19) | 64, // PKTCNT=1, XFRSIZ=64
|
||||||
|
)
|
||||||
|
doep.CTL.SetBits(depctlEPENA | depctlCNAK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleUSBSetAddress applies the new device address from a SET_ADDRESS request
|
||||||
|
// and sends the status ZLP. The address is written to DCFG before the ZLP is
|
||||||
|
// enqueued: the OTG FS core has already committed the current IN token to
|
||||||
|
// address 0, so the ZLP goes out at the old address while the new address is
|
||||||
|
// already in DCFG and ready for the host's next transaction.
|
||||||
|
func handleUSBSetAddress(setup usb.Setup) bool {
|
||||||
|
addr := uint8(setup.WValueL) & 0x7F
|
||||||
|
|
||||||
|
dcfg := otgDevice.DCFG.Get()
|
||||||
|
dcfg &^= dcfgDAD_Msk
|
||||||
|
dcfg |= uint32(addr) << dcfgDAD_Pos
|
||||||
|
otgDevice.DCFG.Set(dcfg)
|
||||||
|
|
||||||
|
SendZlp()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReceiveUSBControlPacket synchronously receives a CDC control OUT packet on EP0.
|
||||||
|
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||||
|
var b [cdcLineInfoSize]byte
|
||||||
|
|
||||||
|
// Arm EP0 OUT for up to 64 bytes.
|
||||||
|
armEP0Out()
|
||||||
|
|
||||||
|
// Busy-wait for data to arrive. We call handleRxFIFO() manually to drain
|
||||||
|
// the shared RX FIFO because we are currently in an interrupt context
|
||||||
|
// (this is called from the setup handler) and the hardware-triggered
|
||||||
|
// handleRxFIFO loop is blocked waiting for us to return.
|
||||||
|
const timeout = 300000
|
||||||
|
for i := 0; i < timeout; i++ {
|
||||||
|
if stm32.OTG_FS_GLOBAL.GINTSTS.HasBits(gintRXFLVL) {
|
||||||
|
handleRxFIFO()
|
||||||
|
}
|
||||||
|
if usbRxBufLen[0] > 0 {
|
||||||
|
n := usbRxBufLen[0]
|
||||||
|
if n > cdcLineInfoSize {
|
||||||
|
n = cdcLineInfoSize
|
||||||
|
}
|
||||||
|
copy(b[:n], udd_ep_out_cache_buffer[0][:n])
|
||||||
|
usbRxBufLen[0] = 0
|
||||||
|
SendZlp()
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b, ErrUSBReadTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStallEPIn stalls an IN endpoint.
|
||||||
|
func (dev *USBDevice) SetStallEPIn(ep uint32) {
|
||||||
|
pep := ep
|
||||||
|
otgInEP(pep).CTL.SetBits(depctlSTALL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearStallEPIn clears the stall condition on an IN endpoint.
|
||||||
|
func (dev *USBDevice) ClearStallEPIn(ep uint32) {
|
||||||
|
pep := ep
|
||||||
|
// Clear STALL and reset DATA0 PID.
|
||||||
|
ctl := &otgInEP(pep).CTL
|
||||||
|
ctl.ClearBits(depctlSTALL)
|
||||||
|
ctl.SetBits(depctlSD0PID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStallEPOut stalls an OUT endpoint.
|
||||||
|
func (dev *USBDevice) SetStallEPOut(ep uint32) {
|
||||||
|
pep := ep
|
||||||
|
otgOutEP(pep).CTL.SetBits(depctlSTALL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearStallEPOut clears the stall condition on an OUT endpoint.
|
||||||
|
func (dev *USBDevice) ClearStallEPOut(ep uint32) {
|
||||||
|
pep := ep
|
||||||
|
ctl := &otgOutEP(pep).CTL
|
||||||
|
ctl.ClearBits(depctlSTALL)
|
||||||
|
ctl.SetBits(depctlSD0PID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// armEP0Out re-arms EP0 OUT to receive the next SETUP or status ZLP from the host.
|
||||||
|
func armEP0Out() {
|
||||||
|
// STUPCNT=3 (bits[30:29]=11): accept up to 3 back-to-back SETUPs.
|
||||||
|
// PKTCNT=1 (bit[19]): one packet.
|
||||||
|
// XFRSIZ=64 (bits[6:0]): max 64 bytes.
|
||||||
|
otgOutEP(0).TSIZ.Set((3 << 29) | (1 << 19) | 64)
|
||||||
|
otgOutEP(0).CTL.SetBits(depctlEPENA | depctlCNAK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// flushTxFIFO flushes the selected TX FIFO(s).
|
||||||
|
// txfnum: 0–3 for a specific FIFO, 0x10 to flush all TX FIFOs.
|
||||||
|
func flushTxFIFO(txfnum uint32) {
|
||||||
|
stm32.OTG_FS_GLOBAL.GRSTCTL.Set(
|
||||||
|
grstTXFFLSH | (txfnum << grstTXFNUM_Pos),
|
||||||
|
)
|
||||||
|
for stm32.OTG_FS_GLOBAL.GRSTCTL.HasBits(grstTXFFLSH) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flushRxFIFO flushes the shared RX FIFO.
|
||||||
|
func flushRxFIFO() {
|
||||||
|
stm32.OTG_FS_GLOBAL.GRSTCTL.Set(grstRXFFLSH)
|
||||||
|
for stm32.OTG_FS_GLOBAL.GRSTCTL.HasBits(grstRXFFLSH) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build stm32 && !(stm32f103 || stm32l0x1 || stm32g0)
|
//go:build stm32 && !(stm32f103 || stm32l0x1 || stm32g0 || stm32u0)
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build stm32 && !stm32f7x2 && !stm32l5x2 && !stm32g0 && !stm32u5
|
//go:build stm32 && !stm32f7x2 && !stm32l5x2 && !stm32g0 && !stm32u5 && !stm32u0
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
//go:build stm32f4 && !(stm32f429 || stm32f427 || stm32f411 || stm32f407 || stm32f405 || stm32f401)
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import "device/stm32"
|
||||||
|
|
||||||
|
// initOTGFSPHY enables the STM32F4 OTG FS PHY and bypasses VBUS sensing.
|
||||||
|
// GCCFG.PWRDWN deactivates the PHY power-down; NOVBUSSENS skips the VBUS pin
|
||||||
|
// check so boards without PA9 connected to VBUS still enumerate.
|
||||||
|
func initOTGFSPHY() {
|
||||||
|
stm32.OTG_FS_GLOBAL.GCCFG.Set(stm32.USB_OTG_FS_GCCFG_PWRDWN)
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build stm32f4 && (stm32f429 || stm32f427 || stm32f411 || stm32f407 || stm32f405 || stm32f401)
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import "device/stm32"
|
||||||
|
|
||||||
|
// initOTGFSPHY enables the STM32F4 OTG FS PHY and bypasses VBUS sensing.
|
||||||
|
// GCCFG.PWRDWN deactivates the PHY power-down; NOVBUSSENS skips the VBUS pin
|
||||||
|
// check so boards without PA9 connected to VBUS still enumerate.
|
||||||
|
func initOTGFSPHY() {
|
||||||
|
stm32.OTG_FS_GLOBAL.GCCFG.Set(
|
||||||
|
stm32.USB_OTG_FS_GCCFG_PWRDWN | // enable FS PHY
|
||||||
|
stm32.USB_OTG_FS_GCCFG_NOVBUSSENS, // bypass VBUS sensing
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
//go:build stm32f7
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
// eraseBlockSize returns the smallest erasable unit for the STM32F7 internal
|
||||||
|
// flash. The first sectors are 32 KB; return that as the nominal page size.
|
||||||
|
// Flash write/erase via machine.Flash is not implemented for STM32F7; this
|
||||||
|
// stub satisfies the flash.go interface so that BlockDevice (needed by MSC)
|
||||||
|
// compiles on this target.
|
||||||
|
func eraseBlockSize() int64 { return 32768 }
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
//go:build stm32f7
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/arm"
|
||||||
|
"device/stm32"
|
||||||
|
)
|
||||||
|
|
||||||
|
// initOTGFSPHY enables the STM32F7 OTG FS PHY and overrides B-session VBUS
|
||||||
|
// detection via GOTGCTL so boards without VBUS sensing still enumerate.
|
||||||
|
func initOTGFSPHY() {
|
||||||
|
// Enable USB voltage regulator (specific to F72x/F73x).
|
||||||
|
// Bit 14 in PWR_CR2 is USBREGEN.
|
||||||
|
stm32.PWR.CR2.SetBits(1 << 14)
|
||||||
|
|
||||||
|
// Stabilization delay for the regulator (~100us is plenty).
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
arm.Asm("nop")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable FS PHY.
|
||||||
|
stm32.OTG_FS_GLOBAL.GCCFG.SetBits(stm32.USB_OTG_FS_GCCFG_PWRDWN)
|
||||||
|
|
||||||
|
// Disable hardware VBUS detection (F7 uses VBDEN, opposite polarity to F4's NOVBUSSENS).
|
||||||
|
// Clearing this prevents the peripheral from gating enumeration on PA9 VBUS level.
|
||||||
|
stm32.OTG_FS_GLOBAL.GCCFG.ClearBits(stm32.USB_OTG_FS_GCCFG_VBDEN)
|
||||||
|
|
||||||
|
// Override B-session valid so GOTGCTL-based detection reports device connected.
|
||||||
|
stm32.OTG_FS_GLOBAL.GOTGCTL.SetBits(
|
||||||
|
stm32.USB_OTG_FS_GOTGCTL_BVALOEN | // enable B-valid override
|
||||||
|
stm32.USB_OTG_FS_GOTGCTL_BVALOVAL, // set B-valid = 1
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,351 @@
|
|||||||
|
//go:build stm32u031
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/stm32"
|
||||||
|
"runtime/interrupt"
|
||||||
|
"runtime/volatile"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var deviceIDAddr = []uintptr{0x1FFF3E50, 0x1FFF3E54, 0x1FFF3E58}
|
||||||
|
|
||||||
|
// Pin constants for all stm32u0 package sizes
|
||||||
|
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.IOPENR.SetBits(stm32.RCC_IOPENR_GPIOAEN)
|
||||||
|
case 1:
|
||||||
|
stm32.RCC.IOPENR.SetBits(stm32.RCC_IOPENR_GPIOBEN)
|
||||||
|
case 2:
|
||||||
|
stm32.RCC.IOPENR.SetBits(stm32.RCC_IOPENR_GPIOCEN)
|
||||||
|
case 3:
|
||||||
|
stm32.RCC.IOPENR.SetBits(stm32.RCC_IOPENR_GPIODEN)
|
||||||
|
case 4:
|
||||||
|
stm32.RCC.IOPENR.SetBits(stm32.RCC_IOPENR_GPIOEEN)
|
||||||
|
case 5:
|
||||||
|
stm32.RCC.IOPENR.SetBits(stm32.RCC_IOPENR_GPIOFEN)
|
||||||
|
default:
|
||||||
|
panic("machine: unknown port")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func enableAltFuncClock(bus unsafe.Pointer) {
|
||||||
|
switch bus {
|
||||||
|
case unsafe.Pointer(stm32.I2C1):
|
||||||
|
stm32.RCC.APBENR1.SetBits(stm32.RCC_APBENR1_I2C1EN)
|
||||||
|
case unsafe.Pointer(stm32.I2C2):
|
||||||
|
stm32.RCC.APBENR1.SetBits(stm32.RCC_APBENR1_I2C2EN)
|
||||||
|
case unsafe.Pointer(stm32.I2C3):
|
||||||
|
stm32.RCC.APBENR1.SetBits(stm32.RCC_APBENR1_I2C3EN)
|
||||||
|
case unsafe.Pointer(stm32.USART1):
|
||||||
|
stm32.RCC.APBENR2.SetBits(stm32.RCC_APBENR2_USART1EN)
|
||||||
|
case unsafe.Pointer(stm32.USART2):
|
||||||
|
stm32.RCC.APBENR1.SetBits(stm32.RCC_APBENR1_USART2EN)
|
||||||
|
case unsafe.Pointer(stm32.USART3):
|
||||||
|
stm32.RCC.APBENR1.SetBits(stm32.RCC_APBENR1_USART3EN)
|
||||||
|
case unsafe.Pointer(stm32.USART4):
|
||||||
|
stm32.RCC.APBENR1.SetBits(stm32.RCC_APBENR1_USART4EN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// TODO: implement output channels
|
||||||
|
|
||||||
|
TIM1 = TIM{
|
||||||
|
EnableRegister: &stm32.RCC.APBENR2,
|
||||||
|
EnableFlag: stm32.RCC_APBENR2_TIM1EN,
|
||||||
|
Device: stm32.TIM1,
|
||||||
|
Channels: [4]TimerChannel{
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
},
|
||||||
|
busFreq: 4e6,
|
||||||
|
}
|
||||||
|
|
||||||
|
TIM2 = TIM{
|
||||||
|
EnableRegister: &stm32.RCC.APBENR1,
|
||||||
|
EnableFlag: stm32.RCC_APBENR1_TIM2EN,
|
||||||
|
Device: stm32.TIM2,
|
||||||
|
Channels: [4]TimerChannel{
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
},
|
||||||
|
busFreq: 4e6,
|
||||||
|
}
|
||||||
|
|
||||||
|
TIM3 = TIM{
|
||||||
|
EnableRegister: &stm32.RCC.APBENR1,
|
||||||
|
EnableFlag: stm32.RCC_APBENR1_TIM3EN,
|
||||||
|
Device: stm32.TIM3,
|
||||||
|
Channels: [4]TimerChannel{
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
},
|
||||||
|
busFreq: 4e6,
|
||||||
|
}
|
||||||
|
|
||||||
|
TIM6 = TIM{
|
||||||
|
EnableRegister: &stm32.RCC.APBENR1,
|
||||||
|
EnableFlag: stm32.RCC_APBENR1_TIM6EN,
|
||||||
|
Device: stm32.TIM6,
|
||||||
|
Channels: [4]TimerChannel{
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
},
|
||||||
|
busFreq: 4e6,
|
||||||
|
}
|
||||||
|
|
||||||
|
TIM7 = TIM{
|
||||||
|
EnableRegister: &stm32.RCC.APBENR1,
|
||||||
|
EnableFlag: stm32.RCC_APBENR1_TIM7EN,
|
||||||
|
Device: stm32.TIM7,
|
||||||
|
Channels: [4]TimerChannel{
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
},
|
||||||
|
busFreq: 4e6,
|
||||||
|
}
|
||||||
|
|
||||||
|
TIM15 = TIM{
|
||||||
|
EnableRegister: &stm32.RCC.APBENR2,
|
||||||
|
EnableFlag: stm32.RCC_APBENR2_TIM15EN,
|
||||||
|
Device: stm32.TIM15,
|
||||||
|
Channels: [4]TimerChannel{
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
},
|
||||||
|
busFreq: 4e6,
|
||||||
|
}
|
||||||
|
|
||||||
|
TIM16 = TIM{
|
||||||
|
EnableRegister: &stm32.RCC.APBENR2,
|
||||||
|
EnableFlag: stm32.RCC_APBENR2_TIM16EN,
|
||||||
|
Device: stm32.TIM16,
|
||||||
|
Channels: [4]TimerChannel{
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
TimerChannel{Pins: []PinFunction{}},
|
||||||
|
},
|
||||||
|
busFreq: 4e6,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
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, TIM3.handleUPInterrupt)
|
||||||
|
case &TIM6:
|
||||||
|
return interrupt.New(stm32.IRQ_TIM6_DAC_LPTIM1, TIM6.handleUPInterrupt)
|
||||||
|
case &TIM7:
|
||||||
|
return interrupt.New(stm32.IRQ_TIM7_LPTIM2, TIM7.handleUPInterrupt)
|
||||||
|
case &TIM15:
|
||||||
|
return interrupt.New(stm32.IRQ_TIM15_LPTIM3, TIM15.handleUPInterrupt)
|
||||||
|
case &TIM16:
|
||||||
|
return interrupt.New(stm32.IRQ_TIM16, TIM16.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, TIM3.handleOCInterrupt)
|
||||||
|
case &TIM6:
|
||||||
|
return interrupt.New(stm32.IRQ_TIM6_DAC_LPTIM1, TIM6.handleOCInterrupt)
|
||||||
|
case &TIM7:
|
||||||
|
return interrupt.New(stm32.IRQ_TIM7_LPTIM2, TIM7.handleOCInterrupt)
|
||||||
|
case &TIM15:
|
||||||
|
return interrupt.New(stm32.IRQ_TIM15_LPTIM3, TIM15.handleOCInterrupt)
|
||||||
|
case &TIM16:
|
||||||
|
return interrupt.New(stm32.IRQ_TIM16, TIM16.handleOCInterrupt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return interrupt.Interrupt{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TIM) enableMainOutput() {
|
||||||
|
t.Device.BDTR.SetBits(stm32.TIM_BDTR_MOE)
|
||||||
|
}
|
||||||
|
|
||||||
|
type arrtype = uint16
|
||||||
|
type psctype = uint16
|
||||||
|
type arrRegType = volatile.Register16
|
||||||
|
|
||||||
|
const (
|
||||||
|
ARR_MAX = 0x10000
|
||||||
|
PSC_MAX = 0x10000
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UART_TX_PIN = NoPin
|
||||||
|
UART_RX_PIN = NoPin
|
||||||
|
)
|
||||||
|
|
||||||
|
func (uart *UART) configurePins(config UARTConfig) {
|
||||||
|
panic("unimplemented: UART")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 {
|
||||||
|
panic("unimplemented: UART")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uart *UART) setRegisters() uint32 {
|
||||||
|
panic("unimplemented: UART")
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
I2C0_SCL_PIN = NoPin
|
||||||
|
I2C0_SDA_PIN = NoPin
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i2c I2C) getFreqRange(br uint32) uint32 {
|
||||||
|
panic("unimplemented: I2C")
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
//go:build !baremetal || atmega || attiny85 || esp32 || esp32c3 || esp32s3 || fe310 || k210 || nrf || (nxp && !mk66f18) || rp2040 || rp2350 || sam || (stm32 && !stm32f7x2 && !stm32l5x2)
|
//go:build !baremetal || atmega || attiny85 || esp32 || esp32c3 || esp32s3 || fe310 || k210 || nrf || (nxp && !mk66f18) || rp2040 || rp2350 || sam || (stm32 && !stm32f7x2 && !stm32l5x2 && !stm32u0)
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build atmega || attiny85 || fe310 || k210 || (nxp && !mk66f18) || (stm32 && !stm32f7x2 && !stm32l5x2)
|
//go:build atmega || attiny85 || fe310 || k210 || (nxp && !mk66f18) || (stm32 && !stm32f7x2 && !stm32l5x2 && !stm32u0)
|
||||||
|
|
||||||
// This file implements the SPI Tx function for targets that don't have a custom
|
// This file implements the SPI Tx function for targets that don't have a custom
|
||||||
// (faster) implementation for it.
|
// (faster) implementation for it.
|
||||||
|
|||||||
+8
-2
@@ -1,4 +1,4 @@
|
|||||||
//go:build sam || nrf52840 || rp2040 || rp2350
|
//go:build sam || nrf52840 || rp2040 || rp2350 || stm32f4 || stm32f7
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
@@ -184,7 +184,9 @@ func sendDescriptor(setup usb.Setup) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
case descriptor.TypeDeviceQualifier:
|
case descriptor.TypeDeviceQualifier:
|
||||||
// skip
|
// Full-speed-only device: STALL to signal no high-speed capability (USB 2.0 §9.6.2).
|
||||||
|
USBDev.SetStallEPIn(0)
|
||||||
|
return
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,6 +371,10 @@ func EnableCDC(txHandler func(), rxHandler func([]byte), setupHandler func(usb.S
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PhysicalEndpoint maps a virtual endpoint index to the physical endpoint number
|
||||||
|
// used by the hardware. This is an identity mapping on all currently supported platforms.
|
||||||
|
func PhysicalEndpoint(ep uint32) uint32 { return ep }
|
||||||
|
|
||||||
func ConfigureUSBEndpoint(desc descriptor.Descriptor, epSettings []usb.EndpointConfig, setup []usb.SetupConfig) {
|
func ConfigureUSBEndpoint(desc descriptor.Descriptor, epSettings []usb.EndpointConfig, setup []usb.SetupConfig) {
|
||||||
usbDescriptor = desc
|
usbDescriptor = desc
|
||||||
|
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ func TestRing512_PutOversize(t *testing.T) {
|
|||||||
|
|
||||||
func TestRing512_MultiplePutPeekDiscard(t *testing.T) {
|
func TestRing512_MultiplePutPeekDiscard(t *testing.T) {
|
||||||
var r ring512
|
var r ring512
|
||||||
for i := 0; i < 2000; i++ {
|
for i := range 2000 {
|
||||||
msg := []byte(fmt.Sprintf("msg%04d", i))
|
msg := []byte(fmt.Sprintf("msg%04d", i))
|
||||||
if !r.Put(msg) {
|
if !r.Put(msg) {
|
||||||
t.Fatalf("Put failed at iteration %d, Free=%d, Used=%d", i, r.Free(), r.Used())
|
t.Fatalf("Put failed at iteration %d, Free=%d, Used=%d", i, r.Free(), r.Used())
|
||||||
@@ -290,7 +290,7 @@ func TestRing512_HeadTailOverflow(t *testing.T) {
|
|||||||
t.Fatalf("Used=%d Free=%d, want 0/512", r.Used(), r.Free())
|
t.Fatalf("Used=%d Free=%d, want 0/512", r.Used(), r.Free())
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 300; i++ {
|
for i := range 300 {
|
||||||
data := []byte{byte(i), byte(i + 1), byte(i + 2)}
|
data := []byte{byte(i), byte(i + 1), byte(i + 2)}
|
||||||
if !r.Put(data) {
|
if !r.Put(data) {
|
||||||
t.Fatalf("Put failed at iter %d (head=%d tail=%d)", i, r.head.Load(), r.tail.Load())
|
t.Fatalf("Put failed at iter %d (head=%d tail=%d)", i, r.head.Load(), r.tail.Load())
|
||||||
@@ -370,7 +370,7 @@ func TestRing512_PeekTotalEqualsUsed(t *testing.T) {
|
|||||||
// --- Concurrent SPSC Test ---
|
// --- Concurrent SPSC Test ---
|
||||||
|
|
||||||
func TestRing512_SPSC(t *testing.T) {
|
func TestRing512_SPSC(t *testing.T) {
|
||||||
for trial := 0; trial < 20; trial++ {
|
for trial := range 20 {
|
||||||
var r ring512
|
var r ring512
|
||||||
const totalBytes = 1 << 18
|
const totalBytes = 1 << 18
|
||||||
produced := make([]byte, totalBytes)
|
produced := make([]byte, totalBytes)
|
||||||
@@ -431,7 +431,7 @@ func TestRing512_SPSCSmallChunks(t *testing.T) {
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
for i := 0; i < totalBytes; i++ {
|
for i := range totalBytes {
|
||||||
for !r.Put([]byte{byte(i)}) {
|
for !r.Put([]byte{byte(i)}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -494,10 +494,7 @@ func FuzzRing512(f *testing.F) {
|
|||||||
|
|
||||||
switch op {
|
switch op {
|
||||||
case 0: // Put
|
case 0: // Put
|
||||||
size := int(arg)
|
size := min(int(arg), 512)
|
||||||
if size > 512 {
|
|
||||||
size = 512
|
|
||||||
}
|
|
||||||
data := make([]byte, size)
|
data := make([]byte, size)
|
||||||
for j := range data {
|
for j := range data {
|
||||||
data[j] = byte(j)
|
data[j] = byte(j)
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ func (m *msc) sendCSW(status csw.Status) {
|
|||||||
residue = expected - m.sentBytes
|
residue = expected - m.sentBytes
|
||||||
}
|
}
|
||||||
m.cbw.CSW(status, residue, m.cswBuf)
|
m.cbw.CSW(status, residue, m.cswBuf)
|
||||||
|
m.queuedBytes = csw.MsgLen
|
||||||
m.state = mscStateStatusSent
|
m.state = mscStateStatusSent
|
||||||
m.queuedBytes = csw.MsgLen
|
m.queuedBytes = csw.MsgLen
|
||||||
m.sendUSBPacket(m.cswBuf)
|
m.sendUSBPacket(m.cswBuf)
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ func (m *msc) handleClearFeature(setup usb.Setup, wValue uint16) bool {
|
|||||||
// (b) a Clear Feature HALT to the Bulk-In endpoint (clear stall IN)
|
// (b) a Clear Feature HALT to the Bulk-In endpoint (clear stall IN)
|
||||||
// (c) a Clear Feature HALT to the Bulk-Out endpoint (clear stall OUT)
|
// (c) a Clear Feature HALT to the Bulk-Out endpoint (clear stall OUT)
|
||||||
// https://usb.org/sites/default/files/usbmassbulk_10.pdf
|
// https://usb.org/sites/default/files/usbmassbulk_10.pdf
|
||||||
|
|
||||||
if m.state == mscStateNeedReset {
|
if m.state == mscStateNeedReset {
|
||||||
wIndex := uint8(setup.WIndex & 0x7F)
|
wIndex := uint8(setup.WIndex & 0x7F)
|
||||||
if wIndex == usb.MSC_ENDPOINT_IN {
|
if wIndex == usb.MSC_ENDPOINT_IN {
|
||||||
|
|||||||
+1
-1
Submodule src/net updated: 1026408a38...d5da3ddeef
@@ -138,6 +138,22 @@ func Symlink(oldname, newname string) error {
|
|||||||
return ErrNotImplemented
|
return ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Chmod(name string, mode FileMode) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func Chown(name string, uid, gid int) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func Link(oldname, newname string) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) Chown(uid, gid int) error {
|
||||||
|
return ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
func Readlink(name string) (string, error) {
|
func Readlink(name string) (string, error) {
|
||||||
return "", ErrNotImplemented
|
return "", ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user