Skip to content

feat: Add runtime/interrupt package and update volatile package#27

Merged
xushiwei merged 1 commit into
goplus:mainfrom
luoliwoshang:feat/runtime-interrupt
Nov 28, 2025
Merged

feat: Add runtime/interrupt package and update volatile package#27
xushiwei merged 1 commit into
goplus:mainfrom
luoliwoshang:feat/runtime-interrupt

Conversation

@luoliwoshang
Copy link
Copy Markdown
Member

@luoliwoshang luoliwoshang commented Nov 26, 2025

Summary

Add runtime/interrupt package and update runtime/volatile package, synced from TinyGo v0.39.0 to provide interrupt handling support for LLGO embedded applications.

TinyGo source reference: https://github.com/tinygo-org/tinygo/tree/v0.39.0/src/runtime

Synced Content

Directory Description
runtime/interrupt/ Interrupt handling code
runtime/volatile/ Volatile memory operations (updated)

Platform Support

The runtime/interrupt package provides platform-specific implementations for:

  • AVR: ATmega series microcontrollers
  • Cortex-M: ARM Cortex-M based MCUs (nRF, STM32, SAMD, etc.)
  • ESP32-C3: RISC-V based ESP32-C3
  • Game Boy Advance: ARM7TDMI based handheld console
  • K210: Kendryte K210 dual-core RISC-V
  • SiFive: SiFive RISC-V platforms
  • Generic RISC-V: TinyGo RISC-V targets
  • Xtensa: ESP8266, ESP32 (Xtensa core)
  • Generic/None: Non-baremetal platforms

LLGO Adaptations

1. Import Path Adaptation

TinyGo uses relative paths, which need to be changed to full module paths.

TinyGo goplus/emb
"device/*" "github.com/goplus/emb/device/*"
"runtime/volatile" "github.com/goplus/emb/runtime/volatile"

2. volatile Package Adaptation

runtime/volatile/volatile.go needs to declare LLGoPackage = "link":

const (
    LLGoPackage = "link"  // Changed from "decl" to "link"
)

This change is required for proper linking with LLGO's C interop mechanism.

3. Compiler Directive Placeholders

The following functions in runtime/interrupt require LLGO compiler or runtime support, currently using panic as placeholders:

File Function Description Status
runtime/interrupt/interrupt.go:24 New() Interrupt creation mechanism TODO: Requires compiler intrinsic
runtime/interrupt/checkpoint.go:39 Checkpoint.Save() Checkpoint save (setjmp-like) TODO: Requires compiler intrinsic

These will be implemented when LLGO compiler adds support for:

  • Interrupt handler registration (intrinsic function)
  • Non-local jump mechanism (setjmp/longjmp equivalent)

Files Changed

  • 11 new files in runtime/interrupt/:

    • checkpoint.go - Checkpoint mechanism for interrupt state
    • interrupt.go - Core interrupt interface
    • interrupt_avr.go - AVR implementation
    • interrupt_cortexm.go - Cortex-M implementation
    • interrupt_esp32c3.go - ESP32-C3 implementation
    • interrupt_gameboyadvance.go - Game Boy Advance implementation
    • interrupt_k210.go - K210 implementation
    • interrupt_none.go - Generic/non-baremetal implementation
    • interrupt_sifive.go - SiFive implementation
    • interrupt_tinygoriscv.go - Generic RISC-V implementation
    • interrupt_xtensa.go - Xtensa implementation
  • 1 modified file:

    • runtime/volatile/volatile.go - Updated LLGoPackage constant

Total: +713 lines, -1 line


Dependencies

This PR is extracted from #26 to allow independent review and merging of the runtime interrupt functionality, separate from the machine package updates.

