Skip to content

Commit 27e5bae

Browse files
committed
esp32s3: use TIMG0 alarm interrupt for sleepTicks
Replace the busy-wait sleepTicks with an interrupt-driven version that sets a TIMG0 timer alarm and waits for the interrupt to fire. The timer alarm handler disables INT_ENA at the peripheral level to prevent level-triggered re-assertion; sleepTicks re-enables it after each wake. This avoids burning CPU cycles during time.Sleep and similar delays.
1 parent a6a4f85 commit 27e5bae

File tree

2 files changed

+60
-4
lines changed

2 files changed

+60
-4
lines changed

src/runtime/runtime_esp32s3.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ func main() {
8080
// Set up the Xtensa interrupt vector table.
8181
interruptInit()
8282

83+
// Initialize timer alarm interrupt for the scheduler.
84+
initTimerInterrupt()
85+
8386
// Initialize the heap, call main.main, etc.
8487
run()
8588

src/runtime/runtime_esp32sx.go

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ package runtime
55
import (
66
"device/esp"
77
"machine"
8+
"runtime/interrupt"
9+
"runtime/volatile"
810
"unsafe"
911
)
1012

@@ -73,11 +75,62 @@ func ticksToNanoseconds(ticks timeUnit) int64 {
7375
return int64(ticks) * 25
7476
}
7577

76-
// sleepTicks busy-waits until the given number of ticks have passed.
78+
// CPU interrupt number used for the TIMG0 timer alarm.
79+
const timerAlarmCPUInterrupt = 9
80+
81+
var interruptPending volatile.Register8
82+
83+
func signalInterrupt() {
84+
interruptPending.Set(1)
85+
}
86+
87+
var timerAlarmInterrupt interrupt.Interrupt
88+
89+
// timerAlarmHandler clears the timer interrupt at the peripheral level
90+
// and disables INT_ENA to prevent level-triggered re-assertion.
91+
func timerAlarmHandler(interrupt.Interrupt) {
92+
esp.TIMG0.INT_ENA_TIMERS.ClearBits(1)
93+
esp.TIMG0.INT_CLR_TIMERS.Set(1)
94+
}
95+
96+
// initTimerInterrupt routes the TIMG0 timer 0 alarm interrupt to a CPU
97+
// interrupt and registers a handler that clears the alarm flag.
98+
func initTimerInterrupt() {
99+
// Clear any stale timer interrupt before enabling.
100+
esp.TIMG0.INT_CLR_TIMERS.Set(1)
101+
102+
// Map the TIMG0 T0 peripheral interrupt to a CPU interrupt line.
103+
esp.INTERRUPT_CORE0.SetTG_T0_INT_MAP(timerAlarmCPUInterrupt)
104+
105+
// Register the interrupt handler and enable it once.
106+
timerAlarmInterrupt = interrupt.New(timerAlarmCPUInterrupt, timerAlarmHandler)
107+
timerAlarmInterrupt.Enable()
108+
}
109+
110+
// sleepTicks spins until the given number of ticks have elapsed, using the
111+
// TIMG0 alarm interrupt to avoid busy-waiting for the entire duration.
77112
func sleepTicks(d timeUnit) {
78-
sleepUntil := ticks() + d
79-
for ticks() < sleepUntil {
80-
// TODO: suspend the CPU to not burn power here unnecessarily.
113+
target := ticks() + d
114+
for ticks() < target {
115+
// Set the alarm to fire at the target tick count.
116+
interruptPending.Set(0)
117+
118+
esp.TIMG0.T0ALARMLO.Set(uint32(target))
119+
esp.TIMG0.T0ALARMHI.Set(uint32(target >> 32))
120+
121+
// Enable the alarm (auto-clears when alarm fires).
122+
esp.TIMG0.T0CONFIG.SetBits(esp.TIMG_TCONFIG_ALARM_EN)
123+
124+
// Re-enable the timer interrupt (handler disables INT_ENA).
125+
esp.TIMG0.INT_CLR_TIMERS.Set(1)
126+
esp.TIMG0.INT_ENA_TIMERS.SetBits(1)
127+
128+
// Wait for any interrupt (timer alarm or other) or timeout.
129+
for interruptPending.Get() == 0 {
130+
if ticks() >= target {
131+
return
132+
}
133+
}
81134
}
82135
}
83136

0 commit comments

Comments
 (0)