github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/internal/task/task_stack_esp32.go (about) 1 //go:build scheduler.tasks && esp32 2 3 package task 4 5 // The windowed ABI (used on the ESP32) is as follows: 6 // a0: return address (link register) 7 // a1: stack pointer (must be 16-byte aligned) 8 // a2-a7: incoming arguments 9 // a7: stack frame pointer (optional, normally unused in TinyGo) 10 // Sources: 11 // http://cholla.mmto.org/esp8266/xtensa.html 12 // https://0x04.net/~mwk/doc/xtensa.pdf 13 14 import ( 15 "unsafe" 16 ) 17 18 var systemStack uintptr 19 20 // calleeSavedRegs is the list of registers that must be saved and restored when 21 // switching between tasks. Also see task_stack_esp8266.S that relies on the 22 // exact layout of this struct. 23 type calleeSavedRegs struct { 24 // Registers in the register window of tinygo_startTask. 25 a0 uintptr 26 a1 uintptr 27 a2 uintptr 28 a3 uintptr 29 30 // Locals that can be used by tinygo_swapTask. 31 // The first field is the a0 loaded in tinygo_swapTask, the rest is unused. 32 locals [4]uintptr 33 } 34 35 // archInit runs architecture-specific setup for the goroutine startup. 36 func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) { 37 // Store the stack pointer for the tinygo_swapTask function (implemented in 38 // assembly). It needs to point to the locals field instead of a0 so that 39 // the retw.n at the end of tinygo_swapTask will return into 40 // tinygo_startTask with a0-a3 loaded (using the register window mechanism). 41 s.sp = uintptr(unsafe.Pointer(&r.locals[0])) 42 43 // Start the goroutine at tinygo_startTask (defined in 44 // src/internal/task/task_stack_esp32.S). The topmost two bits are not part 45 // of the address but instead store the register window of the caller. 46 // In this case there is no caller, instead we set up the return address as 47 // if tinygo_startTask called tinygo_swapTask with a call4 instruction. 48 r.locals[0] = uintptr(unsafe.Pointer(&startTask))&^(3<<30) | (1 << 30) 49 50 // Set up the stack pointer inside tinygo_startTask. 51 // Unlike most calling conventions, the windowed ABI actually saves the 52 // stack pointer on the stack to make register windowing work. 53 r.a1 = uintptr(unsafe.Pointer(r)) + 32 54 55 // Store the function pointer and the (only) parameter on the stack in a 56 // location that will be reloaded into registers when doing the 57 // pseudo-return to tinygo_startTask using the register window mechanism. 58 r.a3 = fn 59 r.a2 = uintptr(args) 60 } 61 62 func (s *state) resume() { 63 swapTask(s.sp, &systemStack) 64 } 65 66 func (s *state) pause() { 67 newStack := systemStack 68 systemStack = 0 69 swapTask(newStack, &s.sp) 70 } 71 72 // SystemStack returns the system stack pointer when called from a task stack. 73 // When called from the system stack, it returns 0. 74 func SystemStack() uintptr { 75 return systemStack 76 }