Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions runtime/interrupt/checkpoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package interrupt

import _ "unsafe"

// A checkpoint is a setjmp like buffer, that can be used as a flag for
// interrupts.
//
// It can be used as follows:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation: Example code references undefined functions

The example calls setupInterrupt() and waitForInterrupt() which are not defined anywhere, making the example non-compilable.

Suggestion: Replace with a concrete example using actual interrupt package functions:

// It can be used as follows:
//
//	// global var
//	var c Checkpoint
//
//	// Set up the checkpoint and configure your interrupt handler.
//	// The handler should call c.Jump() when the checkpoint is saved.
//	if c.Save() {
//		// Returns true on first execution (checkpoint saved)
//		myInterrupt := interrupt.New(5, func(interrupt.Interrupt) {
//			if c.Saved() {
//				c.Jump()
//			}
//		})
//		myInterrupt.Enable()
//		for {
//			// Wait for interrupt (platform-specific)
//		}
//	}
//	// Returns false after c.Jump() is called from interrupt handler

//
// // 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.
// TODO(zzy): implement Save
func (c *Checkpoint) Save() bool {
panic("todo:Checkpoint.Save")
}

// 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")
}
Comment on lines +60 to +62
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This check for jumpPC == 0 is redundant. The c.Saved() check at the beginning of the function on line 53 already ensures that c.jumpPC is not zero, and jumpPC is assigned this value. This block of code is therefore unreachable and can be removed.

checkpointJump(jumpSP, jumpPC)
}

//go:linkname checkpointJump __llgo_checkpointJump
func checkpointJump(jumpSP, jumpPC uintptr)
39 changes: 39 additions & 0 deletions runtime/interrupt/interrupt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Package interrupt provides access to hardware interrupts. It provides a way
// to define interrupts and to enable/disable them.
package interrupt

import "unsafe"

// Interrupt provides direct access to hardware interrupts. You can configure
// this interrupt through this interface.
//
// Do not use the zero value of an Interrupt object. Instead, call New to obtain
// an interrupt handle.
type Interrupt struct {
// Make this number unexported so it cannot be set directly. This provides
// some encapsulation.
num int
}

// New is a compiler intrinsic that creates a new Interrupt object. You may call
// it only once, and must pass constant parameters to it. That means that the
// interrupt ID must be a Go constant and that the handler must be a simple
// function: closures are not supported.
// TODO(zzy): implement New
func New(id int, handler func(Interrupt)) Interrupt {
panic("todo:interrupt.New")
}

// handle is used internally, between IR generation and interrupt lowering. The
// frontend will create runtime/interrupt.handle objects, cast them to an int,
// and use that in an Interrupt object. That way the compiler will be able to
// optimize away all interrupt handles that are never used in a program.
// This system only works when interrupts need to be enabled before use and this
// is done only through calling Enable() on this object. If interrupts cannot
// individually be enabled/disabled, the compiler should create a pseudo-call
// (like runtime/interrupt.use()) that keeps the interrupt alive.
type handle struct {
context unsafe.Pointer
funcPtr uintptr
Interrupt
}
45 changes: 45 additions & 0 deletions runtime/interrupt/interrupt_avr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//go:build avr

package interrupt

import "github.com/goplus/emb/device"

// State represents the previous global interrupt state.
type State uint8

// Disable disables all interrupts and returns the previous interrupt state. It
// can be used in a critical section like this:
//
// state := interrupt.Disable()
// // critical section
// interrupt.Restore(state)
//
// Critical sections can be nested. Make sure to call Restore in the same order
// as you called Disable (this happens naturally with the pattern above).
func Disable() (state State) {
// SREG is at I/O address 0x3f.
return State(device.AsmFull(`
in {}, 0x3f
cli
`, nil))
}

// Restore restores interrupts to what they were before. Give the previous state
// returned by Disable as a parameter. If interrupts were disabled before
// calling Disable, this will not re-enable interrupts, allowing for nested
// critical sections.
func Restore(state State) {
// SREG is at I/O address 0x3f.
device.AsmFull("out 0x3f, {state}", map[string]interface{}{
"state": state,
})
}

// In returns whether the system is currently in an interrupt.
//
// Warning: this always returns false on AVR, as there does not appear to be a
// reliable way to determine whether we're currently running inside an interrupt
// handler.
func In() bool {
return false
}
65 changes: 65 additions & 0 deletions runtime/interrupt/interrupt_cortexm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//go:build cortexm

package interrupt

import (
"github.com/goplus/emb/device/arm"
)

// Enable enables this interrupt. Right after calling this function, the
// interrupt may be invoked if it was already pending.
func (irq Interrupt) Enable() {
// Clear the ARM pending bit, an asserting device may still
// trigger the interrupt once enabled.
arm.ClearPendingIRQ(uint32(irq.num))
arm.EnableIRQ(uint32(irq.num))
}

// Disable disables this interrupt.
func (irq Interrupt) Disable() {
arm.DisableIRQ(uint32(irq.num))
}

// SetPriority sets the interrupt priority for this interrupt. A lower number
// means a higher priority. Additionally, most hardware doesn't implement all
// priority bits (only the uppoer bits).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in documentation

Suggested change
// priority bits (only the uppoer bits).
// priority bits (only the upper bits).

//
// Examples: 0xff (lowest priority), 0xc0 (low priority), 0x00 (highest possible
// priority).
func (irq Interrupt) SetPriority(priority uint8) {
arm.SetPriority(uint32(irq.num), uint32(priority))
}

// State represents the previous global interrupt state.
type State uintptr

// Disable disables all interrupts and returns the previous interrupt state. It
// can be used in a critical section like this:
//
// state := interrupt.Disable()
// // critical section
// interrupt.Restore(state)
//
// Critical sections can be nested. Make sure to call Restore in the same order
// as you called Disable (this happens naturally with the pattern above).
func Disable() (state State) {
return State(arm.DisableInterrupts())
}

// Restore restores interrupts to what they were before. Give the previous state
// returned by Disable as a parameter. If interrupts were disabled before
// calling Disable, this will not re-enable interrupts, allowing for nested
// critical sections.
func Restore(state State) {
arm.EnableInterrupts(uintptr(state))
}

// In returns whether the system is currently in an interrupt.
func In() bool {
// The VECTACTIVE field gives the instruction vector that is currently
// active (in handler mode), or 0 if not in an interrupt.
// Documentation:
// https://developer.arm.com/documentation/dui0497/a/cortex-m0-peripherals/system-control-block/interrupt-control-and-state-register
vectactive := uint8(arm.SCB.ICSR.Get())
return vectactive != 0
}
Loading
Loading