mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 19:43:44 +00:00
runtime/interrupt: add Checkpoint type
This type can be used to jump back to a previous position in a program
from inside an interrupt. This is useful for baremetal systems that
implement wfi but not wfe, and therefore have no easy (race-free) way to
wait until a flag gets changed inside an interrupt. This is an issue on
RISC-V, where this is racy (the interrupt might happen after the check
but before the wfi instruction):
configureInterrupt()
for flag.Load() != 0 {
riscv.Asm("wfi")
}
This commit is contained in:
committed by
Ron Evans
parent
64a30424da
commit
3a2188fc7b
@@ -50,3 +50,12 @@ tinygo_longjmp:
|
||||
lw sp, 0(a0) // jumpSP
|
||||
lw a1, REGSIZE(a0) // jumpPC
|
||||
jr a1
|
||||
|
||||
.section .text.tinygo_checkpointJump
|
||||
.global tinygo_checkpointJump
|
||||
tinygo_checkpointJump:
|
||||
// Note: the code we jump to assumes a0 is non-zero, which is already the
|
||||
// case because that's the stack pointer.
|
||||
mv sp, a0 // jumpSP
|
||||
csrw mepc, a1 // update MEPC value, so we resume there after the mret
|
||||
mret // jump to jumpPC
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package interrupt
|
||||
|
||||
// A checkpoint is a setjmp like buffer, that can be used as a flag for
|
||||
// interrupts.
|
||||
//
|
||||
// It can be used as follows:
|
||||
//
|
||||
// // global var
|
||||
// var c Checkpoint
|
||||
//
|
||||
// // to set up the checkpoint and wait for it
|
||||
// if c.Save() {
|
||||
// setupInterrupt()
|
||||
// for {
|
||||
// waitForInterrupt()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Inside the interrupt handler:
|
||||
// if c.Saved() {
|
||||
// c.Jump()
|
||||
// }
|
||||
type Checkpoint struct {
|
||||
jumpSP uintptr
|
||||
jumpPC uintptr
|
||||
}
|
||||
|
||||
// Save the execution state in the given checkpoint, overwriting a previous
|
||||
// saved checkpoint.
|
||||
//
|
||||
// This function returns twice: once the normal way after saving (returning
|
||||
// true) and once after jumping (returning false).
|
||||
//
|
||||
// This function is a compiler intrinsic, it is not implemented in Go.
|
||||
func (c *Checkpoint) Save() bool
|
||||
|
||||
// Returns whether a jump point was saved (and not erased due to a jump).
|
||||
func (c *Checkpoint) Saved() bool {
|
||||
return c.jumpPC != 0
|
||||
}
|
||||
|
||||
// Jump to the point where the execution state was saved, and erase the saved
|
||||
// jump point. This must *only* be called from inside an interrupt.
|
||||
//
|
||||
// This method does not return in the conventional way, it resumes execution at
|
||||
// the last point a checkpoint was saved.
|
||||
func (c *Checkpoint) Jump() {
|
||||
if !c.Saved() {
|
||||
panic("runtime/interrupt: no checkpoint was saved")
|
||||
}
|
||||
jumpPC := c.jumpPC
|
||||
jumpSP := c.jumpSP
|
||||
c.jumpPC = 0
|
||||
c.jumpSP = 0
|
||||
if jumpPC == 0 {
|
||||
panic("jumping to 0")
|
||||
}
|
||||
checkpointJump(jumpSP, jumpPC)
|
||||
}
|
||||
|
||||
//export tinygo_checkpointJump
|
||||
func checkpointJump(jumpSP, jumpPC uintptr)
|
||||
Reference in New Issue
Block a user