mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-03 02:27:48 +00:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 30d684e637 | |||
| 90b42799a2 | |||
| 71bbe93ab2 | |||
| 0535c1bbad | |||
| 4f6d598ea8 | |||
| 16e7dd83a3 | |||
| f23ba3b023 | |||
| 2aa2e750b9 | |||
| bcce296ca3 | |||
| c5ec955081 | |||
| 5d334922d7 | |||
| e9f9a4b750 | |||
| 1406453350 | |||
| 6d3c11627c | |||
| f800f7507c | |||
| 46a7993fb8 | |||
| 9f3dcf3733 | |||
| c849bccb83 | |||
| cffe424849 | |||
| 51938e9d1c | |||
| 19dec048b0 | |||
| bbb2909283 | |||
| aa7c7b7bd9 | |||
| c7bd5405c3 | |||
| dc981ce509 | |||
| c522569378 | |||
| 0db4b13e37 | |||
| 2709d38d63 | |||
| 71d1b70ab7 | |||
| 24676d4366 | |||
| e2f532709f | |||
| dc1ff80e10 | |||
| b5205cc3ca | |||
| a075cbedf5 | |||
| ef613a5db7 | |||
| 1571b8fd34 | |||
| a41b72578b | |||
| 9f3f9d05b8 | |||
| f9865a08bc | |||
| 13db2c13e5 | |||
| ce8ad3650a | |||
| b0b84c48ec | |||
| 5a4dcfb367 |
@@ -19,3 +19,10 @@ src/device/kendryte/*.s
|
||||
vendor
|
||||
llvm-build
|
||||
llvm-project
|
||||
|
||||
# Ignore files generated by smoketest
|
||||
test.gba
|
||||
test.hex
|
||||
test.nro
|
||||
test.wasm
|
||||
wasm.wasm
|
||||
@@ -107,7 +107,10 @@ fmt-check:
|
||||
@unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1
|
||||
|
||||
|
||||
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32 gen-device-kendryte gen-device-nxp
|
||||
gen-device: gen-device-avr gen-device-esp gen-device-nrf gen-device-sam gen-device-sifive gen-device-kendryte gen-device-nxp
|
||||
ifneq ($(STM32), 0)
|
||||
gen-device: gen-device-stm32
|
||||
endif
|
||||
|
||||
gen-device-avr:
|
||||
@if [ ! -e lib/avr/README.md ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
|
||||
@@ -257,8 +260,6 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10031 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=reelboard examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=reelboard examples/blinky2
|
||||
@@ -267,6 +268,10 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10056 examples/blinky2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10059 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10059 examples/blinky2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m0 examples/blinky1
|
||||
@@ -275,14 +280,6 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-stm32f405 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-bluefruit examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||
@@ -307,12 +304,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=particle-xenon examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f103rb examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pinetime-devkit0 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=lgt92 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=x9pro examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10056-s140v7 examples/blinky1
|
||||
@@ -339,10 +332,6 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=teensy36 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=p1am-100 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
@@ -354,6 +343,28 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pyportal examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(STM32), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-stm32f405 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=lgt92 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f103rb examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-f722ze examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l432kc examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=nucleo-l552ze examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco examples/blinky2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=stm32f4disco-1 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
endif
|
||||
ifneq ($(AVR), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
||||
@$(MD5SUM) test.hex
|
||||
@@ -391,6 +402,9 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -o test.nro -target=nintendoswitch examples/serial
|
||||
@$(MD5SUM) test.nro
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||
@$(MD5SUM) test.hex
|
||||
|
||||
|
||||
wasmtest:
|
||||
$(GO) test ./tests/wasm
|
||||
|
||||
+230
-49
@@ -4,14 +4,18 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -38,6 +42,26 @@ type BuildResult struct {
|
||||
MainDir string
|
||||
}
|
||||
|
||||
// packageAction is the struct that is serialized to JSON and hashed, to work as
|
||||
// a cache key of compiled packages. It should contain all the information that
|
||||
// goes into a compiled package to avoid using stale data.
|
||||
//
|
||||
// Right now it's still important to include a hash of every import, because a
|
||||
// dependency might have a public constant that this package uses and thus this
|
||||
// package will need to be recompiled if that constant changes. In the future,
|
||||
// the type data should be serialized to disk which can then be used as cache
|
||||
// key, avoiding the need for recompiling all dependencies when only the
|
||||
// implementation of an imported package changes.
|
||||
type packageAction struct {
|
||||
ImportPath string
|
||||
CompilerVersion int // compiler.Version
|
||||
LLVMVersion string
|
||||
Config *compiler.Config
|
||||
CFlags []string
|
||||
FileHashes map[string]string // hash of every file that's part of the package
|
||||
Imports map[string]string // map from imported package to action ID hash
|
||||
}
|
||||
|
||||
// Build performs a single package to executable Go build. It takes in a package
|
||||
// name, an output path, and set of compile options and from that it manages the
|
||||
// whole compilation process.
|
||||
@@ -45,6 +69,13 @@ type BuildResult struct {
|
||||
// The error value may be of type *MultiError. Callers will likely want to check
|
||||
// for this case and print such errors individually.
|
||||
func Build(pkgName, outpath string, config *compileopts.Config, action func(BuildResult) error) error {
|
||||
// Create a temporary directory for intermediary files.
|
||||
dir, err := ioutil.TempDir("", "tinygo")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
compilerConfig := &compiler.Config{
|
||||
Triple: config.Triple(),
|
||||
CPU: config.CPU(),
|
||||
@@ -87,23 +118,200 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// Makefile target.
|
||||
var jobs []*compileJob
|
||||
|
||||
// Add job to compile and optimize all Go files at once.
|
||||
// TODO: parallelize this.
|
||||
// Create the *ssa.Program. This does not yet build the entire SSA of the
|
||||
// program so it's pretty fast and doesn't need to be parallelized.
|
||||
program := lprogram.LoadSSA()
|
||||
|
||||
// Add jobs to compile each package.
|
||||
// Packages that have a cache hit will not be compiled again.
|
||||
var packageJobs []*compileJob
|
||||
packageBitcodePaths := make(map[string]string)
|
||||
packageActionIDs := make(map[string]string)
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkg := pkg // necessary to avoid a race condition
|
||||
|
||||
// Create a cache key: a hash from the action ID below that contains all
|
||||
// the parameters for the build.
|
||||
actionID := packageAction{
|
||||
ImportPath: pkg.ImportPath,
|
||||
CompilerVersion: compiler.Version,
|
||||
LLVMVersion: llvm.Version,
|
||||
Config: compilerConfig,
|
||||
CFlags: pkg.CFlags,
|
||||
FileHashes: make(map[string]string, len(pkg.FileHashes)),
|
||||
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
|
||||
}
|
||||
for filePath, hash := range pkg.FileHashes {
|
||||
actionID.FileHashes[filePath] = hex.EncodeToString(hash)
|
||||
}
|
||||
for _, imported := range pkg.Pkg.Imports() {
|
||||
hash, ok := packageActionIDs[imported.Path()]
|
||||
if !ok {
|
||||
return fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
|
||||
}
|
||||
actionID.Imports[imported.Path()] = hash
|
||||
}
|
||||
buf, err := json.Marshal(actionID)
|
||||
if err != nil {
|
||||
panic(err) // shouldn't happen
|
||||
}
|
||||
hash := sha512.Sum512_224(buf)
|
||||
packageActionIDs[pkg.ImportPath] = hex.EncodeToString(hash[:])
|
||||
|
||||
// Determine the path of the bitcode file (which is a serialized version
|
||||
// of a LLVM module).
|
||||
cacheDir := goenv.Get("GOCACHE")
|
||||
if cacheDir == "off" {
|
||||
// Use temporary build directory instead, effectively disabling the
|
||||
// build cache.
|
||||
cacheDir = dir
|
||||
}
|
||||
bitcodePath := filepath.Join(cacheDir, "pkg-"+hex.EncodeToString(hash[:])+".bc")
|
||||
packageBitcodePaths[pkg.ImportPath] = bitcodePath
|
||||
|
||||
// Check whether this package has been compiled before, and if so don't
|
||||
// compile it again.
|
||||
if _, err := os.Stat(bitcodePath); err == nil {
|
||||
// Already cached, don't recreate this package.
|
||||
continue
|
||||
}
|
||||
|
||||
// The package has not yet been compiled, so create a job to do so.
|
||||
job := &compileJob{
|
||||
description: "compile package " + pkg.ImportPath,
|
||||
run: func() error {
|
||||
// Compile AST to IR. The compiler.CompilePackage function will
|
||||
// build the SSA as needed.
|
||||
mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
|
||||
if errs != nil {
|
||||
return newMultiError(errs)
|
||||
}
|
||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||
return errors.New("verification error after compiling package " + pkg.ImportPath)
|
||||
}
|
||||
|
||||
// Serialize the LLVM module as a bitcode file.
|
||||
// Write to a temporary path that is renamed to the destination
|
||||
// file to avoid race conditions with other TinyGo invocatiosn
|
||||
// that might also be compiling this package at the same time.
|
||||
f, err := ioutil.TempFile(filepath.Dir(bitcodePath), filepath.Base(bitcodePath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
// Work around a problem on Windows.
|
||||
// For some reason, WriteBitcodeToFile causes TinyGo to
|
||||
// exit with the following message:
|
||||
// LLVM ERROR: IO failure on output stream: Bad file descriptor
|
||||
buf := llvm.WriteBitcodeToMemoryBuffer(mod)
|
||||
defer buf.Dispose()
|
||||
_, err = f.Write(buf.Bytes())
|
||||
} else {
|
||||
// Otherwise, write bitcode directly to the file (probably
|
||||
// faster).
|
||||
err = llvm.WriteBitcodeToFile(mod, f)
|
||||
}
|
||||
if err != nil {
|
||||
// WriteBitcodeToFile doesn't produce a useful error on its
|
||||
// own, so create a somewhat useful error message here.
|
||||
return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, bitcodePath)
|
||||
}
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(f.Name(), bitcodePath)
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
packageJobs = append(packageJobs, job)
|
||||
}
|
||||
|
||||
// Add job that links and optimizes all packages together.
|
||||
var mod llvm.Module
|
||||
var stackSizeLoads []string
|
||||
programJob := &compileJob{
|
||||
description: "compile Go files",
|
||||
run: func() (err error) {
|
||||
mod, err = compileWholeProgram(pkgName, config, compilerConfig, lprogram, machine)
|
||||
if err != nil {
|
||||
return
|
||||
description: "link+optimize packages (LTO)",
|
||||
dependencies: packageJobs,
|
||||
run: func() error {
|
||||
// Load and link all the bitcode files. This does not yet optimize
|
||||
// anything, it only links the bitcode files together.
|
||||
ctx := llvm.NewContext()
|
||||
mod = ctx.NewModule("")
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkgMod, err := ctx.ParseBitcodeFile(packageBitcodePaths[pkg.ImportPath])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load bitcode file: %w", err)
|
||||
}
|
||||
err = llvm.LinkModules(mod, pkgMod)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to link module: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create runtime.initAll function that calls the runtime
|
||||
// initializer of each package.
|
||||
llvmInitFn := mod.NamedFunction("runtime.initAll")
|
||||
llvmInitFn.SetLinkage(llvm.InternalLinkage)
|
||||
llvmInitFn.SetUnnamedAddr(true)
|
||||
llvmInitFn.Param(0).SetName("context")
|
||||
llvmInitFn.Param(1).SetName("parentHandle")
|
||||
block := mod.Context().AddBasicBlock(llvmInitFn, "entry")
|
||||
irbuilder := mod.Context().NewBuilder()
|
||||
defer irbuilder.Dispose()
|
||||
irbuilder.SetInsertPointAtEnd(block)
|
||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
|
||||
if pkgInit.IsNil() {
|
||||
panic("init not found for " + pkg.Pkg.Path())
|
||||
}
|
||||
irbuilder.CreateCall(pkgInit, []llvm.Value{llvm.Undef(i8ptrType), llvm.Undef(i8ptrType)}, "")
|
||||
}
|
||||
irbuilder.CreateRetVoid()
|
||||
|
||||
// After linking, functions should (as far as possible) be set to
|
||||
// private linkage or internal linkage. The compiler package marks
|
||||
// non-exported functions by setting the visibility to hidden or
|
||||
// (for thunks) to linkonce_odr linkage. Change the linkage here to
|
||||
// internal to benefit much more from interprocedural optimizations.
|
||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
if fn.Visibility() == llvm.HiddenVisibility {
|
||||
fn.SetVisibility(llvm.DefaultVisibility)
|
||||
fn.SetLinkage(llvm.InternalLinkage)
|
||||
} else if fn.Linkage() == llvm.LinkOnceODRLinkage {
|
||||
fn.SetLinkage(llvm.InternalLinkage)
|
||||
}
|
||||
}
|
||||
|
||||
// Do the same for globals.
|
||||
for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||
if global.Visibility() == llvm.HiddenVisibility {
|
||||
global.SetVisibility(llvm.DefaultVisibility)
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
} else if global.Linkage() == llvm.LinkOnceODRLinkage {
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
}
|
||||
}
|
||||
|
||||
if config.Options.PrintIR {
|
||||
fmt.Println("; Generated LLVM IR:")
|
||||
fmt.Println(mod.String())
|
||||
}
|
||||
|
||||
// Run all optimization passes, which are much more effective now
|
||||
// that the optimizer can see the whole program at once.
|
||||
err := optimizeProgram(mod, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure stack sizes are loaded from a separate section so they can be
|
||||
// modified after linking.
|
||||
if config.AutomaticStackSize() {
|
||||
stackSizeLoads = transform.CreateStackSizeLoads(mod, config)
|
||||
}
|
||||
return
|
||||
return nil
|
||||
},
|
||||
}
|
||||
jobs = append(jobs, programJob)
|
||||
@@ -140,13 +348,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// First add all jobs necessary to build this object file, then afterwards
|
||||
// run all jobs in parallel as far as possible.
|
||||
|
||||
// Create a temporary directory for intermediary files.
|
||||
dir, err := ioutil.TempDir("", "tinygo")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Add job to write the output object file.
|
||||
objfile := filepath.Join(dir, "main.o")
|
||||
outputObjectFileJob := &compileJob{
|
||||
@@ -366,29 +567,16 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
})
|
||||
}
|
||||
|
||||
// compileWholeProgram compiles the entire *loader.Program to a LLVM module and
|
||||
// applies most necessary optimizations and transformations.
|
||||
func compileWholeProgram(pkgName string, config *compileopts.Config, compilerConfig *compiler.Config, lprogram *loader.Program, machine llvm.TargetMachine) (llvm.Module, error) {
|
||||
// Compile AST to IR.
|
||||
mod, errs := compiler.CompileProgram(lprogram, machine, compilerConfig, config.DumpSSA())
|
||||
if errs != nil {
|
||||
return mod, newMultiError(errs)
|
||||
}
|
||||
|
||||
if config.Options.PrintIR {
|
||||
fmt.Println("; Generated LLVM IR:")
|
||||
fmt.Println(mod.String())
|
||||
}
|
||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||
return mod, errors.New("verification error after IR construction")
|
||||
}
|
||||
|
||||
// optimizeProgram runs a series of optimizations and transformations that are
|
||||
// needed to convert a program to its final form. Some transformations are not
|
||||
// optional and must be run as the compiler expects them to run.
|
||||
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
||||
err := interp.Run(mod, config.DumpSSA())
|
||||
if err != nil {
|
||||
return mod, err
|
||||
return err
|
||||
}
|
||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||
return mod, errors.New("verification error after interpreting runtime.initAll")
|
||||
return errors.New("verification error after interpreting runtime.initAll")
|
||||
}
|
||||
|
||||
if config.GOOS() != "darwin" {
|
||||
@@ -403,23 +591,16 @@ func compileWholeProgram(pkgName string, config *compileopts.Config, compilerCon
|
||||
if config.WasmAbi() == "js" {
|
||||
err := transform.ExternalInt64AsPtr(mod)
|
||||
if err != nil {
|
||||
return mod, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Optimization levels here are roughly the same as Clang, but probably not
|
||||
// exactly.
|
||||
errs = nil
|
||||
var errs []error
|
||||
switch config.Options.Opt {
|
||||
/*
|
||||
Currently, turning optimizations off causes compile failures.
|
||||
We rely on the optimizer removing some dead symbols.
|
||||
Avoid providing an option that does not work right now.
|
||||
In the future once everything has been fixed we can re-enable this.
|
||||
|
||||
case "none", "0":
|
||||
errs = transform.Optimize(mod, config, 0, 0, 0) // -O0
|
||||
*/
|
||||
case "none", "0":
|
||||
errs = transform.Optimize(mod, config, 0, 0, 0) // -O0
|
||||
case "1":
|
||||
errs = transform.Optimize(mod, config, 1, 0, 0) // -O1
|
||||
case "2":
|
||||
@@ -429,13 +610,13 @@ func compileWholeProgram(pkgName string, config *compileopts.Config, compilerCon
|
||||
case "z":
|
||||
errs = transform.Optimize(mod, config, 2, 2, 5) // -Oz, default
|
||||
default:
|
||||
errs = []error{errors.New("unknown optimization level: -opt=" + config.Options.Opt)}
|
||||
return errors.New("unknown optimization level: -opt=" + config.Options.Opt)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return mod, newMultiError(errs)
|
||||
return newMultiError(errs)
|
||||
}
|
||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||
return mod, errors.New("verification failure after LLVM optimization passes")
|
||||
return errors.New("verification failure after LLVM optimization passes")
|
||||
}
|
||||
|
||||
// LLVM 11 by default tries to emit tail calls (even with the target feature
|
||||
@@ -449,7 +630,7 @@ func compileWholeProgram(pkgName string, config *compileopts.Config, compilerCon
|
||||
transform.DisableTailCalls(mod)
|
||||
}
|
||||
|
||||
return mod, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// functionStackSizes keeps stack size information about a single function
|
||||
|
||||
+8
-5
@@ -42,6 +42,7 @@ type cgoPackage struct {
|
||||
enums map[string]enumInfo
|
||||
anonStructNum int
|
||||
ldflags []string
|
||||
visitedFiles map[string][]byte
|
||||
}
|
||||
|
||||
// constantInfo stores some information about a CGo constant found by libclang
|
||||
@@ -156,9 +157,10 @@ typedef unsigned long long _Cgo_ulonglong;
|
||||
// Process extracts `import "C"` statements from the AST, parses the comment
|
||||
// with libclang, and modifies the AST to use this information. It returns a
|
||||
// newly created *ast.File that should be added to the list of to-be-parsed
|
||||
// files. If there is one or more error, it returns these in the []error slice
|
||||
// but still modifies the AST.
|
||||
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []error) {
|
||||
// files, the LDFLAGS for this package, and a map of file hashes of the accessed
|
||||
// C header files. If there is one or more error, it returns these in the
|
||||
// []error slice but still modifies the AST.
|
||||
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, map[string][]byte, []error) {
|
||||
p := &cgoPackage{
|
||||
dir: dir,
|
||||
fset: fset,
|
||||
@@ -170,6 +172,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
||||
typedefs: map[string]*typedefInfo{},
|
||||
elaboratedTypes: map[string]*elaboratedTypeInfo{},
|
||||
enums: map[string]enumInfo{},
|
||||
visitedFiles: map[string][]byte{},
|
||||
}
|
||||
|
||||
// Disable _FORTIFY_SOURCE as it causes problems on macOS.
|
||||
@@ -185,7 +188,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
||||
// Find the absolute path for this package.
|
||||
packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
|
||||
if err != nil {
|
||||
return nil, nil, []error{
|
||||
return nil, nil, nil, []error{
|
||||
scanner.Error{
|
||||
Pos: fset.Position(files[0].Pos()),
|
||||
Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error
|
||||
@@ -427,7 +430,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
|
||||
// Print the newly generated in-memory AST, for debugging.
|
||||
//ast.Print(fset, p.generated)
|
||||
|
||||
return p.generated, p.ldflags, p.errors
|
||||
return p.generated, p.ldflags, p.visitedFiles, p.errors
|
||||
}
|
||||
|
||||
// makePathsAbsolute converts some common path compiler flags (-I, -L) from
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ func TestCGo(t *testing.T) {
|
||||
}
|
||||
|
||||
// Process the AST with CGo.
|
||||
cgoAST, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
|
||||
cgoAST, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
|
||||
|
||||
// Check the AST for type errors.
|
||||
var typecheckErrors []error
|
||||
|
||||
@@ -4,6 +4,7 @@ package cgo
|
||||
// modification. It does not touch the AST itself.
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/scanner"
|
||||
@@ -56,6 +57,7 @@ unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c);
|
||||
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||
int tinygo_clang_enum_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||
void tinygo_clang_inclusion_visitor(CXFile included_file, CXSourceLocation *inclusion_stack, unsigned include_len, CXClientData client_data);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -114,6 +116,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
||||
}
|
||||
defer C.clang_disposeTranslationUnit(unit)
|
||||
|
||||
// Report parser and type errors.
|
||||
if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 {
|
||||
addDiagnostic := func(diagnostic C.CXDiagnostic) {
|
||||
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
||||
@@ -134,10 +137,36 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
||||
}
|
||||
}
|
||||
|
||||
// Extract information required by CGo.
|
||||
ref := storedRefs.Put(p)
|
||||
defer storedRefs.Remove(ref)
|
||||
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
||||
|
||||
// Determine files read during CGo processing, for caching.
|
||||
inclusionCallback := func(includedFile C.CXFile) {
|
||||
// Get full file path.
|
||||
path := getString(C.clang_getFileName(includedFile))
|
||||
|
||||
// Get contents of file (that should be in-memory).
|
||||
size := C.size_t(0)
|
||||
rawData := C.clang_getFileContents(unit, includedFile, &size)
|
||||
if rawData == nil {
|
||||
// Sanity check. This should (hopefully) never trigger.
|
||||
panic("libclang: file contents was not loaded")
|
||||
}
|
||||
data := (*[1 << 24]byte)(unsafe.Pointer(rawData))[:size]
|
||||
|
||||
// Hash the contents if it isn't hashed yet.
|
||||
if _, ok := p.visitedFiles[path]; !ok {
|
||||
// already stored
|
||||
sum := sha512.Sum512_224(data)
|
||||
p.visitedFiles[path] = sum[:]
|
||||
}
|
||||
}
|
||||
inclusionCallbackRef := storedRefs.Put(inclusionCallback)
|
||||
defer storedRefs.Remove(inclusionCallbackRef)
|
||||
C.clang_getInclusions(unit, C.CXInclusionVisitor(C.tinygo_clang_inclusion_visitor), C.CXClientData(inclusionCallbackRef))
|
||||
}
|
||||
|
||||
//export tinygo_clang_globals_visitor
|
||||
@@ -772,3 +801,9 @@ func tinygo_clang_enum_visitor(c, parent C.GoCXCursor, client_data C.CXClientDat
|
||||
}
|
||||
return C.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
//export tinygo_clang_inclusion_visitor
|
||||
func tinygo_clang_inclusion_visitor(includedFile C.CXFile, inclusionStack *C.CXSourceLocation, includeLen C.unsigned, clientData C.CXClientData) {
|
||||
callback := storedRefs.Get(unsafe.Pointer(clientData)).(func(C.CXFile))
|
||||
callback(includedFile)
|
||||
}
|
||||
|
||||
+18
-3
@@ -7,6 +7,7 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
@@ -42,7 +43,7 @@ type TargetSpec struct {
|
||||
ExtraFiles []string `json:"extra-files"`
|
||||
Emulator []string `json:"emulator" override:"copy"` // inherited Emulator must not be append
|
||||
FlashCommand string `json:"flash-command"`
|
||||
GDB string `json:"gdb"`
|
||||
GDB []string `json:"gdb"`
|
||||
PortReset string `json:"flash-1200-bps-reset"`
|
||||
FlashMethod string `json:"flash-method"`
|
||||
FlashVolume string `json:"msd-volume-name"`
|
||||
@@ -240,7 +241,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
Compiler: "clang",
|
||||
Linker: "cc",
|
||||
CFlags: []string{"--target=" + triple},
|
||||
GDB: "gdb",
|
||||
GDB: []string{"gdb"},
|
||||
PortReset: "false",
|
||||
}
|
||||
if goos == "darwin" {
|
||||
@@ -253,7 +254,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
}
|
||||
if goarch != runtime.GOARCH {
|
||||
// Some educated guesses as to how to invoke helper programs.
|
||||
spec.GDB = "gdb-multiarch"
|
||||
spec.GDB = []string{"gdb-multiarch"}
|
||||
if goarch == "arm" && goos == "linux" {
|
||||
spec.CFlags = append(spec.CFlags, "--sysroot=/usr/arm-linux-gnueabihf")
|
||||
spec.Linker = "arm-linux-gnueabihf-gcc"
|
||||
@@ -271,3 +272,17 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||
}
|
||||
return &spec, nil
|
||||
}
|
||||
|
||||
// LookupGDB looks up a gdb executable.
|
||||
func (spec *TargetSpec) LookupGDB() (string, error) {
|
||||
if len(spec.GDB) == 0 {
|
||||
return "", errors.New("gdb not configured in the target specification")
|
||||
}
|
||||
for _, d := range spec.GDB {
|
||||
_, err := exec.LookPath(d)
|
||||
if err == nil {
|
||||
return d, nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("no gdb found configured in the target specification (" + strings.Join(spec.GDB, ", ") + ")")
|
||||
}
|
||||
|
||||
@@ -159,6 +159,9 @@ func (b *builder) createNilCheck(inst ssa.Value, ptr llvm.Value, blockPrefix str
|
||||
}
|
||||
|
||||
switch inst := inst.(type) {
|
||||
case *ssa.Alloc:
|
||||
// An alloc is never nil.
|
||||
return
|
||||
case *ssa.IndexAddr:
|
||||
// This pointer is the result of an index operation into a slice or
|
||||
// array. Such slices/arrays are already bounds checked so the pointer
|
||||
|
||||
+119
-105
@@ -20,6 +20,11 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// Version of the compiler pacakge. Must be incremented each time the compiler
|
||||
// package changes in a way that affects the generated LLVM module.
|
||||
// This version is independent of the TinyGo version number.
|
||||
const Version = 5 // last change: add method set to interface types
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargets()
|
||||
llvm.InitializeAllTargetMCs()
|
||||
@@ -84,12 +89,13 @@ type compilerContext struct {
|
||||
// importantly with a newly created LLVM context and module.
|
||||
func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *Config, dumpSSA bool) *compilerContext {
|
||||
c := &compilerContext{
|
||||
Config: config,
|
||||
DumpSSA: dumpSSA,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
ditypes: make(map[types.Type]llvm.Metadata),
|
||||
machine: machine,
|
||||
targetData: machine.CreateTargetData(),
|
||||
Config: config,
|
||||
DumpSSA: dumpSSA,
|
||||
difiles: make(map[string]llvm.Metadata),
|
||||
ditypes: make(map[types.Type]llvm.Metadata),
|
||||
machine: machine,
|
||||
targetData: machine.CreateTargetData(),
|
||||
astComments: map[string]*ast.CommentGroup{},
|
||||
}
|
||||
|
||||
c.ctx = llvm.NewContext()
|
||||
@@ -241,21 +247,14 @@ func Sizes(machine llvm.TargetMachine) types.Sizes {
|
||||
}
|
||||
}
|
||||
|
||||
// CompileProgram compiles the given package path or .go file path. Return an
|
||||
// error when this fails (in any stage). If successful it returns the LLVM
|
||||
// module. If not, one or more errors will be returned.
|
||||
func CompileProgram(lprogram *loader.Program, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
||||
c := newCompilerContext("", machine, config, dumpSSA)
|
||||
// CompilePackage compiles a single package to a LLVM module.
|
||||
func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
||||
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
||||
c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg
|
||||
c.program = ssaPkg.Prog
|
||||
|
||||
c.program = lprogram.LoadSSA()
|
||||
c.program.Build()
|
||||
c.runtimePkg = c.program.ImportedPackage("runtime").Pkg
|
||||
|
||||
// Run a simple dead code elimination pass.
|
||||
functions, err := c.simpleDCE(lprogram)
|
||||
if err != nil {
|
||||
return llvm.Module{}, []error{err}
|
||||
}
|
||||
// Convert AST to SSA.
|
||||
ssaPkg.Build()
|
||||
|
||||
// Initialize debug information.
|
||||
if c.Debug {
|
||||
@@ -268,112 +267,37 @@ func CompileProgram(lprogram *loader.Program, machine llvm.TargetMachine, config
|
||||
})
|
||||
}
|
||||
|
||||
c.loadASTComments(lprogram)
|
||||
// Load comments such as //go:extern on globals.
|
||||
c.loadASTComments(pkg)
|
||||
|
||||
// Predeclare the runtime.alloc function, which is used by the wordpack
|
||||
// functionality.
|
||||
c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
|
||||
|
||||
// Add definitions to declarations.
|
||||
var initFuncs []llvm.Value
|
||||
// Compile all functions, methods, and global variables in this package.
|
||||
irbuilder := c.ctx.NewBuilder()
|
||||
defer irbuilder.Dispose()
|
||||
for _, f := range functions {
|
||||
if f.Synthetic == "package initializer" {
|
||||
initFuncs = append(initFuncs, c.getFunction(f))
|
||||
}
|
||||
if f.Blocks == nil {
|
||||
continue // external function
|
||||
}
|
||||
|
||||
// Create the function definition.
|
||||
b := newBuilder(c, irbuilder, f)
|
||||
b.createFunction()
|
||||
}
|
||||
|
||||
// After all packages are imported, add a synthetic initializer function
|
||||
// that calls the initializer of each package.
|
||||
initFn := c.program.ImportedPackage("runtime").Members["initAll"].(*ssa.Function)
|
||||
llvmInitFn := c.getFunction(initFn)
|
||||
llvmInitFn.SetLinkage(llvm.InternalLinkage)
|
||||
llvmInitFn.SetUnnamedAddr(true)
|
||||
if c.Debug {
|
||||
difunc := c.attachDebugInfo(initFn)
|
||||
pos := c.program.Fset.Position(initFn.Pos())
|
||||
irbuilder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
|
||||
}
|
||||
llvmInitFn.Param(0).SetName("context")
|
||||
llvmInitFn.Param(1).SetName("parentHandle")
|
||||
block := c.ctx.AddBasicBlock(llvmInitFn, "entry")
|
||||
irbuilder.SetInsertPointAtEnd(block)
|
||||
for _, fn := range initFuncs {
|
||||
irbuilder.CreateCall(fn, []llvm.Value{llvm.Undef(c.i8ptrType), llvm.Undef(c.i8ptrType)}, "")
|
||||
}
|
||||
irbuilder.CreateRetVoid()
|
||||
c.createPackage(irbuilder, ssaPkg)
|
||||
|
||||
// see: https://reviews.llvm.org/D18355
|
||||
if c.Debug {
|
||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||
c.ctx.MDNode([]llvm.Metadata{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
|
||||
llvm.GlobalContext().MDString("Debug Info Version"),
|
||||
c.ctx.MDString("Debug Info Version"),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
||||
}),
|
||||
)
|
||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
||||
c.ctx.MDNode([]llvm.Metadata{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(),
|
||||
llvm.GlobalContext().MDString("Dwarf Version"),
|
||||
c.ctx.MDString("Dwarf Version"),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
||||
}),
|
||||
)
|
||||
c.dibuilder.Finalize()
|
||||
}
|
||||
|
||||
return c.mod, c.diagnostics
|
||||
}
|
||||
|
||||
// CompilePackage compiles a single package to a LLVM module.
|
||||
func CompilePackage(moduleName string, pkg *loader.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) {
|
||||
c := newCompilerContext(moduleName, machine, config, dumpSSA)
|
||||
|
||||
// Build SSA from AST.
|
||||
ssaPkg := pkg.LoadSSA()
|
||||
ssaPkg.Build()
|
||||
|
||||
// Sort by position, so that the order of the functions in the IR matches
|
||||
// the order of functions in the source file. This is useful for testing,
|
||||
// for example.
|
||||
var members []string
|
||||
for name := range ssaPkg.Members {
|
||||
members = append(members, name)
|
||||
}
|
||||
sort.Slice(members, func(i, j int) bool {
|
||||
iPos := ssaPkg.Members[members[i]].Pos()
|
||||
jPos := ssaPkg.Members[members[j]].Pos()
|
||||
if i == j {
|
||||
// Cannot sort by pos, so do it by name.
|
||||
return members[i] < members[j]
|
||||
}
|
||||
return iPos < jPos
|
||||
})
|
||||
|
||||
// Define all functions.
|
||||
irbuilder := c.ctx.NewBuilder()
|
||||
defer irbuilder.Dispose()
|
||||
for _, name := range members {
|
||||
member := ssaPkg.Members[name]
|
||||
switch member := member.(type) {
|
||||
case *ssa.Function:
|
||||
if member.Blocks == nil {
|
||||
continue // external function
|
||||
}
|
||||
// Create the function definition.
|
||||
b := newBuilder(c, irbuilder, member)
|
||||
b.createFunction()
|
||||
}
|
||||
}
|
||||
|
||||
return c.mod, nil
|
||||
}
|
||||
|
||||
@@ -720,8 +644,9 @@ func (c *compilerContext) attachDebugInfo(f *ssa.Function) llvm.Metadata {
|
||||
// debug info is added to the function.
|
||||
func (c *compilerContext) attachDebugInfoRaw(f *ssa.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata {
|
||||
// Debug info for this function.
|
||||
diparams := make([]llvm.Metadata, 0, len(f.Params))
|
||||
for _, param := range f.Params {
|
||||
params := getParams(f.Signature)
|
||||
diparams := make([]llvm.Metadata, 0, len(params))
|
||||
for _, param := range params {
|
||||
diparams = append(diparams, c.getDIType(param.Type()))
|
||||
}
|
||||
diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
||||
@@ -759,6 +684,83 @@ func (c *compilerContext) getDIFile(filename string) llvm.Metadata {
|
||||
return c.difiles[filename]
|
||||
}
|
||||
|
||||
// createPackage builds the LLVM IR for all types, methods, and global variables
|
||||
// in the given package.
|
||||
func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package) {
|
||||
// Sort by position, so that the order of the functions in the IR matches
|
||||
// the order of functions in the source file. This is useful for testing,
|
||||
// for example.
|
||||
var members []string
|
||||
for name := range pkg.Members {
|
||||
members = append(members, name)
|
||||
}
|
||||
sort.Slice(members, func(i, j int) bool {
|
||||
iPos := pkg.Members[members[i]].Pos()
|
||||
jPos := pkg.Members[members[j]].Pos()
|
||||
if i == j {
|
||||
// Cannot sort by pos, so do it by name.
|
||||
return members[i] < members[j]
|
||||
}
|
||||
return iPos < jPos
|
||||
})
|
||||
|
||||
// Define all functions.
|
||||
for _, name := range members {
|
||||
member := pkg.Members[name]
|
||||
switch member := member.(type) {
|
||||
case *ssa.Function:
|
||||
if member.Blocks == nil {
|
||||
continue // external function
|
||||
}
|
||||
// Create the function definition.
|
||||
b := newBuilder(c, irbuilder, member)
|
||||
b.createFunction()
|
||||
case *ssa.Type:
|
||||
if types.IsInterface(member.Type()) {
|
||||
// Interfaces don't have concrete methods.
|
||||
continue
|
||||
}
|
||||
|
||||
// Named type. We should make sure all methods are created.
|
||||
// This includes both functions with pointer receivers and those
|
||||
// without.
|
||||
methods := getAllMethods(pkg.Prog, member.Type())
|
||||
methods = append(methods, getAllMethods(pkg.Prog, types.NewPointer(member.Type()))...)
|
||||
for _, method := range methods {
|
||||
// Parse this method.
|
||||
fn := pkg.Prog.MethodValue(method)
|
||||
if fn.Blocks == nil {
|
||||
continue // external function
|
||||
}
|
||||
if member.Type().String() != member.String() {
|
||||
// This is a member on a type alias. Do not build such a
|
||||
// function.
|
||||
continue
|
||||
}
|
||||
if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
|
||||
// This function is a kind of wrapper function (created by
|
||||
// the ssa package, not appearing in the source code) that
|
||||
// is created by the getFunction method as needed.
|
||||
// Therefore, don't build it here to avoid "function
|
||||
// redeclared" errors.
|
||||
continue
|
||||
}
|
||||
// Create the function definition.
|
||||
b := newBuilder(c, irbuilder, fn)
|
||||
b.createFunction()
|
||||
}
|
||||
case *ssa.Global:
|
||||
// Global variable.
|
||||
info := c.getGlobalInfo(member)
|
||||
if !info.extern {
|
||||
global := c.getGlobal(member)
|
||||
global.SetInitializer(llvm.ConstNull(global.Type().ElementType()))
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// createFunction builds the LLVM IR implementation for this function. The
|
||||
// function must not yet be defined, otherwise this function will create a
|
||||
// diagnostic.
|
||||
@@ -767,7 +769,7 @@ func (b *builder) createFunction() {
|
||||
fmt.Printf("\nfunc %s:\n", b.fn)
|
||||
}
|
||||
if !b.llvmFn.IsDeclaration() {
|
||||
errValue := b.fn.Name() + " redeclared in this program"
|
||||
errValue := b.llvmFn.Name() + " redeclared in this program"
|
||||
fnPos := getPosition(b.llvmFn)
|
||||
if fnPos.IsValid() {
|
||||
errValue += "\n\tprevious declaration at " + fnPos.String()
|
||||
@@ -776,9 +778,15 @@ func (b *builder) createFunction() {
|
||||
return
|
||||
}
|
||||
if !b.info.exported {
|
||||
b.llvmFn.SetLinkage(llvm.InternalLinkage)
|
||||
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
||||
b.llvmFn.SetUnnamedAddr(true)
|
||||
}
|
||||
if b.info.exported && strings.HasPrefix(b.Triple, "wasm") {
|
||||
// Set the exported name. This is necessary for WebAssembly because
|
||||
// otherwise the function is not exported.
|
||||
functionAttr := b.ctx.CreateStringAttribute("wasm-export-name", b.info.linkName)
|
||||
b.llvmFn.AddFunctionAttr(functionAttr)
|
||||
}
|
||||
|
||||
// Some functions have a pragma controlling the inlining level.
|
||||
switch b.info.inline {
|
||||
@@ -948,6 +956,12 @@ func (b *builder) createFunction() {
|
||||
b.trackValue(phi.llvm)
|
||||
}
|
||||
}
|
||||
|
||||
// Create anonymous functions (closures etc.).
|
||||
for _, sub := range b.fn.AnonFuncs {
|
||||
b := newBuilder(b.compilerContext, b.Builder, sub)
|
||||
b.createFunction()
|
||||
}
|
||||
}
|
||||
|
||||
// createInstruction builds the LLVM IR equivalent instructions for the
|
||||
|
||||
+17
-28
@@ -4,7 +4,6 @@ import (
|
||||
"flag"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -20,6 +19,19 @@ var flagUpdate = flag.Bool("update", false, "update tests based on test output")
|
||||
// Basic tests for the compiler. Build some Go files and compare the output with
|
||||
// the expected LLVM IR for regression testing.
|
||||
func TestCompiler(t *testing.T) {
|
||||
// Check LLVM version.
|
||||
llvmMajor, err := strconv.Atoi(strings.SplitN(llvm.Version, ".", 2)[0])
|
||||
if err != nil {
|
||||
t.Fatal("could not parse LLVM version:", llvm.Version)
|
||||
}
|
||||
if llvmMajor < 11 {
|
||||
// It is likely this version needs to be bumped in the future.
|
||||
// The goal is to at least test the LLVM version that's used by default
|
||||
// in TinyGo and (if possible without too many workarounds) also some
|
||||
// previous versions.
|
||||
t.Skip("compiler tests require LLVM 11 or above, got LLVM ", llvm.Version)
|
||||
}
|
||||
|
||||
target, err := compileopts.LoadTarget("i686--linux")
|
||||
if err != nil {
|
||||
t.Fatal("failed to load target:", err)
|
||||
@@ -47,7 +59,9 @@ func TestCompiler(t *testing.T) {
|
||||
"basic.go",
|
||||
"pointer.go",
|
||||
"slice.go",
|
||||
"string.go",
|
||||
"float.go",
|
||||
"interface.go",
|
||||
}
|
||||
|
||||
for _, testCase := range tests {
|
||||
@@ -65,8 +79,9 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
|
||||
// Compile AST to IR.
|
||||
program := lprogram.LoadSSA()
|
||||
pkg := lprogram.MainPkg()
|
||||
mod, errs := CompilePackage(testCase, pkg, machine, compilerConfig, false)
|
||||
mod, errs := CompilePackage(testCase, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false)
|
||||
if errs != nil {
|
||||
for _, err := range errs {
|
||||
t.Log("error:", err)
|
||||
@@ -107,8 +122,6 @@ func TestCompiler(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var alignRegexp = regexp.MustCompile(", align [0-9]+$")
|
||||
|
||||
// fuzzyEqualIR returns true if the two LLVM IR strings passed in are roughly
|
||||
// equal. That means, only relevant lines are compared (excluding comments
|
||||
// etc.).
|
||||
@@ -120,15 +133,6 @@ func fuzzyEqualIR(s1, s2 string) bool {
|
||||
}
|
||||
for i, line1 := range lines1 {
|
||||
line2 := lines2[i]
|
||||
match1 := alignRegexp.MatchString(line1)
|
||||
match2 := alignRegexp.MatchString(line2)
|
||||
if match1 != match2 {
|
||||
// Only one of the lines has the align keyword. Remove it.
|
||||
// This is a change to make the test work in both LLVM 10 and LLVM
|
||||
// 11 (LLVM 11 appears to automatically add alignment everywhere).
|
||||
line1 = alignRegexp.ReplaceAllString(line1, "")
|
||||
line2 = alignRegexp.ReplaceAllString(line2, "")
|
||||
}
|
||||
if line1 != line2 {
|
||||
return false
|
||||
}
|
||||
@@ -142,12 +146,6 @@ func fuzzyEqualIR(s1, s2 string) bool {
|
||||
// stripped out.
|
||||
func filterIrrelevantIRLines(lines []string) []string {
|
||||
var out []string
|
||||
llvmVersion, err := strconv.Atoi(strings.Split(llvm.Version, ".")[0])
|
||||
if err != nil {
|
||||
// Note: this should never happen and if it does, it will always happen
|
||||
// for a particular build because llvm.Version is a constant.
|
||||
panic(err)
|
||||
}
|
||||
for _, line := range lines {
|
||||
line = strings.Split(line, ";")[0] // strip out comments/info
|
||||
line = strings.TrimRight(line, "\r ") // drop '\r' on Windows and remove trailing spaces from comments
|
||||
@@ -157,15 +155,6 @@ func filterIrrelevantIRLines(lines []string) []string {
|
||||
if strings.HasPrefix(line, "source_filename = ") {
|
||||
continue
|
||||
}
|
||||
if llvmVersion < 10 && strings.HasPrefix(line, "attributes ") {
|
||||
// Ignore attribute groups. These may change between LLVM versions.
|
||||
// Right now test outputs are for LLVM 10.
|
||||
continue
|
||||
}
|
||||
if llvmVersion < 10 && strings.HasPrefix(line, "target datalayout ") {
|
||||
// Ignore the target layout. This may change between LLVM versions.
|
||||
continue
|
||||
}
|
||||
out = append(out, line)
|
||||
}
|
||||
return out
|
||||
|
||||
+2
-2
@@ -352,7 +352,7 @@ func (b *builder) createRunDefers() {
|
||||
|
||||
// Get the real defer struct type and cast to it.
|
||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
||||
for _, param := range callback.Params {
|
||||
for _, param := range getParams(callback.Signature) {
|
||||
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
|
||||
}
|
||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||
@@ -361,7 +361,7 @@ func (b *builder) createRunDefers() {
|
||||
// Extract the params from the struct.
|
||||
forwardParams := []llvm.Value{}
|
||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||
for i := range callback.Params {
|
||||
for i := range getParams(callback.Signature) {
|
||||
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := b.CreateLoad(gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context
|
||||
funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
|
||||
funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
|
||||
funcValueWithSignatureGlobal.SetGlobalConstant(true)
|
||||
funcValueWithSignatureGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
funcValueWithSignatureGlobal.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
}
|
||||
funcValueScalar = llvm.ConstPtrToInt(funcValueWithSignatureGlobal, c.uintptrType)
|
||||
default:
|
||||
|
||||
@@ -84,7 +84,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||
// Create the wrapper.
|
||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
||||
wrapper = llvm.AddFunction(c.mod, name+"$gowrapper", wrapperType)
|
||||
wrapper.SetLinkage(llvm.InternalLinkage)
|
||||
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
wrapper.SetUnnamedAddr(true)
|
||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", name))
|
||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||
@@ -141,7 +141,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fn llvm.Value, prefix stri
|
||||
// Create the wrapper.
|
||||
wrapperType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType}, false)
|
||||
wrapper = llvm.AddFunction(c.mod, prefix+".gowrapper", wrapperType)
|
||||
wrapper.SetLinkage(llvm.InternalLinkage)
|
||||
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
wrapper.SetUnnamedAddr(true)
|
||||
wrapper.AddAttributeAtIndex(-1, c.ctx.CreateStringAttribute("tinygo-gowrapper", ""))
|
||||
entry := c.ctx.AddBasicBlock(wrapper, "entry")
|
||||
|
||||
+32
-23
@@ -24,16 +24,7 @@ import (
|
||||
func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
|
||||
itfValue := b.emitPointerPack([]llvm.Value{val})
|
||||
itfTypeCodeGlobal := b.getTypeCode(typ)
|
||||
itfMethodSetGlobal := b.getTypeMethodSet(typ)
|
||||
itfConcreteTypeGlobal := b.mod.NamedGlobal("typeInInterface:" + itfTypeCodeGlobal.Name())
|
||||
if itfConcreteTypeGlobal.IsNil() {
|
||||
typeInInterface := b.getLLVMRuntimeType("typeInInterface")
|
||||
itfConcreteTypeGlobal = llvm.AddGlobal(b.mod, typeInInterface, "typeInInterface:"+itfTypeCodeGlobal.Name())
|
||||
itfConcreteTypeGlobal.SetInitializer(llvm.ConstNamedStruct(typeInInterface, []llvm.Value{itfTypeCodeGlobal, itfMethodSetGlobal}))
|
||||
itfConcreteTypeGlobal.SetGlobalConstant(true)
|
||||
itfConcreteTypeGlobal.SetLinkage(llvm.PrivateLinkage)
|
||||
}
|
||||
itfTypeCode := b.CreatePtrToInt(itfConcreteTypeGlobal, b.uintptrType, "")
|
||||
itfTypeCode := b.CreatePtrToInt(itfTypeCodeGlobal, b.uintptrType, "")
|
||||
itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
|
||||
itf = b.CreateInsertValue(itf, itfTypeCode, 0, "")
|
||||
itf = b.CreateInsertValue(itf, itfValue, 1, "")
|
||||
@@ -54,6 +45,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
// reflect lowering simpler.
|
||||
var references llvm.Value
|
||||
var length int64
|
||||
var methodSet llvm.Value
|
||||
switch typ := typ.(type) {
|
||||
case *types.Named:
|
||||
references = c.getTypeCode(typ.Underlying())
|
||||
@@ -70,17 +62,28 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
// Take a pointer to the typecodeID of the first field (if it exists).
|
||||
structGlobal := c.makeStructTypeFields(typ)
|
||||
references = llvm.ConstBitCast(structGlobal, global.Type())
|
||||
case *types.Interface:
|
||||
methodSetGlobal := c.getInterfaceMethodSet(typ)
|
||||
references = llvm.ConstBitCast(methodSetGlobal, global.Type())
|
||||
}
|
||||
if !references.IsNil() {
|
||||
if _, ok := typ.Underlying().(*types.Interface); !ok {
|
||||
methodSet = c.getTypeMethodSet(typ)
|
||||
}
|
||||
if !references.IsNil() || length != 0 || !methodSet.IsNil() {
|
||||
// Set the 'references' field of the runtime.typecodeID struct.
|
||||
globalValue := llvm.ConstNull(global.Type().ElementType())
|
||||
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
|
||||
if !references.IsNil() {
|
||||
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
|
||||
}
|
||||
if length != 0 {
|
||||
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
|
||||
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
|
||||
}
|
||||
if !methodSet.IsNil() {
|
||||
globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2})
|
||||
}
|
||||
global.SetInitializer(globalValue)
|
||||
global.SetLinkage(llvm.PrivateLinkage)
|
||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
}
|
||||
global.SetGlobalConstant(true)
|
||||
}
|
||||
@@ -103,8 +106,8 @@ func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
|
||||
fieldName.SetLinkage(llvm.PrivateLinkage)
|
||||
fieldName.SetUnnamedAddr(true)
|
||||
fieldName = llvm.ConstGEP(fieldName, []llvm.Value{
|
||||
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
})
|
||||
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldName, []uint32{1})
|
||||
if typ.Tag(i) != "" {
|
||||
@@ -112,8 +115,8 @@ func (c *compilerContext) makeStructTypeFields(typ *types.Struct) llvm.Value {
|
||||
fieldTag.SetLinkage(llvm.PrivateLinkage)
|
||||
fieldTag.SetUnnamedAddr(true)
|
||||
fieldTag = llvm.ConstGEP(fieldTag, []llvm.Value{
|
||||
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
})
|
||||
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldTag, []uint32{2})
|
||||
}
|
||||
@@ -186,7 +189,7 @@ func getTypeCodeName(t types.Type) string {
|
||||
case *types.Interface:
|
||||
methods := make([]string, t.NumMethods())
|
||||
for i := 0; i < t.NumMethods(); i++ {
|
||||
methods[i] = getTypeCodeName(t.Method(i).Type())
|
||||
methods[i] = t.Method(i).Name() + ":" + getTypeCodeName(t.Method(i).Type())
|
||||
}
|
||||
return "interface:" + "{" + strings.Join(methods, ",") + "}"
|
||||
case *types.Map:
|
||||
@@ -264,7 +267,7 @@ func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
||||
global = llvm.AddGlobal(c.mod, arrayType, typ.String()+"$methodset")
|
||||
global.SetInitializer(value)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetLinkage(llvm.PrivateLinkage)
|
||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||
}
|
||||
|
||||
@@ -295,7 +298,7 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
|
||||
global = llvm.AddGlobal(c.mod, value.Type(), name+"$interface")
|
||||
global.SetInitializer(value)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetLinkage(llvm.PrivateLinkage)
|
||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||
}
|
||||
|
||||
@@ -338,10 +341,16 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
commaOk = b.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "")
|
||||
|
||||
} else {
|
||||
globalName := "reflect/types.type:" + getTypeCodeName(expr.AssertedType) + "$id"
|
||||
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
||||
if assertedTypeCodeGlobal.IsNil() {
|
||||
// Create a new typecode global.
|
||||
assertedTypeCodeGlobal = llvm.AddGlobal(b.mod, b.ctx.Int8Type(), globalName)
|
||||
assertedTypeCodeGlobal.SetGlobalConstant(true)
|
||||
}
|
||||
// Type assert on concrete type.
|
||||
// Call runtime.typeAssert, which will be lowered to a simple icmp or
|
||||
// const false in the interface lowering pass.
|
||||
assertedTypeCodeGlobal := b.getTypeCode(expr.AssertedType)
|
||||
commaOk = b.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode")
|
||||
}
|
||||
|
||||
@@ -445,7 +454,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
||||
}
|
||||
|
||||
// Get the expanded receiver type.
|
||||
receiverType := c.getLLVMType(fn.Params[0].Type())
|
||||
receiverType := c.getLLVMType(fn.Signature.Recv().Type())
|
||||
var expandedReceiverType []llvm.Type
|
||||
for _, info := range expandFormalParamType(receiverType, "", nil) {
|
||||
expandedReceiverType = append(expandedReceiverType, info.llvmType)
|
||||
@@ -467,7 +476,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFn llv
|
||||
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
|
||||
wrapper.LastParam().SetName("parentHandle")
|
||||
|
||||
wrapper.SetLinkage(llvm.InternalLinkage)
|
||||
wrapper.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
wrapper.SetUnnamedAddr(true)
|
||||
|
||||
// Create a new builder just to create this wrapper.
|
||||
|
||||
@@ -46,7 +46,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
||||
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt redeclared in this program")
|
||||
}
|
||||
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
|
||||
global.SetLinkage(llvm.PrivateLinkage)
|
||||
global.SetVisibility(llvm.HiddenVisibility)
|
||||
global.SetGlobalConstant(true)
|
||||
global.SetUnnamedAddr(true)
|
||||
initializer := llvm.ConstNull(globalLLVMType)
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
package compiler
|
||||
|
||||
// This file implements a simple reachability analysis, to reduce compile time.
|
||||
// This DCE pass used to be necessary for improving other passes but now it
|
||||
// isn't necessary anymore.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/types"
|
||||
"sort"
|
||||
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
type dceState struct {
|
||||
*compilerContext
|
||||
functions []*dceFunction
|
||||
functionMap map[*ssa.Function]*dceFunction
|
||||
}
|
||||
|
||||
type dceFunction struct {
|
||||
*ssa.Function
|
||||
functionInfo
|
||||
flag bool // used by dead code elimination
|
||||
}
|
||||
|
||||
func (p *dceState) addFunction(ssaFn *ssa.Function) {
|
||||
if _, ok := p.functionMap[ssaFn]; ok {
|
||||
return
|
||||
}
|
||||
f := &dceFunction{Function: ssaFn}
|
||||
f.functionInfo = p.getFunctionInfo(ssaFn)
|
||||
p.functions = append(p.functions, f)
|
||||
p.functionMap[ssaFn] = f
|
||||
|
||||
for _, anon := range ssaFn.AnonFuncs {
|
||||
p.addFunction(anon)
|
||||
}
|
||||
}
|
||||
|
||||
// simpleDCE returns a list of alive functions in the program. Compiling only
|
||||
// these functions makes the compiler faster.
|
||||
//
|
||||
// This functionality will likely be replaced in the future with build caching.
|
||||
func (c *compilerContext) simpleDCE(lprogram *loader.Program) ([]*ssa.Function, error) {
|
||||
mainPkg := c.program.Package(lprogram.MainPkg().Pkg)
|
||||
if mainPkg == nil {
|
||||
panic("could not find main package")
|
||||
}
|
||||
p := &dceState{
|
||||
compilerContext: c,
|
||||
functionMap: make(map[*ssa.Function]*dceFunction),
|
||||
}
|
||||
|
||||
for _, pkg := range lprogram.Sorted() {
|
||||
pkg := c.program.Package(pkg.Pkg)
|
||||
memberNames := make([]string, 0)
|
||||
for name := range pkg.Members {
|
||||
memberNames = append(memberNames, name)
|
||||
}
|
||||
sort.Strings(memberNames)
|
||||
|
||||
for _, name := range memberNames {
|
||||
member := pkg.Members[name]
|
||||
switch member := member.(type) {
|
||||
case *ssa.Function:
|
||||
p.addFunction(member)
|
||||
case *ssa.Type:
|
||||
methods := getAllMethods(pkg.Prog, member.Type())
|
||||
if !types.IsInterface(member.Type()) {
|
||||
// named type
|
||||
for _, method := range methods {
|
||||
p.addFunction(pkg.Prog.MethodValue(method))
|
||||
}
|
||||
}
|
||||
case *ssa.Global:
|
||||
// Ignore. Globals are not handled here.
|
||||
case *ssa.NamedConst:
|
||||
// Ignore: these are already resolved.
|
||||
default:
|
||||
panic("unknown member type: " + member.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initial set of live functions. Include main.main, *.init and runtime.*
|
||||
// functions.
|
||||
main, ok := mainPkg.Members["main"].(*ssa.Function)
|
||||
if !ok {
|
||||
if mainPkg.Members["main"] == nil {
|
||||
return nil, errors.New("function main is undeclared in the main package")
|
||||
} else {
|
||||
return nil, errors.New("cannot declare main - must be func")
|
||||
}
|
||||
}
|
||||
runtimePkg := c.program.ImportedPackage("runtime")
|
||||
mathPkg := c.program.ImportedPackage("math")
|
||||
taskPkg := c.program.ImportedPackage("internal/task")
|
||||
p.functionMap[main].flag = true
|
||||
worklist := []*ssa.Function{main}
|
||||
for _, f := range p.functions {
|
||||
if f.exported || f.Synthetic == "package initializer" || f.Pkg == runtimePkg || f.Pkg == taskPkg || (f.Pkg == mathPkg && f.Pkg != nil) {
|
||||
if f.flag {
|
||||
continue
|
||||
}
|
||||
f.flag = true
|
||||
worklist = append(worklist, f.Function)
|
||||
}
|
||||
}
|
||||
|
||||
// Mark all called functions recursively.
|
||||
for len(worklist) != 0 {
|
||||
f := worklist[len(worklist)-1]
|
||||
worklist = worklist[:len(worklist)-1]
|
||||
for _, block := range f.Blocks {
|
||||
for _, instr := range block.Instrs {
|
||||
if instr, ok := instr.(*ssa.MakeInterface); ok {
|
||||
for _, sel := range getAllMethods(c.program, instr.X.Type()) {
|
||||
fn := c.program.MethodValue(sel)
|
||||
callee := p.functionMap[fn]
|
||||
if callee == nil {
|
||||
// TODO: why is this necessary?
|
||||
p.addFunction(fn)
|
||||
callee = p.functionMap[fn]
|
||||
}
|
||||
if !callee.flag {
|
||||
callee.flag = true
|
||||
worklist = append(worklist, callee.Function)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, operand := range instr.Operands(nil) {
|
||||
if operand == nil || *operand == nil {
|
||||
continue
|
||||
}
|
||||
switch operand := (*operand).(type) {
|
||||
case *ssa.Function:
|
||||
f := p.functionMap[operand]
|
||||
if f == nil {
|
||||
// FIXME HACK: this function should have been
|
||||
// discovered already. It is not for bound methods.
|
||||
p.addFunction(operand)
|
||||
f = p.functionMap[operand]
|
||||
}
|
||||
if !f.flag {
|
||||
f.flag = true
|
||||
worklist = append(worklist, operand)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return all live functions.
|
||||
liveFunctions := []*ssa.Function{}
|
||||
for _, f := range p.functions {
|
||||
if f.flag {
|
||||
liveFunctions = append(liveFunctions, f.Function)
|
||||
}
|
||||
}
|
||||
return liveFunctions, nil
|
||||
}
|
||||
@@ -150,6 +150,9 @@ func (s *stdSizes) Sizeof(T types.Type) int64 {
|
||||
return s.PtrSize * 2
|
||||
case *types.Pointer:
|
||||
return s.PtrSize
|
||||
case *types.Signature:
|
||||
// Func values in TinyGo are two words in size.
|
||||
return s.PtrSize * 2
|
||||
default:
|
||||
panic("unknown type: " + t.String())
|
||||
}
|
||||
|
||||
+46
-26
@@ -72,7 +72,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
}
|
||||
|
||||
var paramInfos []paramInfo
|
||||
for _, param := range fn.Params {
|
||||
for _, param := range getParams(fn.Signature) {
|
||||
paramType := c.getLLVMType(param.Type())
|
||||
paramFragmentInfos := expandFormalParamType(paramType, param.Name(), param.Type())
|
||||
paramInfos = append(paramInfos, paramFragmentInfos...)
|
||||
@@ -172,6 +172,21 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
}
|
||||
}
|
||||
|
||||
// Synthetic functions are functions that do not appear in the source code,
|
||||
// they are artificially constructed. Usually they are wrapper functions
|
||||
// that are not referenced anywhere except in a SSA call instruction so
|
||||
// should be created right away.
|
||||
// The exception is the package initializer, which does appear in the
|
||||
// *ssa.Package members and so shouldn't be created here.
|
||||
if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
|
||||
irbuilder := c.ctx.NewBuilder()
|
||||
b := newBuilder(c, irbuilder, fn)
|
||||
b.createFunction()
|
||||
irbuilder.Dispose()
|
||||
llvmFn.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
llvmFn.SetUnnamedAddr(true)
|
||||
}
|
||||
|
||||
return llvmFn
|
||||
}
|
||||
|
||||
@@ -278,6 +293,20 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
|
||||
}
|
||||
}
|
||||
|
||||
// getParams returns the function parameters, including the receiver at the
|
||||
// start. This is an alternative to the Params member of *ssa.Function, which is
|
||||
// not yet populated when the package has not yet been built.
|
||||
func getParams(sig *types.Signature) []*types.Var {
|
||||
params := []*types.Var{}
|
||||
if sig.Recv() != nil {
|
||||
params = append(params, sig.Recv())
|
||||
}
|
||||
for i := 0; i < sig.Params().Len(); i++ {
|
||||
params = append(params, sig.Params().At(i))
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
// globalInfo contains some information about a specific global. By default,
|
||||
// linkName is equal to .RelString(nil) on a global and extern is false, but for
|
||||
// some symbols this is different (due to //go:extern for example).
|
||||
@@ -289,25 +318,22 @@ type globalInfo struct {
|
||||
|
||||
// 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.
|
||||
func (c *compilerContext) loadASTComments(lprogram *loader.Program) {
|
||||
c.astComments = map[string]*ast.CommentGroup{}
|
||||
for _, pkgInfo := range lprogram.Sorted() {
|
||||
for _, file := range pkgInfo.Files {
|
||||
for _, decl := range file.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
switch decl.Tok {
|
||||
case token.VAR:
|
||||
if len(decl.Specs) != 1 {
|
||||
continue
|
||||
}
|
||||
for _, spec := range decl.Specs {
|
||||
switch spec := spec.(type) {
|
||||
case *ast.ValueSpec: // decl.Tok == token.VAR
|
||||
for _, name := range spec.Names {
|
||||
id := pkgInfo.Pkg.Path() + "." + name.Name
|
||||
c.astComments[id] = decl.Doc
|
||||
}
|
||||
func (c *compilerContext) loadASTComments(pkg *loader.Package) {
|
||||
for _, file := range pkg.Files {
|
||||
for _, decl := range file.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
switch decl.Tok {
|
||||
case token.VAR:
|
||||
if len(decl.Specs) != 1 {
|
||||
continue
|
||||
}
|
||||
for _, spec := range decl.Specs {
|
||||
switch spec := spec.(type) {
|
||||
case *ast.ValueSpec: // decl.Tok == token.VAR
|
||||
for _, name := range spec.Names {
|
||||
id := pkg.Pkg.Path() + "." + name.Name
|
||||
c.astComments[id] = decl.Doc
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,10 +352,6 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
||||
typ := g.Type().(*types.Pointer).Elem()
|
||||
llvmType := c.getLLVMType(typ)
|
||||
llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName)
|
||||
if !info.extern {
|
||||
llvmGlobal.SetInitializer(llvm.ConstNull(llvmType))
|
||||
llvmGlobal.SetLinkage(llvm.InternalLinkage)
|
||||
}
|
||||
|
||||
// Set alignment from the //go:align comment.
|
||||
var alignInBits uint32
|
||||
@@ -347,8 +369,6 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
|
||||
|
||||
if c.Debug && !info.extern {
|
||||
// Add debug info.
|
||||
// TODO: this should be done for every global in the program, not just
|
||||
// the ones that are referenced from some code.
|
||||
pos := c.program.Fset.Position(g.Pos())
|
||||
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
|
||||
Name: g.RelString(nil),
|
||||
|
||||
Vendored
+16
-14
@@ -3,70 +3,72 @@ source_filename = "basic.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i32 @main.addInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = add i32 %x, %y
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i1 @main.equalInt(i32 %x, i32 %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = icmp eq i32 %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i1 @main.floatEQ(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp oeq float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i1 @main.floatNE(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp une float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i1 @main.floatLower(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp olt float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i1 @main.floatLowerEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp ole float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i1 @main.floatGreater(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp ogt float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i1 @main.floatGreaterEqual(float %x, float %y, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fcmp oge float %x, %y
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define internal float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden float @main.complexReal(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret float %x.r
|
||||
}
|
||||
|
||||
define internal float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden float @main.complexImag(float %x.r, float %x.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret float %x.i
|
||||
}
|
||||
|
||||
define internal { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fadd float %x.r, %y.r
|
||||
%1 = fadd float %x.i, %y.i
|
||||
@@ -75,7 +77,7 @@ entry:
|
||||
ret { float, float } %3
|
||||
}
|
||||
|
||||
define internal { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fsub float %x.r, %y.r
|
||||
%1 = fsub float %x.i, %y.i
|
||||
@@ -84,7 +86,7 @@ entry:
|
||||
ret { float, float } %3
|
||||
}
|
||||
|
||||
define internal { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = fmul float %x.r, %y.r
|
||||
%1 = fmul float %x.i, %y.i
|
||||
|
||||
Vendored
+11
-9
@@ -3,12 +3,14 @@ source_filename = "float.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal i32 @main.f32tou32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i32 @main.f32tou32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||
@@ -19,22 +21,22 @@ entry:
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
define internal float @main.maxu32f(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden float @main.maxu32f(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret float 0x41F0000000000000
|
||||
}
|
||||
|
||||
define internal i32 @main.maxu32tof32(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i32 @main.maxu32tof32(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret i32 -1
|
||||
}
|
||||
|
||||
define internal { i32, i32, i32, i32 } @main.inftoi32(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden { i32, i32, i32, i32 } @main.inftoi32(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 }
|
||||
}
|
||||
|
||||
define internal i32 @main.u32tof32tou32(i32 %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i32 @main.u32tof32tou32(i32 %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = uitofp i32 %v to float
|
||||
%withinmax = fcmp ole float %0, 0x41EFFFFFC0000000
|
||||
@@ -43,7 +45,7 @@ entry:
|
||||
ret i32 %1
|
||||
}
|
||||
|
||||
define internal float @main.f32tou32tof32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden float @main.f32tou32tof32(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 0x41EFFFFFC0000000
|
||||
@@ -55,7 +57,7 @@ entry:
|
||||
ret float %1
|
||||
}
|
||||
|
||||
define internal i8 @main.f32tou8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i8 @main.f32tou8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%positive = fcmp oge float %v, 0.000000e+00
|
||||
%withinmax = fcmp ole float %v, 2.550000e+02
|
||||
@@ -66,7 +68,7 @@ entry:
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
define internal i8 @main.f32toi8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i8 @main.f32toi8(float %v, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%abovemin = fcmp oge float %v, -1.280000e+02
|
||||
%belowmax = fcmp ole float %v, 1.270000e+02
|
||||
|
||||
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
// This file tests interface types and interface builtins.
|
||||
|
||||
package main
|
||||
|
||||
// Test interface construction.
|
||||
|
||||
func simpleType() interface{} {
|
||||
return 0
|
||||
}
|
||||
|
||||
func pointerType() interface{} {
|
||||
// Pointers have an element type, in this case int.
|
||||
var v *int
|
||||
return v
|
||||
}
|
||||
|
||||
func interfaceType() interface{} {
|
||||
// Interfaces can exist in interfaces, but only indirectly (through
|
||||
// pointers).
|
||||
var v *error
|
||||
return v
|
||||
}
|
||||
|
||||
func anonymousInterfaceType() interface{} {
|
||||
var v *interface {
|
||||
String() string
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Test interface builtins.
|
||||
|
||||
func isInt(itf interface{}) bool {
|
||||
_, ok := itf.(int)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isError(itf interface{}) bool {
|
||||
// Interface assert on (builtin) named interface type.
|
||||
_, ok := itf.(error)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isStringer(itf interface{}) bool {
|
||||
// Interface assert on anonymous interface type.
|
||||
_, ok := itf.(interface {
|
||||
String() string
|
||||
})
|
||||
return ok
|
||||
}
|
||||
|
||||
func callErrorMethod(itf error) string {
|
||||
return itf.Error()
|
||||
}
|
||||
Vendored
+100
@@ -0,0 +1,100 @@
|
||||
; ModuleID = 'interface.go'
|
||||
source_filename = "interface.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* }
|
||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||
%runtime._interface = type { i32, i8* }
|
||||
%runtime._string = type { i8*, i32 }
|
||||
|
||||
@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID zeroinitializer
|
||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"func Error() string" = external constant i8
|
||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
||||
@"func String() string" = external constant i8
|
||||
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"]
|
||||
@"reflect/types.type:basic:int$id" = external constant i8
|
||||
@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden %runtime._interface @main.simpleType(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:int" to i32), i8* null }
|
||||
}
|
||||
|
||||
define hidden %runtime._interface @main.pointerType(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:basic:int" to i32), i8* null }
|
||||
}
|
||||
|
||||
define hidden %runtime._interface @main.interfaceType(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:named:error" to i32), i8* null }
|
||||
}
|
||||
|
||||
define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), i8* null }
|
||||
}
|
||||
|
||||
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%typecode = call i1 @runtime.typeAssert(i32 %itf.typecode, i8* nonnull @"reflect/types.type:basic:int$id", i8* undef, i8* null)
|
||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
ret i1 %typecode
|
||||
}
|
||||
|
||||
declare i1 @runtime.typeAssert(i32, i8* dereferenceable_or_null(1), i8*, i8*)
|
||||
|
||||
define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call i1 @runtime.interfaceImplements(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* undef, i8* null)
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
declare i1 @runtime.interfaceImplements(i32, i8** dereferenceable_or_null(4), i8*, i8*)
|
||||
|
||||
define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call i1 @runtime.interfaceImplements(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"reflect/types.interface:interface{String() string}$interface", i32 0, i32 0), i8* undef, i8* null)
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%invoke.func = call i32 @runtime.interfaceMethod(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* nonnull @"func Error() string", i8* undef, i8* null)
|
||||
%invoke.func.cast = inttoptr i32 %invoke.func to %runtime._string (i8*, i8*, i8*)*
|
||||
%0 = call %runtime._string %invoke.func.cast(i8* %itf.value, i8* undef, i8* undef)
|
||||
ret %runtime._string %0
|
||||
}
|
||||
|
||||
declare i32 @runtime.interfaceMethod(i32, i8** dereferenceable_or_null(4), i8* dereferenceable_or_null(1), i8*, i8*)
|
||||
Vendored
+10
-8
@@ -3,46 +3,48 @@ source_filename = "pointer.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden [0 x i32] @main.pointerDerefZero([0 x i32]* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret [0 x i32] zeroinitializer
|
||||
}
|
||||
|
||||
define internal i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i32* @main.pointerCastFromUnsafe(i8* %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = bitcast i8* %x to i32*
|
||||
ret i32* %0
|
||||
}
|
||||
|
||||
define internal i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i8* @main.pointerCastToUnsafe(i32* dereferenceable_or_null(4) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = bitcast i32* %x to i8*
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
define internal i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i8* @main.pointerCastToUnsafeNoop(i8* dereferenceable_or_null(1) %x, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret i8* %x
|
||||
}
|
||||
|
||||
define internal i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i8* @main.pointerUnsafeGEPFixedOffset(i8* dereferenceable_or_null(1) %ptr, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 10
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
define internal i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i8* @main.pointerUnsafeGEPByteOffset(i8* dereferenceable_or_null(1) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = getelementptr inbounds i8, i8* %ptr, i32 %offset
|
||||
ret i8* %0
|
||||
}
|
||||
|
||||
define internal i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i32* @main.pointerUnsafeGEPIntOffset(i32* dereferenceable_or_null(4) %ptr, i32 %offset, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = getelementptr i32, i32* %ptr, i32 %offset
|
||||
ret i32* %0
|
||||
|
||||
Vendored
+16
@@ -7,3 +7,19 @@ func sliceLen(ints []int) int {
|
||||
func sliceCap(ints []int) int {
|
||||
return cap(ints)
|
||||
}
|
||||
|
||||
func sliceElement(ints []int, index int) int {
|
||||
return ints[index]
|
||||
}
|
||||
|
||||
func sliceAppendValues(ints []int) []int {
|
||||
return append(ints, 1, 2, 3)
|
||||
}
|
||||
|
||||
func sliceAppendSlice(ints, added []int) []int {
|
||||
return append(ints, added...)
|
||||
}
|
||||
|
||||
func sliceCopy(dst, src []int) int {
|
||||
return copy(dst, src)
|
||||
}
|
||||
|
||||
Vendored
+72
-3
@@ -3,17 +3,86 @@ source_filename = "slice.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
define internal void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i32 @main.sliceLen(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret i32 %ints.len
|
||||
}
|
||||
|
||||
define internal i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
define hidden i32 @main.sliceCap(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret i32 %ints.cap
|
||||
}
|
||||
|
||||
define hidden i32 @main.sliceElement(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%.not = icmp ult i32 %index, %ints.len
|
||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||
|
||||
lookup.throw: ; preds = %entry
|
||||
call void @runtime.lookupPanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
lookup.next: ; preds = %entry
|
||||
%0 = getelementptr inbounds i32, i32* %ints.data, i32 %index
|
||||
%1 = load i32, i32* %0, align 4
|
||||
ret i32 %1
|
||||
}
|
||||
|
||||
declare void @runtime.lookupPanic(i8*, i8*)
|
||||
|
||||
define hidden { i32*, i32, i32 } @main.sliceAppendValues(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%varargs = call i8* @runtime.alloc(i32 12, i8* undef, i8* null)
|
||||
%0 = bitcast i8* %varargs to i32*
|
||||
store i32 1, i32* %0, align 4
|
||||
%1 = getelementptr inbounds i8, i8* %varargs, i32 4
|
||||
%2 = bitcast i8* %1 to i32*
|
||||
store i32 2, i32* %2, align 4
|
||||
%3 = getelementptr inbounds i8, i8* %varargs, i32 8
|
||||
%4 = bitcast i8* %3 to i32*
|
||||
store i32 3, i32* %4, align 4
|
||||
%append.srcPtr = bitcast i32* %ints.data to i8*
|
||||
%append.new = call { i8*, i32, i32 } @runtime.sliceAppend(i8* %append.srcPtr, i8* nonnull %varargs, i32 %ints.len, i32 %ints.cap, i32 3, i32 4, i8* undef, i8* null)
|
||||
%append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
|
||||
%append.newBuf = bitcast i8* %append.newPtr to i32*
|
||||
%append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
|
||||
%append.newCap = extractvalue { i8*, i32, i32 } %append.new, 2
|
||||
%5 = insertvalue { i32*, i32, i32 } undef, i32* %append.newBuf, 0
|
||||
%6 = insertvalue { i32*, i32, i32 } %5, i32 %append.newLen, 1
|
||||
%7 = insertvalue { i32*, i32, i32 } %6, i32 %append.newCap, 2
|
||||
ret { i32*, i32, i32 } %7
|
||||
}
|
||||
|
||||
declare { i8*, i32, i32 } @runtime.sliceAppend(i8*, i8*, i32, i32, i32, i32, i8*, i8*)
|
||||
|
||||
define hidden { i32*, i32, i32 } @main.sliceAppendSlice(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i32* %added.data, i32 %added.len, i32 %added.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%append.srcPtr = bitcast i32* %ints.data to i8*
|
||||
%append.srcPtr1 = bitcast i32* %added.data to i8*
|
||||
%append.new = call { i8*, i32, i32 } @runtime.sliceAppend(i8* %append.srcPtr, i8* %append.srcPtr1, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, i8* undef, i8* null)
|
||||
%append.newPtr = extractvalue { i8*, i32, i32 } %append.new, 0
|
||||
%append.newBuf = bitcast i8* %append.newPtr to i32*
|
||||
%append.newLen = extractvalue { i8*, i32, i32 } %append.new, 1
|
||||
%append.newCap = extractvalue { i8*, i32, i32 } %append.new, 2
|
||||
%0 = insertvalue { i32*, i32, i32 } undef, i32* %append.newBuf, 0
|
||||
%1 = insertvalue { i32*, i32, i32 } %0, i32 %append.newLen, 1
|
||||
%2 = insertvalue { i32*, i32, i32 } %1, i32 %append.newCap, 2
|
||||
ret { i32*, i32, i32 } %2
|
||||
}
|
||||
|
||||
define hidden i32 @main.sliceCopy(i32* %dst.data, i32 %dst.len, i32 %dst.cap, i32* %src.data, i32 %src.len, i32 %src.cap, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%copy.dstPtr = bitcast i32* %dst.data to i8*
|
||||
%copy.srcPtr = bitcast i32* %src.data to i8*
|
||||
%copy.n = call i32 @runtime.sliceCopy(i8* %copy.dstPtr, i8* %copy.srcPtr, i32 %dst.len, i32 %src.len, i32 4, i8* undef, i8* null)
|
||||
ret i32 %copy.n
|
||||
}
|
||||
|
||||
declare i32 @runtime.sliceCopy(i8*, i8*, i32, i32, i32, i8*, i8*)
|
||||
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
func stringLen(s string) int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func stringIndex(s string, index int) byte {
|
||||
return s[index]
|
||||
}
|
||||
|
||||
func stringCompareEqual(s1, s2 string) bool {
|
||||
return s1 == s2
|
||||
}
|
||||
|
||||
func stringCompareUnequal(s1, s2 string) bool {
|
||||
return s1 != s2
|
||||
}
|
||||
|
||||
func stringCompareLarger(s1, s2 string) bool {
|
||||
return s1 > s2
|
||||
}
|
||||
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
; ModuleID = 'string.go'
|
||||
source_filename = "string.go"
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||
|
||||
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
ret i32 %s.len
|
||||
}
|
||||
|
||||
define hidden i8 @main.stringIndex(i8* %s.data, i32 %s.len, i32 %index, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%.not = icmp ult i32 %index, %s.len
|
||||
br i1 %.not, label %lookup.next, label %lookup.throw
|
||||
|
||||
lookup.throw: ; preds = %entry
|
||||
call void @runtime.lookupPanic(i8* undef, i8* null)
|
||||
unreachable
|
||||
|
||||
lookup.next: ; preds = %entry
|
||||
%0 = getelementptr inbounds i8, i8* %s.data, i32 %index
|
||||
%1 = load i8, i8* %0, align 1
|
||||
ret i8 %1
|
||||
}
|
||||
|
||||
declare void @runtime.lookupPanic(i8*, i8*)
|
||||
|
||||
define hidden i1 @main.stringCompareEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null)
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringEqual(i8*, i32, i8*, i32, i8*, i8*)
|
||||
|
||||
define hidden i1 @main.stringCompareUnequal(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null)
|
||||
%1 = xor i1 %0, true
|
||||
ret i1 %1
|
||||
}
|
||||
|
||||
define hidden i1 @main.stringCompareLarger(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* %context, i8* %parentHandle) unnamed_addr {
|
||||
entry:
|
||||
%0 = call i1 @runtime.stringLess(i8* %s1.data, i32 %s1.len, i8* %s2.data, i32 %s2.len, i8* undef, i8* null)
|
||||
%1 = xor i1 %0, true
|
||||
ret i1 %1
|
||||
}
|
||||
|
||||
declare i1 @runtime.stringLess(i8*, i32, i8*, i32, i8*, i8*)
|
||||
@@ -12,5 +12,5 @@ require (
|
||||
go.bug.st/serial v1.1.2
|
||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78
|
||||
golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2
|
||||
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f
|
||||
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4
|
||||
)
|
||||
|
||||
@@ -57,5 +57,5 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbO
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f h1:FP5Do5omlQ/dLQ3Hfy7oyJo69VS5Hn46rZw004r0lGU=
|
||||
tinygo.org/x/go-llvm v0.0.0-20210206225315-7fe719483a0f/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4 h1:CMUHxVTb+UuUePuMf8vkWjZ3gTp9BBK91KrgOCwoNHs=
|
||||
tinygo.org/x/go-llvm v0.0.0-20210308112806-9ef958b6bed4/go.mod h1:fv1F0BSNpxMfCL0zF3M4OPFbgYHnhtB6ST0HvUtu/LE=
|
||||
|
||||
+72
-20
@@ -155,11 +155,8 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// which case this call won't even get to this point but will
|
||||
// already be emitted in initAll.
|
||||
continue
|
||||
case callFn.name == "(reflect.Type).Elem" || strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet":
|
||||
case strings.HasPrefix(callFn.name, "runtime.print") || callFn.name == "runtime._panic" || callFn.name == "runtime.hashmapGet":
|
||||
// These functions should be run at runtime. Specifically:
|
||||
// * (reflect.Type).Elem is a special function. It should
|
||||
// eventually be interpreted, but fall back to a runtime call
|
||||
// for now.
|
||||
// * Print and panic functions are best emitted directly without
|
||||
// interpreting them, otherwise we get a ton of putchar (etc.)
|
||||
// calls.
|
||||
@@ -280,26 +277,48 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
|
||||
dstObj.buffer = dstBuf
|
||||
mem.put(dst.index(), dstObj)
|
||||
case callFn.name == "(reflect.rawType).elem":
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"call (reflect.rawType).elem:", operands[1:])
|
||||
}
|
||||
// Extract the type code global from the first parameter.
|
||||
typecodeID := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem).Operand(0)
|
||||
|
||||
// Get the type class.
|
||||
// See also: getClassAndValueFromTypeCode in transform/reflect.go.
|
||||
typecodeName := typecodeID.Name()
|
||||
const prefix = "reflect/types.type:"
|
||||
if !strings.HasPrefix(typecodeName, prefix) {
|
||||
panic("unexpected typecode name: " + typecodeName)
|
||||
}
|
||||
id := typecodeName[len(prefix):]
|
||||
class := id[:strings.IndexByte(id, ':')]
|
||||
value := id[len(class)+1:]
|
||||
if class == "named" {
|
||||
// Get the underlying type.
|
||||
class = value[:strings.IndexByte(value, ':')]
|
||||
value = value[len(class)+1:]
|
||||
}
|
||||
|
||||
// Elem() is only valid for certain type classes.
|
||||
switch class {
|
||||
case "chan", "pointer", "slice", "array":
|
||||
elementType := llvm.ConstExtractValue(typecodeID.Initializer(), []uint32{0})
|
||||
uintptrType := r.mod.Context().IntType(int(mem.r.pointerSize) * 8)
|
||||
locals[inst.localIndex] = r.getValue(llvm.ConstPtrToInt(elementType, uintptrType))
|
||||
default:
|
||||
return nil, mem, r.errorAt(inst, fmt.Errorf("(reflect.Type).Elem() called on %s type", class))
|
||||
}
|
||||
case callFn.name == "runtime.typeAssert":
|
||||
// This function must be implemented manually as it is normally
|
||||
// implemented by the interface lowering pass.
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"typeassert:", operands[1:])
|
||||
}
|
||||
typeInInterfacePtr, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
actualType, err := mem.load(typeInInterfacePtr, r.pointerSize).asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
assertedType, err := operands[2].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
result := assertedType.asRawValue(r).equal(actualType.asRawValue(r))
|
||||
if result {
|
||||
assertedType := operands[2].toLLVMValue(inst.llvmInst.Operand(1).Type(), &mem)
|
||||
actualTypePtrToInt := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
|
||||
actualType := actualTypePtrToInt.Operand(0)
|
||||
if actualType.Name()+"$id" == assertedType.Name() {
|
||||
locals[inst.localIndex] = literalValue{uint8(1)}
|
||||
} else {
|
||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||
@@ -310,11 +329,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
}
|
||||
|
||||
// Load various values for the interface implements check below.
|
||||
typeInInterfacePtr, err := operands[1].asPointer(r)
|
||||
typecodePtr, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
methodSetPtr, err := mem.load(typeInInterfacePtr.addOffset(r.pointerSize), r.pointerSize).asPointer(r)
|
||||
methodSetPtr, err := mem.load(typecodePtr.addOffset(r.pointerSize*2), r.pointerSize).asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
@@ -349,6 +368,39 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
}
|
||||
// If assertOk is still 1, the assertion succeeded.
|
||||
locals[inst.localIndex] = literalValue{assertOk}
|
||||
case callFn.name == "runtime.interfaceMethod":
|
||||
// This builtin returns the function (which may be a thunk) to
|
||||
// invoke a method on an interface. It does not call the method.
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"interface method:", operands[1:])
|
||||
}
|
||||
|
||||
// Load the first param, which is the type code (ptrtoint of the
|
||||
// type code global).
|
||||
typecodeID := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem).Operand(0).Initializer()
|
||||
|
||||
// Load the method set, which is part of the typecodeID object.
|
||||
methodSet := llvm.ConstExtractValue(typecodeID, []uint32{2}).Operand(0).Initializer()
|
||||
|
||||
// We don't need to load the interface method set.
|
||||
|
||||
// Load the signature of the to-be-called function.
|
||||
signature := inst.llvmInst.Operand(2)
|
||||
|
||||
// Iterate through all methods, looking for the one method that
|
||||
// should be returned.
|
||||
numMethods := methodSet.Type().ArrayLength()
|
||||
var method llvm.Value
|
||||
for i := 0; i < numMethods; i++ {
|
||||
methodSignature := llvm.ConstExtractValue(methodSet, []uint32{uint32(i), 0})
|
||||
if methodSignature == signature {
|
||||
method = llvm.ConstExtractValue(methodSet, []uint32{uint32(i), 1}).Operand(0)
|
||||
}
|
||||
}
|
||||
if method.IsNil() {
|
||||
return nil, mem, r.errorAt(inst, errors.New("could not find method: "+signature.Name()))
|
||||
}
|
||||
locals[inst.localIndex] = r.getValue(method)
|
||||
case callFn.name == "runtime.hashmapMake":
|
||||
// Create a new map.
|
||||
hashmapPointerType := inst.llvmInst.Type()
|
||||
|
||||
+1
-1
@@ -1048,7 +1048,7 @@ func (v rawValue) rawLLVMValue(mem *memoryView) llvm.Value {
|
||||
// There are some special pointer types that should be used as a
|
||||
// ptrtoint, so that they can be used in certain optimizations.
|
||||
name := elementType.StructName()
|
||||
if name == "runtime.typeInInterface" || name == "runtime.funcValueWithSignature" {
|
||||
if name == "runtime.typecodeID" || name == "runtime.funcValueWithSignature" {
|
||||
uintptrType := ctx.IntType(int(mem.r.pointerSize) * 8)
|
||||
field = llvm.ConstPtrToInt(field, uintptrType)
|
||||
}
|
||||
|
||||
Vendored
+5
-6
@@ -1,17 +1,16 @@
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64--linux"
|
||||
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i64 }
|
||||
%runtime.typecodeID = type { %runtime.typecodeID*, i64, %runtime.interfaceMethodInfo* }
|
||||
%runtime.interfaceMethodInfo = type { i8*, i64 }
|
||||
%runtime.typeInInterface = type { %runtime.typecodeID*, %runtime.interfaceMethodInfo* }
|
||||
|
||||
@main.v1 = global i1 0
|
||||
@"reflect/types.type:named:main.foo" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i64 0 }
|
||||
@"reflect/types.type:named:main.foo" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i64 0, %runtime.interfaceMethodInfo* null }
|
||||
@"reflect/types.type:named:main.foo$id" = external constant i8
|
||||
@"reflect/types.type:basic:int" = external constant %runtime.typecodeID
|
||||
@"typeInInterface:reflect/types.type:named:main.foo" = private constant %runtime.typeInInterface { %runtime.typecodeID* @"reflect/types.type:named:main.foo", %runtime.interfaceMethodInfo* null }
|
||||
|
||||
|
||||
declare i1 @runtime.typeAssert(i64, %runtime.typecodeID*, i8*, i8*)
|
||||
declare i1 @runtime.typeAssert(i64, i8*, i8*, i8*)
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
@@ -22,7 +21,7 @@ entry:
|
||||
define internal void @main.init() unnamed_addr {
|
||||
entry:
|
||||
; Test type asserts.
|
||||
%typecode = call i1 @runtime.typeAssert(i64 ptrtoint (%runtime.typeInInterface* @"typeInInterface:reflect/types.type:named:main.foo" to i64), %runtime.typecodeID* @"reflect/types.type:named:main.foo", i8* undef, i8* null)
|
||||
%typecode = call i1 @runtime.typeAssert(i64 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:main.foo" to i64), i8* @"reflect/types.type:named:main.foo$id", i8* undef, i8* null)
|
||||
store i1 %typecode, i1* @main.v1
|
||||
ret void
|
||||
}
|
||||
|
||||
+22
-15
@@ -2,6 +2,7 @@ package loader
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha512"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -66,10 +68,12 @@ type PackageJSON struct {
|
||||
type Package struct {
|
||||
PackageJSON
|
||||
|
||||
program *Program
|
||||
Files []*ast.File
|
||||
Pkg *types.Package
|
||||
info types.Info
|
||||
program *Program
|
||||
Files []*ast.File
|
||||
FileHashes map[string][]byte
|
||||
CFlags []string // CFlags used during CGo preprocessing (only set if CGo is used)
|
||||
Pkg *types.Package
|
||||
info types.Info
|
||||
}
|
||||
|
||||
// Load loads the given package with all dependencies (including the runtime
|
||||
@@ -118,7 +122,8 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
decoder := json.NewDecoder(buf)
|
||||
for {
|
||||
pkg := &Package{
|
||||
program: p,
|
||||
program: p,
|
||||
FileHashes: make(map[string][]byte),
|
||||
info: types.Info{
|
||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||
Defs: make(map[*ast.Ident]types.Object),
|
||||
@@ -277,17 +282,15 @@ func (p *Program) Parse() error {
|
||||
}
|
||||
|
||||
// parseFile is a wrapper around parser.ParseFile.
|
||||
func (p *Program) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
||||
if p.fset == nil {
|
||||
p.fset = token.NewFileSet()
|
||||
}
|
||||
|
||||
rd, err := os.Open(path)
|
||||
func (p *Package) parseFile(path string, mode parser.Mode) (*ast.File, error) {
|
||||
originalPath := p.program.getOriginalPath(path)
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rd.Close()
|
||||
return parser.ParseFile(p.fset, p.getOriginalPath(path), rd, mode)
|
||||
sum := sha512.Sum512_224(data)
|
||||
p.FileHashes[originalPath] = sum[:]
|
||||
return parser.ParseFile(p.program.fset, originalPath, data, mode)
|
||||
}
|
||||
|
||||
// Parse parses and typechecks this package.
|
||||
@@ -363,7 +366,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||
if !filepath.IsAbs(file) {
|
||||
file = filepath.Join(p.Dir, file)
|
||||
}
|
||||
f, err := p.program.parseFile(file, parser.ParseComments)
|
||||
f, err := p.parseFile(file, parser.ParseComments)
|
||||
if err != nil {
|
||||
fileErrs = append(fileErrs, err)
|
||||
return
|
||||
@@ -385,7 +388,11 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
||||
if p.program.clangHeaders != "" {
|
||||
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
|
||||
}
|
||||
generated, ldflags, errs := cgo.Process(files, p.program.workingDir, p.program.fset, cflags)
|
||||
p.CFlags = cflags
|
||||
generated, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.program.fset, cflags)
|
||||
for path, hash := range accessedFiles {
|
||||
p.FileHashes[path] = hash
|
||||
}
|
||||
if errs != nil {
|
||||
fileErrs = append(fileErrs, errs...)
|
||||
}
|
||||
|
||||
@@ -344,8 +344,9 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config.Target.GDB == "" {
|
||||
return errors.New("gdb not configured in the target specification")
|
||||
gdb, err := config.Target.LookupGDB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return builder.Build(pkgName, "", config, func(result builder.BuildResult) error {
|
||||
@@ -490,7 +491,7 @@ func FlashGDB(pkgName string, ocdOutput bool, options *compileopts.Options) erro
|
||||
for _, cmd := range gdbCommands {
|
||||
params = append(params, "-ex", cmd)
|
||||
}
|
||||
cmd := executeCommand(config.Options, config.Target.GDB, params...)
|
||||
cmd := executeCommand(config.Options, gdb, params...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
+10
-5
@@ -59,7 +59,10 @@ func TestCompiler(t *testing.T) {
|
||||
t.Run("Host", func(t *testing.T) {
|
||||
runPlatTests("", matches, t)
|
||||
if runtime.GOOS == "darwin" {
|
||||
runTest("testdata/libc/env.go", "", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...)
|
||||
runTest("testdata/libc/filesystem.go", "", t,
|
||||
nil, nil)
|
||||
runTest("testdata/libc/env.go", "", t,
|
||||
[]string{"ENV1=VALUE1", "ENV2=VALUE2"}, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -107,7 +110,9 @@ func TestCompiler(t *testing.T) {
|
||||
|
||||
t.Run("WASI", func(t *testing.T) {
|
||||
runPlatTests("wasi", matches, t)
|
||||
runTest("testdata/libc/env.go", "wasi", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...)
|
||||
runTest("testdata/libc/env.go", "wasi", t,
|
||||
[]string{"--env", "ENV1=VALUE1", "--env", "ENV2=VALUE2"}, nil)
|
||||
runTest("testdata/libc/filesystem.go", "wasi", t, nil, []string{"--dir=."})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -119,7 +124,7 @@ func runPlatTests(target string, matches []string, t *testing.T) {
|
||||
path := path // redefine to avoid race condition
|
||||
t.Run(filepath.Base(path), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runTest(path, target, t)
|
||||
runTest(path, target, t, nil, nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -136,7 +141,7 @@ func runBuild(src, out string, opts *compileopts.Options) error {
|
||||
return Build(src, out, opts)
|
||||
}
|
||||
|
||||
func runTest(path, target string, t *testing.T, environmentVars ...string) {
|
||||
func runTest(path, target string, t *testing.T, environmentVars []string, additionalArgs []string) {
|
||||
// Get the expected output for this test.
|
||||
txtpath := path[:len(path)-3] + ".txt"
|
||||
if path[len(path)-1] == os.PathSeparator {
|
||||
@@ -195,7 +200,7 @@ func runTest(path, target string, t *testing.T, environmentVars ...string) {
|
||||
cmd = exec.Command(binary)
|
||||
} else {
|
||||
args := append(spec.Emulator[1:], binary)
|
||||
cmd = exec.Command(spec.Emulator[0], args...)
|
||||
cmd = exec.Command(spec.Emulator[0], append(args, additionalArgs...)...)
|
||||
}
|
||||
|
||||
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
type rawState uint8
|
||||
|
||||
//export llvm.coro.resume
|
||||
func (s *rawState) resume()
|
||||
func coroResume(*rawState)
|
||||
|
||||
type state struct{ *rawState }
|
||||
|
||||
@@ -20,7 +20,7 @@ func noopState() *rawState
|
||||
|
||||
// Resume the task until it pauses or completes.
|
||||
func (t *Task) Resume() {
|
||||
t.state.resume()
|
||||
coroResume(t.state.rawState)
|
||||
}
|
||||
|
||||
// setState is used by the compiler to set the state of the function at the beginning of a function call.
|
||||
@@ -77,22 +77,9 @@ func Current() *Task
|
||||
// This is implemented inside the compiler.
|
||||
func Pause()
|
||||
|
||||
type taskHolder interface {
|
||||
setState(*rawState) *rawState
|
||||
returnTo(*rawState)
|
||||
returnCurrent()
|
||||
setReturnPtr(unsafe.Pointer)
|
||||
getReturnPtr() unsafe.Pointer
|
||||
}
|
||||
|
||||
// If there are no direct references to the task methods, they will not be discovered by the compiler, and this will trigger a compiler error.
|
||||
// Instantiating this interface forces discovery of these methods.
|
||||
var _ = taskHolder((*Task)(nil))
|
||||
|
||||
func fake() {
|
||||
// Hack to ensure intrinsics are discovered.
|
||||
Current()
|
||||
go func() {}()
|
||||
Pause()
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ func init() {
|
||||
|
||||
// I2C on the Arduino Nano 33.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM4_I2CM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
|
||||
@@ -85,6 +85,6 @@ const (
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = PB7
|
||||
SCL_PIN = PB6
|
||||
I2C0_SDA_PIN = PB7
|
||||
I2C0_SCL_PIN = PB6
|
||||
)
|
||||
|
||||
@@ -23,12 +23,12 @@ func init() {
|
||||
// I2C on the Circuit Playground Express.
|
||||
var (
|
||||
// external device
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
// internal device
|
||||
I2C1 = I2C{
|
||||
I2C1 = &I2C{
|
||||
Bus: sam.SERCOM1_I2CM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ const (
|
||||
|
||||
// I2C on the Feather M0.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM3_I2CM,
|
||||
SERCOM: 3,
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func init() {
|
||||
|
||||
// I2C on the Feather M4.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
|
||||
@@ -120,19 +120,22 @@ const (
|
||||
|
||||
var (
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART3,
|
||||
AltFuncSelector: AF7_USART1_2_3,
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART3,
|
||||
TxAltFuncSelector: AF7_USART1_2_3,
|
||||
RxAltFuncSelector: AF7_USART1_2_3,
|
||||
}
|
||||
UART2 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART6,
|
||||
AltFuncSelector: AF8_USART4_5_6,
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART6,
|
||||
TxAltFuncSelector: AF8_USART4_5_6,
|
||||
RxAltFuncSelector: AF8_USART4_5_6,
|
||||
}
|
||||
UART3 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART1,
|
||||
AltFuncSelector: AF7_USART1_2_3,
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART1,
|
||||
TxAltFuncSelector: AF7_USART1_2_3,
|
||||
RxAltFuncSelector: AF7_USART1_2_3,
|
||||
}
|
||||
UART0 = UART1
|
||||
)
|
||||
@@ -227,15 +230,15 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
I2C1 = I2C{
|
||||
I2C1 = &I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: AF4_I2C1_2_3,
|
||||
}
|
||||
I2C2 = I2C{
|
||||
I2C2 = &I2C{
|
||||
Bus: stm32.I2C2,
|
||||
AltFuncSelector: AF4_I2C1_2_3,
|
||||
}
|
||||
I2C3 = I2C{
|
||||
I2C3 = &I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: AF4_I2C1_2_3,
|
||||
}
|
||||
|
||||
@@ -40,11 +40,11 @@ var (
|
||||
|
||||
// I2C on the Grand Central M4
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM3_I2CM,
|
||||
SERCOM: 3,
|
||||
}
|
||||
I2C1 = I2C{
|
||||
I2C1 = &I2C{
|
||||
Bus: sam.SERCOM6_I2CM,
|
||||
SERCOM: 6,
|
||||
}
|
||||
|
||||
@@ -10,10 +10,3 @@ var (
|
||||
Bus: sifive.QSPI1,
|
||||
}
|
||||
)
|
||||
|
||||
// I2C on the HiFive1 rev B.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: sifive.I2C0,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -75,7 +75,7 @@ const (
|
||||
|
||||
// I2C on the ItsyBitsy M0.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM3_I2CM,
|
||||
SERCOM: 3,
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func init() {
|
||||
|
||||
// I2C on the ItsyBitsy M4.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
|
||||
@@ -54,16 +54,18 @@ var (
|
||||
|
||||
// Console UART (LPUSART1)
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.LPUART1,
|
||||
AltFuncSelector: 6,
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.LPUART1,
|
||||
TxAltFuncSelector: 6,
|
||||
RxAltFuncSelector: 6,
|
||||
}
|
||||
|
||||
// Gps UART
|
||||
UART1 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART1,
|
||||
AltFuncSelector: 0,
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART1,
|
||||
TxAltFuncSelector: 0,
|
||||
RxAltFuncSelector: 0,
|
||||
}
|
||||
|
||||
// SPI
|
||||
|
||||
@@ -13,16 +13,3 @@ var (
|
||||
Bus: kendryte.SPI1,
|
||||
}
|
||||
)
|
||||
|
||||
// I2C on the MAix Bit.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
Bus: kendryte.I2C0,
|
||||
}
|
||||
I2C1 = I2C{
|
||||
Bus: kendryte.I2C1,
|
||||
}
|
||||
I2C2 = I2C{
|
||||
Bus: kendryte.I2C2,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ func init() {
|
||||
|
||||
// I2C on the MatrixPortal M4
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func init() {
|
||||
|
||||
// I2C on the Metro M4.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ const (
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK USB Dongle"
|
||||
usb_STRING_MANUFACTURER = "Makerdiary"
|
||||
usb_STRING_MANUFACTURER = "Nordic Semiconductor ASA"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -39,7 +39,7 @@ const (
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Makerdiary nRF52840 MDK"
|
||||
usb_STRING_MANUFACTURER = "Makerdiary"
|
||||
usb_STRING_MANUFACTURER = "Nordic Semiconductor ASA"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -120,6 +120,6 @@ const (
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SCL_PIN = PB6
|
||||
SDA_PIN = PB7
|
||||
I2C0_SCL_PIN = PB6
|
||||
I2C0_SDA_PIN = PB7
|
||||
)
|
||||
|
||||
@@ -33,9 +33,10 @@ var (
|
||||
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
||||
// Both UART0 and UART1 refer to USART2.
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART3,
|
||||
AltFuncSelector: UART_ALT_FN,
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART3,
|
||||
TxAltFuncSelector: UART_ALT_FN,
|
||||
RxAltFuncSelector: UART_ALT_FN,
|
||||
}
|
||||
UART1 = &UART0
|
||||
)
|
||||
@@ -53,6 +54,15 @@ const (
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SCL_PIN = PB6
|
||||
SDA_PIN = PB7
|
||||
I2C0_SCL_PIN = PB8
|
||||
I2C0_SDA_PIN = PB9
|
||||
)
|
||||
|
||||
var (
|
||||
// I2C1 is documented, alias to I2C0 as well
|
||||
I2C1 = &I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: 4,
|
||||
}
|
||||
I2C0 = I2C1
|
||||
)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// +build nucleol432kc
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const (
|
||||
LED = LED_BUILTIN
|
||||
LED_BUILTIN = LED_GREEN
|
||||
LED_GREEN = PB3
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
// PA2 and PA15 are connected to the ST-Link Virtual Com Port (VCP)
|
||||
UART_TX_PIN = PA2
|
||||
UART_RX_PIN = PA15
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
// With default solder bridge settings:
|
||||
// PB6 / Arduino D5 / CN3 Pin 8 is SCL
|
||||
// PB7 / Arduino D4 / CN3 Pin 7 is SDA
|
||||
I2C0_SCL_PIN = PB6
|
||||
I2C0_SDA_PIN = PB7
|
||||
)
|
||||
|
||||
var (
|
||||
// USART2 is the hardware serial port connected to the onboard ST-LINK
|
||||
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
||||
// Both UART0 and UART1 refer to USART2.
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART2,
|
||||
TxAltFuncSelector: 7,
|
||||
RxAltFuncSelector: 3,
|
||||
}
|
||||
UART1 = &UART0
|
||||
)
|
||||
|
||||
var (
|
||||
// I2C1 is documented, alias to I2C0 as well
|
||||
I2C1 = &I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: 4,
|
||||
}
|
||||
I2C0 = I2C1
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART0.Interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
|
||||
}
|
||||
@@ -33,13 +33,28 @@ var (
|
||||
// debugger to be exposed as virtual COM port over USB on Nucleo boards.
|
||||
// Both UART0 and UART1 refer to LPUART1.
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.LPUART1,
|
||||
AltFuncSelector: UART_ALT_FN,
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.LPUART1,
|
||||
TxAltFuncSelector: UART_ALT_FN,
|
||||
RxAltFuncSelector: UART_ALT_FN,
|
||||
}
|
||||
UART1 = &UART0
|
||||
)
|
||||
|
||||
const (
|
||||
I2C0_SCL_PIN = PB8
|
||||
I2C0_SDA_PIN = PB9
|
||||
)
|
||||
|
||||
var (
|
||||
// I2C1 is documented, alias to I2C0 as well
|
||||
I2C1 = &I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: 4,
|
||||
}
|
||||
I2C0 = I2C1
|
||||
)
|
||||
|
||||
func init() {
|
||||
UART0.Interrupt = interrupt.New(stm32.IRQ_LPUART1, UART0.handleInterrupt)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func init() {
|
||||
|
||||
// I2C on the P1AM-100.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM0_I2CM,
|
||||
SERCOM: 0,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// +build pca10059
|
||||
|
||||
package machine
|
||||
|
||||
// The PCA10040 has a low-frequency (32kHz) crystal oscillator on board.
|
||||
const HasLowFrequencyCrystal = true
|
||||
|
||||
// LEDs on the PCA10059 (nRF52840 dongle)
|
||||
const (
|
||||
LED Pin = LED1
|
||||
LED1 Pin = 6
|
||||
LED2 Pin = 8
|
||||
LED3 Pin = (1 << 5) | 9
|
||||
LED4 Pin = 12
|
||||
)
|
||||
|
||||
// Buttons on the PCA10059 (nRF52840 dongle)
|
||||
const (
|
||||
BUTTON Pin = BUTTON1
|
||||
BUTTON1 Pin = (1 << 5) | 6
|
||||
)
|
||||
|
||||
// ADC pins
|
||||
const (
|
||||
ADC1 Pin = 2
|
||||
ADC2 Pin = 4
|
||||
ADC3 Pin = 29
|
||||
ADC4 Pin = 31
|
||||
)
|
||||
|
||||
// UART pins
|
||||
const (
|
||||
UART_TX_PIN Pin = NoPin
|
||||
UART_RX_PIN Pin = NoPin
|
||||
)
|
||||
|
||||
// UART0 is the USB device
|
||||
var (
|
||||
UART0 = USB
|
||||
)
|
||||
|
||||
// I2C pins (unused)
|
||||
const (
|
||||
SDA_PIN = NoPin
|
||||
SCL_PIN = NoPin
|
||||
)
|
||||
|
||||
// SPI pins (unused)
|
||||
const (
|
||||
SPI0_SCK_PIN = NoPin
|
||||
SPI0_SDO_PIN = NoPin
|
||||
SPI0_SDI_PIN = NoPin
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "nRF52840 Dongle"
|
||||
usb_STRING_MANUFACTURER = "Nordic Semiconductor ASA"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x1915
|
||||
usb_PID uint16 = 0xCAFE
|
||||
)
|
||||
@@ -28,7 +28,7 @@ func init() {
|
||||
|
||||
// I2C on the ItsyBitsy M4.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ const (
|
||||
|
||||
// I2C on the PyGamer.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func init() {
|
||||
|
||||
// I2C on the PyPortal.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM5_I2CM,
|
||||
SERCOM: 5,
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ const (
|
||||
|
||||
// I2C on the QT Py M0.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
// +build bluepill nucleof103rb stm32f4
|
||||
|
||||
package machine
|
||||
|
||||
// Peripheral abstraction layer for the stm32.
|
||||
@@ -28,9 +28,10 @@ const (
|
||||
|
||||
var (
|
||||
UART0 = UART{
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART2,
|
||||
AltFuncSelector: AF7_USART1_2_3,
|
||||
Buffer: NewRingBuffer(),
|
||||
Bus: stm32.USART2,
|
||||
TxAltFuncSelector: AF7_USART1_2_3,
|
||||
RxAltFuncSelector: AF7_USART1_2_3,
|
||||
}
|
||||
UART1 = &UART0
|
||||
)
|
||||
@@ -73,7 +74,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: stm32.I2C1,
|
||||
AltFuncSelector: AF4_I2C1_2_3,
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ const (
|
||||
|
||||
// I2C on the Trinket M0.
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
|
||||
@@ -29,12 +29,12 @@ func init() {
|
||||
|
||||
// I2C on the Wio Terminal
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM4_I2CM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
|
||||
I2C1 = I2C{
|
||||
I2C1 = &I2C{
|
||||
Bus: sam.SERCOM4_I2CM,
|
||||
SERCOM: 4,
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ const (
|
||||
|
||||
// I2C on the Xiao
|
||||
var (
|
||||
I2C0 = I2C{
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
SERCOM: 2,
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
// +build avr nrf sam stm32,!stm32f7x2,!stm32l5x2,!stm32l0 fe310 k210
|
||||
// +build atmega nrf sam stm32,!stm32l0 fe310 k210
|
||||
|
||||
package machine
|
||||
|
||||
@@ -29,7 +29,7 @@ var (
|
||||
// Many I2C-compatible devices are organized in terms of registers. This method
|
||||
// is a shortcut to easily write to such registers. Also, it only works for
|
||||
// devices with 7-bit addresses, which is the vast majority.
|
||||
func (i2c I2C) WriteRegister(address uint8, register uint8, data []byte) error {
|
||||
func (i2c *I2C) WriteRegister(address uint8, register uint8, data []byte) error {
|
||||
buf := make([]uint8, len(data)+1)
|
||||
buf[0] = register
|
||||
copy(buf[1:], data)
|
||||
@@ -42,6 +42,6 @@ func (i2c I2C) WriteRegister(address uint8, register uint8, data []byte) error {
|
||||
// Many I2C-compatible devices are organized in terms of registers. This method
|
||||
// is a shortcut to easily read such registers. Also, it only works for devices
|
||||
// with 7-bit addresses, which is the vast majority.
|
||||
func (i2c I2C) ReadRegister(address uint8, register uint8, data []byte) error {
|
||||
func (i2c *I2C) ReadRegister(address uint8, register uint8, data []byte) error {
|
||||
return i2c.Tx(uint16(address), []byte{register}, data)
|
||||
}
|
||||
|
||||
@@ -9,13 +9,20 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// I2C on AVR.
|
||||
type I2C struct {
|
||||
}
|
||||
|
||||
// I2C0 is the only I2C interface on most AVRs.
|
||||
var I2C0 *I2C = nil
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
Frequency uint32
|
||||
}
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) error {
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
// Default I2C bus speed is 100 kHz.
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = TWI_FREQ_100KHZ
|
||||
@@ -42,7 +49,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
if len(w) != 0 {
|
||||
i2c.start(uint8(addr), true) // start transmission for writing
|
||||
for _, b := range w {
|
||||
@@ -63,7 +70,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
}
|
||||
|
||||
// start starts an I2C communication session.
|
||||
func (i2c I2C) start(address uint8, write bool) {
|
||||
func (i2c *I2C) start(address uint8, write bool) {
|
||||
// Clear TWI interrupt flag, put start condition on SDA, and enable TWI.
|
||||
avr.TWCR.Set((avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN))
|
||||
|
||||
@@ -80,7 +87,7 @@ func (i2c I2C) start(address uint8, write bool) {
|
||||
}
|
||||
|
||||
// stop ends an I2C communication session.
|
||||
func (i2c I2C) stop() {
|
||||
func (i2c *I2C) stop() {
|
||||
// Send stop condition.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO)
|
||||
|
||||
@@ -90,7 +97,7 @@ func (i2c I2C) stop() {
|
||||
}
|
||||
|
||||
// writeByte writes a single byte to the I2C bus.
|
||||
func (i2c I2C) writeByte(data byte) {
|
||||
func (i2c *I2C) writeByte(data byte) {
|
||||
// Write data to register.
|
||||
avr.TWDR.Set(data)
|
||||
|
||||
@@ -103,7 +110,7 @@ func (i2c I2C) writeByte(data byte) {
|
||||
}
|
||||
|
||||
// readByte reads a single byte from the I2C bus.
|
||||
func (i2c I2C) readByte() byte {
|
||||
func (i2c *I2C) readByte() byte {
|
||||
// Clear TWI interrupt flag and enable TWI.
|
||||
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA)
|
||||
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device"
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
@@ -667,7 +669,7 @@ const (
|
||||
const i2cTimeout = 1000
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) error {
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
// Default I2C bus speed is 100 kHz.
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = TWI_FREQ_100KHZ
|
||||
@@ -723,7 +725,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the I2C.
|
||||
func (i2c I2C) SetBaudRate(br uint32) {
|
||||
func (i2c *I2C) SetBaudRate(br uint32) {
|
||||
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
|
||||
// SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
|
||||
baud := CPUFrequency()/(2*br) - 5 - (((CPUFrequency() / 1000000) * riseTimeNanoseconds) / (2 * 1000))
|
||||
@@ -733,7 +735,7 @@ func (i2c I2C) SetBaudRate(br uint32) {
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
var err error
|
||||
if len(w) != 0 {
|
||||
// send start/address for write
|
||||
@@ -810,7 +812,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte to the I2C bus.
|
||||
func (i2c I2C) WriteByte(data byte) error {
|
||||
func (i2c *I2C) WriteByte(data byte) error {
|
||||
// Send data byte
|
||||
i2c.Bus.DATA.Set(data)
|
||||
|
||||
@@ -835,7 +837,7 @@ func (i2c I2C) WriteByte(data byte) error {
|
||||
}
|
||||
|
||||
// sendAddress sends the address and start signal
|
||||
func (i2c I2C) sendAddress(address uint16, write bool) error {
|
||||
func (i2c *I2C) sendAddress(address uint16, write bool) error {
|
||||
data := (address << 1)
|
||||
if !write {
|
||||
data |= 1 // set read flag
|
||||
@@ -855,7 +857,7 @@ func (i2c I2C) sendAddress(address uint16, write bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) signalStop() error {
|
||||
func (i2c *I2C) signalStop() error {
|
||||
i2c.Bus.CTRLB.SetBits(wireCmdStop << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Stop command
|
||||
timeout := i2cTimeout
|
||||
for i2c.Bus.SYNCBUSY.HasBits(sam.SERCOM_I2CM_SYNCBUSY_SYSOP) {
|
||||
@@ -867,7 +869,7 @@ func (i2c I2C) signalStop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) signalRead() error {
|
||||
func (i2c *I2C) signalRead() error {
|
||||
i2c.Bus.CTRLB.SetBits(wireCmdRead << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Read command
|
||||
timeout := i2cTimeout
|
||||
for i2c.Bus.SYNCBUSY.HasBits(sam.SERCOM_I2CM_SYNCBUSY_SYSOP) {
|
||||
@@ -879,7 +881,7 @@ func (i2c I2C) signalRead() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) readByte() byte {
|
||||
func (i2c *I2C) readByte() byte {
|
||||
for !i2c.Bus.INTFLAG.HasBits(sam.SERCOM_I2CM_INTFLAG_SB) {
|
||||
}
|
||||
return byte(i2c.Bus.DATA.Get())
|
||||
@@ -1265,6 +1267,160 @@ func (spi SPI) Transfer(w byte) (byte, error) {
|
||||
return byte(spi.Bus.DATA.Get()), nil
|
||||
}
|
||||
|
||||
var (
|
||||
ErrTxInvalidSliceSize = errors.New("SPI write and read slices must be same size")
|
||||
)
|
||||
|
||||
// Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read
|
||||
// interface, there must always be the same number of bytes written as bytes read.
|
||||
// The Tx method knows about this, and offers a few different ways of calling it.
|
||||
//
|
||||
// This form sends the bytes in tx buffer, putting the resulting bytes read into the rx buffer.
|
||||
// Note that the tx and rx buffers must be the same size:
|
||||
//
|
||||
// spi.Tx(tx, rx)
|
||||
//
|
||||
// This form sends the tx buffer, ignoring the result. Useful for sending "commands" that return zeros
|
||||
// until all the bytes in the command packet have been received:
|
||||
//
|
||||
// spi.Tx(tx, nil)
|
||||
//
|
||||
// This form sends zeros, putting the result into the rx buffer. Good for reading a "result packet":
|
||||
//
|
||||
// spi.Tx(nil, rx)
|
||||
//
|
||||
func (spi SPI) Tx(w, r []byte) error {
|
||||
if spi.Bus.BAUD.Get() == 0x00 {
|
||||
// When the SPI Freq is 24MHz, special processing is performed to improve the speed.
|
||||
|
||||
switch {
|
||||
case w == nil:
|
||||
// read only, so write zero and read a result.
|
||||
spi.rx(r)
|
||||
case r == nil:
|
||||
// write only
|
||||
spi.tx24mhz(w)
|
||||
|
||||
default:
|
||||
// write/read
|
||||
if len(w) != len(r) {
|
||||
return ErrTxInvalidSliceSize
|
||||
}
|
||||
|
||||
spi.txrx24mhz(w, r)
|
||||
}
|
||||
|
||||
} else {
|
||||
switch {
|
||||
case w == nil:
|
||||
// read only, so write zero and read a result.
|
||||
spi.rx(r)
|
||||
case r == nil:
|
||||
// write only
|
||||
spi.tx(w)
|
||||
|
||||
default:
|
||||
// write/read
|
||||
if len(w) != len(r) {
|
||||
return ErrTxInvalidSliceSize
|
||||
}
|
||||
|
||||
spi.txrx(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (spi SPI) tx(tx []byte) {
|
||||
for i := 0; i < len(tx); i++ {
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
|
||||
}
|
||||
spi.Bus.DATA.Set(uint32(tx[i]))
|
||||
}
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_TXC) {
|
||||
}
|
||||
|
||||
// read to clear RXC register
|
||||
for spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||
spi.Bus.DATA.Get()
|
||||
}
|
||||
}
|
||||
|
||||
func (spi SPI) rx(rx []byte) {
|
||||
spi.Bus.DATA.Set(0)
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
|
||||
}
|
||||
|
||||
for i := 1; i < len(rx); i++ {
|
||||
spi.Bus.DATA.Set(0)
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||
}
|
||||
rx[i-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||
}
|
||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
|
||||
func (spi SPI) txrx(tx, rx []byte) {
|
||||
spi.Bus.DATA.Set(uint32(tx[0]))
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_DRE) {
|
||||
}
|
||||
|
||||
for i := 1; i < len(rx); i++ {
|
||||
spi.Bus.DATA.Set(uint32(tx[i]))
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||
}
|
||||
rx[i-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||
}
|
||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
|
||||
// tx24mhz is a special tx/rx function for CPU Clock 48 Mhz and SPI Freq 24 Mhz
|
||||
func (spi SPI) tx24mhz(tx []byte) {
|
||||
spi.Bus.DATA.Set(uint32(tx[0]))
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
|
||||
for i := 1; i < len(tx); i++ {
|
||||
spi.Bus.DATA.Set(uint32(tx[i]))
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
spi.Bus.DATA.Get()
|
||||
}
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||
}
|
||||
spi.Bus.DATA.Get()
|
||||
}
|
||||
|
||||
// txrx24mhz is a special tx/rx function for CPU Clock 48 Mhz and SPI Freq 24 Mhz
|
||||
func (spi SPI) txrx24mhz(tx, rx []byte) {
|
||||
spi.Bus.DATA.Set(uint32(tx[0]))
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
|
||||
for i := 1; i < len(rx); i++ {
|
||||
spi.Bus.DATA.Set(uint32(tx[i]))
|
||||
device.Asm("nop")
|
||||
device.Asm("nop")
|
||||
rx[i-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPI_INTFLAG_RXC) {
|
||||
}
|
||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
|
||||
// PWM
|
||||
const period = 0xFFFF
|
||||
|
||||
|
||||
@@ -1104,7 +1104,7 @@ const (
|
||||
const i2cTimeout = 1000
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) error {
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
// Default I2C bus speed is 100 kHz.
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = TWI_FREQ_100KHZ
|
||||
@@ -1163,7 +1163,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the I2C.
|
||||
func (i2c I2C) SetBaudRate(br uint32) {
|
||||
func (i2c *I2C) SetBaudRate(br uint32) {
|
||||
// Synchronous arithmetic baudrate, via Adafruit SAMD51 implementation:
|
||||
// sercom->I2CM.BAUD.bit.BAUD = SERCOM_FREQ_REF / ( 2 * baudrate) - 1 ;
|
||||
baud := SERCOM_FREQ_REF/(2*br) - 1
|
||||
@@ -1173,7 +1173,7 @@ func (i2c I2C) SetBaudRate(br uint32) {
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
var err error
|
||||
if len(w) != 0 {
|
||||
// send start/address for write
|
||||
@@ -1250,7 +1250,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte to the I2C bus.
|
||||
func (i2c I2C) WriteByte(data byte) error {
|
||||
func (i2c *I2C) WriteByte(data byte) error {
|
||||
// Send data byte
|
||||
i2c.Bus.DATA.Set(data)
|
||||
|
||||
@@ -1275,7 +1275,7 @@ func (i2c I2C) WriteByte(data byte) error {
|
||||
}
|
||||
|
||||
// sendAddress sends the address and start signal
|
||||
func (i2c I2C) sendAddress(address uint16, write bool) error {
|
||||
func (i2c *I2C) sendAddress(address uint16, write bool) error {
|
||||
data := (address << 1)
|
||||
if !write {
|
||||
data |= 1 // set read flag
|
||||
@@ -1295,7 +1295,7 @@ func (i2c I2C) sendAddress(address uint16, write bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) signalStop() error {
|
||||
func (i2c *I2C) signalStop() error {
|
||||
i2c.Bus.CTRLB.SetBits(wireCmdStop << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Stop command
|
||||
timeout := i2cTimeout
|
||||
for i2c.Bus.SYNCBUSY.HasBits(sam.SERCOM_I2CM_SYNCBUSY_SYSOP) {
|
||||
@@ -1307,7 +1307,7 @@ func (i2c I2C) signalStop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) signalRead() error {
|
||||
func (i2c *I2C) signalRead() error {
|
||||
i2c.Bus.CTRLB.SetBits(wireCmdRead << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Read command
|
||||
timeout := i2cTimeout
|
||||
for i2c.Bus.SYNCBUSY.HasBits(sam.SERCOM_I2CM_SYNCBUSY_SYSOP) {
|
||||
@@ -1319,7 +1319,7 @@ func (i2c I2C) signalRead() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) readByte() byte {
|
||||
func (i2c *I2C) readByte() byte {
|
||||
for !i2c.Bus.INTFLAG.HasBits(sam.SERCOM_I2CM_INTFLAG_SB) {
|
||||
}
|
||||
return byte(i2c.Bus.DATA.Get())
|
||||
@@ -1560,6 +1560,76 @@ func (spi SPI) txrx(tx, rx []byte) {
|
||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
|
||||
// TxN handles read/write operation for SPI interface. The difference with Tx() is that
|
||||
// it repeats the process n times.
|
||||
//
|
||||
func (spi SPI) TxN(w, r []byte, n int) error {
|
||||
switch {
|
||||
case w == nil:
|
||||
// read only, so write zero and read a result.
|
||||
spi.rxn(r, n)
|
||||
case r == nil:
|
||||
// write only
|
||||
spi.txn(w, n)
|
||||
|
||||
default:
|
||||
// write/read
|
||||
if len(w) != len(r) {
|
||||
return ErrTxInvalidSliceSize
|
||||
}
|
||||
|
||||
spi.txrxn(w, r, n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (spi SPI) txn(tx []byte, n int) {
|
||||
for i := 0; i < len(tx)*n; i++ {
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
|
||||
}
|
||||
spi.Bus.DATA.Set(uint32(tx[i%len(tx)]))
|
||||
}
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_TXC) {
|
||||
}
|
||||
|
||||
// read to clear RXC register
|
||||
for spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
|
||||
spi.Bus.DATA.Get()
|
||||
}
|
||||
}
|
||||
|
||||
func (spi SPI) rxn(rx []byte, n int) {
|
||||
spi.Bus.DATA.Set(0)
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
|
||||
}
|
||||
|
||||
for i := 1; i < len(rx)*n; i++ {
|
||||
spi.Bus.DATA.Set(0)
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
|
||||
}
|
||||
rx[(i-1)%len(rx)] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
|
||||
}
|
||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
|
||||
func (spi SPI) txrxn(tx, rx []byte, n int) {
|
||||
spi.Bus.DATA.Set(uint32(tx[0]))
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_DRE) {
|
||||
}
|
||||
|
||||
for i := 1; i < len(rx)*n; i++ {
|
||||
spi.Bus.DATA.Set(uint32(tx[i%len(rx)]))
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
|
||||
}
|
||||
rx[(i-1)%len(rx)] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
for !spi.Bus.INTFLAG.HasBits(sam.SERCOM_SPIM_INTFLAG_RXC) {
|
||||
}
|
||||
rx[len(rx)-1] = byte(spi.Bus.DATA.Get())
|
||||
}
|
||||
|
||||
// The QSPI peripheral on ATSAMD51 is only available on the following pins
|
||||
const (
|
||||
QSPI_SCK = PB10
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// +build avr,attiny
|
||||
|
||||
package machine
|
||||
|
||||
// Tx is a dummy implementation. I2C has not been implemented for ATtiny
|
||||
// devices.
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
return nil
|
||||
}
|
||||
@@ -141,10 +141,3 @@ func (a ADC) Get() uint16 {
|
||||
|
||||
return uint16(avr.ADCL.Get()) | uint16(avr.ADCH.Get())<<8
|
||||
}
|
||||
|
||||
// I2C on AVR.
|
||||
type I2C struct {
|
||||
}
|
||||
|
||||
// I2C0 is the only I2C interface on most AVRs.
|
||||
var I2C0 = I2C{}
|
||||
|
||||
@@ -5,6 +5,7 @@ package machine
|
||||
import (
|
||||
"device/sifive"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func CPUFrequency() uint32 {
|
||||
@@ -185,9 +186,13 @@ func (spi SPI) Transfer(w byte) (byte, error) {
|
||||
|
||||
// I2C on the FE310-G002.
|
||||
type I2C struct {
|
||||
Bus *sifive.I2C_Type
|
||||
Bus sifive.I2C_Type
|
||||
}
|
||||
|
||||
var (
|
||||
I2C0 = (*I2C)(unsafe.Pointer(sifive.I2C0))
|
||||
)
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
Frequency uint32
|
||||
@@ -196,7 +201,7 @@ type I2CConfig struct {
|
||||
}
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) error {
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
var i2cClockFrequency uint32 = 32000000
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = TWI_FREQ_100KHZ
|
||||
@@ -228,7 +233,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
var err error
|
||||
if len(w) != 0 {
|
||||
// send start/address for write
|
||||
@@ -276,7 +281,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
}
|
||||
|
||||
// Writes a single byte to the I2C bus.
|
||||
func (i2c I2C) writeByte(data byte) error {
|
||||
func (i2c *I2C) writeByte(data byte) error {
|
||||
// Send data byte
|
||||
i2c.Bus.TXR_RXR.Set(uint32(data))
|
||||
|
||||
@@ -295,7 +300,7 @@ func (i2c I2C) writeByte(data byte) error {
|
||||
}
|
||||
|
||||
// Reads a single byte from the I2C bus.
|
||||
func (i2c I2C) readByte() byte {
|
||||
func (i2c *I2C) readByte() byte {
|
||||
i2c.Bus.CR_SR.Set(sifive.I2C_CR_RD)
|
||||
|
||||
// wait until transmission complete
|
||||
@@ -306,7 +311,7 @@ func (i2c I2C) readByte() byte {
|
||||
}
|
||||
|
||||
// Sends the address and start signal.
|
||||
func (i2c I2C) sendAddress(address uint16, write bool) error {
|
||||
func (i2c *I2C) sendAddress(address uint16, write bool) error {
|
||||
data := (address << 1)
|
||||
if !write {
|
||||
data |= 1 // set read flag in transmit register
|
||||
|
||||
@@ -6,7 +6,7 @@ package machine
|
||||
|
||||
var (
|
||||
SPI0 = SPI{0}
|
||||
I2C0 = I2C{0}
|
||||
I2C0 = &I2C{0}
|
||||
UART0 = UART{0}
|
||||
)
|
||||
|
||||
@@ -115,13 +115,13 @@ type I2CConfig struct {
|
||||
}
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) error {
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
i2cConfigure(i2c.Bus, config.SCL, config.SDA)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
i2cTransfer(i2c.Bus, &w[0], len(w), &r[0], len(r))
|
||||
// TODO: do something with the returned error code.
|
||||
return nil
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"device/riscv"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func CPUFrequency() uint32 {
|
||||
@@ -493,9 +494,15 @@ func (spi SPI) Transfer(w byte) (byte, error) {
|
||||
|
||||
// I2C on the K210.
|
||||
type I2C struct {
|
||||
Bus *kendryte.I2C_Type
|
||||
Bus kendryte.I2C_Type
|
||||
}
|
||||
|
||||
var (
|
||||
I2C0 = (*I2C)(unsafe.Pointer(kendryte.I2C0))
|
||||
I2C1 = (*I2C)(unsafe.Pointer(kendryte.I2C1))
|
||||
I2C2 = (*I2C)(unsafe.Pointer(kendryte.I2C2))
|
||||
)
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
Frequency uint32
|
||||
@@ -504,7 +511,7 @@ type I2CConfig struct {
|
||||
}
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) error {
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = TWI_FREQ_100KHZ
|
||||
@@ -518,7 +525,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
||||
// Enable APB0 clock.
|
||||
kendryte.SYSCTL.CLK_EN_CENT.SetBits(kendryte.SYSCTL_CLK_EN_CENT_APB0_CLK_EN)
|
||||
|
||||
switch i2c.Bus {
|
||||
switch &i2c.Bus {
|
||||
case kendryte.I2C0:
|
||||
// Initialize I2C0 clock.
|
||||
kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_I2C0_CLK_EN)
|
||||
@@ -567,7 +574,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
// Set peripheral address.
|
||||
i2c.Bus.TAR.Set(uint32(addr))
|
||||
// Enable controller.
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"device/nrf"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -203,13 +204,13 @@ func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
|
||||
// I2C on the NRF.
|
||||
type I2C struct {
|
||||
Bus *nrf.TWI_Type
|
||||
Bus nrf.TWI_Type
|
||||
}
|
||||
|
||||
// There are 2 I2C interfaces on the NRF.
|
||||
var (
|
||||
I2C0 = I2C{Bus: nrf.TWI0}
|
||||
I2C1 = I2C{Bus: nrf.TWI1}
|
||||
I2C0 = (*I2C)(unsafe.Pointer(nrf.TWI0))
|
||||
I2C1 = (*I2C)(unsafe.Pointer(nrf.TWI1))
|
||||
)
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
@@ -220,7 +221,7 @@ type I2CConfig struct {
|
||||
}
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) error {
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
// Default I2C bus speed is 100 kHz.
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = TWI_FREQ_100KHZ
|
||||
@@ -261,7 +262,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) (err error) {
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) (err error) {
|
||||
i2c.Bus.ADDRESS.Set(uint32(addr))
|
||||
|
||||
if len(w) != 0 {
|
||||
@@ -299,7 +300,7 @@ cleanUp:
|
||||
// signalStop sends a stop signal when writing or tells the I2C peripheral that
|
||||
// it must generate a stop condition after the next character is retrieved when
|
||||
// reading.
|
||||
func (i2c I2C) signalStop() {
|
||||
func (i2c *I2C) signalStop() {
|
||||
i2c.Bus.TASKS_STOP.Set(1)
|
||||
for i2c.Bus.EVENTS_STOPPED.Get() == 0 {
|
||||
}
|
||||
@@ -307,7 +308,7 @@ func (i2c I2C) signalStop() {
|
||||
}
|
||||
|
||||
// writeByte writes a single byte to the I2C bus.
|
||||
func (i2c I2C) writeByte(data byte) error {
|
||||
func (i2c *I2C) writeByte(data byte) error {
|
||||
i2c.Bus.TXD.Set(uint32(data))
|
||||
for i2c.Bus.EVENTS_TXDSENT.Get() == 0 {
|
||||
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
|
||||
@@ -320,7 +321,7 @@ func (i2c I2C) writeByte(data byte) error {
|
||||
}
|
||||
|
||||
// readByte reads a single byte from the I2C bus.
|
||||
func (i2c I2C) readByte() (byte, error) {
|
||||
func (i2c *I2C) readByte() (byte, error) {
|
||||
for i2c.Bus.EVENTS_RXDREADY.Get() == 0 {
|
||||
if e := i2c.Bus.EVENTS_ERROR.Get(); e != 0 {
|
||||
i2c.Bus.EVENTS_ERROR.Set(0)
|
||||
|
||||
@@ -24,7 +24,7 @@ func (uart UART) setPins(tx, rx Pin) {
|
||||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
func (i2c *I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func (uart UART) setPins(tx, rx Pin) {
|
||||
nrf.UART0.PSELRXD.Set(uint32(rx))
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
func (i2c *I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSELSCL.Set(uint32(scl))
|
||||
i2c.Bus.PSELSDA.Set(uint32(sda))
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func (uart UART) setPins(tx, rx Pin) {
|
||||
nrf.UART0.PSEL.RXD.Set(uint32(rx))
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
func (i2c *I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSEL.SCL.Set(uint32(scl))
|
||||
i2c.Bus.PSEL.SDA.Set(uint32(sda))
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ func (uart UART) setPins(tx, rx Pin) {
|
||||
nrf.UART0.PSEL.RXD.Set(uint32(rx))
|
||||
}
|
||||
|
||||
func (i2c I2C) setPins(scl, sda Pin) {
|
||||
func (i2c *I2C) setPins(scl, sda Pin) {
|
||||
i2c.Bus.PSEL.SCL.Set(uint32(scl))
|
||||
i2c.Bus.PSEL.SDA.Set(uint32(sda))
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// +build stm32,!stm32f103,!stm32f7x2,!stm32l5x2,!stm32l0
|
||||
// +build stm32f4 stm32f1
|
||||
|
||||
package machine
|
||||
|
||||
// Peripheral abstraction layer for I2C on the stm32 family
|
||||
// I2C implementation for 'older' STM32 MCUs, including the F1 and F4 series
|
||||
// of MCUs.
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
@@ -28,7 +29,7 @@ const (
|
||||
flagMSL = 0x00100001
|
||||
)
|
||||
|
||||
func (i2c I2C) hasFlag(flag uint32) bool {
|
||||
func (i2c *I2C) hasFlag(flag uint32) bool {
|
||||
const mask = 0x0000FFFF
|
||||
if uint8(flag>>16) == 1 {
|
||||
return i2c.Bus.SR1.HasBits(flag & mask)
|
||||
@@ -37,18 +38,18 @@ func (i2c I2C) hasFlag(flag uint32) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (i2c I2C) clearFlag(flag uint32) {
|
||||
func (i2c *I2C) clearFlag(flag uint32) {
|
||||
const mask = 0x0000FFFF
|
||||
i2c.Bus.SR1.Set(^(flag & mask))
|
||||
}
|
||||
|
||||
// clearFlagADDR reads both status registers to clear any pending ADDR flags.
|
||||
func (i2c I2C) clearFlagADDR() {
|
||||
func (i2c *I2C) clearFlagADDR() {
|
||||
i2c.Bus.SR1.Get()
|
||||
i2c.Bus.SR2.Get()
|
||||
}
|
||||
|
||||
func (i2c I2C) waitForFlag(flag uint32, set bool) bool {
|
||||
func (i2c *I2C) waitForFlag(flag uint32, set bool) bool {
|
||||
const tryMax = 10000
|
||||
hasFlag := false
|
||||
for i := 0; !hasFlag && i < tryMax; i++ {
|
||||
@@ -57,7 +58,7 @@ func (i2c I2C) waitForFlag(flag uint32, set bool) bool {
|
||||
return hasFlag
|
||||
}
|
||||
|
||||
func (i2c I2C) waitForFlagOrError(flag uint32, set bool) bool {
|
||||
func (i2c *I2C) waitForFlagOrError(flag uint32, set bool) bool {
|
||||
const tryMax = 10000
|
||||
hasFlag := false
|
||||
for i := 0; !hasFlag && i < tryMax; i++ {
|
||||
@@ -106,7 +107,7 @@ type I2CConfig struct {
|
||||
}
|
||||
|
||||
// Configure is intended to setup the STM32 I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) error {
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
|
||||
// The following is the required sequence in controller mode.
|
||||
// 1. Program the peripheral input clock in I2C_CR2 Register in order to
|
||||
@@ -156,7 +157,7 @@ func (i2c I2C) Configure(config I2CConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
|
||||
if err := i2c.controllerTransmit(addr, w); nil != err {
|
||||
return err
|
||||
@@ -171,7 +172,7 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) controllerTransmit(addr uint16, w []byte) error {
|
||||
func (i2c *I2C) controllerTransmit(addr uint16, w []byte) error {
|
||||
|
||||
if !i2c.waitForFlag(flagBUSY, false) {
|
||||
return errI2CBusReadyTimeout
|
||||
@@ -223,7 +224,7 @@ func (i2c I2C) controllerTransmit(addr uint16, w []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) controllerRequestWrite(addr uint16, option transferOption) error {
|
||||
func (i2c *I2C) controllerRequestWrite(addr uint16, option transferOption) error {
|
||||
|
||||
if frameFirstAndLast == option || frameFirst == option || frameNoOption == option {
|
||||
// generate start condition
|
||||
@@ -249,7 +250,7 @@ func (i2c I2C) controllerRequestWrite(addr uint16, option transferOption) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) controllerReceive(addr uint16, r []byte) error {
|
||||
func (i2c *I2C) controllerReceive(addr uint16, r []byte) error {
|
||||
|
||||
if !i2c.waitForFlag(flagBUSY, false) {
|
||||
return errI2CBusReadyTimeout
|
||||
@@ -399,7 +400,7 @@ func (i2c I2C) controllerReceive(addr uint16, r []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) controllerRequestRead(addr uint16, option transferOption) error {
|
||||
func (i2c *I2C) controllerRequestRead(addr uint16, option transferOption) error {
|
||||
|
||||
// enable ACK
|
||||
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
|
||||
@@ -0,0 +1,347 @@
|
||||
// +build stm32l5 stm32f7 stm32l4
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:linkname ticks runtime.ticks
|
||||
func ticks() int64
|
||||
|
||||
// I2C implementation for 'newer' STM32 MCUs, including the F7, L5 and L4
|
||||
// series of MCUs.
|
||||
//
|
||||
// Currently, only 100KHz mode is supported
|
||||
|
||||
const (
|
||||
flagBUSY = stm32.I2C_ISR_BUSY
|
||||
flagTCR = stm32.I2C_ISR_TCR
|
||||
flagRXNE = stm32.I2C_ISR_RXNE
|
||||
flagSTOPF = stm32.I2C_ISR_STOPF
|
||||
flagAF = stm32.I2C_ISR_NACKF
|
||||
flagTXIS = stm32.I2C_ISR_TXIS
|
||||
flagTXE = stm32.I2C_ISR_TXE
|
||||
)
|
||||
|
||||
const (
|
||||
MAX_NBYTE_SIZE = 255
|
||||
TIMEOUT_TICKS = 100 // 100ms
|
||||
|
||||
I2C_NO_STARTSTOP = 0x0
|
||||
I2C_GENERATE_START_WRITE = 0x80000000 | stm32.I2C_CR2_START
|
||||
I2C_GENERATE_START_READ = 0x80000000 | stm32.I2C_CR2_START | stm32.I2C_CR2_RD_WRN
|
||||
I2C_GENERATE_STOP = 0x80000000 | stm32.I2C_CR2_STOP
|
||||
)
|
||||
|
||||
type I2C struct {
|
||||
Bus *stm32.I2C_Type
|
||||
AltFuncSelector uint8
|
||||
}
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
SCL Pin
|
||||
SDA Pin
|
||||
}
|
||||
|
||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||
// disable I2C interface before any configuration changes
|
||||
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_PE)
|
||||
|
||||
// enable clock for I2C
|
||||
enableAltFuncClock(unsafe.Pointer(i2c.Bus))
|
||||
|
||||
// init pins
|
||||
if config.SCL == 0 && config.SDA == 0 {
|
||||
config.SCL = I2C0_SCL_PIN
|
||||
config.SDA = I2C0_SDA_PIN
|
||||
}
|
||||
i2c.configurePins(config)
|
||||
|
||||
// Frequency range
|
||||
i2c.Bus.TIMINGR.Set(i2c.getFreqRange())
|
||||
|
||||
// Disable Own Address1 before set the Own Address1 configuration
|
||||
i2c.Bus.OAR1.ClearBits(stm32.I2C_OAR1_OA1EN)
|
||||
|
||||
// 7 bit addressing, no self address
|
||||
i2c.Bus.OAR1.Set(stm32.I2C_OAR1_OA1EN)
|
||||
|
||||
// Enable the AUTOEND by default, and enable NACK (should be disable only during Slave process
|
||||
i2c.Bus.CR2.Set(stm32.I2C_CR2_AUTOEND | stm32.I2C_CR2_NACK)
|
||||
|
||||
// Disable Own Address2 / Dual Addressing
|
||||
i2c.Bus.OAR2.Set(0)
|
||||
|
||||
// Disable Generalcall and NoStretch, Enable peripheral
|
||||
i2c.Bus.CR1.Set(stm32.I2C_CR1_PE)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||
if len(w) > 0 {
|
||||
if err := i2c.controllerTransmit(addr, w); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(r) > 0 {
|
||||
if err := i2c.controllerReceive(addr, r); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c *I2C) configurePins(config I2CConfig) {
|
||||
config.SCL.ConfigureAltFunc(PinConfig{Mode: PinModeI2CSCL}, i2c.AltFuncSelector)
|
||||
config.SDA.ConfigureAltFunc(PinConfig{Mode: PinModeI2CSDA}, i2c.AltFuncSelector)
|
||||
}
|
||||
|
||||
func (i2c *I2C) controllerTransmit(addr uint16, w []byte) error {
|
||||
start := ticks()
|
||||
|
||||
if !i2c.waitOnFlagUntilTimeout(flagBUSY, false, start) {
|
||||
return errI2CBusReadyTimeout
|
||||
}
|
||||
|
||||
pos := 0
|
||||
xferCount := len(w)
|
||||
xferSize := uint8(xferCount)
|
||||
if xferCount > MAX_NBYTE_SIZE {
|
||||
// Large write, indicate reload
|
||||
xferSize = MAX_NBYTE_SIZE
|
||||
i2c.transferConfig(addr, xferSize, stm32.I2C_CR2_RELOAD, I2C_GENERATE_START_WRITE)
|
||||
} else {
|
||||
// Small write, auto-end
|
||||
i2c.transferConfig(addr, xferSize, stm32.I2C_CR2_AUTOEND, I2C_GENERATE_START_WRITE)
|
||||
}
|
||||
|
||||
for xferCount > 0 {
|
||||
if !i2c.waitOnTXISFlagUntilTimeout(start) {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
|
||||
i2c.Bus.TXDR.Set(uint32(w[pos]))
|
||||
pos++
|
||||
xferCount--
|
||||
xferSize--
|
||||
|
||||
// If we've written the last byte of this chunk
|
||||
if xferCount != 0 && xferSize == 0 {
|
||||
// Wait for Transfer Complete Reload to be flagged
|
||||
if !i2c.waitOnFlagUntilTimeout(flagTCR, true, start) {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
|
||||
if xferCount > MAX_NBYTE_SIZE {
|
||||
// Large write remaining, indicate reload
|
||||
xferSize = MAX_NBYTE_SIZE
|
||||
i2c.transferConfig(addr, xferSize, stm32.I2C_CR2_RELOAD, I2C_NO_STARTSTOP)
|
||||
} else {
|
||||
// Small write, auto-end
|
||||
xferSize = uint8(xferCount)
|
||||
i2c.transferConfig(addr, xferSize, stm32.I2C_CR2_AUTOEND, I2C_NO_STARTSTOP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !i2c.waitOnStopFlagUntilTimeout(start) {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
|
||||
i2c.clearFlag(stm32.I2C_ISR_STOPF)
|
||||
|
||||
i2c.resetCR2()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c *I2C) controllerReceive(addr uint16, r []byte) error {
|
||||
start := ticks()
|
||||
|
||||
if !i2c.waitOnFlagUntilTimeout(flagBUSY, false, start) {
|
||||
return errI2CBusReadyTimeout
|
||||
}
|
||||
|
||||
pos := 0
|
||||
xferCount := len(r)
|
||||
xferSize := uint8(xferCount)
|
||||
if xferCount > MAX_NBYTE_SIZE {
|
||||
// Large read, indicate reload
|
||||
xferSize = MAX_NBYTE_SIZE
|
||||
i2c.transferConfig(addr, xferSize, stm32.I2C_CR2_RELOAD, I2C_GENERATE_START_READ)
|
||||
} else {
|
||||
// Small read, auto-end
|
||||
i2c.transferConfig(addr, xferSize, stm32.I2C_CR2_AUTOEND, I2C_GENERATE_START_READ)
|
||||
}
|
||||
|
||||
for xferCount > 0 {
|
||||
if !i2c.waitOnRXNEFlagUntilTimeout(start) {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
|
||||
r[pos] = uint8(i2c.Bus.RXDR.Get())
|
||||
pos++
|
||||
xferCount--
|
||||
xferSize--
|
||||
|
||||
// If we've read the last byte of this chunk
|
||||
if xferCount != 0 && xferSize == 0 {
|
||||
// Wait for Transfer Complete Reload to be flagged
|
||||
if !i2c.waitOnFlagUntilTimeout(flagTCR, true, start) {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
|
||||
if xferCount > MAX_NBYTE_SIZE {
|
||||
// Large read remaining, indicate reload
|
||||
xferSize = MAX_NBYTE_SIZE
|
||||
i2c.transferConfig(addr, xferSize, stm32.I2C_CR2_RELOAD, I2C_NO_STARTSTOP)
|
||||
} else {
|
||||
// Small read, auto-end
|
||||
xferSize = uint8(xferCount)
|
||||
i2c.transferConfig(addr, xferSize, stm32.I2C_CR2_AUTOEND, I2C_NO_STARTSTOP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !i2c.waitOnStopFlagUntilTimeout(start) {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
|
||||
i2c.clearFlag(stm32.I2C_ISR_STOPF)
|
||||
|
||||
i2c.resetCR2()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c *I2C) waitOnFlagUntilTimeout(flag uint32, set bool, startTicks int64) bool {
|
||||
for i2c.hasFlag(flag) != set {
|
||||
if (ticks() - startTicks) > TIMEOUT_TICKS {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (i2c *I2C) waitOnRXNEFlagUntilTimeout(startTicks int64) bool {
|
||||
for !i2c.hasFlag(flagRXNE) {
|
||||
if i2c.isAcknowledgeFailed(startTicks) {
|
||||
return false
|
||||
}
|
||||
|
||||
if i2c.hasFlag(flagSTOPF) {
|
||||
i2c.clearFlag(flagSTOPF)
|
||||
i2c.resetCR2()
|
||||
return false
|
||||
}
|
||||
|
||||
if (ticks() - startTicks) > TIMEOUT_TICKS {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (i2c *I2C) waitOnTXISFlagUntilTimeout(startTicks int64) bool {
|
||||
for !i2c.hasFlag(flagTXIS) {
|
||||
if i2c.isAcknowledgeFailed(startTicks) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (ticks() - startTicks) > TIMEOUT_TICKS {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (i2c *I2C) waitOnStopFlagUntilTimeout(startTicks int64) bool {
|
||||
for !i2c.hasFlag(flagSTOPF) {
|
||||
if i2c.isAcknowledgeFailed(startTicks) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (ticks() - startTicks) > TIMEOUT_TICKS {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (i2c *I2C) isAcknowledgeFailed(startTicks int64) bool {
|
||||
if i2c.hasFlag(flagAF) {
|
||||
// Wait until STOP Flag is reset
|
||||
// AutoEnd should be initiate after AF
|
||||
for !i2c.hasFlag(flagSTOPF) {
|
||||
if (ticks() - startTicks) > TIMEOUT_TICKS {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
i2c.clearFlag(flagAF)
|
||||
i2c.clearFlag(flagSTOPF)
|
||||
i2c.flushTXDR()
|
||||
i2c.resetCR2()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (i2c *I2C) flushTXDR() {
|
||||
// If a pending TXIS flag is set, write a dummy data in TXDR to clear it
|
||||
if i2c.hasFlag(flagTXIS) {
|
||||
i2c.Bus.TXDR.Set(0)
|
||||
}
|
||||
|
||||
// Flush TX register if not empty
|
||||
if !i2c.hasFlag(flagTXE) {
|
||||
i2c.clearFlag(flagTXE)
|
||||
}
|
||||
}
|
||||
|
||||
func (i2c *I2C) resetCR2() {
|
||||
i2c.Bus.CR2.ClearBits(stm32.I2C_CR2_SADD_Msk |
|
||||
stm32.I2C_CR2_HEAD10R_Msk |
|
||||
stm32.I2C_CR2_NBYTES_Msk |
|
||||
stm32.I2C_CR2_RELOAD_Msk |
|
||||
stm32.I2C_CR2_RD_WRN_Msk)
|
||||
}
|
||||
|
||||
func (i2c *I2C) transferConfig(addr uint16, size uint8, mode uint32, request uint32) {
|
||||
mask := uint32(stm32.I2C_CR2_SADD_Msk |
|
||||
stm32.I2C_CR2_NBYTES_Msk |
|
||||
stm32.I2C_CR2_RELOAD_Msk |
|
||||
stm32.I2C_CR2_AUTOEND_Msk |
|
||||
(stm32.I2C_CR2_RD_WRN & uint32(request>>(31-stm32.I2C_CR2_RD_WRN_Pos))) |
|
||||
stm32.I2C_CR2_START_Msk |
|
||||
stm32.I2C_CR2_STOP_Msk)
|
||||
|
||||
value := (uint32(addr<<1) & stm32.I2C_CR2_SADD_Msk) |
|
||||
((uint32(size) << stm32.I2C_CR2_NBYTES_Pos) & stm32.I2C_CR2_NBYTES_Msk) |
|
||||
mode | request
|
||||
|
||||
i2c.Bus.CR2.ReplaceBits(value, mask, 0)
|
||||
}
|
||||
|
||||
func (i2c *I2C) hasFlag(flag uint32) bool {
|
||||
return i2c.Bus.ISR.HasBits(flag)
|
||||
}
|
||||
|
||||
func (i2c *I2C) clearFlag(flag uint32) {
|
||||
if flag == stm32.I2C_ISR_TXE {
|
||||
i2c.Bus.ISR.SetBits(flag)
|
||||
} else {
|
||||
i2c.Bus.ICR.SetBits(flag)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build stm32,!stm32f7x2,!stm32l5x2
|
||||
// +build stm32,!stm32f7x2,!stm32l5x2,!stm32l4x2
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build stm32,!stm32l0
|
||||
// +build stm32
|
||||
|
||||
package machine
|
||||
|
||||
@@ -13,10 +13,11 @@ import (
|
||||
|
||||
// UART representation
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
Bus *stm32.USART_Type
|
||||
Interrupt interrupt.Interrupt
|
||||
AltFuncSelector uint8
|
||||
Buffer *RingBuffer
|
||||
Bus *stm32.USART_Type
|
||||
Interrupt interrupt.Interrupt
|
||||
TxAltFuncSelector uint8
|
||||
RxAltFuncSelector uint8
|
||||
|
||||
// Registers specific to the chip
|
||||
rxReg *volatile.Register32
|
||||
|
||||
@@ -197,463 +197,81 @@ func (spi SPI) configurePins(config SPIConfig) {
|
||||
|
||||
//---------- I2C related types and code
|
||||
|
||||
type I2C struct {
|
||||
Bus *stm32.I2C_Type
|
||||
}
|
||||
|
||||
// There are 2 I2C interfaces on the STM32F103xx.
|
||||
// Since the first interface is named I2C1, both I2C0 and I2C1 refer to I2C1.
|
||||
// TODO: implement I2C2.
|
||||
var (
|
||||
I2C1 = I2C{Bus: stm32.I2C1}
|
||||
I2C1 = (*I2C)(unsafe.Pointer(stm32.I2C1))
|
||||
I2C0 = I2C1
|
||||
)
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
Frequency uint32
|
||||
SCL Pin
|
||||
SDA Pin
|
||||
type I2C struct {
|
||||
Bus *stm32.I2C_Type
|
||||
}
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) error {
|
||||
// Default I2C bus speed is 100 kHz.
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = TWI_FREQ_100KHZ
|
||||
}
|
||||
|
||||
// enable clock for I2C
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_I2C1EN)
|
||||
|
||||
// I2C1 pins
|
||||
switch config.SDA {
|
||||
case PB9:
|
||||
config.SCL = PB8
|
||||
func (i2c *I2C) configurePins(config I2CConfig) {
|
||||
if config.SDA == PB9 {
|
||||
// use alternate I2C1 pins PB8/PB9 via AFIO mapping
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_AFIOEN)
|
||||
stm32.AFIO.MAPR.SetBits(stm32.AFIO_MAPR_I2C1_REMAP)
|
||||
default:
|
||||
// use default I2C1 pins PB6/PB7
|
||||
config.SDA = SDA_PIN
|
||||
config.SCL = SCL_PIN
|
||||
}
|
||||
|
||||
config.SDA.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltOpenDrain})
|
||||
config.SCL.Configure(PinConfig{Mode: PinOutput50MHz + PinOutputModeAltOpenDrain})
|
||||
}
|
||||
|
||||
// Disable the selected I2C peripheral to configure
|
||||
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_PE)
|
||||
|
||||
func (i2c *I2C) getFreqRange(config I2CConfig) uint32 {
|
||||
// pclk1 clock speed is main frequency divided by PCLK1 prescaler (div 2)
|
||||
pclk1 := CPUFrequency() / 2
|
||||
|
||||
// set freqency range to PCLK1 clock speed in MHz
|
||||
// aka setting the value 36 means to use 36 MHz clock
|
||||
pclk1Mhz := pclk1 / 1000000
|
||||
i2c.Bus.CR2.SetBits(pclk1Mhz)
|
||||
|
||||
switch config.Frequency {
|
||||
case TWI_FREQ_100KHZ:
|
||||
// Normal mode speed calculation
|
||||
ccr := pclk1 / (config.Frequency * 2)
|
||||
i2c.Bus.CCR.Set(ccr)
|
||||
|
||||
// duty cycle 2
|
||||
i2c.Bus.CCR.ClearBits(stm32.I2C_CCR_DUTY)
|
||||
|
||||
// frequency standard mode
|
||||
i2c.Bus.CCR.ClearBits(stm32.I2C_CCR_F_S)
|
||||
|
||||
// Set Maximum Rise Time for standard mode
|
||||
i2c.Bus.TRISE.Set(pclk1Mhz)
|
||||
|
||||
case TWI_FREQ_400KHZ:
|
||||
// Fast mode speed calculation
|
||||
ccr := pclk1 / (config.Frequency * 3)
|
||||
i2c.Bus.CCR.Set(ccr)
|
||||
|
||||
// duty cycle 2
|
||||
i2c.Bus.CCR.ClearBits(stm32.I2C_CCR_DUTY)
|
||||
|
||||
// frequency fast mode
|
||||
i2c.Bus.CCR.SetBits(stm32.I2C_CCR_F_S)
|
||||
|
||||
// Set Maximum Rise Time for fast mode
|
||||
i2c.Bus.TRISE.Set(((pclk1Mhz * 300) / 1000))
|
||||
}
|
||||
|
||||
// re-enable the selected I2C peripheral
|
||||
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_PE)
|
||||
|
||||
return nil
|
||||
return pclk1 / 1000000
|
||||
}
|
||||
|
||||
// Tx does a single I2C transaction at the specified address.
|
||||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
||||
// bytes and stores them in r, and generates a stop condition on the bus.
|
||||
func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
||||
var err error
|
||||
if len(w) != 0 {
|
||||
// start transmission for writing
|
||||
err = i2c.signalStart()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// send address
|
||||
err = i2c.sendAddress(uint8(addr), true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, b := range w {
|
||||
err = i2c.WriteByte(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// sending stop here for write
|
||||
err = i2c.signalStop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (i2c *I2C) getRiseTime(config I2CConfig) uint32 {
|
||||
// These bits must be programmed with the maximum SCL rise time given in the
|
||||
// I2C bus specification, incremented by 1.
|
||||
// For instance: in Sm mode, the maximum allowed SCL rise time is 1000 ns.
|
||||
// If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to 0x08
|
||||
// and PCLK1 = 125 ns, therefore the TRISE[5:0] bits must be programmed with
|
||||
// 09h (1000 ns / 125 ns = 8 + 1)
|
||||
freqRange := i2c.getFreqRange(config)
|
||||
if config.Frequency > 100000 {
|
||||
// fast mode (Fm) adjustment
|
||||
freqRange *= 300
|
||||
freqRange /= 1000
|
||||
}
|
||||
if len(r) != 0 {
|
||||
// re-start transmission for reading
|
||||
err = i2c.signalStart()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 1 byte
|
||||
switch len(r) {
|
||||
case 1:
|
||||
// send address
|
||||
err = i2c.sendAddress(uint8(addr), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Disable ACK of received data
|
||||
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
|
||||
|
||||
// clear timeout here
|
||||
timeout := i2cTimeout
|
||||
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Generate stop condition
|
||||
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
||||
|
||||
timeout = i2cTimeout
|
||||
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_RxNE) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Read and return data byte from I2C data register
|
||||
r[0] = byte(i2c.Bus.DR.Get())
|
||||
|
||||
// wait for stop
|
||||
return i2c.waitForStop()
|
||||
|
||||
case 2:
|
||||
// enable pos
|
||||
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_POS)
|
||||
|
||||
// Enable ACK of received data
|
||||
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
|
||||
|
||||
// send address
|
||||
err = i2c.sendAddress(uint8(addr), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// clear address here
|
||||
timeout := i2cTimeout
|
||||
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Disable ACK of received data
|
||||
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
|
||||
|
||||
// wait for btf. we need a longer timeout here than normal.
|
||||
timeout = 1000
|
||||
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Generate stop condition
|
||||
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
||||
|
||||
// read the 2 bytes by reading twice.
|
||||
r[0] = byte(i2c.Bus.DR.Get())
|
||||
r[1] = byte(i2c.Bus.DR.Get())
|
||||
|
||||
// wait for stop
|
||||
err = i2c.waitForStop()
|
||||
|
||||
//disable pos
|
||||
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_POS)
|
||||
|
||||
return err
|
||||
|
||||
case 3:
|
||||
// Enable ACK of received data
|
||||
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
|
||||
|
||||
// send address
|
||||
err = i2c.sendAddress(uint8(addr), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// clear address here
|
||||
timeout := i2cTimeout
|
||||
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Enable ACK of received data
|
||||
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
|
||||
|
||||
// wait for btf. we need a longer timeout here than normal.
|
||||
timeout = 1000
|
||||
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Disable ACK of received data
|
||||
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
|
||||
|
||||
// read the first byte
|
||||
r[0] = byte(i2c.Bus.DR.Get())
|
||||
|
||||
timeout = 1000
|
||||
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Generate stop condition
|
||||
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
||||
|
||||
// read the last 2 bytes by reading twice.
|
||||
r[1] = byte(i2c.Bus.DR.Get())
|
||||
r[2] = byte(i2c.Bus.DR.Get())
|
||||
|
||||
// wait for stop
|
||||
return i2c.waitForStop()
|
||||
|
||||
default:
|
||||
// more than 3 bytes of data to read
|
||||
|
||||
// send address
|
||||
err = i2c.sendAddress(uint8(addr), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// clear address here
|
||||
timeout := i2cTimeout
|
||||
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(r)-3; i++ {
|
||||
// Enable ACK of received data
|
||||
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_ACK)
|
||||
|
||||
// wait for btf. we need a longer timeout here than normal.
|
||||
timeout = 1000
|
||||
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// read the next byte
|
||||
r[i] = byte(i2c.Bus.DR.Get())
|
||||
}
|
||||
|
||||
// wait for btf. we need a longer timeout here than normal.
|
||||
timeout = 1000
|
||||
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_BTF) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Disable ACK of received data
|
||||
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_ACK)
|
||||
|
||||
// get third from last byte
|
||||
r[len(r)-3] = byte(i2c.Bus.DR.Get())
|
||||
|
||||
// Generate stop condition
|
||||
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
||||
|
||||
// get second from last byte
|
||||
r[len(r)-2] = byte(i2c.Bus.DR.Get())
|
||||
|
||||
timeout = i2cTimeout
|
||||
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_RxNE) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// get last byte
|
||||
r[len(r)-1] = byte(i2c.Bus.DR.Get())
|
||||
|
||||
// wait for stop
|
||||
return i2c.waitForStop()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return (freqRange + 1) << stm32.I2C_TRISE_TRISE_Pos
|
||||
}
|
||||
|
||||
const i2cTimeout = 1000
|
||||
|
||||
// signalStart sends a start signal.
|
||||
func (i2c I2C) signalStart() error {
|
||||
// Wait until I2C is not busy
|
||||
timeout := i2cTimeout
|
||||
for i2c.Bus.SR2.HasBits(stm32.I2C_SR2_BUSY) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CSignalStartTimeout
|
||||
func (i2c *I2C) getSpeed(config I2CConfig) uint32 {
|
||||
ccr := func(pclk uint32, freq uint32, coeff uint32) uint32 {
|
||||
return (((pclk - 1) / (freq * coeff)) + 1) & stm32.I2C_CCR_CCR_Msk
|
||||
}
|
||||
sm := func(pclk uint32, freq uint32) uint32 { // standard mode (Sm)
|
||||
if s := ccr(pclk, freq, 2); s < 4 {
|
||||
return 4
|
||||
} else {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
// clear stop
|
||||
i2c.Bus.CR1.ClearBits(stm32.I2C_CR1_STOP)
|
||||
|
||||
// Generate start condition
|
||||
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_START)
|
||||
|
||||
// Wait for I2C EV5 aka SB flag.
|
||||
timeout = i2cTimeout
|
||||
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_SB) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CSignalStartTimeout
|
||||
fm := func(pclk uint32, freq uint32, duty uint8) uint32 { // fast mode (Fm)
|
||||
if duty == DutyCycle2 {
|
||||
return ccr(pclk, freq, 3)
|
||||
} else {
|
||||
return ccr(pclk, freq, 25) | stm32.I2C_CCR_DUTY
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// signalStop sends a stop signal and waits for it to succeed.
|
||||
func (i2c I2C) signalStop() error {
|
||||
// Generate stop condition
|
||||
i2c.Bus.CR1.SetBits(stm32.I2C_CR1_STOP)
|
||||
|
||||
// wait for stop
|
||||
return i2c.waitForStop()
|
||||
}
|
||||
|
||||
// waitForStop waits after a stop signal.
|
||||
func (i2c I2C) waitForStop() error {
|
||||
// Wait until I2C is stopped
|
||||
timeout := i2cTimeout
|
||||
for i2c.Bus.SR1.HasBits(stm32.I2C_SR1_STOPF) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CSignalStopTimeout
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send address of device we want to talk to
|
||||
func (i2c I2C) sendAddress(address uint8, write bool) error {
|
||||
data := (address << 1)
|
||||
if !write {
|
||||
data |= 1 // set read flag
|
||||
}
|
||||
|
||||
i2c.Bus.DR.Set(uint32(data))
|
||||
|
||||
// Wait for I2C EV6 event.
|
||||
// Destination device acknowledges address
|
||||
timeout := i2cTimeout
|
||||
if write {
|
||||
// EV6 which is ADDR flag.
|
||||
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_ADDR) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
}
|
||||
|
||||
timeout = i2cTimeout
|
||||
for !i2c.Bus.SR2.HasBits(stm32.I2C_SR2_MSL | stm32.I2C_SR2_BUSY | stm32.I2C_SR2_TRA) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
}
|
||||
clock := CPUFrequency() / 2
|
||||
if config.Frequency <= 100000 {
|
||||
return sm(clock, config.Frequency)
|
||||
} else {
|
||||
// I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED which is ADDR flag.
|
||||
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_ADDR) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
s := fm(clock, config.Frequency, config.DutyCycle)
|
||||
if (s & stm32.I2C_CCR_CCR_Msk) == 0 {
|
||||
return 1
|
||||
} else {
|
||||
return s | stm32.I2C_CCR_F_S
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte to the I2C bus.
|
||||
func (i2c I2C) WriteByte(data byte) error {
|
||||
// Send data byte
|
||||
i2c.Bus.DR.Set(uint32(data))
|
||||
|
||||
// Wait for I2C EV8_2 when data has been physically shifted out and
|
||||
// output on the bus.
|
||||
// I2C_EVENT_MASTER_BYTE_TRANSMITTED is TXE flag.
|
||||
timeout := i2cTimeout
|
||||
for !i2c.Bus.SR1.HasBits(stm32.I2C_SR1_TxE) {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errI2CWriteTimeout
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ const (
|
||||
|
||||
func (uart *UART) configurePins(config UARTConfig) {
|
||||
// enable the alternate functions on the TX and RX pins
|
||||
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.AltFuncSelector)
|
||||
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.AltFuncSelector)
|
||||
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.TxAltFuncSelector)
|
||||
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector)
|
||||
}
|
||||
|
||||
func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 {
|
||||
@@ -116,12 +116,12 @@ type I2C struct {
|
||||
AltFuncSelector uint8
|
||||
}
|
||||
|
||||
func (i2c I2C) configurePins(config I2CConfig) {
|
||||
func (i2c *I2C) configurePins(config I2CConfig) {
|
||||
config.SCL.ConfigureAltFunc(PinConfig{Mode: PinModeI2CSCL}, i2c.AltFuncSelector)
|
||||
config.SDA.ConfigureAltFunc(PinConfig{Mode: PinModeI2CSDA}, i2c.AltFuncSelector)
|
||||
}
|
||||
|
||||
func (i2c I2C) getFreqRange(config I2CConfig) uint32 {
|
||||
func (i2c *I2C) getFreqRange(config I2CConfig) uint32 {
|
||||
// all I2C interfaces are on APB1 (42 MHz)
|
||||
clock := CPUFrequency() / 4
|
||||
// convert to MHz
|
||||
@@ -139,7 +139,7 @@ func (i2c I2C) getFreqRange(config I2CConfig) uint32 {
|
||||
return clock << stm32.I2C_CR2_FREQ_Pos
|
||||
}
|
||||
|
||||
func (i2c I2C) getRiseTime(config I2CConfig) uint32 {
|
||||
func (i2c *I2C) getRiseTime(config I2CConfig) uint32 {
|
||||
// These bits must be programmed with the maximum SCL rise time given in the
|
||||
// I2C bus specification, incremented by 1.
|
||||
// For instance: in Sm mode, the maximum allowed SCL rise time is 1000 ns.
|
||||
@@ -155,7 +155,7 @@ func (i2c I2C) getRiseTime(config I2CConfig) uint32 {
|
||||
return (freqRange + 1) << stm32.I2C_TRISE_TRISE_Pos
|
||||
}
|
||||
|
||||
func (i2c I2C) getSpeed(config I2CConfig) uint32 {
|
||||
func (i2c *I2C) getSpeed(config I2CConfig) uint32 {
|
||||
ccr := func(pclk uint32, freq uint32, coeff uint32) uint32 {
|
||||
return (((pclk - 1) / (freq * coeff)) + 1) & stm32.I2C_CCR_CCR_Msk
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ const (
|
||||
// Configure the UART.
|
||||
func (uart *UART) configurePins(config UARTConfig) {
|
||||
// enable the alternate functions on the TX and RX pins
|
||||
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.AltFuncSelector)
|
||||
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.AltFuncSelector)
|
||||
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.TxAltFuncSelector)
|
||||
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector)
|
||||
}
|
||||
|
||||
// UART baudrate calc based on the bus and clockspeed
|
||||
@@ -128,12 +128,12 @@ type I2C struct {
|
||||
AltFuncSelector uint8
|
||||
}
|
||||
|
||||
func (i2c I2C) configurePins(config I2CConfig) {
|
||||
func (i2c *I2C) configurePins(config I2CConfig) {
|
||||
config.SCL.ConfigureAltFunc(PinConfig{Mode: PinModeI2CSCL}, i2c.AltFuncSelector)
|
||||
config.SDA.ConfigureAltFunc(PinConfig{Mode: PinModeI2CSDA}, i2c.AltFuncSelector)
|
||||
}
|
||||
|
||||
func (i2c I2C) getFreqRange(config I2CConfig) uint32 {
|
||||
func (i2c *I2C) getFreqRange(config I2CConfig) uint32 {
|
||||
// all I2C interfaces are on APB1 (42 MHz)
|
||||
clock := CPUFrequency() / 4
|
||||
// convert to MHz
|
||||
@@ -151,7 +151,7 @@ func (i2c I2C) getFreqRange(config I2CConfig) uint32 {
|
||||
return clock << stm32.I2C_CR2_FREQ_Pos
|
||||
}
|
||||
|
||||
func (i2c I2C) getRiseTime(config I2CConfig) uint32 {
|
||||
func (i2c *I2C) getRiseTime(config I2CConfig) uint32 {
|
||||
// These bits must be programmed with the maximum SCL rise time given in the
|
||||
// I2C bus specification, incremented by 1.
|
||||
// For instance: in Sm mode, the maximum allowed SCL rise time is 1000 ns.
|
||||
@@ -167,7 +167,7 @@ func (i2c I2C) getRiseTime(config I2CConfig) uint32 {
|
||||
return (freqRange + 1) << stm32.I2C_TRISE_TRISE_Pos
|
||||
}
|
||||
|
||||
func (i2c I2C) getSpeed(config I2CConfig) uint32 {
|
||||
func (i2c *I2C) getSpeed(config I2CConfig) uint32 {
|
||||
ccr := func(pclk uint32, freq uint32, coeff uint32) uint32 {
|
||||
return (((pclk - 1) / (freq * coeff)) + 1) & stm32.I2C_CCR_CCR_Msk
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ func CPUFrequency() uint32 {
|
||||
// Configure the UART.
|
||||
func (uart *UART) configurePins(config UARTConfig) {
|
||||
// enable the alternate functions on the TX and RX pins
|
||||
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.AltFuncSelector)
|
||||
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.AltFuncSelector)
|
||||
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.TxAltFuncSelector)
|
||||
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector)
|
||||
}
|
||||
|
||||
// UART baudrate calc based on the bus and clockspeed
|
||||
@@ -41,3 +41,13 @@ func (uart *UART) setRegisters() {
|
||||
uart.statusReg = &uart.Bus.ISR
|
||||
uart.txEmptyFlag = stm32.USART_ISR_TXE
|
||||
}
|
||||
|
||||
//---------- I2C related code
|
||||
|
||||
// Gets the value for TIMINGR register
|
||||
func (i2c *I2C) getFreqRange() uint32 {
|
||||
// This is a 'magic' value calculated by STM32CubeMX
|
||||
// for 27MHz PCLK1 (216MHz CPU Freq / 8).
|
||||
// TODO: Do calculations based on PCLK1
|
||||
return 0x00606A9B
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -189,19 +188,11 @@ func enableAltFuncClock(bus unsafe.Pointer) {
|
||||
|
||||
//---------- UART related types and code
|
||||
|
||||
// UART representation
|
||||
type UART struct {
|
||||
Buffer *RingBuffer
|
||||
Bus *stm32.USART_Type
|
||||
Interrupt interrupt.Interrupt
|
||||
AltFuncSelector uint8
|
||||
}
|
||||
|
||||
// Configure the UART.
|
||||
func (uart UART) configurePins(config UARTConfig) {
|
||||
// enable the alternate functions on the TX and RX pins
|
||||
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.AltFuncSelector)
|
||||
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.AltFuncSelector)
|
||||
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.TxAltFuncSelector)
|
||||
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector)
|
||||
}
|
||||
|
||||
// UART baudrate calc based on the bus and clockspeed
|
||||
@@ -222,6 +213,14 @@ func (uart UART) getBaudRateDivisor(baudRate uint32) uint32 {
|
||||
return rate
|
||||
}
|
||||
|
||||
// Register names vary by ST processor, these are for STM L0 family
|
||||
func (uart *UART) setRegisters() {
|
||||
uart.rxReg = &uart.Bus.RDR
|
||||
uart.txReg = &uart.Bus.TDR
|
||||
uart.statusReg = &uart.Bus.ISR
|
||||
uart.txEmptyFlag = stm32.USART_ISR_TXE
|
||||
}
|
||||
|
||||
//---------- SPI related types and code
|
||||
|
||||
// SPI on the STM32Fxxx using MODER / alternate function pins
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
// +build stm32,stm32l0
|
||||
|
||||
package machine
|
||||
|
||||
// Peripheral abstraction layer for UARTs on the stm32 family.
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Configure the UART.
|
||||
func (uart UART) Configure(config UARTConfig) {
|
||||
// Default baud rate to 115200.
|
||||
if config.BaudRate == 0 {
|
||||
config.BaudRate = 115200
|
||||
}
|
||||
|
||||
// Set the GPIO pins to defaults if they're not set
|
||||
if config.TX == 0 && config.RX == 0 {
|
||||
config.TX = UART_TX_PIN
|
||||
config.RX = UART_RX_PIN
|
||||
}
|
||||
|
||||
// Enable USART clock
|
||||
enableAltFuncClock(unsafe.Pointer(uart.Bus))
|
||||
|
||||
uart.configurePins(config)
|
||||
|
||||
// Set baud rate
|
||||
uart.SetBaudRate(config.BaudRate)
|
||||
|
||||
// Enable USART port, tx, rx and rx interrupts
|
||||
uart.Bus.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
|
||||
|
||||
// Enable RX IRQ
|
||||
uart.Interrupt.SetPriority(0xc0)
|
||||
uart.Interrupt.Enable()
|
||||
}
|
||||
|
||||
// handleInterrupt should be called from the appropriate interrupt handler for
|
||||
// this UART instance.
|
||||
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
|
||||
uart.Receive(byte((uart.Bus.RDR.Get() & 0xFF)))
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the UART. Defer to chip-specific
|
||||
// routines for calculation
|
||||
func (uart UART) SetBaudRate(br uint32) {
|
||||
divider := uart.getBaudRateDivisor(br)
|
||||
uart.Bus.BRR.Set(divider)
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the UART.
|
||||
func (uart UART) WriteByte(c byte) error {
|
||||
|
||||
uart.Bus.TDR.Set(uint32(c))
|
||||
|
||||
for !uart.Bus.ISR.HasBits(stm32.USART_ISR_TXE) {
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
// +build stm32l4
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Peripheral abstraction layer for the stm32l4
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
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
|
||||
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.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_GPIOAEN)
|
||||
case 1:
|
||||
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_GPIOBEN)
|
||||
case 2:
|
||||
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_GPIOCEN)
|
||||
case 3:
|
||||
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_GPIODEN)
|
||||
case 4:
|
||||
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_GPIOEEN)
|
||||
default:
|
||||
panic("machine: unknown port")
|
||||
}
|
||||
}
|
||||
|
||||
// Enable peripheral clock
|
||||
func enableAltFuncClock(bus unsafe.Pointer) {
|
||||
switch bus {
|
||||
case unsafe.Pointer(stm32.PWR): // Power interface clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_PWREN)
|
||||
case unsafe.Pointer(stm32.I2C3): // I2C3 clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_I2C3EN)
|
||||
case unsafe.Pointer(stm32.I2C2): // I2C2 clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_I2C2EN)
|
||||
case unsafe.Pointer(stm32.I2C1): // I2C1 clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_I2C1EN)
|
||||
case unsafe.Pointer(stm32.UART4): // UART4 clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_UART4EN)
|
||||
case unsafe.Pointer(stm32.USART3): // USART3 clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_USART3EN)
|
||||
case unsafe.Pointer(stm32.USART2): // USART2 clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_USART2EN)
|
||||
case unsafe.Pointer(stm32.SPI3): // SPI3 clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_SPI3EN)
|
||||
case unsafe.Pointer(stm32.SPI2): // SPI2 clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_SPI2EN)
|
||||
case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_WWDGEN)
|
||||
case unsafe.Pointer(stm32.TIM7): // TIM7 clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_TIM7EN)
|
||||
case unsafe.Pointer(stm32.TIM6): // TIM6 clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_TIM6EN)
|
||||
case unsafe.Pointer(stm32.TIM3): // TIM3 clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_TIM3EN)
|
||||
case unsafe.Pointer(stm32.TIM2): // TIM2 clock enable
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_TIM2EN)
|
||||
case unsafe.Pointer(stm32.LPTIM2): // LPTIM2 clock enable
|
||||
stm32.RCC.APB1ENR2.SetBits(stm32.RCC_APB1ENR2_LPTIM2EN)
|
||||
case unsafe.Pointer(stm32.I2C4): // I2C4 clock enable
|
||||
stm32.RCC.APB1ENR2.SetBits(stm32.RCC_APB1ENR2_I2C4EN)
|
||||
case unsafe.Pointer(stm32.LPUART1): // LPUART1 clock enable
|
||||
stm32.RCC.APB1ENR2.SetBits(stm32.RCC_APB1ENR2_LPUART1EN)
|
||||
case unsafe.Pointer(stm32.TIM16): // TIM16 clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM16EN)
|
||||
case unsafe.Pointer(stm32.TIM15): // TIM15 clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM15EN)
|
||||
case unsafe.Pointer(stm32.SYSCFG): // System configuration controller clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SYSCFGEN)
|
||||
case unsafe.Pointer(stm32.SPI1): // SPI1 clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_SPI1EN)
|
||||
case unsafe.Pointer(stm32.USART1): // USART1 clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_USART1EN)
|
||||
case unsafe.Pointer(stm32.TIM1): // TIM1 clock enable
|
||||
stm32.RCC.APB2ENR.SetBits(stm32.RCC_APB2ENR_TIM1EN)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// +build stm32l4x2
|
||||
|
||||
package machine
|
||||
|
||||
// Peripheral abstraction layer for the stm32l4x2
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
)
|
||||
|
||||
func CPUFrequency() uint32 {
|
||||
return 80000000
|
||||
}
|
||||
|
||||
//---------- UART related code
|
||||
|
||||
// Configure the UART.
|
||||
func (uart *UART) configurePins(config UARTConfig) {
|
||||
// enable the alternate functions on the TX and RX pins
|
||||
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.TxAltFuncSelector)
|
||||
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector)
|
||||
}
|
||||
|
||||
// UART baudrate calc based on the bus and clockspeed
|
||||
// NOTE: keep this in sync with the runtime/runtime_stm32l5x2.go clock init code
|
||||
func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 {
|
||||
return (CPUFrequency() / baudRate)
|
||||
}
|
||||
|
||||
// Register names vary by ST processor, these are for STM L5
|
||||
func (uart *UART) setRegisters() {
|
||||
uart.rxReg = &uart.Bus.RDR
|
||||
uart.txReg = &uart.Bus.TDR
|
||||
uart.statusReg = &uart.Bus.ISR
|
||||
uart.txEmptyFlag = stm32.USART_ISR_TXE
|
||||
}
|
||||
|
||||
//---------- I2C related code
|
||||
|
||||
// Gets the value for TIMINGR register
|
||||
func (i2c *I2C) getFreqRange() uint32 {
|
||||
// This is a 'magic' value calculated by STM32CubeMX
|
||||
// for 80MHz PCLK1.
|
||||
// TODO: Do calculations based on PCLK1
|
||||
return 0x10909CEC
|
||||
}
|
||||
@@ -22,8 +22,8 @@ func (uart *UART) configurePins(config UARTConfig) {
|
||||
}
|
||||
|
||||
// enable the alternate functions on the TX and RX pins
|
||||
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.AltFuncSelector)
|
||||
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.AltFuncSelector)
|
||||
config.TX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTTX}, uart.TxAltFuncSelector)
|
||||
config.RX.ConfigureAltFunc(PinConfig{Mode: PinModeUARTRX}, uart.RxAltFuncSelector)
|
||||
}
|
||||
|
||||
// UART baudrate calc based on the bus and clockspeed
|
||||
@@ -39,3 +39,13 @@ func (uart *UART) setRegisters() {
|
||||
uart.statusReg = &uart.Bus.ISR
|
||||
uart.txEmptyFlag = stm32.USART_ISR_TXE
|
||||
}
|
||||
|
||||
//---------- I2C related code
|
||||
|
||||
// Gets the value for TIMINGR register
|
||||
func (i2c *I2C) getFreqRange() uint32 {
|
||||
// This is a 'magic' value calculated by STM32CubeMX
|
||||
// for 110MHz PCLK1.
|
||||
// TODO: Do calculations based on PCLK1
|
||||
return 0x40505681
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build !baremetal atsamd21 stm32,!stm32f7x2,!stm32l5x2 fe310 k210 atmega
|
||||
// +build !baremetal stm32,!stm32f7x2,!stm32l5x2,!stm32l4x2 fe310 k210 atmega
|
||||
|
||||
package machine
|
||||
|
||||
|
||||
+39
-3
@@ -2,6 +2,7 @@ package os
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -17,9 +18,8 @@ var (
|
||||
ErrExist = errors.New("file exists")
|
||||
)
|
||||
|
||||
func IsPermission(err error) bool {
|
||||
return err == ErrPermission
|
||||
}
|
||||
// The following code is copied from the official implementation.
|
||||
// https://github.com/golang/go/blob/4ce6a8e89668b87dce67e2f55802903d6eb9110a/src/os/error.go#L65-L104
|
||||
|
||||
func NewSyscallError(syscall string, err error) error {
|
||||
if err == nil {
|
||||
@@ -37,3 +37,39 @@ type SyscallError struct {
|
||||
func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() }
|
||||
|
||||
func (e *SyscallError) Unwrap() error { return e.Err }
|
||||
|
||||
func IsExist(err error) bool {
|
||||
return underlyingErrorIs(err, ErrExist)
|
||||
}
|
||||
|
||||
func IsNotExist(err error) bool {
|
||||
return underlyingErrorIs(err, ErrNotExist)
|
||||
}
|
||||
|
||||
func IsPermission(err error) bool {
|
||||
return underlyingErrorIs(err, ErrPermission)
|
||||
}
|
||||
|
||||
func underlyingErrorIs(err, target error) bool {
|
||||
// Note that this function is not errors.Is:
|
||||
// underlyingError only unwraps the specific error-wrapping types
|
||||
// that it historically did, not all errors implementing Unwrap().
|
||||
err = underlyingError(err)
|
||||
if err == target {
|
||||
return true
|
||||
}
|
||||
// To preserve prior behavior, only examine syscall errors.
|
||||
e, ok := err.(syscall.Errno)
|
||||
return ok && e.Is(target)
|
||||
}
|
||||
|
||||
// underlyingError returns the underlying error for known os error types.
|
||||
func underlyingError(err error) error {
|
||||
switch err := err.(type) {
|
||||
case *PathError:
|
||||
return err.Err
|
||||
case *SyscallError:
|
||||
return err.Err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
+10
-61
@@ -6,6 +6,7 @@
|
||||
package os
|
||||
|
||||
import (
|
||||
"io"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@@ -76,7 +77,7 @@ func Create(name string) (*File, error) {
|
||||
// read and any error encountered. At end of file, Read returns 0, io.EOF.
|
||||
func (f *File) Read(b []byte) (n int, err error) {
|
||||
n, err = f.handle.Read(b)
|
||||
if err != nil {
|
||||
if err != nil && err != io.EOF {
|
||||
err = &PathError{"read", f.name, err}
|
||||
}
|
||||
return
|
||||
@@ -155,59 +156,17 @@ func (e *PathError) Error() string {
|
||||
return e.Op + " " + e.Path + ": " + e.Err.Error()
|
||||
}
|
||||
|
||||
type FileMode uint32
|
||||
|
||||
// Mode constants, copied from the mainline Go source
|
||||
// https://github.com/golang/go/blob/4ce6a8e89668b87dce67e2f55802903d6eb9110a/src/os/types.go#L35-L63
|
||||
const (
|
||||
// The single letters are the abbreviations used by the String method's formatting.
|
||||
ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory
|
||||
ModeAppend // a: append-only
|
||||
ModeExclusive // l: exclusive use
|
||||
ModeTemporary // T: temporary file; Plan 9 only
|
||||
ModeSymlink // L: symbolic link
|
||||
ModeDevice // D: device file
|
||||
ModeNamedPipe // p: named pipe (FIFO)
|
||||
ModeSocket // S: Unix domain socket
|
||||
ModeSetuid // u: setuid
|
||||
ModeSetgid // g: setgid
|
||||
ModeCharDevice // c: Unix character device, when ModeDevice is set
|
||||
ModeSticky // t: sticky
|
||||
ModeIrregular // ?: non-regular file; nothing else is known about this file
|
||||
|
||||
// Mask for the type bits. For regular files, none will be set.
|
||||
ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular
|
||||
|
||||
ModePerm FileMode = 0777 // Unix permission bits
|
||||
O_RDONLY int = syscall.O_RDONLY
|
||||
O_WRONLY int = syscall.O_WRONLY
|
||||
O_RDWR int = syscall.O_RDWR
|
||||
O_APPEND int = syscall.O_APPEND
|
||||
O_CREATE int = syscall.O_CREAT
|
||||
O_EXCL int = syscall.O_EXCL
|
||||
O_SYNC int = syscall.O_SYNC
|
||||
O_TRUNC int = syscall.O_TRUNC
|
||||
)
|
||||
|
||||
// IsDir is a stub, always returning false
|
||||
func (m FileMode) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Stub constants
|
||||
const (
|
||||
O_RDONLY int = 1
|
||||
O_WRONLY int = 2
|
||||
O_RDWR int = 4
|
||||
O_APPEND int = 8
|
||||
O_CREATE int = 16
|
||||
O_EXCL int = 32
|
||||
O_SYNC int = 64
|
||||
O_TRUNC int = 128
|
||||
)
|
||||
|
||||
// A FileInfo describes a file and is returned by Stat and Lstat.
|
||||
type FileInfo interface {
|
||||
Name() string // base name of the file
|
||||
Size() int64 // length in bytes for regular files; system-dependent for others
|
||||
Mode() FileMode // file mode bits
|
||||
// TODO ModTime() time.Time // modification time
|
||||
IsDir() bool // abbreviation for Mode().IsDir()
|
||||
Sys() interface{} // underlying data source (can return nil)
|
||||
}
|
||||
|
||||
// Stat is a stub, not yet implemented
|
||||
func Stat(name string) (FileInfo, error) {
|
||||
return nil, &PathError{"stat", name, ErrNotImplemented}
|
||||
@@ -233,16 +192,6 @@ func TempDir() string {
|
||||
return "/tmp"
|
||||
}
|
||||
|
||||
// IsExist is a stub (for now), always returning false
|
||||
func IsExist(err error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsNotExist is a stub (for now), always returning false
|
||||
func IsNotExist(err error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Getpid is a stub (for now), always returning 1
|
||||
func Getpid() int {
|
||||
return 1
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// +build go1.16
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
type (
|
||||
DirEntry = fs.DirEntry
|
||||
FileMode = fs.FileMode
|
||||
FileInfo = fs.FileInfo
|
||||
)
|
||||
|
||||
func (f *File) ReadDir(n int) ([]DirEntry, error) {
|
||||
return nil, &PathError{"ReadDir", f.name, ErrNotImplemented}
|
||||
}
|
||||
|
||||
// The followings are copied from Go 1.16 official implementation:
|
||||
// https://github.com/golang/go/blob/go1.16/src/os/file.go
|
||||
|
||||
// ReadFile reads the named file and returns the contents.
|
||||
// A successful call returns err == nil, not err == EOF.
|
||||
// Because ReadFile reads the whole file, it does not treat an EOF from Read
|
||||
// as an error to be reported.
|
||||
func ReadFile(name string) ([]byte, error) {
|
||||
f, err := Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var size int
|
||||
if info, err := f.Stat(); err == nil {
|
||||
size64 := info.Size()
|
||||
if int64(int(size64)) == size64 {
|
||||
size = int(size64)
|
||||
}
|
||||
}
|
||||
size++ // one byte for final read at EOF
|
||||
|
||||
// If a file claims a small size, read at least 512 bytes.
|
||||
// In particular, files in Linux's /proc claim size 0 but
|
||||
// then do not work right if read in small pieces,
|
||||
// so an initial read of 1 byte would not work correctly.
|
||||
if size < 512 {
|
||||
size = 512
|
||||
}
|
||||
|
||||
data := make([]byte, 0, size)
|
||||
for {
|
||||
if len(data) >= cap(data) {
|
||||
d := append(data[:cap(data)], 0)
|
||||
data = d[:len(data)]
|
||||
}
|
||||
n, err := f.Read(data[len(data):cap(data)])
|
||||
data = data[:len(data)+n]
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WriteFile writes data to the named file, creating it if necessary.
|
||||
// If the file does not exist, WriteFile creates it with permissions perm (before umask);
|
||||
// otherwise WriteFile truncates it before writing, without changing permissions.
|
||||
func WriteFile(name string, data []byte, perm FileMode) error {
|
||||
f, err := OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Write(data)
|
||||
if err1 := f.Close(); err1 != nil && err == nil {
|
||||
err = err1
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// +build !go1.16
|
||||
|
||||
package os
|
||||
|
||||
import "time"
|
||||
|
||||
// A FileInfo describes a file and is returned by Stat and Lstat.
|
||||
type FileInfo interface {
|
||||
Name() string // base name of the file
|
||||
Size() int64 // length in bytes for regular files; system-dependent for others
|
||||
Mode() FileMode // file mode bits
|
||||
ModTime() time.Time // modification time
|
||||
IsDir() bool // abbreviation for Mode().IsDir()
|
||||
Sys() interface{} // underlying data source (can return nil)
|
||||
}
|
||||
|
||||
type FileMode uint32
|
||||
|
||||
// Mode constants, copied from the mainline Go source
|
||||
// https://github.com/golang/go/blob/4ce6a8e89668b87dce67e2f55802903d6eb9110a/src/os/types.go#L35-L63
|
||||
const (
|
||||
// The single letters are the abbreviations used by the String method's formatting.
|
||||
ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory
|
||||
ModeAppend // a: append-only
|
||||
ModeExclusive // l: exclusive use
|
||||
ModeTemporary // T: temporary file; Plan 9 only
|
||||
ModeSymlink // L: symbolic link
|
||||
ModeDevice // D: device file
|
||||
ModeNamedPipe // p: named pipe (FIFO)
|
||||
ModeSocket // S: Unix domain socket
|
||||
ModeSetuid // u: setuid
|
||||
ModeSetgid // g: setgid
|
||||
ModeCharDevice // c: Unix character device, when ModeDevice is set
|
||||
ModeSticky // t: sticky
|
||||
ModeIrregular // ?: non-regular file; nothing else is known about this file
|
||||
|
||||
// Mask for the type bits. For regular files, none will be set.
|
||||
ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular
|
||||
|
||||
ModePerm FileMode = 0777 // Unix permission bits
|
||||
)
|
||||
|
||||
// IsDir is a stub, always returning false
|
||||
func (m FileMode) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build baremetal wasm
|
||||
// +build baremetal wasm,!wasi
|
||||
|
||||
package os
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user