embeddedgo / go

The Go programming language with support for bare-matal programing
https://embeddedgo.github.io
BSD 3-Clause "New" or "Revised" License
76 stars 5 forks source link

Implement some runtime initialization outside the runtime #9

Open michalderkacz opened 1 month ago

michalderkacz commented 1 month ago

Consider to perform a target specific initialization outside the runtime package in target specific packages as proposed by n64 port:

https://github.com/embeddedgo/go/blob/8df86cc268aa9fefcf2eedd52f6337a1a189bf6d/src/runtime/tasker_noos_mips64.go#L9-L15

It may give us more flexibility when it comes to early hardware initialization. For example, with this approach we can probably avoid the plugin code in imxmbr.

Questions

  1. Does this //go:linkname approach affects the order of package initialization if we use Go (nosplit) instead of assembly?
  2. Can we reduce the number of external functions to only _rt0_GOARCH_noos and in some way force the init function in some external package to be the first one (with dependencies) executed by runtime?
  3. If the init approach described above doesn't work, can we reduce the number of external functions to only two. For example the second one in the form of: func noos_defaults() map[string]any?
  4. Maybe only one _rt0_GOARCH_noos function is enough to fulfill our needs?

I personally prefer 4. You can probably use DATA unhandledException set to very dumb handler that stops execution here and update it in the _rt0_GOARCH_noos to more intelligent one.

clktmr commented 1 month ago

_rt0_GOARCH_noos is the only function which is mandatory to be implemented by the n64 target, the others are just for convenience. I was probably biased towards using linkname to override the defaultWriter and exceptionHandler, because I was debugging often during runtime initialization. It's probably sufficient to have the default writer initialized very early during init(). I also like setting idea of setting the exceptionHandler in rt0 instead of using linkname.

I would move the n64 target towards reducing usage of linkname to _rt0_mips64_noos. I have noticed there is a system package for the teensy, which has a similar purpose to the n64's machine package. For consistency I will rename the machine package.

Regarding question (1.), I haven't tested it but I would assume linkname doesn't affect package initialization.

michalderkacz commented 1 month ago

I have noticed there is a system package for the teensy, which has a similar purpose to the n64's machine package.

Yes. I thought exactly the same thing. The hal/system is always imported to initialize hardware so it's a good place for _rt0_GOARCH_noos. But I want to avoid doing the whole initialization in assembly because it can be quiet complicated. See for example the setup functions for STM32L4.

I wonder about introducing hal/system/setup package that doesn't export anything and is imported only for its side effects. It should avoid any imports other than from p so you can carefully write part of the initialization in Go and call it from assembly. If there is more possible configurations we can have multiple setup packages in hal/system, eg:

hal/system/setupHSE168MHz
hal/system/setupSPIFlash528MHz
hal/system/setupDebugPicoCart64
hal/system/setupDebugEverDrive64

or if using some constants/variables in assembly, maybe is it possible to configure the initial system this way:

import (
  _ "hal/system/debugEverDrive64"
  _ "hal/system/setup"
)

where hal/system/debugEverDrive64 can contain only this Go and assembly code:

package debugEverDrive64

import "path/to/ed64"

func write(fd int, p []byte) int {
  //...
}
DATA    hal/system/internal·defaultWriter+0(SB)/8,$·write(SB)
GLOBL   hal/system/internal·defaultWriter(SB),RODATA,$8

The best option for N64 is probably to try detect the available debugging interface in the hal/system/setup so it can be used by you, me and any other people without reconfiguration.