Compare commits

...

43 Commits

Author SHA1 Message Date
sago35 30d684e637 atsamd5x: add SPI.TxN() 2021-03-29 13:31:36 +09:00
Ayke van Laethem 90b42799a2 machine: make machine.I2C0 and similar objects pointers
This makes it possible to assign I2C objects (machine.I2C0,
machine.I2C1, etc.) without needing to take a pointer.

This is important especially in the future when I2C may be driven using
DMA and the machine.I2C type needs to store some state.
2021-03-29 02:02:04 +02:00
Ayke van Laethem 71bbe93ab2 avr: remove I2C stubs from attiny support
These stubs don't really belong there: attiny currently doesn't directly
support I2C at all (although it has hardware to support a software
implementation).
2021-03-29 02:02:04 +02:00
Elliott Sales de Andrade 0535c1bbad Group together STM32 smoke tests.
And then allow them to be disabled with one option.
2021-03-29 01:46:54 +02:00
Ayke van Laethem 4f6d598ea8 reflect: implement Sizeof and Alignof for func values
This is a small change that appears to be necessary for encoding/json
support. It's simple enough to implement.
2021-03-29 01:04:11 +02:00
sago35 16e7dd83a3 gdb: enable to specify multiple candidates for gdb 2021-03-29 00:21:38 +02:00
Olaf Flebbe f23ba3b023 initial support for pca10059
Using the official USB Vendor name even for other boards.
2021-03-28 21:38:05 +02:00
Ayke van Laethem 2aa2e750b9 reflect: implement Value.CanAddr
It is used in the crypto/sha512 test, for example. And it is very simple
to implement.
2021-03-28 19:28:55 +02:00
Ayke van Laethem bcce296ca3 transform: optimize reflect.Type Implements() method
This commit adds a new transform that converts reflect Implements()
calls to runtime.interfaceImplements. At the moment, the Implements()
method is not yet implemented (how ironic) but if the value passed to
Implements is known at compile time the method call can be optimized to
runtime.interfaceImplements to make it a regular interface assert.

This commit is the last change necessary to add basic support for the
encoding/json package. The json package is certainly not yet fully
supported, but some trivial objects can be converted to JSON.
2021-03-28 14:00:37 +02:00
Ayke van Laethem c5ec955081 compiler: fix lack of method name in interface matching
This is required for correctly differentiating between interface types.
2021-03-28 14:00:37 +02:00
Ayke van Laethem 5d334922d7 compiler: add interface IR test
This is important as golden test output and to verify that the output is
correct. Later improvements and bug fixes are clearly visible in the IR,
and unintentional changes will also be immediately spotted.
2021-03-28 14:00:37 +02:00
Ayke van Laethem e9f9a4b750 reflect: fix AssignableTo and Implements methods
They both reversed the direction of the check, in a way that mostly
cancelled each other out. Of course they're still mostly unimplemented,
but it's better if they're not wrong.
2021-03-28 14:00:37 +02:00
Takeshi Yoneda 1406453350 WASI & darwin: support basic file io based on libc
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
2021-03-28 12:37:15 +02:00
Ayke van Laethem 6d3c11627c compiler: fix use of global context: llvm.Int32Type()
This patch fixes a use of the global context. I've seen a few instances
of crashes in the llvm.ConstInt function when called from
makeStructTypeFields, which I believe are caused by this bug.
2021-03-25 13:05:09 +01:00
Ayke van Laethem f800f7507c reflect: check for access in the Interface method call
This fixes a type system loophole. The following program would
incorrectly run in TinyGo, while it would trigger a panic in Go:

    package main

    import "reflect"

    func main() {
        v := reflect.ValueOf(struct {
            x int
        }{})
        x := v.Field(0).Interface()
        println("x:", x.(int))
    }

Playground link: https://play.golang.org/p/nvvA18XFqFC

The panic in Go is the following:

    panic: reflect.Value.Interface: cannot return value obtained from unexported field or method

I've shortened it in TinyGo to save a little bit of space.
2021-03-24 12:19:16 +01:00
Kenneth Bell 46a7993fb8 stm32: i2c implementation for F7, L5 and L4 MCUs 2021-03-24 08:35:34 +01:00
Ayke van Laethem 9f3dcf3733 reflect: implement a number of stub functions
These stub functions are necessary for the encoding/json package. They
don't seem to be called in trivial cases, so leave them as simple stubs
for now.
2021-03-23 15:48:33 +01:00
Ayke van Laethem c849bccb83 reflect: let reflect.Type be of interface type
This matches the main Go implementation and (among others) fixes a
compatibility issue with the encoding/json package. The encoding/json
package compares reflect.Type variables against nil, which does not work
as long as reflect.Type is of integer type.

This also adds a reflect.RawType() function (like reflect.Type()) that
makes it easier to avoid working with interfaces in the runtime package.
It is internal only, but exported to let the runtime package use it.

This change introduces a small code size increase when working with the
reflect package, but I've tried to keep it to a minimum. Most programs
that don't make extensive use of the reflect package (and don't use
package like fmt) should not be impacted by this.
2021-03-23 14:32:33 +01:00
Ayke van Laethem cffe424849 interp: add support for runtime.interfaceMethod
This is necessary so that when reflect.Type is converted from a concrete
type to an interface type, the errors package can still be interpreted.
Without this change, basically every program would grow in size by a few
bytes.
2021-03-23 14:32:33 +01:00
Ayke van Laethem 51938e9d1c interp: handle (reflect.Type).Elem()
The errors package has a call like this in the package initializer. This
commit adds support for running it at compile time, avoiding the call at
runtime.

This doesn't always help (the call is already optimized away in many
small programs) but it does help to shave off some binary size in larger
programs. Perhaps more importantly, it will avoid a penalty in code size
when the reflect package will convert reflect.Type from a regular type
to an interface type.
2021-03-23 14:32:33 +01:00
Ayke van Laethem 19dec048b0 compiler: do not check for impossible type asserts
Previously there was code to avoid impossible type asserts but it wasn't
great and in fact was too aggressive when combined with reflection.

This commit improves this by checking all types that exist in the
program that may appear in an interface (even struct fields and the
like) but without creating runtime.typecodeID objects with the type
assert. This has two advantages:

  * As mentioned, it optimizes impossible type asserts away.
  * It allows methods on types that were only asserted on (in
    runtime.typeAssert) but never used in an interface to be optimized
    away using GlobalDCE. This may have a cascading effect so that other
    parts of the code can be further optimized.

This sometimes massively improves code size and mostly negates the code
size regression of the previous commit.
2021-03-23 14:32:33 +01:00
Ayke van Laethem bbb2909283 compiler: merge runtime.typecodeID and runtime.typeInInterface
This distinction was useful before when reflect wasn't properly
supported. Back then it made sense to only include method sets that were
actually used in an interface. But now that it is possible to get to
other values (for example, by extracting fields from structs) and it is
possible to turn them back into interfaces, it is necessary to preserve
all method sets that can possibly be used in the program in a type
assert, interface assert or interface method call.

In the future, this logic will need to be revisited again when
reflect.New or reflect.Zero gets implemented.

Code size increases a bit in some cases, but usually in a very limited
way (except for one outlier in the drivers smoke tests). The next commit
will improve the situation significantly.
2021-03-23 14:32:33 +01:00
Kenneth Bell aa7c7b7bd9 lgt92: update to new UART structure 2021-03-23 08:33:59 +01:00
Kenneth Bell c7bd5405c3 Add support for nucleol432 board
LED and UART are working
2021-03-23 08:33:59 +01:00
Kenneth Bell dc981ce509 stm32: separate altfunc selection for UART Tx/Rx
This is needed for stm32l432 nucleo with different altfun for tx and rx
2021-03-23 08:33:59 +01:00
Ayke van Laethem c522569378 wasm: only export explicitly exported functions
Previously we used the --export-all linker flag to export most
functions. However, this is not needed and possibly increases binary
size. Instead, we should be exporting the specific functions to be
exported.
2021-03-22 13:48:12 +01:00
Ayke van Laethem 0db4b13e37 compiler: do not emit nil checks for *ssa.Alloc instructions
An allocated object is never nil, so there is no need for a nil check.
This probably does not result in any better optimization (the nil check
is easily optimized away by LLVM because the result of runtime.alloc is
marked nonnull) but it makes the slice tests a bit cleaner.
2021-03-22 11:35:06 +01:00
Ayke van Laethem 2709d38d63 compiler: add some more slice tests 2021-03-22 11:35:06 +01:00
Ayke van Laethem 71d1b70ab7 compiler: only run tests on LLVM 11 or above
It's difficult to create clean test cases while remaining compatible
with multiple LLVM versions. Most test outputs are much more readable
after an instcombine pass but instcombine rules change between LLVM
versions, leading to different (but semantically equivalent) test
outputs.

This reduces the test coverage a little bit (because old LLVM versions
aren't tested as well), but it als makes it easier to add more complex
tests.

In the future it might be a good idea to make the compiler output a bit
less messy so these workarounds are not needed.
2021-03-22 11:35:06 +01:00
Ayke van Laethem 24676d4366 compiler: add tests for strings
This tests a few common operations on strings.
2021-03-22 11:35:06 +01:00
Ayke van Laethem e2f532709f builder, compiler: compile and cache packages in parallel
This commit switches from the previous behavior of compiling the whole
program at once, to compiling every package in parallel and linking the
LLVM bitcode files together for further whole-program optimization.
This is a small performance win, but it has several advantages in the
future:

  - There are many more things that can be done per package in parallel,
    avoiding the bottleneck at the end of the compiler phase. This
    should speed up the compiler futher.
  - This change is a necessary step towards a non-LTO build mode for
    fast incremental builds that only rebuild the changed package, when
    compiler speed is more important than binary size.
  - This change refactors the compiler in such a way that it will be
    easier to inspect the IR for one package only. Inspecting this IR
    will be very helpful for compiler developers.
2021-03-21 11:51:35 +01:00
Ayke van Laethem dc1ff80e10 compiler: remove SimpleDCE pass
The SimpleDCE pass was previously used to only compile the parts of the
program that were in use. However, lately the only real purpose has been
to speed up the compiler a bit by only compiling the necessary
functions.

This pass however is a problem for compiling (and caching) packages in
parallel. Therefore, this commit removes it as a preparatory step
towards that goal.
2021-03-21 11:51:35 +01:00
Kenneth Bell b5205cc3ca stm32: move f103 (bluepill) to common i2c code 2021-03-21 11:25:10 +01:00
akif999 a075cbedf5 fix msd-volume-name (qtpy)
at issue #1713
2021-03-21 10:00:43 +01:00
Kenneth Bell ef613a5db7 stm32: housekeeping - remove empty file 2021-03-21 00:45:29 +01:00
sago35 1571b8fd34 Add special handling when SPI Freq is 24Mhz 2021-03-19 17:34:49 +01:00
sago35 a41b72578b atsamd21: improve SPI 2021-03-19 17:34:49 +01:00
Kenneth Bell 9f3f9d05b8 housekeeping: ignore files generated by smoketests 2021-03-19 12:42:09 +01:00
Ayke van Laethem f9865a08bc transform: optimize string comparisons against ""
This optimizes a common pattern like:

    if s != "" {
        ...
    }

to:

    if len(s) != 0 {
        ...
    }

This avoids a runtime call and thus produces slightly better code.
2021-03-18 17:22:00 +01:00
Ayke van Laethem 13db2c13e5 compiler: do not use llvm.GlobalContext()
This is a leftover from a long time ago, when everything was still in
the global context. The fact that this uses the global context is most
certainly a bug.

I have seen occasional crashes in the build-packages-indepedently branch
(and PRs based on it) which I suspect are caused by this bug. I think
this is a long-dormant bug that only surfaced when doing the compilation
steps in parallel.
2021-03-18 16:49:33 +01:00
Kenneth Bell ce8ad3650a stm32l0: use unified UART logic 2021-03-18 12:10:36 +01:00
kenbell b0b84c48ec Harmonize stm32 ticks and sleep (#1673)
machine/stm32f*: move to harmonized tick / sleep logic code
2021-03-18 11:54:15 +01:00
Ayke van Laethem 5a4dcfb367 builder: add support for -opt=0
This optimization level wasn't working before because some passes expect
some globals to be cleaned up afterwards. Cleaning these globals is
easy, just add the pass necessary for it. This shouldn't reduce the
usefulness of the -opt=0 build flag as most optimizations are still
skipped.
2021-03-15 19:36:21 +01:00
153 changed files with 4311 additions and 2372 deletions
+7
View File
@@ -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
+33 -19
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+35
View File
@@ -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
View File
@@ -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, ", ") + ")")
}
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+2 -2
View File
@@ -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
View File
@@ -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.
+1 -1
View File
@@ -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)
-164
View File
@@ -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
}
+3
View File
@@ -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
View File
@@ -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),
+16 -14
View File
@@ -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
+11 -9
View File
@@ -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
+54
View File
@@ -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()
}
+100
View File
@@ -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*)
+10 -8
View File
@@ -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
+16
View File
@@ -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)
}
+72 -3
View File
@@ -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*)
+21
View File
@@ -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
}
+57
View File
@@ -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*)
+1 -1
View File
@@ -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
)
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
+5 -6
View File
@@ -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
View File
@@ -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...)
}
+4 -3
View File
@@ -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
View File
@@ -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" {
+2 -15
View File
@@ -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,
}
+2 -2
View File
@@ -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,
}
+1 -1
View File
@@ -75,7 +75,7 @@ const (
// I2C on the Feather M0.
var (
I2C0 = I2C{
I2C0 = &I2C{
Bus: sam.SERCOM3_I2CM,
SERCOM: 3,
}
+1 -1
View File
@@ -28,7 +28,7 @@ func init() {
// I2C on the Feather M4.
var (
I2C0 = I2C{
I2C0 = &I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
+15 -12
View File
@@ -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,
}
-7
View File
@@ -10,10 +10,3 @@ var (
Bus: sifive.QSPI1,
}
)
// I2C on the HiFive1 rev B.
var (
I2C0 = I2C{
Bus: sifive.I2C0,
}
)
+1 -1
View File
@@ -75,7 +75,7 @@ const (
// I2C on the ItsyBitsy M0.
var (
I2C0 = I2C{
I2C0 = &I2C{
Bus: sam.SERCOM3_I2CM,
SERCOM: 3,
}
+1 -1
View File
@@ -28,7 +28,7 @@ func init() {
// I2C on the ItsyBitsy M4.
var (
I2C0 = I2C{
I2C0 = &I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
+8 -6
View File
@@ -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
View File
@@ -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,
}
+1 -1
View File
@@ -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 (
+1 -1
View File
@@ -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 (
+2 -2
View File
@@ -120,6 +120,6 @@ const (
// I2C pins
const (
SCL_PIN = PB6
SDA_PIN = PB7
I2C0_SCL_PIN = PB6
I2C0_SDA_PIN = PB7
)
+15 -5
View File
@@ -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
)
+56
View File
@@ -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)
}
+18 -3
View File
@@ -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)
}
+1 -1
View File
@@ -22,7 +22,7 @@ func init() {
// I2C on the P1AM-100.
var (
I2C0 = I2C{
I2C0 = &I2C{
Bus: sam.SERCOM0_I2CM,
SERCOM: 0,
}
+64
View File
@@ -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
)
+1 -1
View File
@@ -28,7 +28,7 @@ func init() {
// I2C on the ItsyBitsy M4.
var (
I2C0 = I2C{
I2C0 = &I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
+1 -1
View File
@@ -99,7 +99,7 @@ const (
// I2C on the PyGamer.
var (
I2C0 = I2C{
I2C0 = &I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
+1 -1
View File
@@ -21,7 +21,7 @@ func init() {
// I2C on the PyPortal.
var (
I2C0 = I2C{
I2C0 = &I2C{
Bus: sam.SERCOM5_I2CM,
SERCOM: 5,
}
+1 -1
View File
@@ -93,7 +93,7 @@ const (
// I2C on the QT Py M0.
var (
I2C0 = I2C{
I2C0 = &I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
-5
View File
@@ -1,5 +0,0 @@
// +build bluepill nucleof103rb stm32f4
package machine
// Peripheral abstraction layer for the stm32.
+5 -4
View File
@@ -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,
}
+1 -1
View File
@@ -81,7 +81,7 @@ const (
// I2C on the Trinket M0.
var (
I2C0 = I2C{
I2C0 = &I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
+2 -2
View File
@@ -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,
}
+1 -1
View File
@@ -81,7 +81,7 @@ const (
// I2C on the Xiao
var (
I2C0 = I2C{
I2C0 = &I2C{
Bus: sam.SERCOM2_I2CM,
SERCOM: 2,
}
+3 -3
View File
@@ -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)
}
+13 -6
View File
@@ -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)
+164 -8
View File
@@ -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
+78 -8
View File
@@ -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
-9
View File
@@ -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
}
-7
View File
@@ -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{}
+11 -6
View File
@@ -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
+3 -3
View File
@@ -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
+11 -4
View File
@@ -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.
+9 -8
View File
@@ -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)
+1 -1
View File
@@ -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))
}
+1 -1
View File
@@ -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))
}
+1 -1
View File
@@ -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))
}
+1 -1
View File
@@ -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)
+347
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
// +build stm32,!stm32f7x2,!stm32l5x2
// +build stm32,!stm32f7x2,!stm32l5x2,!stm32l4x2
package machine
+6 -5
View File
@@ -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
+43 -425
View File
@@ -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
}
+6 -6
View File
@@ -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
}
+6 -6
View File
@@ -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
}
+12 -2
View File
@@ -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
}
+10 -11
View File
@@ -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
-64
View File
@@ -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
}
+184
View File
@@ -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)
}
}
+46
View File
@@ -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
}
+12 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+81
View File
@@ -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
}
+46
View File
@@ -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 -1
View File
@@ -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