mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-15 00:13:43 +00:00
runtime/esp32c3: use TIMG0 alarm interrupt for sleepTicks instead of busy-waiting
Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
@@ -17,7 +17,7 @@ import (
|
|||||||
// CDC-ACM serial port. The USB protocol and enumeration are handled entirely
|
// CDC-ACM serial port. The USB protocol and enumeration are handled entirely
|
||||||
// in hardware; software only reads/writes the EP1 FIFO.
|
// in hardware; software only reads/writes the EP1 FIFO.
|
||||||
|
|
||||||
const cpuInterruptFromUSB = 8
|
const cpuInterruptFromUSB = 10
|
||||||
|
|
||||||
// flushTimeout is the maximum number of busy-wait iterations in flush().
|
// flushTimeout is the maximum number of busy-wait iterations in flush().
|
||||||
// Prevents hanging when no USB host is connected.
|
// Prevents hanging when no USB host is connected.
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ func (i Interrupt) Enable() error {
|
|||||||
//go:linkname callHandlers runtime/interrupt.callHandlers
|
//go:linkname callHandlers runtime/interrupt.callHandlers
|
||||||
func callHandlers(num int)
|
func callHandlers(num int)
|
||||||
|
|
||||||
|
//go:linkname signalInterrupt runtime.signalInterrupt
|
||||||
|
func signalInterrupt()
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IRQNUM_1 = 1 + iota
|
IRQNUM_1 = 1 + iota
|
||||||
IRQNUM_2
|
IRQNUM_2
|
||||||
@@ -203,6 +206,9 @@ func handleInterrupt() {
|
|||||||
// Call registered interrupt handler(s)
|
// Call registered interrupt handler(s)
|
||||||
callHandler(int(interruptNumber))
|
callHandler(int(interruptNumber))
|
||||||
|
|
||||||
|
// Signal to sleepTicks that an interrupt has occurred.
|
||||||
|
signalInterrupt()
|
||||||
|
|
||||||
// disable CPU interrupts
|
// disable CPU interrupts
|
||||||
riscv.MSTATUS.ClearBits(riscv.MSTATUS_MIE)
|
riscv.MSTATUS.ClearBits(riscv.MSTATUS_MIE)
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,14 @@ var _sbss [0]byte
|
|||||||
//go:extern _ebss
|
//go:extern _ebss
|
||||||
var _ebss [0]byte
|
var _ebss [0]byte
|
||||||
|
|
||||||
|
// sleepTicks busy-waits until the given number of ticks have passed.
|
||||||
|
func sleepTicks(d timeUnit) {
|
||||||
|
sleepUntil := ticks() + d
|
||||||
|
for ticks() < sleepUntil {
|
||||||
|
// TODO: suspend the CPU to not burn power here unnecessarily.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func abort() {
|
func abort() {
|
||||||
for {
|
for {
|
||||||
device.Asm("waiti 0")
|
device.Asm("waiti 0")
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"device/esp"
|
"device/esp"
|
||||||
"device/riscv"
|
"device/riscv"
|
||||||
"machine"
|
"machine"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@@ -57,6 +58,9 @@ func main() {
|
|||||||
// Initialize main system timer used for time.Now.
|
// Initialize main system timer used for time.Now.
|
||||||
initTimer()
|
initTimer()
|
||||||
|
|
||||||
|
// Initialize timer alarm interrupt for the scheduler.
|
||||||
|
initTimerInterrupt()
|
||||||
|
|
||||||
// Initialize the heap, call main.main, etc.
|
// Initialize the heap, call main.main, etc.
|
||||||
run()
|
run()
|
||||||
|
|
||||||
@@ -98,5 +102,75 @@ func interruptInit() {
|
|||||||
riscv.EnableInterrupts(mie)
|
riscv.EnableInterrupts(mie)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CPU interrupt number used for the TIMG0 timer alarm.
|
||||||
|
const timerAlarmCPUInterrupt = 9
|
||||||
|
|
||||||
|
var interruptPending volatile.Register8
|
||||||
|
|
||||||
|
func signalInterrupt() {
|
||||||
|
interruptPending.Set(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initTimerInterrupt routes the TIMG0 timer 0 alarm interrupt to a CPU
|
||||||
|
// interrupt and registers a handler that signals timerWakeup.
|
||||||
|
func initTimerInterrupt() {
|
||||||
|
// Map the TIMG0 T0 peripheral interrupt to a CPU interrupt line.
|
||||||
|
esp.INTERRUPT_CORE0.TG_T0_INT_MAP.Set(timerAlarmCPUInterrupt)
|
||||||
|
|
||||||
|
// Enable T0 interrupt at the timer group level.
|
||||||
|
esp.TIMG0.INT_ENA_TIMERS.SetBits(1)
|
||||||
|
|
||||||
|
// Register the interrupt handler (compile-time wiring).
|
||||||
|
interrupt.New(timerAlarmCPUInterrupt, func(interrupt.Interrupt) {
|
||||||
|
// Clear the timer interrupt at the peripheral level.
|
||||||
|
esp.TIMG0.INT_CLR_TIMERS.Set(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Manually enable the CPU interrupt with correct ordering:
|
||||||
|
// 1) clear any stale pending bit first
|
||||||
|
// 2) set edge-triggered
|
||||||
|
// 3) set priority above threshold
|
||||||
|
// 4) enable the interrupt last
|
||||||
|
mie := riscv.DisableInterrupts()
|
||||||
|
|
||||||
|
esp.INTERRUPT_CORE0.CPU_INT_CLEAR.SetBits(1 << timerAlarmCPUInterrupt)
|
||||||
|
esp.INTERRUPT_CORE0.CPU_INT_CLEAR.ClearBits(1 << timerAlarmCPUInterrupt)
|
||||||
|
|
||||||
|
esp.INTERRUPT_CORE0.CPU_INT_TYPE.SetBits(1 << timerAlarmCPUInterrupt)
|
||||||
|
|
||||||
|
priReg := (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.INTERRUPT_CORE0.CPU_INT_PRI_0), timerAlarmCPUInterrupt*4))
|
||||||
|
priReg.Set(10)
|
||||||
|
|
||||||
|
riscv.Asm("fence")
|
||||||
|
|
||||||
|
esp.INTERRUPT_CORE0.CPU_INT_ENABLE.SetBits(1 << timerAlarmCPUInterrupt)
|
||||||
|
|
||||||
|
riscv.EnableInterrupts(mie)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sleepTicks spins until the given number of ticks have elapsed, using the
|
||||||
|
// TIMG0 alarm interrupt to avoid busy-waiting for the entire duration.
|
||||||
|
func sleepTicks(d timeUnit) {
|
||||||
|
target := ticks() + d
|
||||||
|
for ticks() < target {
|
||||||
|
// Set the alarm to fire at the target tick count (or as close
|
||||||
|
// as the 54-bit counter allows).
|
||||||
|
interruptPending.Set(0)
|
||||||
|
|
||||||
|
esp.TIMG0.T0ALARMLO.Set(uint32(target))
|
||||||
|
esp.TIMG0.T0ALARMHI.Set(uint32(target >> 32))
|
||||||
|
|
||||||
|
// Enable the alarm (auto-clears when alarm fires).
|
||||||
|
esp.TIMG0.T0CONFIG.SetBits(esp.TIMG_T0CONFIG_ALARM_EN)
|
||||||
|
|
||||||
|
// Wait for any interrupt (timer alarm or other) or a timeout.
|
||||||
|
for interruptPending.Get() == 0 {
|
||||||
|
if ticks() >= target {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//go:extern _vector_table
|
//go:extern _vector_table
|
||||||
var _vector_table [0]uintptr
|
var _vector_table [0]uintptr
|
||||||
|
|||||||
@@ -55,14 +55,6 @@ func ticksToNanoseconds(ticks timeUnit) int64 {
|
|||||||
return int64(ticks) * 25
|
return int64(ticks) * 25
|
||||||
}
|
}
|
||||||
|
|
||||||
// sleepTicks busy-waits until the given number of ticks have passed.
|
|
||||||
func sleepTicks(d timeUnit) {
|
|
||||||
sleepUntil := ticks() + d
|
|
||||||
for ticks() < sleepUntil {
|
|
||||||
// TODO: suspend the CPU to not burn power here unnecessarily.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func exit(code int) {
|
func exit(code int) {
|
||||||
abort()
|
abort()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user