Compare commits

..

1 Commits

Author SHA1 Message Date
Ayke van Laethem 2b7fa93fcb machine: add flash start/end address that can be used for data 2023-02-20 20:58:36 +01:00
46 changed files with 217 additions and 1958 deletions
+1 -1
View File
@@ -89,7 +89,7 @@ jobs:
run: make wasi-libc
- name: Test TinyGo
shell: bash
run: make test GOTESTFLAGS="-short"
run: make test GOTESTFLAGS="-v -short"
- name: Build TinyGo release tarball
run: make release -j3
- name: Test stdlib packages
+1 -1
View File
@@ -99,7 +99,7 @@ jobs:
scoop install wasmtime
- name: Test TinyGo
shell: bash
run: make test GOTESTFLAGS="-short"
run: make test GOTESTFLAGS="-v -short"
- name: Build TinyGo release tarball
shell: bash
run: make build/release -j4
+1 -3
View File
@@ -30,7 +30,7 @@ GO ?= go
export GOROOT = $(shell $(GO) env GOROOT)
# Flags to pass to go test.
GOTESTFLAGS ?=
GOTESTFLAGS ?= -v
# md5sum binary
MD5SUM = md5sum
@@ -474,8 +474,6 @@ smoketest:
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/rtcinterrupt
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick
+1 -1
View File
@@ -41,7 +41,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
## Supported boards/targets
You can compile TinyGo programs for microcontrollers, WebAssembly, Windows, and Linux.
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
The following 94 microcontroller boards are currently supported:
+56 -36
View File
@@ -169,7 +169,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
CodeModel: config.CodeModel(),
RelocationModel: config.RelocationModel(),
SizeLevel: sizeLevel,
TinyGoVersion: goenv.Version,
Scheduler: config.Scheduler(),
AutomaticStackSize: config.AutomaticStackSize(),
@@ -191,9 +190,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
Sizes: compiler.Sizes(machine),
})
if err != nil {
return BuildResult{}, err
}
result := BuildResult{
ModuleRoot: lprogram.MainPkg().Module.Dir,
MainDir: lprogram.MainPkg().Dir,
@@ -203,6 +199,9 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// If there is no module root, just the regular root.
result.ModuleRoot = lprogram.MainPkg().Root
}
if err != nil { // failed to load AST
return result, err
}
err = lprogram.Parse()
if err != nil {
return result, err
@@ -306,6 +305,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
actionID := packageAction{
ImportPath: pkg.ImportPath,
CompilerBuildID: string(compilerBuildID),
TinyGoVersion: goenv.Version,
LLVMVersion: llvm.Version,
Config: compilerConfig,
CFlags: pkg.CFlags,
@@ -594,7 +594,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
defer llvmBuf.Dispose()
return result, os.WriteFile(outpath, llvmBuf.Bytes(), 0666)
case ".bc":
buf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
var buf llvm.MemoryBuffer
if config.UseThinLTO() {
buf = llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
} else {
buf = llvm.WriteBitcodeToMemoryBuffer(mod)
}
defer buf.Dispose()
return result, os.WriteFile(outpath, buf.Bytes(), 0666)
case ".ll":
@@ -616,7 +621,16 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
dependencies: []*compileJob{programJob},
result: objfile,
run: func(*compileJob) error {
llvmBuf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
var llvmBuf llvm.MemoryBuffer
if config.UseThinLTO() {
llvmBuf = llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
} else {
var err error
llvmBuf, err = machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
if err != nil {
return err
}
}
defer llvmBuf.Dispose()
return os.WriteFile(objfile, llvmBuf.Bytes(), 0666)
},
@@ -650,7 +664,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
job := &compileJob{
description: "compile extra file " + path,
run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.Options.PrintCommands)
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.UseThinLTO(), config.Options.PrintCommands)
job.result = result
return err
},
@@ -668,7 +682,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
job := &compileJob{
description: "compile CGo file " + abspath,
run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, tmpdir, pkg.CFlags, config.Options.PrintCommands)
result, err := compileAndCacheCFile(abspath, tmpdir, pkg.CFlags, config.UseThinLTO(), config.Options.PrintCommands)
job.result = result
return err
},
@@ -727,34 +741,36 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
}
ldflags = append(ldflags, dependency.result)
}
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
if config.GOOS() == "windows" {
// Options for the MinGW wrapper for the lld COFF linker.
ldflags = append(ldflags,
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
} else if config.GOOS() == "darwin" {
// Options for the ld64-compatible lld linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel),
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
} else {
// Options for the ELF linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
)
}
if config.CodeModel() != "default" {
ldflags = append(ldflags,
"-mllvm", "-code-model="+config.CodeModel())
}
if sizeLevel >= 2 {
// Workaround with roughly the same effect as
// https://reviews.llvm.org/D119342.
// Can hopefully be removed in LLVM 15.
ldflags = append(ldflags,
"-mllvm", "--rotation-max-header-size=0")
if config.UseThinLTO() {
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
if config.GOOS() == "windows" {
// Options for the MinGW wrapper for the lld COFF linker.
ldflags = append(ldflags,
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
} else if config.GOOS() == "darwin" {
// Options for the ld64-compatible lld linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel),
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
} else {
// Options for the ELF linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(optLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
)
}
if config.CodeModel() != "default" {
ldflags = append(ldflags,
"-mllvm", "-code-model="+config.CodeModel())
}
if sizeLevel >= 2 {
// Workaround with roughly the same effect as
// https://reviews.llvm.org/D119342.
// Can hopefully be removed in LLVM 15.
ldflags = append(ldflags,
"-mllvm", "--rotation-max-header-size=0")
}
}
if config.Options.PrintCommands != nil {
config.Options.PrintCommands(config.Target.Linker, ldflags...)
@@ -1053,6 +1069,10 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
}
}
if config.GOOS() != "darwin" && !config.UseThinLTO() {
transform.ApplyFunctionSections(mod) // -ffunction-sections
}
// Insert values from -ldflags="-X ..." into the IR.
err = setGlobalValues(mod, config.Options.GlobalValues)
if err != nil {
+16 -8
View File
@@ -56,7 +56,7 @@ import (
// depfile but without invalidating its name. For this reason, the depfile is
// written on each new compilation (even when it seems unnecessary). However, it
// could in rare cases lead to a stale file fetched from the cache.
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands func(string, ...string)) (string, error) {
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool, printCommands func(string, ...string)) (string, error) {
// Hash input file.
fileHash, err := hashFile(abspath)
if err != nil {
@@ -67,6 +67,11 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
unlock := lock(filepath.Join(goenv.Get("GOCACHE"), fileHash+".c.lock"))
defer unlock()
ext := ".o"
if thinlto {
ext = ".bc"
}
// Create cache key for the dependencies file.
buf, err := json.Marshal(struct {
Path string
@@ -99,7 +104,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
}
// Obtain hashes of all the files listed as a dependency.
outpath, err := makeCFileCachePath(dependencies, depfileNameHash)
outpath, err := makeCFileCachePath(dependencies, depfileNameHash, ext)
if err == nil {
if _, err := os.Stat(outpath); err == nil {
return outpath, nil
@@ -112,7 +117,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
return "", err
}
objTmpFile, err := os.CreateTemp(goenv.Get("GOCACHE"), "tmp-*.bc")
objTmpFile, err := os.CreateTemp(goenv.Get("GOCACHE"), "tmp-*"+ext)
if err != nil {
return "", err
}
@@ -122,8 +127,11 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
return "", err
}
depTmpFile.Close()
flags := append([]string{}, cflags...) // copy cflags
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name(), "-flto=thin") // autogenerate dependencies
flags := append([]string{}, cflags...) // copy cflags
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
if thinlto {
flags = append(flags, "-flto=thin")
}
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
if strings.ToLower(filepath.Ext(abspath)) == ".s" {
// If this is an assembly file (.s or .S, lowercase or uppercase), then
@@ -181,7 +189,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
}
// Move temporary object file to final location.
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash)
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash, ext)
if err != nil {
return "", err
}
@@ -196,7 +204,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
// Create a cache path (a path in GOCACHE) to store the output of a compiler
// job. This path is based on the dep file name (which is a hash of metadata
// including compiler flags) and the hash of all input files in the paths slice.
func makeCFileCachePath(paths []string, depfileNameHash string) (string, error) {
func makeCFileCachePath(paths []string, depfileNameHash, ext string) (string, error) {
// Hash all input files.
fileHashes := make(map[string]string, len(paths))
for _, path := range paths {
@@ -221,7 +229,7 @@ func makeCFileCachePath(paths []string, depfileNameHash string) (string, error)
outFileNameBuf := sha512.Sum512_224(buf)
cacheKey := hex.EncodeToString(outFileNameBuf[:])
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+".bc")
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+ext)
return outpath, nil
}
+8
View File
@@ -191,6 +191,14 @@ func (c *Config) StackSize() uint64 {
return c.Target.DefaultStackSize
}
// UseThinLTO returns whether ThinLTO should be used for the given target.
func (c *Config) UseThinLTO() bool {
// All architectures support ThinLTO now. However, this code is kept for the
// time being in case there are regressions. The non-ThinLTO code support
// should be removed when it is proven to work reliably.
return true
}
// RP2040BootPatch returns whether the RP2040 boot patch should be applied that
// calculates and patches in the checksum for the 2nd stage bootloader.
func (c *Config) RP2040BootPatch() bool {
-9
View File
@@ -47,7 +47,6 @@ type Config struct {
CodeModel string
RelocationModel string
SizeLevel int
TinyGoVersion string // for llvm.ident
// Various compiler options that determine how code is generated.
Scheduler string
@@ -322,14 +321,6 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
}),
)
if c.TinyGoVersion != "" {
// It is necessary to set llvm.ident, otherwise debugging on MacOS
// won't work.
c.mod.AddNamedMetadataOperand("llvm.ident",
c.ctx.MDNode(([]llvm.Metadata{
c.ctx.MDString("TinyGo version " + c.TinyGoVersion),
})))
}
c.dibuilder.Finalize()
c.dibuilder.Destroy()
}
-1
View File
@@ -49,7 +49,6 @@ func TestCompiler(t *testing.T) {
{"goroutine.go", "cortex-m-qemu", "tasks"},
{"channel.go", "", ""},
{"gc.go", "", ""},
{"zeromap.go", "", ""},
}
if goMinor >= 20 {
tests = append(tests, testCase{"go1.20.go", "", ""})
-4
View File
@@ -131,8 +131,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
case *types.Map:
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "keyType", types.Typ[types.UnsafePointer]),
)
case *types.Struct:
typeFieldTypes = append(typeFieldTypes,
@@ -195,8 +193,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
case *types.Map:
typeFields = []llvm.Value{
c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Elem()), // elem
c.getTypeCode(typ.Key()), // key
}
case *types.Struct:
typeFields = []llvm.Value{
+1 -78
View File
@@ -89,7 +89,6 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
// growth.
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, mapKeyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca)
// Fetch the value from the hashmap.
params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize}
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
@@ -134,7 +133,6 @@ func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value,
// key can be compared with runtime.memequal
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, keyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
params := []llvm.Value{m, keyPtr, valuePtr}
b.createRuntimeCall("hashmapBinarySet", params, "")
b.emitLifetimeEnd(keyPtr, keySize)
@@ -163,7 +161,6 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok
} else if hashmapIsBinaryKey(keyType) {
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
b.CreateStore(key, keyAlloca)
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
params := []llvm.Value{m, keyPtr}
b.createRuntimeCall("hashmapBinaryDelete", params, "")
b.emitLifetimeEnd(keyPtr, keySize)
@@ -243,8 +240,7 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
}
// Returns true if this key type does not contain strings, interfaces etc., so
// can be compared with runtime.memequal. Note that padding bytes are undef
// and can alter two "equal" structs being equal when compared with memequal.
// can be compared with runtime.memequal.
func hashmapIsBinaryKey(keyType types.Type) bool {
switch keyType := keyType.(type) {
case *types.Basic:
@@ -267,76 +263,3 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
return false
}
}
func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error {
// We know that hashmapIsBinaryKey is true, so we only have to handle those types that can show up there.
// To zero all undefined bytes, we iterate over all the fields in the type. For each element, compute the
// offset of that element. If it's Basic type, there are no internal padding bytes. For compound types, we recurse to ensure
// we handle nested types. Next, we determine if there are any padding bytes before the next
// element and zero those as well.
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
switch llvmType.TypeKind() {
case llvm.IntegerTypeKind:
// no padding bytes
return nil
case llvm.PointerTypeKind:
// mo padding bytes
return nil
case llvm.ArrayTypeKind:
llvmArrayType := llvmType
llvmElemType := llvmType.ElementType()
for i := 0; i < llvmArrayType.ArrayLength(); i++ {
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
elemPtr := b.CreateInBoundsGEP(llvmArrayType, ptr, []llvm.Value{zero, idx}, "")
// zero any padding bytes in this element
b.zeroUndefBytes(llvmElemType, elemPtr)
}
case llvm.StructTypeKind:
llvmStructType := llvmType
numFields := llvmStructType.StructElementTypesCount()
llvmElementTypes := llvmStructType.StructElementTypes()
for i := 0; i < numFields; i++ {
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
elemPtr := b.CreateInBoundsGEP(llvmStructType, ptr, []llvm.Value{zero, idx}, "")
// zero any padding bytes in this field
llvmElemType := llvmElementTypes[i]
b.zeroUndefBytes(llvmElemType, elemPtr)
// zero any padding bytes before the next field, if any
offset := b.targetData.ElementOffset(llvmStructType, i)
storeSize := b.targetData.TypeStoreSize(llvmElemType)
fieldEndOffset := offset + storeSize
var nextOffset uint64
if i < numFields-1 {
nextOffset = b.targetData.ElementOffset(llvmStructType, i+1)
} else {
// Last field? Next offset is the total size of the allcoate struct.
nextOffset = b.targetData.TypeAllocSize(llvmStructType)
}
if fieldEndOffset != nextOffset {
n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
gepPtr := elemPtr
if gepPtr.Type() != b.i8ptrType {
gepPtr = b.CreateBitCast(gepPtr, b.i8ptrType, "") // LLVM 14
}
paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), gepPtr, []llvm.Value{llvmStoreSize}, "")
if paddingStart.Type() != b.i8ptrType {
paddingStart = b.CreateBitCast(paddingStart, b.i8ptrType, "") // LLVM 14
}
b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "")
}
}
}
return nil
}
-37
View File
@@ -1,37 +0,0 @@
package main
type hasPadding struct {
b1 bool
i int
b2 bool
}
type nestedPadding struct {
b bool
hasPadding
i int
}
//go:noinline
func testZeroGet(m map[hasPadding]int, s hasPadding) int {
return m[s]
}
//go:noinline
func testZeroSet(m map[hasPadding]int, s hasPadding) {
m[s] = 5
}
//go:noinline
func testZeroArrayGet(m map[[2]hasPadding]int, s [2]hasPadding) int {
return m[s]
}
//go:noinline
func testZeroArraySet(m map[[2]hasPadding]int, s [2]hasPadding) {
m[s] = 5
}
func main() {
}
-170
View File
@@ -1,170 +0,0 @@
; ModuleID = 'zeromap.go'
source_filename = "zeromap.go"
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasi"
%main.hasPadding = type { i1, i32, i1 }
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
entry:
ret void
}
; Function Attrs: noinline nounwind
define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
entry:
%hashmap.key = alloca %main.hasPadding, align 8
%hashmap.value = alloca i32, align 4
%s = alloca %main.hasPadding, align 8
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s, align 8
call void @runtime.trackPointer(ptr nonnull %s, ptr nonnull %stackalloc, ptr undef) #4
store %main.hasPadding %2, ptr %s, align 8
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
store %main.hasPadding %2, ptr %hashmap.key, align 8
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #4
%4 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
call void @runtime.memzero(ptr nonnull %4, i32 3, ptr undef) #4
%5 = call i1 @runtime.hashmapBinaryGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
%6 = load i32, ptr %hashmap.value, align 4
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
ret i32 %6
}
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
declare void @runtime.memzero(ptr, i32, ptr) #0
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #0
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
; Function Attrs: noinline nounwind
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
entry:
%hashmap.key = alloca %main.hasPadding, align 8
%hashmap.value = alloca i32, align 4
%s = alloca %main.hasPadding, align 8
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s, align 8
call void @runtime.trackPointer(ptr nonnull %s, ptr nonnull %stackalloc, ptr undef) #4
store %main.hasPadding %2, ptr %s, align 8
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
store i32 5, ptr %hashmap.value, align 4
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
store %main.hasPadding %2, ptr %hashmap.key, align 8
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #4
%4 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
call void @runtime.memzero(ptr nonnull %4, i32 3, ptr undef) #4
call void @runtime.hashmapBinarySet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
ret void
}
declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(40), ptr, ptr, ptr) #0
; Function Attrs: noinline nounwind
define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
entry:
%hashmap.key = alloca [2 x %main.hasPadding], align 8
%hashmap.value = alloca i32, align 4
%s1 = alloca [2 x %main.hasPadding], align 8
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s1, align 8
%s1.repack2 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
store %main.hasPadding zeroinitializer, ptr %s1.repack2, align 4
call void @runtime.trackPointer(ptr nonnull %s1, ptr nonnull %stackalloc, ptr undef) #4
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt, ptr %s1, align 8
%s1.repack3 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
%s.elt4 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt4, ptr %s1.repack3, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
%s.elt7 = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt7, ptr %hashmap.key, align 8
%hashmap.key.repack8 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
%s.elt9 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt9, ptr %hashmap.key.repack8, align 4
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #4
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
call void @runtime.memzero(ptr nonnull %1, i32 3, ptr undef) #4
%2 = getelementptr inbounds i8, ptr %hashmap.key, i32 13
call void @runtime.memzero(ptr nonnull %2, i32 3, ptr undef) #4
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 21
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #4
%4 = call i1 @runtime.hashmapBinaryGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
%5 = load i32, ptr %hashmap.value, align 4
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
ret i32 %5
}
; Function Attrs: noinline nounwind
define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
entry:
%hashmap.key = alloca [2 x %main.hasPadding], align 8
%hashmap.value = alloca i32, align 4
%s1 = alloca [2 x %main.hasPadding], align 8
%stackalloc = alloca i8, align 1
store %main.hasPadding zeroinitializer, ptr %s1, align 8
%s1.repack2 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
store %main.hasPadding zeroinitializer, ptr %s1.repack2, align 4
call void @runtime.trackPointer(ptr nonnull %s1, ptr nonnull %stackalloc, ptr undef) #4
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt, ptr %s1, align 8
%s1.repack3 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
%s.elt4 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt4, ptr %s1.repack3, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
store i32 5, ptr %hashmap.value, align 4
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
%s.elt7 = extractvalue [2 x %main.hasPadding] %s, 0
store %main.hasPadding %s.elt7, ptr %hashmap.key, align 8
%hashmap.key.repack8 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
%s.elt9 = extractvalue [2 x %main.hasPadding] %s, 1
store %main.hasPadding %s.elt9, ptr %hashmap.key.repack8, align 4
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #4
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
call void @runtime.memzero(ptr nonnull %1, i32 3, ptr undef) #4
%2 = getelementptr inbounds i8, ptr %hashmap.key, i32 13
call void @runtime.memzero(ptr nonnull %2, i32 3, ptr undef) #4
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 21
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #4
call void @runtime.hashmapBinarySet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
ret void
}
; Function Attrs: nounwind
define hidden void @main.main(ptr %context) unnamed_addr #1 {
entry:
ret void
}
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #2 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
attributes #3 = { argmemonly nocallback nofree nosync nounwind willreturn }
attributes #4 = { nounwind }
+9 -6
View File
@@ -180,8 +180,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
// Skip the ones that aren't.
switch name {
case "reflect.go":
// Reflect tests do not run correctly, probably because of the
// limited amount of memory.
// Reflect tests do not work due to type code issues.
continue
case "gc.go":
@@ -189,16 +188,20 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
continue
case "json.go", "stdlib.go", "testing.go":
// Too big for AVR. Doesn't fit in flash/RAM.
// Breaks interp.
continue
case "math.go":
// Needs newer picolibc version (for sqrt).
// Stuck somewhere, not sure what's happening.
continue
case "cgo/":
// CGo function pointers don't work on AVR (needs LLVM 16 and
// some compiler changes).
// CGo does not work on AVR.
continue
case "timers.go":
// Doesn't compile:
// panic: compiler: could not store type code number inside interface type code
continue
default:
+3 -52
View File
@@ -1,57 +1,8 @@
package main
import (
"machine"
"time"
)
var (
err error
message = "1234567887654321123456788765432112345678876543211234567887654321"
)
import "machine"
func main() {
time.Sleep(3 * time.Second)
// Print out general information
println("Flash data start: ", machine.FlashDataStart())
println("Flash data end: ", machine.FlashDataEnd())
println("Flash data size, bytes:", machine.Flash.Size())
println("Flash write block size:", machine.Flash.WriteBlockSize())
println("Flash erase block size:", machine.Flash.EraseBlockSize())
println()
flash := machine.OpenFlashBuffer(machine.Flash, machine.FlashDataStart())
original := make([]byte, len(message))
saved := make([]byte, len(message))
// Read flash contents on start (data shall survive power off)
print("Reading data from flash: ")
_, err = flash.Read(original)
checkError(err)
println(string(original))
// Write the message to flash
print("Writing data to flash: ")
flash.Seek(0, 0) // rewind back to beginning
_, err = flash.Write([]byte(message))
checkError(err)
println(string(message))
// Read back flash contents after write (verify data is the same as written)
print("Reading data back from flash: ")
flash.Seek(0, 0) // rewind back to beginning
_, err = flash.Read(saved)
checkError(err)
println(string(saved))
println()
}
func checkError(err error) {
if err != nil {
for {
println(err.Error())
time.Sleep(time.Second)
}
}
println("flash data start:", machine.FlashDataStart())
println("flash data end: ", machine.FlashDataEnd())
}
-35
View File
@@ -1,35 +0,0 @@
//go:build rp2040
package main
// This example demonstrates scheduling a delayed interrupt by real time clock.
//
// An interrupt may execute user callback function or used for its side effects
// like waking up from sleep or dormant states.
//
// The interrupt can be configured to repeat.
//
// There is no separate method to disable interrupt, use 0 delay for that.
//
// Unfortunately, it is not possible to use time.Duration to work with RTC directly,
// that would introduce a circular dependency between "machine" and "time" packages.
import (
"fmt"
"machine"
"time"
)
func main() {
// Schedule and enable recurring interrupt.
// The callback function is executed in the context of an interrupt handler,
// so regular restructions for this sort of code apply: no blocking, no memory allocation, etc.
delay := time.Minute + 12*time.Second
machine.RTC.SetInterrupt(uint32(delay.Seconds()), true, func() { println("Peekaboo!") })
for {
fmt.Printf("%v\r\n", time.Now().Format(time.RFC3339))
time.Sleep(1 * time.Second)
}
}
+1 -7
View File
@@ -2,10 +2,7 @@
package task
import (
"runtime/interrupt"
"unsafe"
)
import "unsafe"
//go:linkname runtimePanic runtime.runtimePanic
func runtimePanic(str string)
@@ -48,9 +45,6 @@ func Pause() {
if *currentTask.state.canaryPtr != stackCanary {
runtimePanic("goroutine stack overflow")
}
if interrupt.In() {
runtimePanic("blocked inside interrupt")
}
currentTask.state.pause()
}
-13
View File
@@ -56,18 +56,6 @@ var (
}
DefaultUART = UART0
// Since we treat UART1 as zero, let's also call it by the real name
UART1 = UART0
// UART2
UART2 = &_UART2
_UART2 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART2,
TxAltFuncSelector: AF7_USART1_2,
RxAltFuncSelector: AF7_USART1_2,
}
// I2C Busses
I2C1 = &I2C{
Bus: stm32.I2C1,
@@ -84,5 +72,4 @@ var (
func init() {
// Enable UARTs Interrupts
UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART0.handleInterrupt)
UART2.Interrupt = interrupt.New(stm32.IRQ_USART2, _UART2.handleInterrupt)
}
+4 -4
View File
@@ -50,11 +50,11 @@ const (
// I2C pins
const (
I2C0_SDA_PIN Pin = D2
I2C0_SCL_PIN Pin = D3
I2C0_SDA_PIN Pin = D4
I2C0_SCL_PIN Pin = D5
I2C1_SDA_PIN Pin = D4
I2C1_SCL_PIN Pin = D5
I2C1_SDA_PIN Pin = NoPin
I2C1_SCL_PIN Pin = NoPin
)
// SPI pins
+3 -124
View File
@@ -1,12 +1,8 @@
//go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l4 || stm32wlx
//go:build nrf
package machine
import (
"errors"
"io"
"unsafe"
)
import "unsafe"
//go:extern __flash_data_start
var flashDataStart [0]byte
@@ -17,8 +13,7 @@ var flashDataEnd [0]byte
// Return the start of the writable flash area, aligned on a page boundary. This
// is usually just after the program and static data.
func FlashDataStart() uintptr {
pagesize := uintptr(eraseBlockSize())
return (uintptr(unsafe.Pointer(&flashDataStart)) + pagesize - 1) &^ (pagesize - 1)
return (uintptr(unsafe.Pointer(&flashDataStart)) + flashPageSize - 1) &^ (flashPageSize - 1)
}
// Return the end of the writable flash area. Usually this is the address one
@@ -26,119 +21,3 @@ func FlashDataStart() uintptr {
func FlashDataEnd() uintptr {
return uintptr(unsafe.Pointer(&flashDataEnd))
}
var (
errFlashCannotErasePage = errors.New("cannot erase flash page")
errFlashInvalidWriteLength = errors.New("write flash data must align to correct number of bits")
errFlashNotAllowedWriteData = errors.New("not allowed to write flash data")
errFlashCannotWriteData = errors.New("cannot write flash data")
errFlashCannotReadPastEOF = errors.New("cannot read beyond end of flash data")
errFlashCannotWritePastEOF = errors.New("cannot write beyond end of flash data")
)
// BlockDevice is the raw device that is meant to store flash data.
type BlockDevice interface {
// ReadAt reads the given number of bytes from the block device.
io.ReaderAt
// WriteAt writes the given number of bytes to the block device.
io.WriterAt
// Size returns the number of bytes in this block device.
Size() int64
// WriteBlockSize returns the block size in which data can be written to
// memory. It can be used by a client to optimize writes, non-aligned writes
// should always work correctly.
WriteBlockSize() int64
// EraseBlockSize returns the smallest erasable area on this particular chip
// in bytes. This is used for the block size in EraseBlocks.
// It must be a power of two, and may be as small as 1. A typical size is 4096.
EraseBlockSize() int64
// EraseBlocks erases the given number of blocks. An implementation may
// transparently coalesce ranges of blocks into larger bundles if the chip
// supports this. The start and len parameters are in block numbers, use
// EraseBlockSize to map addresses to blocks.
EraseBlocks(start, len int64) error
}
// FlashBuffer implements the ReadWriteCloser interface using the BlockDevice interface.
type FlashBuffer struct {
b BlockDevice
start uintptr
current uintptr
}
// OpenFlashBuffer opens a FlashBuffer.
func OpenFlashBuffer(b BlockDevice, address uintptr) *FlashBuffer {
return &FlashBuffer{b: b, start: address, current: address}
}
// Read data from a FlashBuffer.
func (fl *FlashBuffer) Read(p []byte) (n int, err error) {
fl.b.ReadAt(p, int64(fl.current))
fl.current += uintptr(len(p))
return len(p), nil
}
// Write data to a FlashBuffer.
func (fl *FlashBuffer) Write(p []byte) (n int, err error) {
// any new pages needed?
// NOTE probably will not work as expected if you try to write over page boundary
// of pages with different sizes.
pagesize := uintptr(fl.b.EraseBlockSize())
currentPageCount := (fl.current - fl.start + pagesize - 1) / pagesize
totalPagesNeeded := (fl.current - fl.start + uintptr(len(p)) + pagesize - 1) / pagesize
if currentPageCount == totalPagesNeeded {
// just write the data
n, err := fl.b.WriteAt(p, int64(fl.current))
if err != nil {
return 0, err
}
fl.current += uintptr(n)
return n, nil
}
// erase enough blocks to hold the data
page := fl.flashPageFromAddress(fl.start + (currentPageCount * pagesize))
fl.b.EraseBlocks(page, int64(totalPagesNeeded-currentPageCount))
// write the data
for i := 0; i < len(p); i += int(pagesize) {
var last int = i + int(pagesize)
if i+int(pagesize) > len(p) {
last = len(p)
}
_, err := fl.b.WriteAt(p[i:last], int64(fl.current))
if err != nil {
return 0, err
}
fl.current += uintptr(last - i)
}
return len(p), nil
}
// Close the FlashBuffer.
func (fl *FlashBuffer) Close() error {
return nil
}
// Seek implements io.Seeker interface, but with limitations.
// You can only seek relative to the start.
// Also, you cannot use seek before write operations, only read.
func (fl *FlashBuffer) Seek(offset int64, whence int) (int64, error) {
fl.current = fl.start + uintptr(offset)
return offset, nil
}
// calculate page number from address
func (fl *FlashBuffer) flashPageFromAddress(address uintptr) int64 {
return int64(address-memoryStart) / fl.b.EraseBlockSize()
}
+28 -28
View File
@@ -406,61 +406,61 @@ func (p Pin) getPinCfg() uint8 {
return uint8(sam.PORT.PINCFG1_0.Get()>>16) & 0xff
case 35: // PB03
return uint8(sam.PORT.PINCFG1_0.Get()>>24) & 0xff
case 36: // PB04
case 37: // PB04
return uint8(sam.PORT.PINCFG1_4.Get()>>0) & 0xff
case 37: // PB05
case 38: // PB05
return uint8(sam.PORT.PINCFG1_4.Get()>>8) & 0xff
case 38: // PB06
case 39: // PB06
return uint8(sam.PORT.PINCFG1_4.Get()>>16) & 0xff
case 39: // PB07
case 40: // PB07
return uint8(sam.PORT.PINCFG1_4.Get()>>24) & 0xff
case 40: // PB08
case 41: // PB08
return uint8(sam.PORT.PINCFG1_8.Get()>>0) & 0xff
case 41: // PB09
case 42: // PB09
return uint8(sam.PORT.PINCFG1_8.Get()>>8) & 0xff
case 42: // PB10
case 43: // PB10
return uint8(sam.PORT.PINCFG1_8.Get()>>16) & 0xff
case 43: // PB11
case 44: // PB11
return uint8(sam.PORT.PINCFG1_8.Get()>>24) & 0xff
case 44: // PB12
case 45: // PB12
return uint8(sam.PORT.PINCFG1_12.Get()>>0) & 0xff
case 45: // PB13
case 46: // PB13
return uint8(sam.PORT.PINCFG1_12.Get()>>8) & 0xff
case 46: // PB14
case 47: // PB14
return uint8(sam.PORT.PINCFG1_12.Get()>>16) & 0xff
case 47: // PB15
case 48: // PB15
return uint8(sam.PORT.PINCFG1_12.Get()>>24) & 0xff
case 48: // PB16
case 49: // PB16
return uint8(sam.PORT.PINCFG1_16.Get()>>0) & 0xff
case 49: // PB17
case 50: // PB17
return uint8(sam.PORT.PINCFG1_16.Get()>>8) & 0xff
case 50: // PB18
case 51: // PB18
return uint8(sam.PORT.PINCFG1_16.Get()>>16) & 0xff
case 51: // PB19
case 52: // PB19
return uint8(sam.PORT.PINCFG1_16.Get()>>24) & 0xff
case 52: // PB20
case 53: // PB20
return uint8(sam.PORT.PINCFG1_20.Get()>>0) & 0xff
case 53: // PB21
case 54: // PB21
return uint8(sam.PORT.PINCFG1_20.Get()>>8) & 0xff
case 54: // PB22
case 55: // PB22
return uint8(sam.PORT.PINCFG1_20.Get()>>16) & 0xff
case 55: // PB23
case 56: // PB23
return uint8(sam.PORT.PINCFG1_20.Get()>>24) & 0xff
case 56: // PB24
case 57: // PB24
return uint8(sam.PORT.PINCFG1_24.Get()>>0) & 0xff
case 57: // PB25
case 58: // PB25
return uint8(sam.PORT.PINCFG1_24.Get()>>8) & 0xff
case 58: // PB26
case 59: // PB26
return uint8(sam.PORT.PINCFG1_24.Get()>>16) & 0xff
case 59: // PB27
case 60: // PB27
return uint8(sam.PORT.PINCFG1_24.Get()>>24) & 0xff
case 60: // PB28
case 61: // PB28
return uint8(sam.PORT.PINCFG1_28.Get()>>0) & 0xff
case 61: // PB29
case 62: // PB29
return uint8(sam.PORT.PINCFG1_28.Get()>>8) & 0xff
case 62: // PB30
case 63: // PB30
return uint8(sam.PORT.PINCFG1_28.Get()>>16) & 0xff
case 63: // PB31
case 64: // PB31
return uint8(sam.PORT.PINCFG1_28.Get()>>24) & 0xff
default:
return 0
-108
View File
@@ -3,9 +3,7 @@
package machine
import (
"bytes"
"device/nrf"
"encoding/binary"
"runtime/interrupt"
"unsafe"
)
@@ -384,109 +382,3 @@ func ReadTemperature() int32 {
nrf.TEMP.EVENTS_DATARDY.Set(0)
return temp
}
const memoryStart = 0x0
// compile-time check for ensuring we fulfill BlockDevice interface
var _ BlockDevice = flashBlockDevice{}
var Flash flashBlockDevice
type flashBlockDevice struct {
}
// ReadAt reads the given number of bytes from the block device.
func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
return 0, errFlashCannotReadPastEOF
}
data := unsafe.Slice((*byte)(unsafe.Pointer(FlashDataStart()+uintptr(off))), len(p))
copy(p, data)
return len(p), nil
}
// WriteAt writes the given number of bytes to the block device.
// Only double-word (64 bits) length data can be programmed. See rm0461 page 78.
// If the length of p is not long enough it will be padded with 0xFF bytes.
// This method assumes that the destination is already erased.
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
return 0, errFlashCannotWritePastEOF
}
address := FlashDataStart() + uintptr(off)
padded := f.pad(p)
waitWhileFlashBusy()
nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Wen)
defer nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Ren)
for j := 0; j < len(padded); j += int(f.WriteBlockSize()) {
// write word
*(*uint32)(unsafe.Pointer(address)) = binary.LittleEndian.Uint32(padded[j : j+int(f.WriteBlockSize())])
address += uintptr(f.WriteBlockSize())
waitWhileFlashBusy()
}
return len(padded), nil
}
// Size returns the number of bytes in this block device.
func (f flashBlockDevice) Size() int64 {
return int64(FlashDataEnd() - FlashDataStart())
}
const writeBlockSize = 4
// WriteBlockSize returns the block size in which data can be written to
// memory. It can be used by a client to optimize writes, non-aligned writes
// should always work correctly.
func (f flashBlockDevice) WriteBlockSize() int64 {
return writeBlockSize
}
// EraseBlockSize returns the smallest erasable area on this particular chip
// in bytes. This is used for the block size in EraseBlocks.
// It must be a power of two, and may be as small as 1. A typical size is 4096.
func (f flashBlockDevice) EraseBlockSize() int64 {
return eraseBlockSize()
}
// EraseBlocks erases the given number of blocks. An implementation may
// transparently coalesce ranges of blocks into larger bundles if the chip
// supports this. The start and len parameters are in block numbers, use
// EraseBlockSize to map addresses to blocks.
func (f flashBlockDevice) EraseBlocks(start, len int64) error {
address := FlashDataStart() + uintptr(start*f.EraseBlockSize())
waitWhileFlashBusy()
nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Een)
defer nrf.NVMC.SetCONFIG_WEN(nrf.NVMC_CONFIG_WEN_Ren)
for i := start; i < start+len; i++ {
nrf.NVMC.ERASEPAGE.Set(uint32(address))
waitWhileFlashBusy()
address += uintptr(f.EraseBlockSize())
}
return nil
}
// pad data if needed so it is long enough for correct byte alignment on writes.
func (f flashBlockDevice) pad(p []byte) []byte {
paddingNeeded := f.WriteBlockSize() - (int64(len(p)) % f.WriteBlockSize())
if paddingNeeded == 0 {
return p
}
padding := bytes.Repeat([]byte{0xff}, int(paddingNeeded))
return append(p, padding...)
}
func waitWhileFlashBusy() {
for nrf.NVMC.GetREADY() != nrf.NVMC_READY_READY_Ready {
}
}
+1 -5
View File
@@ -6,11 +6,7 @@ import (
"device/nrf"
)
const eraseBlockSizeValue = 1024
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
const flashPageSize = 1024
// Get peripheral and pin number for this GPIO pin.
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
-6
View File
@@ -63,9 +63,3 @@ var (
PWM1 = &PWM{PWM: nrf.PWM1}
PWM2 = &PWM{PWM: nrf.PWM2}
)
const eraseBlockSizeValue = 4096
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
-6
View File
@@ -84,9 +84,3 @@ var (
PWM2 = &PWM{PWM: nrf.PWM2}
PWM3 = &PWM{PWM: nrf.PWM3}
)
const eraseBlockSizeValue = 4096
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
-6
View File
@@ -102,9 +102,3 @@ func (pdm *PDM) Read(buf []int16) (uint32, error) {
return uint32(len(buf)), nil
}
const eraseBlockSizeValue = 4096
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
+2
View File
@@ -12,6 +12,8 @@ func CPUFrequency() uint32 {
return 64000000
}
const flashPageSize = 4096
// InitADC initializes the registers needed for ADC.
func InitADC() {
return // no specific setup on nrf52 machine.
+1 -20
View File
@@ -57,8 +57,6 @@ var (
ErrInvalidTgtAddr = errors.New("invalid target i2c address not in 0..0x80 or is reserved")
ErrI2CGeneric = errors.New("i2c error")
ErrRP2040I2CDisable = errors.New("i2c rp2040 peripheral timeout in disable")
errInvalidI2CSDA = errors.New("invalid I2C SDA pin")
errInvalidI2CSCL = errors.New("invalid I2C SCL pin")
)
// Tx performs a write and then a read transfer placing the result in
@@ -92,7 +90,7 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
// SCL: 3, 7, 11, 15, 19, 27
func (i2c *I2C) Configure(config I2CConfig) error {
const defaultBaud uint32 = 100_000 // 100kHz standard mode
if config.SCL == 0 && config.SDA == 0 {
if config.SCL == 0 {
// If config pins are zero valued or clock pin is invalid then we set default values.
switch i2c.Bus {
case rp.I2C0:
@@ -103,23 +101,6 @@ func (i2c *I2C) Configure(config I2CConfig) error {
config.SDA = I2C1_SDA_PIN
}
}
var okSCL, okSDA bool
switch i2c.Bus {
case rp.I2C0:
okSCL = (config.SCL+3)%4 == 0
okSDA = (config.SDA+4)%4 == 0
case rp.I2C1:
okSCL = (config.SCL+1)%4 == 0
okSDA = (config.SDA+2)%4 == 0
}
switch {
case !okSCL:
return errInvalidI2CSCL
case !okSDA:
return errInvalidI2CSDA
}
if config.Frequency == 0 {
config.Frequency = defaultBaud
}
-240
View File
@@ -1,240 +0,0 @@
//go:build rp2040
// Implementation based on code located here:
// https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_rtc/rtc.c
package machine
import (
"device/rp"
"errors"
"runtime/interrupt"
"unsafe"
)
type rtcType rp.RTC_Type
type rtcTime struct {
Year int16
Month int8
Day int8
Dotw int8
Hour int8
Min int8
Sec int8
}
var RTC = (*rtcType)(unsafe.Pointer(rp.RTC))
const (
second = 1
minute = 60 * second
hour = 60 * minute
day = 24 * hour
)
var (
rtcAlarmRepeats bool
rtcCallback func()
rtcEpoch = rtcTime{
Year: 1970, Month: 1, Day: 1, Dotw: 4, Hour: 0, Min: 0, Sec: 0,
}
)
var (
ErrRtcDelayTooSmall = errors.New("RTC interrupt deplay is too small, shall be at least 1 second")
ErrRtcDelayTooLarge = errors.New("RTC interrupt deplay is too large, shall be no more than 1 day")
)
// SetInterrupt configures delayed and optionally recurring interrupt by real time clock.
//
// Delay is specified in whole seconds, allowed range depends on platform.
// Zero delay disables previously configured interrupt, if any.
//
// RP2040 implementation allows delay to be up to 1 day, otherwise a respective error is emitted.
func (rtc *rtcType) SetInterrupt(delay uint32, repeat bool, callback func()) error {
// Verify delay range
if delay > day {
return ErrRtcDelayTooLarge
}
// De-configure delayed interrupt if delay is zero
if delay == 0 {
rtc.disableInterruptMatch()
return nil
}
// Configure delayed interrupt
rtc.setDivider()
rtcAlarmRepeats = repeat
rtcCallback = callback
err := rtc.setTime(rtcEpoch)
if err != nil {
return err
}
rtc.setAlarm(toAlarmTime(delay), callback)
return nil
}
func toAlarmTime(delay uint32) rtcTime {
result := rtcEpoch
remainder := delay + 1 // needed "+1", otherwise alarm fires one second too early
if remainder >= hour {
result.Hour = int8(remainder / hour)
remainder %= hour
}
if remainder >= minute {
result.Min = int8(remainder / minute)
remainder %= minute
}
result.Sec = int8(remainder)
return result
}
func (rtc *rtcType) setDivider() {
// Get clk_rtc freq and make sure it is running
rtcFreq := configuredFreq[clkRTC]
if rtcFreq == 0 {
panic("can not set RTC divider, clock is not running")
}
// Take rtc out of reset now that we know clk_rtc is running
resetBlock(rp.RESETS_RESET_RTC)
unresetBlockWait(rp.RESETS_RESET_RTC)
// Set up the 1 second divider.
// If rtc_freq is 400 then clkdiv_m1 should be 399
rtcFreq -= 1
// Check the freq is not too big to divide
if rtcFreq > rp.RTC_CLKDIV_M1_CLKDIV_M1_Msk {
panic("can not set RTC divider, clock frequency is too big to divide")
}
// Write divide value
rtc.CLKDIV_M1.Set(rtcFreq)
}
// setTime configures RTC with supplied time, initialises and activates it.
func (rtc *rtcType) setTime(t rtcTime) error {
// Disable RTC and wait while it is still running
rtc.CTRL.Set(0)
for rtc.isActive() {
}
rtc.SETUP_0.Set((uint32(t.Year) << rp.RTC_SETUP_0_YEAR_Pos) |
(uint32(t.Month) << rp.RTC_SETUP_0_MONTH_Pos) |
(uint32(t.Day) << rp.RTC_SETUP_0_DAY_Pos))
rtc.SETUP_1.Set((uint32(t.Dotw) << rp.RTC_SETUP_1_DOTW_Pos) |
(uint32(t.Hour) << rp.RTC_SETUP_1_HOUR_Pos) |
(uint32(t.Min) << rp.RTC_SETUP_1_MIN_Pos) |
(uint32(t.Sec) << rp.RTC_SETUP_1_SEC_Pos))
// Load setup values into RTC clock domain
rtc.CTRL.SetBits(rp.RTC_CTRL_LOAD)
// Enable RTC and wait for it to be running
rtc.CTRL.SetBits(rp.RTC_CTRL_RTC_ENABLE)
for !rtc.isActive() {
}
return nil
}
func (rtc *rtcType) isActive() bool {
return rtc.CTRL.HasBits(rp.RTC_CTRL_RTC_ACTIVE)
}
// setAlarm configures alarm in RTC and arms it.
// The callback is executed in the context of an interrupt handler,
// so regular restructions for this sort of code apply: no blocking, no memory allocation, etc.
func (rtc *rtcType) setAlarm(t rtcTime, callback func()) {
rtc.disableInterruptMatch()
// Clear all match enable bits
rtc.IRQ_SETUP_0.ClearBits(rp.RTC_IRQ_SETUP_0_YEAR_ENA | rp.RTC_IRQ_SETUP_0_MONTH_ENA | rp.RTC_IRQ_SETUP_0_DAY_ENA)
rtc.IRQ_SETUP_1.ClearBits(rp.RTC_IRQ_SETUP_1_DOTW_ENA | rp.RTC_IRQ_SETUP_1_HOUR_ENA | rp.RTC_IRQ_SETUP_1_MIN_ENA | rp.RTC_IRQ_SETUP_1_SEC_ENA)
// Only add to setup if it isn't -1 and set the match enable bits for things we care about
if t.Year >= 0 {
rtc.IRQ_SETUP_0.SetBits(uint32(t.Year) << rp.RTC_SETUP_0_YEAR_Pos)
rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_YEAR_ENA)
}
if t.Month >= 0 {
rtc.IRQ_SETUP_0.SetBits(uint32(t.Month) << rp.RTC_SETUP_0_MONTH_Pos)
rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_MONTH_ENA)
}
if t.Day >= 0 {
rtc.IRQ_SETUP_0.SetBits(uint32(t.Day) << rp.RTC_SETUP_0_DAY_Pos)
rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_DAY_ENA)
}
if t.Dotw >= 0 {
rtc.IRQ_SETUP_1.SetBits(uint32(t.Dotw) << rp.RTC_SETUP_1_DOTW_Pos)
rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_DOTW_ENA)
}
if t.Hour >= 0 {
rtc.IRQ_SETUP_1.SetBits(uint32(t.Hour) << rp.RTC_SETUP_1_HOUR_Pos)
rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_HOUR_ENA)
}
if t.Min >= 0 {
rtc.IRQ_SETUP_1.SetBits(uint32(t.Min) << rp.RTC_SETUP_1_MIN_Pos)
rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_MIN_ENA)
}
if t.Sec >= 0 {
rtc.IRQ_SETUP_1.SetBits(uint32(t.Sec) << rp.RTC_SETUP_1_SEC_Pos)
rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_SEC_ENA)
}
// Enable the IRQ at the proc
interrupt.New(rp.IRQ_RTC_IRQ, rtcHandleInterrupt).Enable()
// Enable the IRQ at the peri
rtc.INTE.Set(rp.RTC_INTE_RTC)
rtc.enableInterruptMatch()
}
func (rtc *rtcType) enableInterruptMatch() {
// Set matching and wait for it to be enabled
rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_MATCH_ENA)
for !rtc.IRQ_SETUP_0.HasBits(rp.RTC_IRQ_SETUP_0_MATCH_ACTIVE) {
}
}
func (rtc *rtcType) disableInterruptMatch() {
// Disable matching and wait for it to stop being active
rtc.IRQ_SETUP_0.ClearBits(rp.RTC_IRQ_SETUP_0_MATCH_ENA)
for rtc.IRQ_SETUP_0.HasBits(rp.RTC_IRQ_SETUP_0_MATCH_ACTIVE) {
}
}
func rtcHandleInterrupt(itr interrupt.Interrupt) {
// Always disable the alarm to clear the current IRQ.
// Even if it is a repeatable alarm, we don't want it to keep firing.
// If it matches on a second it can keep firing for that second.
RTC.disableInterruptMatch()
// Call user callback function
if rtcCallback != nil {
rtcCallback()
}
if rtcAlarmRepeats {
// If it is a repeatable alarm, reset time and re-enable the alarm.
RTC.setTime(rtcEpoch)
RTC.enableInterruptMatch()
}
}
+1 -25
View File
@@ -40,9 +40,6 @@ var (
ErrLSBNotSupported = errors.New("SPI LSB unsupported on PL022")
ErrSPITimeout = errors.New("SPI timeout")
ErrSPIBaud = errors.New("SPI baud too low or above 66.5Mhz")
errSPIInvalidSDI = errors.New("invalid SPI SDI pin")
errSPIInvalidSDO = errors.New("invalid SPI SDO pin")
errSPIInvalidSCK = errors.New("invalid SPI SCK pin")
)
type SPI struct {
@@ -165,7 +162,7 @@ func (spi SPI) GetBaudRate() uint32 {
// No pin configuration is needed of SCK, SDO and SDI needed after calling Configure.
func (spi SPI) Configure(config SPIConfig) error {
const defaultBaud uint32 = 115200
if config.SCK == 0 && config.SDO == 0 && config.SDI == 0 {
if config.SCK == 0 {
// set default pins if config zero valued or invalid clock pin supplied.
switch spi.Bus {
case rp.SPI0:
@@ -178,27 +175,6 @@ func (spi SPI) Configure(config SPIConfig) error {
config.SDI = SPI1_SDI_PIN
}
}
var okSDI, okSDO, okSCK bool
switch spi.Bus {
case rp.SPI0:
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18
case rp.SPI1:
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 28
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
}
switch {
case !okSDI:
return errSPIInvalidSDI
case !okSDO:
return errSPIInvalidSDO
case !okSCK:
return errSPIInvalidSCK
}
if config.DataBits < 4 || config.DataBits > 16 {
config.DataBits = 8
}
-122
View File
@@ -1,122 +0,0 @@
//go:build stm32f4 || stm32l4 || stm32wlx
package machine
import (
"device/stm32"
"bytes"
"unsafe"
)
// compile-time check for ensuring we fulfill BlockDevice interface
var _ BlockDevice = flashBlockDevice{}
var Flash flashBlockDevice
type flashBlockDevice struct {
}
// ReadAt reads the given number of bytes from the block device.
func (f flashBlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
return 0, errFlashCannotReadPastEOF
}
data := unsafe.Slice((*byte)(unsafe.Pointer(FlashDataStart()+uintptr(off))), len(p))
copy(p, data)
return len(p), nil
}
// WriteAt writes the given number of bytes to the block device.
// Only double-word (64 bits) length data can be programmed. See rm0461 page 78.
// If the length of p is not long enough it will be padded with 0xFF bytes.
// This method assumes that the destination is already erased.
func (f flashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
if FlashDataStart()+uintptr(off)+uintptr(len(p)) > FlashDataEnd() {
return 0, errFlashCannotWritePastEOF
}
unlockFlash()
defer lockFlash()
return writeFlashData(FlashDataStart()+uintptr(off), f.pad(p))
}
// Size returns the number of bytes in this block device.
func (f flashBlockDevice) Size() int64 {
return int64(FlashDataEnd() - FlashDataStart())
}
// WriteBlockSize returns the block size in which data can be written to
// memory. It can be used by a client to optimize writes, non-aligned writes
// should always work correctly.
func (f flashBlockDevice) WriteBlockSize() int64 {
return writeBlockSize
}
func eraseBlockSize() int64 {
return eraseBlockSizeValue
}
// EraseBlockSize returns the smallest erasable area on this particular chip
// in bytes. This is used for the block size in EraseBlocks.
// It must be a power of two, and may be as small as 1. A typical size is 4096.
// TODO: correctly handle processors that have differently sized blocks
// in different areas of memory like the STM32F40x and STM32F1x.
func (f flashBlockDevice) EraseBlockSize() int64 {
return eraseBlockSize()
}
// EraseBlocks erases the given number of blocks. An implementation may
// transparently coalesce ranges of blocks into larger bundles if the chip
// supports this. The start and len parameters are in block numbers, use
// EraseBlockSize to map addresses to blocks.
func (f flashBlockDevice) EraseBlocks(start, len int64) error {
unlockFlash()
defer lockFlash()
for i := start; i < start+len; i++ {
if err := eraseBlock(uint32(i)); err != nil {
return err
}
}
return nil
}
// pad data if needed so it is long enough for correct byte alignment on writes.
func (f flashBlockDevice) pad(p []byte) []byte {
paddingNeeded := f.WriteBlockSize() - (int64(len(p)) % f.WriteBlockSize())
if paddingNeeded == 0 {
return p
}
padded := bytes.Repeat([]byte{0xff}, int(paddingNeeded))
return append(p, padded...)
}
const memoryStart = 0x08000000
func unlockFlash() {
// keys as described rm0461 page 76
var fkey1 uint32 = 0x45670123
var fkey2 uint32 = 0xCDEF89AB
// Wait for the flash memory not to be busy
for stm32.FLASH.GetSR_BSY() != 0 {
}
// Check if the controller is unlocked already
if stm32.FLASH.GetCR_LOCK() != 0 {
// Write the first key
stm32.FLASH.SetKEYR(fkey1)
// Write the second key
stm32.FLASH.SetKEYR(fkey2)
}
}
func lockFlash() {
stm32.FLASH.SetCR_LOCK(1)
}
-141
View File
@@ -6,8 +6,6 @@ package machine
import (
"device/stm32"
"encoding/binary"
"errors"
"math/bits"
"runtime/interrupt"
"runtime/volatile"
@@ -793,142 +791,3 @@ func (i2c *I2C) getSpeed(config I2CConfig) uint32 {
}
}
}
//---------- Flash related code
// the block size actually depends on the sector.
// TODO: handle this correctly for sectors > 3
const eraseBlockSizeValue = 16384
// see RM0090 page 75
func sectorNumber(address uintptr) uint32 {
switch {
// 0x0800 0000 - 0x0800 3FFF
case address >= 0x08000000 && address <= 0x08003FFF:
return 0
// 0x0800 4000 - 0x0800 7FFF
case address >= 0x08004000 && address <= 0x08007FFF:
return 1
// 0x0800 8000 - 0x0800 BFFF
case address >= 0x08008000 && address <= 0x0800BFFF:
return 2
// 0x0800 C000 - 0x0800 FFFF
case address >= 0x0800C000 && address <= 0x0800FFFF:
return 3
// 0x0801 0000 - 0x0801 FFFF
case address >= 0x08010000 && address <= 0x0801FFFF:
return 4
// 0x0802 0000 - 0x0803 FFFF
case address >= 0x08020000 && address <= 0x0803FFFF:
return 5
// 0x0804 0000 - 0x0805 FFFF
case address >= 0x08040000 && address <= 0x0805FFFF:
return 6
case address >= 0x08060000 && address <= 0x0807FFFF:
return 7
case address >= 0x08080000 && address <= 0x0809FFFF:
return 8
case address >= 0x080A0000 && address <= 0x080BFFFF:
return 9
case address >= 0x080C0000 && address <= 0x080DFFFF:
return 10
case address >= 0x080E0000 && address <= 0x080FFFFF:
return 11
default:
return 0
}
}
// calculate sector number from address
// var sector uint32 = sectorNumber(address)
// see RM0090 page 85
// eraseBlock at the passed in block number
func eraseBlock(block uint32) error {
waitUntilFlashDone()
// clear any previous errors
stm32.FLASH.SR.SetBits(0xF0)
// set SER bit
stm32.FLASH.SetCR_SER(1)
defer stm32.FLASH.SetCR_SER(0)
// set the block (aka sector) to be erased
stm32.FLASH.SetCR_SNB(block)
defer stm32.FLASH.SetCR_SNB(0)
// start the page erase
stm32.FLASH.SetCR_STRT(1)
waitUntilFlashDone()
if err := checkError(); err != nil {
return err
}
return nil
}
const writeBlockSize = 2
// see RM0090 page 86
// must write data in word-length
func writeFlashData(address uintptr, data []byte) (int, error) {
if len(data)%writeBlockSize != 0 {
return 0, errFlashInvalidWriteLength
}
waitUntilFlashDone()
// clear any previous errors
stm32.FLASH.SR.SetBits(0xF0)
// set parallelism to x32
stm32.FLASH.SetCR_PSIZE(2)
for i := 0; i < len(data); i += writeBlockSize {
// start write operation
stm32.FLASH.SetCR_PG(1)
*(*uint16)(unsafe.Pointer(address)) = binary.BigEndian.Uint16(data[i : i+writeBlockSize])
waitUntilFlashDone()
if err := checkError(); err != nil {
return i, err
}
// end write operation
stm32.FLASH.SetCR_PG(0)
}
return len(data), nil
}
func waitUntilFlashDone() {
for stm32.FLASH.GetSR_BSY() != 0 {
}
}
var (
errFlashPGS = errors.New("errFlashPGS")
errFlashPGP = errors.New("errFlashPGP")
errFlashPGA = errors.New("errFlashPGA")
errFlashWRP = errors.New("errFlashWRP")
)
func checkError() error {
switch {
case stm32.FLASH.GetSR_PGSERR() != 0:
return errFlashPGS
case stm32.FLASH.GetSR_PGPERR() != 0:
return errFlashPGP
case stm32.FLASH.GetSR_PGAERR() != 0:
return errFlashPGA
case stm32.FLASH.GetSR_WRPERR() != 0:
return errFlashWRP
}
return nil
}
-103
View File
@@ -4,8 +4,6 @@ package machine
import (
"device/stm32"
"encoding/binary"
"errors"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
@@ -545,104 +543,3 @@ func initRNG() {
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_RNGEN)
stm32.RNG.CR.SetBits(stm32.RNG_CR_RNGEN)
}
//---------- Flash related code
const eraseBlockSizeValue = 2048
// see RM0394 page 83
// eraseBlock of the passed in block number
func eraseBlock(block uint32) error {
waitUntilFlashDone()
// clear any previous errors
stm32.FLASH.SR.SetBits(0x3FA)
// page erase operation
stm32.FLASH.SetCR_PER(1)
defer stm32.FLASH.SetCR_PER(0)
// set the page to be erased
stm32.FLASH.SetCR_PNB(block)
// start the page erase
stm32.FLASH.SetCR_START(1)
waitUntilFlashDone()
if err := checkError(); err != nil {
return err
}
return nil
}
const writeBlockSize = 8
// see RM0394 page 84
// It is only possible to program double word (2 x 32-bit data).
func writeFlashData(address uintptr, data []byte) (int, error) {
if len(data)%writeBlockSize != 0 {
return 0, errFlashInvalidWriteLength
}
waitUntilFlashDone()
// clear any previous errors
stm32.FLASH.SR.SetBits(0x3FA)
for j := 0; j < len(data); j += writeBlockSize {
// start page write operation
stm32.FLASH.SetCR_PG(1)
// write first word using double-word low order word
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j+writeBlockSize/2 : j+writeBlockSize])
address += writeBlockSize / 2
// write second word using double-word high order word
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j : j+writeBlockSize/2])
waitUntilFlashDone()
if err := checkError(); err != nil {
return j, err
}
// end flash write
stm32.FLASH.SetCR_PG(0)
address += writeBlockSize / 2
}
return len(data), nil
}
func waitUntilFlashDone() {
for stm32.FLASH.GetSR_BSY() != 0 {
}
}
var (
errFlashPGS = errors.New("errFlashPGS")
errFlashSIZE = errors.New("errFlashSIZE")
errFlashPGA = errors.New("errFlashPGA")
errFlashWRP = errors.New("errFlashWRP")
errFlashPROG = errors.New("errFlashPROG")
)
func checkError() error {
switch {
case stm32.FLASH.GetSR_PGSERR() != 0:
return errFlashPGS
case stm32.FLASH.GetSR_SIZERR() != 0:
return errFlashSIZE
case stm32.FLASH.GetSR_PGAERR() != 0:
return errFlashPGA
case stm32.FLASH.GetSR_WRPERR() != 0:
return errFlashWRP
case stm32.FLASH.GetSR_PROGERR() != 0:
return errFlashPROG
}
return nil
}
-114
View File
@@ -6,8 +6,6 @@ package machine
import (
"device/stm32"
"encoding/binary"
"errors"
"math/bits"
"runtime/interrupt"
"runtime/volatile"
@@ -426,115 +424,3 @@ const (
ARR_MAX = 0x10000
PSC_MAX = 0x10000
)
//---------- Flash related code
const eraseBlockSizeValue = 2048
// eraseBlock of the passed in block number
func eraseBlock(block uint32) error {
waitUntilFlashDone()
// check if operation is allowed.
if stm32.FLASH.GetSR_PESD() != 0 {
return errFlashCannotErasePage
}
// clear any previous errors
stm32.FLASH.SR.SetBits(0x3FA)
// page erase operation
stm32.FLASH.SetCR_PER(1)
defer stm32.FLASH.SetCR_PER(0)
// set the address to the page to be written
stm32.FLASH.SetCR_PNB(block)
defer stm32.FLASH.SetCR_PNB(0)
// start the page erase
stm32.FLASH.SetCR_STRT(1)
waitUntilFlashDone()
if err := checkError(); err != nil {
return err
}
return nil
}
const writeBlockSize = 8
func writeFlashData(address uintptr, data []byte) (int, error) {
if len(data)%writeBlockSize != 0 {
return 0, errFlashInvalidWriteLength
}
waitUntilFlashDone()
// check if operation is allowed
if stm32.FLASH.GetSR_PESD() != 0 {
return 0, errFlashNotAllowedWriteData
}
// clear any previous errors
stm32.FLASH.SR.SetBits(0x3FA)
for j := 0; j < len(data); j += writeBlockSize {
// start page write operation
stm32.FLASH.SetCR_PG(1)
// write first word using double-word low order word
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j+writeBlockSize/2 : j+writeBlockSize])
address += writeBlockSize / 2
// write second word using double-word high order word
*(*uint32)(unsafe.Pointer(address)) = binary.BigEndian.Uint32(data[j : j+writeBlockSize/2])
waitUntilFlashDone()
if err := checkError(); err != nil {
return j, err
}
// end flash write
stm32.FLASH.SetCR_PG(0)
address += writeBlockSize / 2
}
return len(data), nil
}
func waitUntilFlashDone() {
for stm32.FLASH.GetSR_BSY() != 0 {
}
for stm32.FLASH.GetSR_CFGBSY() != 0 {
}
}
var (
errFlashPGS = errors.New("errFlashPGS")
errFlashSIZE = errors.New("errFlashSIZE")
errFlashPGA = errors.New("errFlashPGA")
errFlashWRP = errors.New("errFlashWRP")
errFlashPROG = errors.New("errFlashPROG")
)
func checkError() error {
switch {
case stm32.FLASH.GetSR_PGSERR() != 0:
return errFlashPGS
case stm32.FLASH.GetSR_SIZERR() != 0:
return errFlashSIZE
case stm32.FLASH.GetSR_PGAERR() != 0:
return errFlashPGA
case stm32.FLASH.GetSR_WRPERR() != 0:
return errFlashWRP
case stm32.FLASH.GetSR_PROGERR() != 0:
return errFlashPROG
}
return nil
}
+12 -12
View File
@@ -71,7 +71,7 @@ var deepEqualTests = []DeepEqualTest{
{&[3]int{1, 2, 3}, &[3]int{1, 2, 3}, true},
{Basic{1, 0.5}, Basic{1, 0.5}, true},
{error(nil), error(nil), true},
{map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true},
//{map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true},
{fn1, fn2, true},
{[]byte{1, 2, 3}, []byte{1, 2, 3}, true},
{[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true},
@@ -87,10 +87,10 @@ var deepEqualTests = []DeepEqualTest{
{&[3]int{1, 2, 3}, &[3]int{1, 2, 4}, false},
{Basic{1, 0.5}, Basic{1, 0.6}, false},
{Basic{1, 0}, Basic{2, 0}, false},
{map[int]string{1: "one", 3: "two"}, map[int]string{2: "two", 1: "one"}, false},
{map[int]string{1: "one", 2: "txo"}, map[int]string{2: "two", 1: "one"}, false},
{map[int]string{1: "one"}, map[int]string{2: "two", 1: "one"}, false},
{map[int]string{2: "two", 1: "one"}, map[int]string{1: "one"}, false},
//{map[int]string{1: "one", 3: "two"}, map[int]string{2: "two", 1: "one"}, false},
//{map[int]string{1: "one", 2: "txo"}, map[int]string{2: "two", 1: "one"}, false},
//{map[int]string{1: "one"}, map[int]string{2: "two", 1: "one"}, false},
//{map[int]string{2: "two", 1: "one"}, map[int]string{1: "one"}, false},
{nil, 1, false},
{1, nil, false},
{fn1, fn3, false},
@@ -104,16 +104,16 @@ var deepEqualTests = []DeepEqualTest{
{&[1]float64{math.NaN()}, self{}, true},
{[]float64{math.NaN()}, []float64{math.NaN()}, false},
{[]float64{math.NaN()}, self{}, true},
{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false},
{map[float64]float64{math.NaN(): 1}, self{}, true},
//{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false},
//{map[float64]float64{math.NaN(): 1}, self{}, true},
// Nil vs empty: not the same.
{[]int{}, []int(nil), false},
{[]int{}, []int{}, true},
{[]int(nil), []int(nil), true},
{map[int]int{}, map[int]int(nil), false},
{map[int]int{}, map[int]int{}, true},
{map[int]int(nil), map[int]int(nil), true},
//{map[int]int{}, map[int]int(nil), false},
//{map[int]int{}, map[int]int{}, true},
//{map[int]int(nil), map[int]int(nil), true},
// Mismatched types
{1, 1.0, false},
@@ -130,8 +130,8 @@ var deepEqualTests = []DeepEqualTest{
// Possible loops.
{&loopy1, &loopy1, true},
{&loopy1, &loopy2, true},
{&cycleMap1, &cycleMap2, true},
{&cycleMap1, &cycleMap3, false},
//{&cycleMap1, &cycleMap2, true},
//{&cycleMap1, &cycleMap3, false},
}
func TestDeepEqual(t *testing.T) {
+4 -44
View File
@@ -33,8 +33,6 @@
// - map types (this is still missing the key and element types)
// meta uint8
// ptrTo *typeStruct
// elem *typeStruct
// key *typeStruct
// - struct types (see structType):
// meta uint8
// numField uint16
@@ -410,13 +408,6 @@ type arrayType struct {
arrayLen uintptr
}
type mapType struct {
rawType
ptrTo *rawType
elem *rawType
key *rawType
}
// Type for struct types. The numField value is intentionally put before ptrTo
// for better struct packing on 32-bit and 64-bit architectures. On these
// architectures, the ptrTo field still has the same offset as in all the other
@@ -481,21 +472,13 @@ func (t *rawType) elem() *rawType {
switch underlying.Kind() {
case Pointer:
return (*ptrType)(unsafe.Pointer(underlying)).elem
case Chan, Slice, Array, Map:
case Chan, Slice, Array:
return (*elemType)(unsafe.Pointer(underlying)).elem
default:
panic(&TypeError{"Elem"})
default: // not implemented: Map
panic("unimplemented: (reflect.Type).Elem()")
}
}
func (t *rawType) key() *rawType {
underlying := t.underlying()
if underlying.Kind() != Map {
panic(&TypeError{"Key"})
}
return (*mapType)(unsafe.Pointer(underlying)).key
}
// Field returns the type of the i'th field of this struct type. It panics if t
// is not a struct type.
func (t *rawType) Field(i int) StructField {
@@ -785,29 +768,6 @@ func (t *rawType) Comparable() bool {
}
}
// isbinary() returns if the hashmapAlgorithmBinary functions can be used on this type
func (t *rawType) isBinary() bool {
switch t.Kind() {
case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return true
case Float32, Float64, Complex64, Complex128:
return true
case Pointer:
return true
case Array:
return t.elem().isBinary()
case Struct:
numField := t.NumField()
for i := 0; i < numField; i++ {
if !t.rawField(i).Type.isBinary() {
return false
}
}
return true
}
return false
}
func (t rawType) ChanDir() ChanDir {
panic("unimplemented: (reflect.Type).ChanDir()")
}
@@ -837,7 +797,7 @@ func (t *rawType) Name() string {
}
func (t *rawType) Key() Type {
return t.key()
panic("unimplemented: (reflect.Type).Key()")
}
func (t rawType) In(i int) Type {
+7 -202
View File
@@ -641,119 +641,30 @@ func (v Value) OverflowFloat(x float64) bool {
}
func (v Value) MapKeys() []Value {
if v.Kind() != Map {
panic(&ValueError{Method: "MapKeys", Kind: v.Kind()})
}
// empty map
if v.Len() == 0 {
return nil
}
keys := make([]Value, 0, v.Len())
it := hashmapNewIterator()
k := New(v.typecode.Key())
e := New(v.typecode.Elem())
for hashmapNext(v.pointer(), it, k.value, e.value) {
keys = append(keys, k.Elem())
k = New(v.typecode.Key())
}
return keys
panic("unimplemented: (reflect.Value).MapKeys()")
}
//go:linkname hashmapStringGet runtime.hashmapStringGetUnsafePointer
func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool
//go:linkname hashmapBinaryGet runtime.hashmapBinaryGetUnsafePointer
func hashmapBinaryGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool
func (v Value) MapIndex(key Value) Value {
if v.Kind() != Map {
panic(&ValueError{Method: "MapIndex", Kind: v.Kind()})
}
// compare key type with actual key type of map
if key.typecode != v.typecode.key() {
// type error?
panic("reflect.Value.MapIndex: incompatible types for key")
}
elemType := v.typecode.Elem()
elem := New(elemType)
if key.Kind() == String {
if ok := hashmapStringGet(v.pointer(), *(*string)(key.value), elem.value, elemType.Size()); !ok {
return Value{}
}
return elem.Elem()
} else if key.typecode.isBinary() {
var keyptr unsafe.Pointer
if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
keyptr = key.value
} else {
keyptr = unsafe.Pointer(&key.value)
}
//TODO(dgryski): zero out padding bytes in key, if any
if ok := hashmapBinaryGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok {
return Value{}
}
return elem.Elem()
}
// TODO(dgryski): Add other map types. For now, just string and binary types are supported.
panic("unimplemented: (reflect.Value).MapIndex()")
}
//go:linkname hashmapNewIterator runtime.hashmapNewIterator
func hashmapNewIterator() unsafe.Pointer
//go:linkname hashmapNext runtime.hashmapNextUnsafePointer
func hashmapNext(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool
func (v Value) MapRange() *MapIter {
if v.Kind() != Map {
panic(&ValueError{Method: "MapRange", Kind: v.Kind()})
}
return &MapIter{
m: v,
it: hashmapNewIterator(),
key: New(v.typecode.Key()),
val: New(v.typecode.Elem()),
}
panic("unimplemented: (reflect.Value).MapRange()")
}
type MapIter struct {
m Value
it unsafe.Pointer
key Value
val Value
valid bool
}
func (it *MapIter) Key() Value {
if !it.valid {
panic("reflect.MapIter.Key called on invalid iterator")
}
return it.key.Elem()
panic("unimplemented: (*reflect.MapIter).Key()")
}
func (it *MapIter) Value() Value {
if !it.valid {
panic("reflect.MapIter.Value called on invalid iterator")
}
return it.val.Elem()
panic("unimplemented: (*reflect.MapIter).Value()")
}
func (it *MapIter) Next() bool {
it.valid = hashmapNext(it.m.pointer(), it.it, it.key.value, it.val.value)
return it.valid
panic("unimplemented: (*reflect.MapIter).Next()")
}
func (v Value) Set(x Value) {
@@ -992,70 +903,8 @@ func AppendSlice(s, t Value) Value {
}
}
//go:linkname hashmapStringSet runtime.hashmapStringSetUnsafePointer
func hashmapStringSet(m unsafe.Pointer, key string, value unsafe.Pointer)
//go:linkname hashmapBinarySet runtime.hashmapBinarySetUnsafePointer
func hashmapBinarySet(m unsafe.Pointer, key, value unsafe.Pointer)
//go:linkname hashmapStringDelete runtime.hashmapStringDeleteUnsafePointer
func hashmapStringDelete(m unsafe.Pointer, key string)
//go:linkname hashmapBinaryDelete runtime.hashmapBinaryDeleteUnsafePointer
func hashmapBinaryDelete(m unsafe.Pointer, key unsafe.Pointer)
func (v Value) SetMapIndex(key, elem Value) {
if v.Kind() != Map {
panic(&ValueError{Method: "SetMapIndex", Kind: v.Kind()})
}
// compare key type with actual key type of map
if key.typecode != v.typecode.key() {
panic("reflect.Value.SetMapIndex: incompatible types for key")
}
// if elem is the zero Value, it means delete
del := elem == Value{}
if !del && elem.typecode != v.typecode.elem() {
panic("reflect.Value.SetMapIndex: incompatible types for value")
}
if key.Kind() == String {
if del {
hashmapStringDelete(v.pointer(), *(*string)(key.value))
} else {
var elemptr unsafe.Pointer
if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
elemptr = elem.value
} else {
elemptr = unsafe.Pointer(&elem.value)
}
hashmapStringSet(v.pointer(), *(*string)(key.value), elemptr)
}
} else if key.typecode.isBinary() {
var keyptr unsafe.Pointer
if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
keyptr = key.value
} else {
keyptr = unsafe.Pointer(&key.value)
}
if del {
hashmapBinaryDelete(v.pointer(), keyptr)
} else {
var elemptr unsafe.Pointer
if elem.isIndirect() || elem.typecode.Size() > unsafe.Sizeof(uintptr(0)) {
elemptr = elem.value
} else {
elemptr = unsafe.Pointer(&elem.value)
}
hashmapBinarySet(v.pointer(), keyptr, elemptr)
}
} else {
panic("unimplemented: (reflect.Value).MapIndex()")
}
panic("unimplemented: (reflect.Value).SetMapIndex()")
}
// FieldByIndex returns the nested field corresponding to index.
@@ -1072,53 +921,9 @@ func (v Value) FieldByName(name string) Value {
panic("unimplemented: (reflect.Value).FieldByName()")
}
//go:linkname hashmapMake runtime.hashmapMakeUnsafePointer
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer
// MakeMapWithSize creates a new map with the specified type and initial space
// for approximately n elements.
func MakeMapWithSize(typ Type, n int) Value {
// TODO(dgryski): deduplicate these? runtime and reflect both need them.
const (
hashmapAlgorithmBinary uint8 = iota
hashmapAlgorithmString
hashmapAlgorithmInterface
)
if typ.Kind() != Map {
panic(&ValueError{"MakeMap", typ.Kind()})
}
if n < 0 {
panic("reflect.MakeMapWithSize: negative size hint")
}
key := typ.Key().(*rawType)
val := typ.Elem().(*rawType)
var alg uint8
if key.Kind() == String {
alg = hashmapAlgorithmString
} else if key.isBinary() {
alg = hashmapAlgorithmBinary
} else {
panic("reflect.MakeMap: unimplemented key type")
}
m := hashmapMake(key.Size(), val.Size(), uintptr(n), alg)
return Value{
typecode: typ.(*rawType),
value: m,
flags: valueFlagExported,
}
}
// MakeMap creates a new map with the specified type.
func MakeMap(typ Type) Value {
return MakeMapWithSize(typ, 8)
panic("unimplemented: reflect.MakeMap()")
}
func (v Value) Call(in []Value) []Value {
-99
View File
@@ -2,7 +2,6 @@ package reflect_test
import (
. "reflect"
"sort"
"testing"
)
@@ -31,101 +30,3 @@ func TestIndirectPointers(t *testing.T) {
t.Errorf("bad indirect array index via reflect")
}
}
func TestMap(t *testing.T) {
m := make(map[string]int)
mtyp := TypeOf(m)
if got, want := mtyp.Key().Kind().String(), "string"; got != want {
t.Errorf("m.Type().Key().String()=%q, want %q", got, want)
}
if got, want := mtyp.Elem().Kind().String(), "int"; got != want {
t.Errorf("m.Elem().String()=%q, want %q", got, want)
}
m["foo"] = 2
mref := ValueOf(m)
two := mref.MapIndex(ValueOf("foo"))
if got, want := two.Interface().(int), 2; got != want {
t.Errorf("MapIndex(`foo`)=%v, want %v", got, want)
}
m["bar"] = 3
m["baz"] = 4
m["qux"] = 5
it := mref.MapRange()
var gotKeys []string
for it.Next() {
k := it.Key()
v := it.Value()
kstr := k.Interface().(string)
vint := v.Interface().(int)
gotKeys = append(gotKeys, kstr)
if m[kstr] != vint {
t.Errorf("m[%v]=%v, want %v", kstr, vint, m[kstr])
}
}
var wantKeys []string
for k := range m {
wantKeys = append(wantKeys, k)
}
sort.Strings(gotKeys)
sort.Strings(wantKeys)
if !equal(gotKeys, wantKeys) {
t.Errorf("MapRange return unexpected keys: got %v, want %v", gotKeys, wantKeys)
}
refMapKeys := mref.MapKeys()
gotKeys = gotKeys[:0]
for _, v := range refMapKeys {
gotKeys = append(gotKeys, v.Interface().(string))
}
sort.Strings(gotKeys)
if !equal(gotKeys, wantKeys) {
t.Errorf("MapKeys return unexpected keys: got %v, want %v", gotKeys, wantKeys)
}
mref.SetMapIndex(ValueOf("bar"), Value{})
if _, ok := m["bar"]; ok {
t.Errorf("SetMapIndex failed to delete `bar`")
}
mref.SetMapIndex(ValueOf("baz"), ValueOf(6))
if got, want := m["baz"], 6; got != want {
t.Errorf("SetMapIndex(bar, 6) got %v, want %v", got, want)
}
m2ref := MakeMap(mref.Type())
m2ref.SetMapIndex(ValueOf("foo"), ValueOf(2))
m2 := m2ref.Interface().(map[string]int)
if m2["foo"] != 2 {
t.Errorf("MakeMap failed to create map")
}
}
func equal[T comparable](a, b []T) bool {
if len(a) != len(b) {
return false
}
for i, aa := range a {
if b[i] != aa {
return false
}
}
return true
}
+5 -47
View File
@@ -49,10 +49,6 @@ type hashmapIterator struct {
bucketIndex uint8 // current index into bucket
}
func hashmapNewIterator() unsafe.Pointer {
return unsafe.Pointer(new(hashmapIterator))
}
// Get the topmost 8 bits of the hash, without using a special value (like 0).
func hashmapTopHash(hash uint32) uint8 {
tophash := uint8(hash >> 24)
@@ -88,10 +84,6 @@ func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) *hashm
}
}
func hashmapMakeUnsafePointer(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer {
return (unsafe.Pointer)(hashmapMake(keySize, valueSize, sizeHint, alg))
}
func hashmapKeyEqualAlg(alg hashmapAlgorithm) func(x, y unsafe.Pointer, n uintptr) bool {
switch alg {
case hashmapAlgorithmBinary:
@@ -150,8 +142,10 @@ func hashmapLen(m *hashmap) int {
return int(m.count)
}
func hashmapLenUnsafePointer(m unsafe.Pointer) int {
return hashmapLen((*hashmap)(m))
// wrapper for use in reflect
func hashmapLenUnsafePointer(p unsafe.Pointer) int {
m := (*hashmap)(p)
return hashmapLen(m)
}
// Set a specified key to a given value. Grow the map if necessary.
@@ -214,10 +208,6 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
*emptySlotTophash = tophash
}
func hashmapSetUnsafePointer(m unsafe.Pointer, key unsafe.Pointer, value unsafe.Pointer, hash uint32) {
hashmapSet((*hashmap)(m), key, value, hash)
}
// hashmapInsertIntoNewBucket creates a new bucket, inserts the given key and
// value into the bucket, and returns a pointer to this bucket.
func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket {
@@ -309,10 +299,6 @@ func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash u
return false
}
func hashmapGetUnsafePointer(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr, hash uint32) bool {
return hashmapGet((*hashmap)(m), key, value, valueSize, hash)
}
// Delete a given key from the map. No-op when the key does not exist in the
// map.
//
@@ -423,10 +409,6 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo
}
}
func hashmapNextUnsafePointer(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool {
return hashmapNext((*hashmap)(m), (*hashmapIterator)(it), key, value)
}
// Hashmap with plain binary data keys (not containing strings etc.).
func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
if m == nil {
@@ -436,10 +418,6 @@ func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
hashmapSet(m, key, value, hash)
}
func hashmapBinarySetUnsafePointer(m unsafe.Pointer, key, value unsafe.Pointer) {
hashmapBinarySet((*hashmap)(m), key, value)
}
func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool {
if m == nil {
memzero(value, uintptr(valueSize))
@@ -449,10 +427,6 @@ func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr)
return hashmapGet(m, key, value, valueSize, hash)
}
func hashmapBinaryGetUnsafePointer(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool {
return hashmapBinaryGet((*hashmap)(m), key, value, valueSize)
}
func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
if m == nil {
return
@@ -461,10 +435,6 @@ func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
hashmapDelete(m, key, hash)
}
func hashmapBinaryDeleteUnsafePointer(m unsafe.Pointer, key unsafe.Pointer) {
hashmapBinaryDelete((*hashmap)(m), key)
}
// Hashmap with string keys (a common case).
func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
@@ -489,10 +459,6 @@ func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) {
hashmapSet(m, unsafe.Pointer(&key), value, hash)
}
func hashmapStringSetUnsafePointer(m unsafe.Pointer, key string, value unsafe.Pointer) {
hashmapStringSet((*hashmap)(m), key, value)
}
func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool {
if m == nil {
memzero(value, uintptr(valueSize))
@@ -502,10 +468,6 @@ func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize ui
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash)
}
func hashmapStringGetUnsafePointer(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool {
return hashmapStringGet((*hashmap)(m), key, value, valueSize)
}
func hashmapStringDelete(m *hashmap, key string) {
if m == nil {
return
@@ -514,10 +476,6 @@ func hashmapStringDelete(m *hashmap, key string) {
hashmapDelete(m, unsafe.Pointer(&key), hash)
}
func hashmapStringDeleteUnsafePointer(m unsafe.Pointer, key string) {
hashmapStringDelete((*hashmap)(m), key)
}
// Hashmap with interface keys (for everything else).
// This is a method that is intentionally unexported in the reflect package. It
@@ -605,7 +563,7 @@ func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
}
func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr, seed uintptr) uint32 {
_i := *(*interface{})(iptr)
_i := *(*_interface)(iptr)
return hashmapInterfaceHash(_i, seed)
}
-5
View File
@@ -27,7 +27,6 @@ func init() {
initSERCOMClocks()
initUSBClock()
initADCClock()
enableCache()
cdc.EnableUSBCDC()
machine.USBDev.Configure(machine.UARTConfig{})
@@ -368,10 +367,6 @@ func initADCClock() {
sam.GCLK_PCHCTRL_CHEN)
}
func enableCache() {
sam.CMCC.CTRL.SetBits(sam.CMCC_CTRL_CEN)
}
func waitForEvents() {
arm.Asm("wfe")
}
-34
View File
@@ -129,8 +129,6 @@ func main() {
floatcmplx()
mapgrow()
interfacerehash()
}
func floatcmplx() {
@@ -276,35 +274,3 @@ func mapgrow() {
}
println("done")
}
type Counter interface {
count() int
}
type counter struct {
i int
}
func (c *counter) count() int {
return c.i
}
func interfacerehash() {
m := make(map[Counter]int)
for i := 0; i < 20; i++ {
c := &counter{i}
m[c] = i
}
var failures int
for k, v := range m {
if got := m[k]; got != v {
println("lookup failure got", got, "want", v)
failures++
}
}
if failures == 0 {
println("no interface lookup failures")
}
}
-1
View File
@@ -80,4 +80,3 @@ tested growing of a map
2
2
done
no interface lookup failures
+20
View File
@@ -0,0 +1,20 @@
package transform
import "tinygo.org/x/go-llvm"
// This file implements small transformations on globals (functions and global
// variables) for specific ABIs/architectures.
// ApplyFunctionSections puts every function in a separate section. This makes
// it possible for the linker to remove dead code. It is the equivalent of
// passing -ffunction-sections to a C compiler.
func ApplyFunctionSections(mod llvm.Module) {
llvmFn := mod.FirstFunction()
for !llvmFn.IsNil() {
if !llvmFn.IsDeclaration() && llvmFn.Section() == "" {
name := llvmFn.Name()
llvmFn.SetSection(".text." + name)
}
llvmFn = llvm.NextFunction(llvmFn)
}
}
+15
View File
@@ -0,0 +1,15 @@
package transform_test
import (
"testing"
"github.com/tinygo-org/tinygo/transform"
"tinygo.org/x/go-llvm"
)
func TestApplyFunctionSections(t *testing.T) {
t.Parallel()
testTransform(t, "testdata/globals-function-sections", func(mod llvm.Module) {
transform.ApplyFunctionSections(mod)
})
}
+8
View File
@@ -0,0 +1,8 @@
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "armv7em-none-eabi"
declare void @foo()
define void @bar() {
ret void
}
+8
View File
@@ -0,0 +1,8 @@
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "armv7em-none-eabi"
declare void @foo()
define void @bar() section ".text.bar" {
ret void
}