The machine package (PR #26) will depend on this PR being merged first.


🤖 Generated with Claude Code

Add complete runtime/interrupt package with platform-specific implementations:
- interrupt.go: Core interrupt handling interface
- checkpoint.go: Checkpoint mechanism for interrupt state management
- Platform-specific implementations for AVR, Cortex-M, ESP32-C3, GBA, K210, SiFive, RISC-V, Xtensa

Update runtime/volatile package:
- Change LLGoPackage from "decl" to "link" for proper linking

This change provides a cross-platform interrupt handling abstraction that supports multiple embedded architectures.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @luoliwoshang, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the embedded systems support by introducing a new runtime/interrupt package. This package provides a standardized and platform-agnostic way to manage hardware interrupts, crucial for real-time and event-driven embedded applications. It abstracts away the complexities of different architectures, making interrupt handling more consistent across various targets. Additionally, a minor but important change was made to the runtime/volatile package to improve its integration with the LLGO compiler.

Highlights

  • New runtime/interrupt package: Introduces a comprehensive package for cross-platform hardware interrupt handling, offering a unified abstraction for embedded systems.
  • Platform-specific implementations: Provides tailored interrupt handling logic for various architectures including AVR, Cortex-M, ESP32-C3, Game Boy Advance, K210, SiFive, generic RISC-V, and Xtensa.
  • Interrupt Checkpoint Mechanism: Includes a Checkpoint type to manage and restore execution state during interrupt handling, similar to setjmp.
  • runtime/volatile update: Modifies the LLGoPackage constant from "decl" to "link" in runtime/volatile/volatile.go to ensure proper linking with LLGO.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive runtime/interrupt package, providing a crucial cross-platform abstraction for interrupt handling in embedded systems, along with a minor but important update to the runtime/volatile package. The architecture-specific implementations are extensive and well-structured. My review has identified a critical logic error in the ESP32-C3 implementation's boundary check, as well as some significant issues in the Game Boy Advance implementation regarding type correctness and reliance on a confusing and likely incorrect device peripheral definition. These issues should be addressed to ensure the reliability and maintainability of the new package.

// ...
// }).Enable()
func (i Interrupt) Enable() error {
if i.num < 1 && i.num > 31 {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

The condition i.num < 1 && i.num > 31 for checking the interrupt range is incorrect and will always evaluate to false, as a number cannot be simultaneously less than 1 and greater than 31. This effectively disables the bounds check for the interrupt ID. The logical operator should be || (OR) to correctly validate that the ID is outside the valid range of [1, 31].

Suggested change
if i.num < 1 && i.num > 31 {
if i.num < 1 || i.num > 31 {

Comment on lines +86 to +88
state = State(gba.INTERRUPT.PAUSE.Get())
// Disable all interrupts.
gba.INTERRUPT.PAUSE.Set(0)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

This code uses gba.INTERRUPT.PAUSE to disable interrupts. This is very confusing, as the GBA's PAUSE register is for halt control, while the IME (Interrupt Master Enable) register at 0x04000208 should be used for controlling interrupts.

The gba.INTERRUPT_Type struct in device/gba/gba.go appears to have incorrect field offsets and names, causing the PAUSE field to point to the IME register's address. This implementation is error-prone and relies on an underlying bug in the device definition. The device definition in device/gba/gba.go should be corrected to accurately represent the hardware registers, and this code should be updated to use gba.INTERRUPT.IME.

Comment on lines +60 to +62
if jumpPC == 0 {
panic("jumping to 0")
}
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.

}

// State represents the previous global interrupt state.
type State uint8
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

The State type is defined as uint8, but it's used to store the value from a 16-bit register (IME, which is accessed via the misnamed PAUSE field). The Get() method on volatile.Register16 returns a uint16. Using uint8 here truncates the value, which could lead to unexpected behavior. To ensure type safety and correctness, State should be defined as uint16.

Suggested change
type State uint8
type State uint16

// ...
// }).Enable()
func (i Interrupt) Enable() error {
if i.num < 1 && i.num > 31 {
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.

Critical: Logic error in range validation

This condition uses && (AND) when it should use || (OR). A number cannot simultaneously be less than 1 AND greater than 31, so this validation will never trigger.

Suggested change
if i.num < 1 && i.num > 31 {
if i.num < 1 || i.num > 31 {

This bug allows invalid interrupt numbers to proceed to line 40 where unsafe pointer arithmetic is performed, potentially causing memory corruption.


// 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).

return errors.New("interrupt for ESP32-C3 must be in range of 1 through 31")
}

// TODO(zzy):https://github.com/goplus/llgo/issues/1290
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.

Security: Missing critical section protection - race condition

The commented-out critical section protection creates a race condition. Multiple read-modify-write operations (lines 36, 39, 43, 44) can be interrupted, potentially corrupting register state.

Impact: If an interrupt fires between any of these operations, the hardware state may become inconsistent.

Recommendation: Until LLGO issue #1290 is resolved, this should be documented as a known limitation in the function documentation, or consider using platform-specific atomic operations if available.


// Pseudo function call that is replaced by the compiler with the actual
// functions registered through interrupt.New. If there are none, calls will be
// replaced with 'unreachablecalls will be replaced with 'unreachable'.
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: Duplicate text

Suggested change
// replaced with 'unreachablecalls will be replaced with 'unreachable'.
// replaced with 'unreachable'.

// _ = Interrupt.New(5, func(interrupt.Interrupt) {
// ...
// }).Enable()
func (i Interrupt) Enable() error {
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.

API Inconsistency: This is the only platform where Enable() returns an error. All other platforms have func (irq Interrupt) Enable() with no return value.

Recommendations:

  1. Standardize the API across all platforms (either all return errors or none do)
  2. Document this platform-specific behavior in the package-level documentation
  3. Consider using panics for invalid inputs (consistent with embedded runtime patterns) rather than error returns

// 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

@fennoai
Copy link
Copy Markdown
Contributor

fennoai Bot commented Nov 26, 2025

Code Review Summary

This PR introduces a well-structured interrupt handling package with good platform separation. However, there are critical issues that must be fixed before merging.

Critical Issues (Must Fix)

  1. runtime/interrupt/interrupt_esp32c3.go:25 - Logic error in validation (&& should be ||) - allows invalid interrupt numbers
  2. runtime/interrupt/interrupt_esp32c3.go:29 - Missing critical section protection creates race condition

High Priority

  1. API inconsistency - ESP32-C3 Enable() returns error while other platforms don't
  2. Documentation - Example code in checkpoint.go references undefined functions

Additional Fixes

  1. Typo: "uppoer" → "upper" in interrupt_cortexm.go:25
  2. Duplicate text in interrupt_gameboyadvance.go:34

Positive Aspects

  • Excellent platform separation with build tags
  • Clear critical section API design with Disable()/Restore() pattern
  • Good documentation for platform limitations (AVR, Xtensa warnings)
  • Appropriate use of unsafe for hardware access

Please address the critical issues before merging.

@xushiwei xushiwei merged commit fcaa423 into goplus:main Nov 28, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants