mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-30 00:28:41 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f84dcb92f | |||
| 1cce1ea423 | |||
| a2bb1d3805 | |||
| c4dadbaaab | |||
| 828c3169ab | |||
| f6ee470eda | |||
| d0f4702f8b | |||
| c0a50e9b47 | |||
| 9541525402 | |||
| 60a93e8e2d | |||
| cdf785629a | |||
| ea183e9197 | |||
| 74160c0e32 | |||
| 6e1b8a54aa |
@@ -41,7 +41,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for
|
|||||||
|
|
||||||
## Supported boards/targets
|
## Supported boards/targets
|
||||||
|
|
||||||
You can compile TinyGo programs for microcontrollers, WebAssembly and Linux.
|
You can compile TinyGo programs for microcontrollers, WebAssembly, Windows, and Linux.
|
||||||
|
|
||||||
The following 94 microcontroller boards are currently supported:
|
The following 94 microcontroller boards are currently supported:
|
||||||
|
|
||||||
|
|||||||
+31
-94
@@ -176,7 +176,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
DefaultStackSize: config.StackSize(),
|
DefaultStackSize: config.StackSize(),
|
||||||
NeedsStackObjects: config.NeedsStackObjects(),
|
NeedsStackObjects: config.NeedsStackObjects(),
|
||||||
Debug: true,
|
Debug: true,
|
||||||
LTO: config.LTO() != "legacy",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the target machine, which is the LLVM object that contains all
|
// Load the target machine, which is the LLVM object that contains all
|
||||||
@@ -192,6 +191,9 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
|
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
|
||||||
Sizes: compiler.Sizes(machine),
|
Sizes: compiler.Sizes(machine),
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return BuildResult{}, err
|
||||||
|
}
|
||||||
result := BuildResult{
|
result := BuildResult{
|
||||||
ModuleRoot: lprogram.MainPkg().Module.Dir,
|
ModuleRoot: lprogram.MainPkg().Module.Dir,
|
||||||
MainDir: lprogram.MainPkg().Dir,
|
MainDir: lprogram.MainPkg().Dir,
|
||||||
@@ -201,9 +203,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
// If there is no module root, just the regular root.
|
// If there is no module root, just the regular root.
|
||||||
result.ModuleRoot = lprogram.MainPkg().Root
|
result.ModuleRoot = lprogram.MainPkg().Root
|
||||||
}
|
}
|
||||||
if err != nil { // failed to load AST
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
err = lprogram.Parse()
|
err = lprogram.Parse()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
@@ -453,24 +452,18 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if compilerConfig.LTO {
|
if runtime.GOOS == "windows" {
|
||||||
buf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
|
// 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()
|
defer buf.Dispose()
|
||||||
_, err = f.Write(buf.Bytes())
|
_, err = f.Write(buf.Bytes())
|
||||||
} else {
|
} else {
|
||||||
if runtime.GOOS == "windows" {
|
// Otherwise, write bitcode directly to the file (probably
|
||||||
// Work around a problem on Windows.
|
// faster).
|
||||||
// For some reason, WriteBitcodeToFile causes TinyGo to
|
err = llvm.WriteBitcodeToFile(mod, f)
|
||||||
// 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 {
|
if err != nil {
|
||||||
// WriteBitcodeToFile doesn't produce a useful error on its
|
// WriteBitcodeToFile doesn't produce a useful error on its
|
||||||
@@ -518,11 +511,24 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
|
|
||||||
// Create runtime.initAll function that calls the runtime
|
// Create runtime.initAll function that calls the runtime
|
||||||
// initializer of each package.
|
// initializer of each package.
|
||||||
initAllModule := createInitAll(ctx, config, compilerConfig, lprogram.Sorted())
|
llvmInitFn := mod.NamedFunction("runtime.initAll")
|
||||||
err := llvm.LinkModules(mod, initAllModule)
|
llvmInitFn.SetLinkage(llvm.InternalLinkage)
|
||||||
if err != nil {
|
llvmInitFn.SetUnnamedAddr(true)
|
||||||
return fmt.Errorf("failed to link module: %w", err)
|
transform.AddStandardAttributes(llvmInitFn, config)
|
||||||
|
llvmInitFn.Param(0).SetName("context")
|
||||||
|
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.GlobalValueType(), pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "")
|
||||||
}
|
}
|
||||||
|
irbuilder.CreateRetVoid()
|
||||||
|
|
||||||
// After linking, functions should (as far as possible) be set to
|
// After linking, functions should (as far as possible) be set to
|
||||||
// private linkage or internal linkage. The compiler package marks
|
// private linkage or internal linkage. The compiler package marks
|
||||||
@@ -555,7 +561,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
|
|
||||||
// Run all optimization passes, which are much more effective now
|
// Run all optimization passes, which are much more effective now
|
||||||
// that the optimizer can see the whole program at once.
|
// that the optimizer can see the whole program at once.
|
||||||
err = optimizeProgram(mod, config)
|
err := optimizeProgram(mod, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -616,38 +622,8 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add job to create the runtime.initAll function in a new module.
|
|
||||||
initAllJob := &compileJob{
|
|
||||||
description: "create runtime.initAll",
|
|
||||||
result: filepath.Join(tmpdir, "runtime-initAll.bc"),
|
|
||||||
run: func(job *compileJob) (err error) {
|
|
||||||
// Create module with runtime.initAll.
|
|
||||||
ctx := llvm.NewContext()
|
|
||||||
defer ctx.Dispose()
|
|
||||||
initAllMod := createInitAll(ctx, config, compilerConfig, lprogram.Sorted())
|
|
||||||
defer initAllMod.Dispose()
|
|
||||||
|
|
||||||
// Write module to bitcode file.
|
|
||||||
llvmBuf := llvm.WriteThinLTOBitcodeToMemoryBuffer(initAllMod)
|
|
||||||
defer llvmBuf.Dispose()
|
|
||||||
return os.WriteFile(job.result, llvmBuf.Bytes(), 0666)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare link command.
|
// Prepare link command.
|
||||||
var linkerDependencies []*compileJob
|
linkerDependencies := []*compileJob{outputObjectFileJob}
|
||||||
switch config.LTO() {
|
|
||||||
case "legacy":
|
|
||||||
// Link all Go bitcode files together into a single large module and
|
|
||||||
// then do a ThinLTO link with the resulting large module + extra C
|
|
||||||
// files (from CGo etc).
|
|
||||||
linkerDependencies = append(linkerDependencies, outputObjectFileJob)
|
|
||||||
case "thin":
|
|
||||||
// Do a real thin link, with each Go package in a separate translation
|
|
||||||
// unit. This is faster than merging them into one big LTO module.
|
|
||||||
linkerDependencies = append(linkerDependencies, packageJobs...)
|
|
||||||
linkerDependencies = append(linkerDependencies, initAllJob)
|
|
||||||
}
|
|
||||||
result.Executable = filepath.Join(tmpdir, "main")
|
result.Executable = filepath.Join(tmpdir, "main")
|
||||||
if config.GOOS() == "windows" {
|
if config.GOOS() == "windows" {
|
||||||
result.Executable += ".exe"
|
result.Executable += ".exe"
|
||||||
@@ -1056,45 +1032,6 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
|
|||||||
return outfile.Name(), outfile.Close()
|
return outfile.Name(), outfile.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a module with the runtime.initAll function that calls all package
|
|
||||||
// initializers in initialization order.
|
|
||||||
func createInitAll(ctx llvm.Context, config *compileopts.Config, compilerConfig *compiler.Config, pkgs []*loader.Package) llvm.Module {
|
|
||||||
// Create LLVM module.
|
|
||||||
mod := ctx.NewModule("runtime-initAll")
|
|
||||||
|
|
||||||
// Add datalayout string.
|
|
||||||
machine, err := compiler.NewTargetMachine(compilerConfig)
|
|
||||||
if err != nil {
|
|
||||||
panic(err) // extremely unlikely to fail here (NewTargetMachine is called many times before)
|
|
||||||
}
|
|
||||||
defer machine.Dispose()
|
|
||||||
targetData := machine.CreateTargetData()
|
|
||||||
defer targetData.Dispose()
|
|
||||||
mod.SetTarget(config.Triple())
|
|
||||||
mod.SetDataLayout(targetData.String())
|
|
||||||
|
|
||||||
// Create empty runtime.initAll function.
|
|
||||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
|
||||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{i8ptrType}, false)
|
|
||||||
fn := llvm.AddFunction(mod, "runtime.initAll", fnType)
|
|
||||||
fn.SetUnnamedAddr(true)
|
|
||||||
transform.AddStandardAttributes(fn, config)
|
|
||||||
|
|
||||||
// Add calls to package initializers.
|
|
||||||
fn.Param(0).SetName("context")
|
|
||||||
block := mod.Context().AddBasicBlock(fn, "entry")
|
|
||||||
irbuilder := mod.Context().NewBuilder()
|
|
||||||
defer irbuilder.Dispose()
|
|
||||||
irbuilder.SetInsertPointAtEnd(block)
|
|
||||||
for _, pkg := range pkgs {
|
|
||||||
pkgInit := llvm.AddFunction(mod, pkg.Pkg.Path()+".init", fnType)
|
|
||||||
irbuilder.CreateCall(fnType, pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "")
|
|
||||||
}
|
|
||||||
irbuilder.CreateRetVoid()
|
|
||||||
|
|
||||||
return mod
|
|
||||||
}
|
|
||||||
|
|
||||||
// optimizeProgram runs a series of optimizations and transformations that are
|
// optimizeProgram runs a series of optimizations and transformations that are
|
||||||
// needed to convert a program to its final form. Some transformations are not
|
// 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.
|
// optional and must be run as the compiler expects them to run.
|
||||||
|
|||||||
@@ -165,14 +165,6 @@ func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LTO returns one of the possible LTO configurations: legacy or thin.
|
|
||||||
func (c *Config) LTO() string {
|
|
||||||
if c.Options.LTO != "" {
|
|
||||||
return c.Options.LTO
|
|
||||||
}
|
|
||||||
return "legacy"
|
|
||||||
}
|
|
||||||
|
|
||||||
// PanicStrategy returns the panic strategy selected for this target. Valid
|
// PanicStrategy returns the panic strategy selected for this target. Valid
|
||||||
// values are "print" (print the panic value, then exit) or "trap" (issue a trap
|
// values are "print" (print the panic value, then exit) or "trap" (issue a trap
|
||||||
// instruction).
|
// instruction).
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ var (
|
|||||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||||
validPanicStrategyOptions = []string{"print", "trap"}
|
validPanicStrategyOptions = []string{"print", "trap"}
|
||||||
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
||||||
validLTOOptions = []string{"legacy", "thin"}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options contains extra options to give to the compiler. These options are
|
// Options contains extra options to give to the compiler. These options are
|
||||||
@@ -26,7 +25,6 @@ type Options struct {
|
|||||||
GOARM string // environment variable (only used with GOARCH=arm)
|
GOARM string // environment variable (only used with GOARCH=arm)
|
||||||
Target string
|
Target string
|
||||||
Opt string
|
Opt string
|
||||||
LTO string
|
|
||||||
GC string
|
GC string
|
||||||
PanicStrategy string
|
PanicStrategy string
|
||||||
Scheduler string
|
Scheduler string
|
||||||
@@ -109,12 +107,6 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.LTO != "" {
|
|
||||||
if !isInArray(validLTOOptions, o.LTO) {
|
|
||||||
return fmt.Errorf("invalid -lto=%s: valid values are %s", o.LTO, strings.Join(validLTOOptions, ", "))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -34,9 +34,7 @@ var stdlibAliases = map[string]string{
|
|||||||
// createAlias implements the function (in the builder) as a call to the alias
|
// createAlias implements the function (in the builder) as a call to the alias
|
||||||
// function.
|
// function.
|
||||||
func (b *builder) createAlias(alias llvm.Value) {
|
func (b *builder) createAlias(alias llvm.Value) {
|
||||||
if !b.LTO {
|
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
||||||
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
|
||||||
}
|
|
||||||
b.llvmFn.SetUnnamedAddr(true)
|
b.llvmFn.SetUnnamedAddr(true)
|
||||||
|
|
||||||
if b.Debug {
|
if b.Debug {
|
||||||
|
|||||||
+5
-15
@@ -55,7 +55,6 @@ type Config struct {
|
|||||||
DefaultStackSize uint64
|
DefaultStackSize uint64
|
||||||
NeedsStackObjects bool
|
NeedsStackObjects bool
|
||||||
Debug bool // Whether to emit debug information in the LLVM module.
|
Debug bool // Whether to emit debug information in the LLVM module.
|
||||||
LTO bool // non-legacy LTO (meaning: package bitcode is merged by the linker)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// compilerContext contains function-independent data that should still be
|
// compilerContext contains function-independent data that should still be
|
||||||
@@ -898,9 +897,7 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
c.createEmbedGlobal(member, global, files)
|
c.createEmbedGlobal(member, global, files)
|
||||||
} else if !info.extern {
|
} else if !info.extern {
|
||||||
global.SetInitializer(llvm.ConstNull(global.GlobalValueType()))
|
global.SetInitializer(llvm.ConstNull(global.GlobalValueType()))
|
||||||
if !c.LTO {
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
|
||||||
}
|
|
||||||
if info.section != "" {
|
if info.section != "" {
|
||||||
global.SetSection(info.section)
|
global.SetSection(info.section)
|
||||||
}
|
}
|
||||||
@@ -947,9 +944,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
|
|||||||
}
|
}
|
||||||
strObj := c.getEmbedFileString(files[0])
|
strObj := c.getEmbedFileString(files[0])
|
||||||
global.SetInitializer(strObj)
|
global.SetInitializer(strObj)
|
||||||
if !c.LTO {
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
if typ.Elem().Underlying().(*types.Basic).Kind() != types.Byte {
|
if typ.Elem().Underlying().(*types.Basic).Kind() != types.Byte {
|
||||||
@@ -973,9 +968,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
|
|||||||
sliceLen := llvm.ConstInt(c.uintptrType, file.Size, false)
|
sliceLen := llvm.ConstInt(c.uintptrType, file.Size, false)
|
||||||
sliceObj := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
|
sliceObj := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
|
||||||
global.SetInitializer(sliceObj)
|
global.SetInitializer(sliceObj)
|
||||||
if !c.LTO {
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
// Assume this is an embed.FS struct:
|
// Assume this is an embed.FS struct:
|
||||||
@@ -1058,9 +1051,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
|
|||||||
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
|
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
|
||||||
globalInitializer = c.builder.CreateInsertValue(globalInitializer, sliceGlobal, 0, "")
|
globalInitializer = c.builder.CreateInsertValue(globalInitializer, sliceGlobal, 0, "")
|
||||||
global.SetInitializer(globalInitializer)
|
global.SetInitializer(globalInitializer)
|
||||||
if !c.LTO {
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
|
||||||
}
|
|
||||||
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
|
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1107,8 +1098,7 @@ func (b *builder) createFunctionStart(intrinsic bool) {
|
|||||||
// assertion error in llvm-project/llvm/include/llvm/IR/GlobalValue.h:236
|
// assertion error in llvm-project/llvm/include/llvm/IR/GlobalValue.h:236
|
||||||
// is thrown.
|
// is thrown.
|
||||||
if b.llvmFn.Linkage() != llvm.InternalLinkage &&
|
if b.llvmFn.Linkage() != llvm.InternalLinkage &&
|
||||||
b.llvmFn.Linkage() != llvm.PrivateLinkage &&
|
b.llvmFn.Linkage() != llvm.PrivateLinkage {
|
||||||
!b.LTO {
|
|
||||||
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
||||||
}
|
}
|
||||||
b.llvmFn.SetUnnamedAddr(true)
|
b.llvmFn.SetUnnamedAddr(true)
|
||||||
|
|||||||
+14
-15
@@ -131,6 +131,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
case *types.Map:
|
case *types.Map:
|
||||||
typeFieldTypes = append(typeFieldTypes,
|
typeFieldTypes = append(typeFieldTypes,
|
||||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
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:
|
case *types.Struct:
|
||||||
typeFieldTypes = append(typeFieldTypes,
|
typeFieldTypes = append(typeFieldTypes,
|
||||||
@@ -193,6 +195,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
|||||||
case *types.Map:
|
case *types.Map:
|
||||||
typeFields = []llvm.Value{
|
typeFields = []llvm.Value{
|
||||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||||
|
c.getTypeCode(typ.Elem()), // elem
|
||||||
|
c.getTypeCode(typ.Key()), // key
|
||||||
}
|
}
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
typeFields = []llvm.Value{
|
typeFields = []llvm.Value{
|
||||||
@@ -490,22 +494,17 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
|||||||
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
|
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if b.LTO {
|
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
||||||
assertedTypeCodeGlobal := b.getTypeCode(expr.AssertedType)
|
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
||||||
commaOk = b.CreateICmp(llvm.IntEQ, actualTypeNum, assertedTypeCodeGlobal, "commaok")
|
if assertedTypeCodeGlobal.IsNil() {
|
||||||
} else {
|
// Create a new typecode global.
|
||||||
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
assertedTypeCodeGlobal = llvm.AddGlobal(b.mod, b.ctx.Int8Type(), globalName)
|
||||||
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
assertedTypeCodeGlobal.SetGlobalConstant(true)
|
||||||
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.
|
|
||||||
commaOk = b.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode")
|
|
||||||
}
|
}
|
||||||
|
// Type assert on concrete type.
|
||||||
|
// Call runtime.typeAssert, which will be lowered to a simple icmp or
|
||||||
|
// const false in the interface lowering pass.
|
||||||
|
commaOk = b.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add 2 new basic blocks (that should get optimized away): one for the
|
// Add 2 new basic blocks (that should get optimized away): one for the
|
||||||
|
|||||||
@@ -45,9 +45,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
|||||||
globalLLVMType := b.getLLVMType(globalType)
|
globalLLVMType := b.getLLVMType(globalType)
|
||||||
globalName := b.fn.Package().Pkg.Path() + "$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
globalName := b.fn.Package().Pkg.Path() + "$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
||||||
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
|
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
|
||||||
if !b.LTO {
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
|
||||||
}
|
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
initializer := llvm.ConstNull(globalLLVMType)
|
initializer := llvm.ConstNull(globalLLVMType)
|
||||||
|
|||||||
@@ -1357,7 +1357,6 @@ func main() {
|
|||||||
command := os.Args[1]
|
command := os.Args[1]
|
||||||
|
|
||||||
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
||||||
lto := flag.String("lto", "legacy", "LTO mode: legacy or thin")
|
|
||||||
gc := flag.String("gc", "", "garbage collector to use (none, leaking, conservative)")
|
gc := flag.String("gc", "", "garbage collector to use (none, leaking, conservative)")
|
||||||
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
|
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
|
||||||
scheduler := flag.String("scheduler", "", "which scheduler to use (none, tasks, asyncify)")
|
scheduler := flag.String("scheduler", "", "which scheduler to use (none, tasks, asyncify)")
|
||||||
@@ -1460,7 +1459,6 @@ func main() {
|
|||||||
Target: *target,
|
Target: *target,
|
||||||
StackSize: stackSize,
|
StackSize: stackSize,
|
||||||
Opt: *opt,
|
Opt: *opt,
|
||||||
LTO: *lto,
|
|
||||||
GC: *gc,
|
GC: *gc,
|
||||||
PanicStrategy: *panicStrategy,
|
PanicStrategy: *panicStrategy,
|
||||||
Scheduler: *scheduler,
|
Scheduler: *scheduler,
|
||||||
|
|||||||
@@ -105,13 +105,6 @@ func TestBuild(t *testing.T) {
|
|||||||
runTestWithConfig("print.go", t, opts, nil, nil)
|
runTestWithConfig("print.go", t, opts, nil, nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("lto=thin", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
opts := optionsFromTarget("", sema)
|
|
||||||
opts.LTO = "thin"
|
|
||||||
runTestWithConfig("init.go", t, opts, nil, nil)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("ldflags", func(t *testing.T) {
|
t.Run("ldflags", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
opts := optionsFromTarget("", sema)
|
opts := optionsFromTarget("", sema)
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
message = "1234567887654321123456788765432112345678876543211234567887654321"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
//go:build nrf || nrf51 || nrf52 || nrf528xx || stm32f4 || stm32l4 || stm32wlx
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:extern __flash_data_start
|
||||||
|
var flashDataStart [0]byte
|
||||||
|
|
||||||
|
//go:extern __flash_data_end
|
||||||
|
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 the end of the writable flash area. Usually this is the address one
|
||||||
|
// past the end of the on-chip flash.
|
||||||
|
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()
|
||||||
|
}
|
||||||
@@ -3,7 +3,9 @@
|
|||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"device/nrf"
|
"device/nrf"
|
||||||
|
"encoding/binary"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@@ -382,3 +384,109 @@ func ReadTemperature() int32 {
|
|||||||
nrf.TEMP.EVENTS_DATARDY.Set(0)
|
nrf.TEMP.EVENTS_DATARDY.Set(0)
|
||||||
return temp
|
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 {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ import (
|
|||||||
"device/nrf"
|
"device/nrf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const eraseBlockSizeValue = 1024
|
||||||
|
|
||||||
|
func eraseBlockSize() int64 {
|
||||||
|
return eraseBlockSizeValue
|
||||||
|
}
|
||||||
|
|
||||||
// Get peripheral and pin number for this GPIO pin.
|
// Get peripheral and pin number for this GPIO pin.
|
||||||
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
func (p Pin) getPortPin() (*nrf.GPIO_Type, uint32) {
|
||||||
return nrf.GPIO, uint32(p)
|
return nrf.GPIO, uint32(p)
|
||||||
|
|||||||
@@ -63,3 +63,9 @@ var (
|
|||||||
PWM1 = &PWM{PWM: nrf.PWM1}
|
PWM1 = &PWM{PWM: nrf.PWM1}
|
||||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const eraseBlockSizeValue = 4096
|
||||||
|
|
||||||
|
func eraseBlockSize() int64 {
|
||||||
|
return eraseBlockSizeValue
|
||||||
|
}
|
||||||
|
|||||||
@@ -84,3 +84,9 @@ var (
|
|||||||
PWM2 = &PWM{PWM: nrf.PWM2}
|
PWM2 = &PWM{PWM: nrf.PWM2}
|
||||||
PWM3 = &PWM{PWM: nrf.PWM3}
|
PWM3 = &PWM{PWM: nrf.PWM3}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const eraseBlockSizeValue = 4096
|
||||||
|
|
||||||
|
func eraseBlockSize() int64 {
|
||||||
|
return eraseBlockSizeValue
|
||||||
|
}
|
||||||
|
|||||||
@@ -102,3 +102,9 @@ func (pdm *PDM) Read(buf []int16) (uint32, error) {
|
|||||||
|
|
||||||
return uint32(len(buf)), nil
|
return uint32(len(buf)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const eraseBlockSizeValue = 4096
|
||||||
|
|
||||||
|
func eraseBlockSize() int64 {
|
||||||
|
return eraseBlockSizeValue
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
//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)
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
@@ -791,3 +793,142 @@ 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
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@@ -543,3 +545,104 @@ func initRNG() {
|
|||||||
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_RNGEN)
|
stm32.RCC.AHB2ENR.SetBits(stm32.RCC_AHB2ENR_RNGEN)
|
||||||
stm32.RNG.CR.SetBits(stm32.RNG_CR_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
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ package machine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"device/stm32"
|
"device/stm32"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"runtime/interrupt"
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
@@ -424,3 +426,115 @@ const (
|
|||||||
ARR_MAX = 0x10000
|
ARR_MAX = 0x10000
|
||||||
PSC_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
@@ -71,7 +71,7 @@ var deepEqualTests = []DeepEqualTest{
|
|||||||
{&[3]int{1, 2, 3}, &[3]int{1, 2, 3}, true},
|
{&[3]int{1, 2, 3}, &[3]int{1, 2, 3}, true},
|
||||||
{Basic{1, 0.5}, Basic{1, 0.5}, true},
|
{Basic{1, 0.5}, Basic{1, 0.5}, true},
|
||||||
{error(nil), error(nil), 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},
|
{fn1, fn2, true},
|
||||||
{[]byte{1, 2, 3}, []byte{1, 2, 3}, true},
|
{[]byte{1, 2, 3}, []byte{1, 2, 3}, true},
|
||||||
{[]MyByte{1, 2, 3}, []MyByte{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},
|
{&[3]int{1, 2, 3}, &[3]int{1, 2, 4}, false},
|
||||||
{Basic{1, 0.5}, Basic{1, 0.6}, false},
|
{Basic{1, 0.5}, Basic{1, 0.6}, false},
|
||||||
{Basic{1, 0}, Basic{2, 0}, 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", 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", 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{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{2: "two", 1: "one"}, map[int]string{1: "one"}, false},
|
||||||
{nil, 1, false},
|
{nil, 1, false},
|
||||||
{1, nil, false},
|
{1, nil, false},
|
||||||
{fn1, fn3, false},
|
{fn1, fn3, false},
|
||||||
@@ -104,16 +104,16 @@ var deepEqualTests = []DeepEqualTest{
|
|||||||
{&[1]float64{math.NaN()}, self{}, true},
|
{&[1]float64{math.NaN()}, self{}, true},
|
||||||
{[]float64{math.NaN()}, []float64{math.NaN()}, false},
|
{[]float64{math.NaN()}, []float64{math.NaN()}, false},
|
||||||
{[]float64{math.NaN()}, self{}, true},
|
{[]float64{math.NaN()}, self{}, true},
|
||||||
//{map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false},
|
{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}, self{}, true},
|
||||||
|
|
||||||
// Nil vs empty: not the same.
|
// Nil vs empty: not the same.
|
||||||
{[]int{}, []int(nil), false},
|
{[]int{}, []int(nil), false},
|
||||||
{[]int{}, []int{}, true},
|
{[]int{}, []int{}, true},
|
||||||
{[]int(nil), []int(nil), true},
|
{[]int(nil), []int(nil), true},
|
||||||
//{map[int]int{}, map[int]int(nil), false},
|
{map[int]int{}, map[int]int(nil), false},
|
||||||
//{map[int]int{}, map[int]int{}, true},
|
{map[int]int{}, map[int]int{}, true},
|
||||||
//{map[int]int(nil), map[int]int(nil), true},
|
{map[int]int(nil), map[int]int(nil), true},
|
||||||
|
|
||||||
// Mismatched types
|
// Mismatched types
|
||||||
{1, 1.0, false},
|
{1, 1.0, false},
|
||||||
@@ -130,8 +130,8 @@ var deepEqualTests = []DeepEqualTest{
|
|||||||
// Possible loops.
|
// Possible loops.
|
||||||
{&loopy1, &loopy1, true},
|
{&loopy1, &loopy1, true},
|
||||||
{&loopy1, &loopy2, true},
|
{&loopy1, &loopy2, true},
|
||||||
//{&cycleMap1, &cycleMap2, true},
|
{&cycleMap1, &cycleMap2, true},
|
||||||
//{&cycleMap1, &cycleMap3, false},
|
{&cycleMap1, &cycleMap3, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeepEqual(t *testing.T) {
|
func TestDeepEqual(t *testing.T) {
|
||||||
|
|||||||
+44
-4
@@ -33,6 +33,8 @@
|
|||||||
// - map types (this is still missing the key and element types)
|
// - map types (this is still missing the key and element types)
|
||||||
// meta uint8
|
// meta uint8
|
||||||
// ptrTo *typeStruct
|
// ptrTo *typeStruct
|
||||||
|
// elem *typeStruct
|
||||||
|
// key *typeStruct
|
||||||
// - struct types (see structType):
|
// - struct types (see structType):
|
||||||
// meta uint8
|
// meta uint8
|
||||||
// numField uint16
|
// numField uint16
|
||||||
@@ -408,6 +410,13 @@ type arrayType struct {
|
|||||||
arrayLen uintptr
|
arrayLen uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mapType struct {
|
||||||
|
rawType
|
||||||
|
ptrTo *rawType
|
||||||
|
elem *rawType
|
||||||
|
key *rawType
|
||||||
|
}
|
||||||
|
|
||||||
// Type for struct types. The numField value is intentionally put before ptrTo
|
// 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
|
// 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
|
// architectures, the ptrTo field still has the same offset as in all the other
|
||||||
@@ -472,13 +481,21 @@ func (t *rawType) elem() *rawType {
|
|||||||
switch underlying.Kind() {
|
switch underlying.Kind() {
|
||||||
case Pointer:
|
case Pointer:
|
||||||
return (*ptrType)(unsafe.Pointer(underlying)).elem
|
return (*ptrType)(unsafe.Pointer(underlying)).elem
|
||||||
case Chan, Slice, Array:
|
case Chan, Slice, Array, Map:
|
||||||
return (*elemType)(unsafe.Pointer(underlying)).elem
|
return (*elemType)(unsafe.Pointer(underlying)).elem
|
||||||
default: // not implemented: Map
|
default:
|
||||||
panic("unimplemented: (reflect.Type).Elem()")
|
panic(&TypeError{"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
|
// Field returns the type of the i'th field of this struct type. It panics if t
|
||||||
// is not a struct type.
|
// is not a struct type.
|
||||||
func (t *rawType) Field(i int) StructField {
|
func (t *rawType) Field(i int) StructField {
|
||||||
@@ -768,6 +785,29 @@ 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 {
|
func (t rawType) ChanDir() ChanDir {
|
||||||
panic("unimplemented: (reflect.Type).ChanDir()")
|
panic("unimplemented: (reflect.Type).ChanDir()")
|
||||||
}
|
}
|
||||||
@@ -797,7 +837,7 @@ func (t *rawType) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *rawType) Key() Type {
|
func (t *rawType) Key() Type {
|
||||||
panic("unimplemented: (reflect.Type).Key()")
|
return t.key()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t rawType) In(i int) Type {
|
func (t rawType) In(i int) Type {
|
||||||
|
|||||||
+202
-7
@@ -641,30 +641,119 @@ func (v Value) OverflowFloat(x float64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) MapKeys() []Value {
|
func (v Value) MapKeys() []Value {
|
||||||
panic("unimplemented: (reflect.Value).MapKeys()")
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//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 {
|
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()")
|
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 {
|
func (v Value) MapRange() *MapIter {
|
||||||
panic("unimplemented: (reflect.Value).MapRange()")
|
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()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type MapIter struct {
|
type MapIter struct {
|
||||||
|
m Value
|
||||||
|
it unsafe.Pointer
|
||||||
|
key Value
|
||||||
|
val Value
|
||||||
|
|
||||||
|
valid bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *MapIter) Key() Value {
|
func (it *MapIter) Key() Value {
|
||||||
panic("unimplemented: (*reflect.MapIter).Key()")
|
if !it.valid {
|
||||||
|
panic("reflect.MapIter.Key called on invalid iterator")
|
||||||
|
}
|
||||||
|
|
||||||
|
return it.key.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *MapIter) Value() Value {
|
func (it *MapIter) Value() Value {
|
||||||
panic("unimplemented: (*reflect.MapIter).Value()")
|
if !it.valid {
|
||||||
|
panic("reflect.MapIter.Value called on invalid iterator")
|
||||||
|
}
|
||||||
|
|
||||||
|
return it.val.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *MapIter) Next() bool {
|
func (it *MapIter) Next() bool {
|
||||||
panic("unimplemented: (*reflect.MapIter).Next()")
|
it.valid = hashmapNext(it.m.pointer(), it.it, it.key.value, it.val.value)
|
||||||
|
return it.valid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) Set(x Value) {
|
func (v Value) Set(x Value) {
|
||||||
@@ -903,8 +992,70 @@ 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) {
|
func (v Value) SetMapIndex(key, elem Value) {
|
||||||
panic("unimplemented: (reflect.Value).SetMapIndex()")
|
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()")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FieldByIndex returns the nested field corresponding to index.
|
// FieldByIndex returns the nested field corresponding to index.
|
||||||
@@ -921,9 +1072,53 @@ func (v Value) FieldByName(name string) Value {
|
|||||||
panic("unimplemented: (reflect.Value).FieldByName()")
|
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.
|
// MakeMap creates a new map with the specified type.
|
||||||
func MakeMap(typ Type) Value {
|
func MakeMap(typ Type) Value {
|
||||||
panic("unimplemented: reflect.MakeMap()")
|
return MakeMapWithSize(typ, 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) Call(in []Value) []Value {
|
func (v Value) Call(in []Value) []Value {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package reflect_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
. "reflect"
|
. "reflect"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,3 +31,101 @@ func TestIndirectPointers(t *testing.T) {
|
|||||||
t.Errorf("bad indirect array index via reflect")
|
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
|
||||||
|
}
|
||||||
|
|||||||
+46
-4
@@ -49,6 +49,10 @@ type hashmapIterator struct {
|
|||||||
bucketIndex uint8 // current index into bucket
|
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).
|
// Get the topmost 8 bits of the hash, without using a special value (like 0).
|
||||||
func hashmapTopHash(hash uint32) uint8 {
|
func hashmapTopHash(hash uint32) uint8 {
|
||||||
tophash := uint8(hash >> 24)
|
tophash := uint8(hash >> 24)
|
||||||
@@ -84,6 +88,10 @@ 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 {
|
func hashmapKeyEqualAlg(alg hashmapAlgorithm) func(x, y unsafe.Pointer, n uintptr) bool {
|
||||||
switch alg {
|
switch alg {
|
||||||
case hashmapAlgorithmBinary:
|
case hashmapAlgorithmBinary:
|
||||||
@@ -142,10 +150,8 @@ func hashmapLen(m *hashmap) int {
|
|||||||
return int(m.count)
|
return int(m.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
// wrapper for use in reflect
|
func hashmapLenUnsafePointer(m unsafe.Pointer) int {
|
||||||
func hashmapLenUnsafePointer(p unsafe.Pointer) int {
|
return hashmapLen((*hashmap)(m))
|
||||||
m := (*hashmap)(p)
|
|
||||||
return hashmapLen(m)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a specified key to a given value. Grow the map if necessary.
|
// Set a specified key to a given value. Grow the map if necessary.
|
||||||
@@ -208,6 +214,10 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
|||||||
*emptySlotTophash = tophash
|
*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
|
// hashmapInsertIntoNewBucket creates a new bucket, inserts the given key and
|
||||||
// value into the bucket, and returns a pointer to this bucket.
|
// value into the bucket, and returns a pointer to this bucket.
|
||||||
func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket {
|
func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket {
|
||||||
@@ -299,6 +309,10 @@ func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash u
|
|||||||
return false
|
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
|
// Delete a given key from the map. No-op when the key does not exist in the
|
||||||
// map.
|
// map.
|
||||||
//
|
//
|
||||||
@@ -409,6 +423,10 @@ 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.).
|
// Hashmap with plain binary data keys (not containing strings etc.).
|
||||||
func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
|
func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
@@ -418,6 +436,10 @@ func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
|
|||||||
hashmapSet(m, key, value, hash)
|
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 {
|
func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
memzero(value, uintptr(valueSize))
|
memzero(value, uintptr(valueSize))
|
||||||
@@ -427,6 +449,10 @@ func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr)
|
|||||||
return hashmapGet(m, key, value, valueSize, hash)
|
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) {
|
func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return
|
return
|
||||||
@@ -435,6 +461,10 @@ func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
|
|||||||
hashmapDelete(m, key, hash)
|
hashmapDelete(m, key, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hashmapBinaryDeleteUnsafePointer(m unsafe.Pointer, key unsafe.Pointer) {
|
||||||
|
hashmapBinaryDelete((*hashmap)(m), key)
|
||||||
|
}
|
||||||
|
|
||||||
// Hashmap with string keys (a common case).
|
// Hashmap with string keys (a common case).
|
||||||
|
|
||||||
func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
|
func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
|
||||||
@@ -459,6 +489,10 @@ func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) {
|
|||||||
hashmapSet(m, unsafe.Pointer(&key), value, hash)
|
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 {
|
func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
memzero(value, uintptr(valueSize))
|
memzero(value, uintptr(valueSize))
|
||||||
@@ -468,6 +502,10 @@ func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize ui
|
|||||||
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash)
|
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) {
|
func hashmapStringDelete(m *hashmap, key string) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return
|
return
|
||||||
@@ -476,6 +514,10 @@ func hashmapStringDelete(m *hashmap, key string) {
|
|||||||
hashmapDelete(m, unsafe.Pointer(&key), hash)
|
hashmapDelete(m, unsafe.Pointer(&key), hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hashmapStringDeleteUnsafePointer(m unsafe.Pointer, key string) {
|
||||||
|
hashmapStringDelete((*hashmap)(m), key)
|
||||||
|
}
|
||||||
|
|
||||||
// Hashmap with interface keys (for everything else).
|
// Hashmap with interface keys (for everything else).
|
||||||
|
|
||||||
// This is a method that is intentionally unexported in the reflect package. It
|
// This is a method that is intentionally unexported in the reflect package. It
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ func init() {
|
|||||||
initSERCOMClocks()
|
initSERCOMClocks()
|
||||||
initUSBClock()
|
initUSBClock()
|
||||||
initADCClock()
|
initADCClock()
|
||||||
|
enableCache()
|
||||||
|
|
||||||
cdc.EnableUSBCDC()
|
cdc.EnableUSBCDC()
|
||||||
machine.USBDev.Configure(machine.UARTConfig{})
|
machine.USBDev.Configure(machine.UARTConfig{})
|
||||||
@@ -367,6 +368,10 @@ func initADCClock() {
|
|||||||
sam.GCLK_PCHCTRL_CHEN)
|
sam.GCLK_PCHCTRL_CHEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func enableCache() {
|
||||||
|
sam.CMCC.CTRL.SetBits(sam.CMCC_CTRL_CEN)
|
||||||
|
}
|
||||||
|
|
||||||
func waitForEvents() {
|
func waitForEvents() {
|
||||||
arm.Asm("wfe")
|
arm.Asm("wfe")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,3 +69,7 @@ _heap_start = _ebss;
|
|||||||
_heap_end = ORIGIN(RAM) + LENGTH(RAM);
|
_heap_end = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
_globals_start = _sdata;
|
_globals_start = _sdata;
|
||||||
_globals_end = _ebss;
|
_globals_end = _ebss;
|
||||||
|
|
||||||
|
/* For the flash API */
|
||||||
|
__flash_data_start = LOADADDR(.data) + SIZEOF(.data);
|
||||||
|
__flash_data_end = ORIGIN(FLASH_TEXT) + LENGTH(FLASH_TEXT);
|
||||||
|
|||||||
Reference in New Issue
Block a user