114 строки
3,5 КиБ
Go
114 строки
3,5 КиБ
Go
// +build esp32c3
|
|
|
|
package interrupt
|
|
|
|
import (
|
|
"device/esp"
|
|
"device/riscv"
|
|
"errors"
|
|
"runtime/volatile"
|
|
"unsafe"
|
|
)
|
|
|
|
// Enable register CPU interrupt with interrupt.Interrupt.
|
|
// The ESP32-C3 has 31 CPU independent interrupts.
|
|
// The Interrupt.New(x, f) (x = [1..31]) attaches CPU interrupt to function f.
|
|
// Caller must map the selected interrupt using following sequence (for example using id 5):
|
|
//
|
|
// // map interrupt 5 to my XXXX module
|
|
// esp.INTERRUPT_CORE0.XXXX_INTERRUPT_PRO_MAP.Set( 5 )
|
|
// _ = Interrupt.New(5, func(interrupt.Interrupt) {
|
|
// ...
|
|
// }).Enable()
|
|
func (i Interrupt) Enable() error {
|
|
if i.num < 1 && i.num > 31 {
|
|
return errors.New("interrupt for ESP32-C3 must be in range of 1 through 31")
|
|
}
|
|
mask := riscv.DisableInterrupts()
|
|
defer riscv.EnableInterrupts(mask)
|
|
|
|
// enable CPU interrupt number i.num
|
|
esp.INTERRUPT_CORE0.CPU_INT_ENABLE.SetBits(1 << i.num)
|
|
|
|
// Set pulse interrupt type (rising edge detection)
|
|
esp.INTERRUPT_CORE0.CPU_INT_TYPE.SetBits(1 << i.num)
|
|
|
|
// Set default threshold to 5
|
|
reg := (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.INTERRUPT_CORE0.CPU_INT_PRI_0)) + uintptr(i.num)*4)))
|
|
reg.Set(5)
|
|
|
|
// Reset interrupt before reenabling
|
|
esp.INTERRUPT_CORE0.CPU_INT_CLEAR.SetBits(1 << i.num)
|
|
esp.INTERRUPT_CORE0.CPU_INT_CLEAR.ClearBits(1 << i.num)
|
|
|
|
// we must wait for any pending write operations to complete
|
|
riscv.Asm("fence")
|
|
return nil
|
|
}
|
|
|
|
//export handleInterrupt
|
|
func handleInterrupt() {
|
|
mcause := riscv.MCAUSE.Get()
|
|
exception := mcause&(1<<31) == 0
|
|
interruptNumber := uint32(mcause & 0x1f)
|
|
|
|
if !exception && interruptNumber > 0 {
|
|
// save mepc, which could be overwritten by another CPU interrupt
|
|
mepc := riscv.MEPC.Get()
|
|
|
|
// disable interrupt
|
|
interruptBit := uint32(1 << interruptNumber)
|
|
esp.INTERRUPT_CORE0.CPU_INT_ENABLE.ClearBits(interruptBit)
|
|
|
|
// reset pending status interrupt
|
|
if esp.INTERRUPT_CORE0.CPU_INT_TYPE.Get()&interruptBit != 0 {
|
|
// this is edge type interrupt
|
|
esp.INTERRUPT_CORE0.CPU_INT_CLEAR.SetBits(interruptBit)
|
|
esp.INTERRUPT_CORE0.CPU_INT_CLEAR.ClearBits(interruptBit)
|
|
} else {
|
|
// this is level type interrupt
|
|
esp.INTERRUPT_CORE0.CPU_INT_CLEAR.ClearBits(interruptBit)
|
|
}
|
|
|
|
// enable CPU interrupts
|
|
riscv.MSTATUS.SetBits(0x8)
|
|
|
|
// Call registered interrupt handler(s)
|
|
callInterruptHandler(int(interruptNumber))
|
|
|
|
// disable CPU interrupts
|
|
riscv.MSTATUS.ClearBits(0x8)
|
|
|
|
// mpie must be set to 1 to resume interrupts after 'MRET'
|
|
riscv.MSTATUS.SetBits(0x80)
|
|
|
|
// restore MEPC
|
|
riscv.MEPC.Set(mepc)
|
|
|
|
// enable this interrupt
|
|
esp.INTERRUPT_CORE0.CPU_INT_ENABLE.SetBits(interruptBit)
|
|
|
|
// do not enable CPU interrupts now
|
|
// the 'MRET' in src/device/riscv/handleinterrupt.S will copies the state of MPIE back into MIE, and subsequently clears MPIE.
|
|
// riscv.MSTATUS.SetBits(0x8)
|
|
} else {
|
|
// Topmost bit is clear, so it is an exception of some sort.
|
|
// We could implement support for unsupported instructions here (such as
|
|
// misaligned loads). However, for now we'll just print a fatal error.
|
|
handleException(mcause)
|
|
}
|
|
}
|
|
|
|
func handleException(mcause uintptr) {
|
|
println("*** Exception: pc:", riscv.MEPC.Get())
|
|
println("*** Exception: code:", uint32(mcause&0x1f))
|
|
println("*** Exception: mcause:", mcause)
|
|
for {
|
|
riscv.Asm("wfi")
|
|
}
|
|
}
|
|
|
|
// callInterruptHandler is a compiler-generated function that calls the
|
|
// appropriate interrupt handler for the given interrupt ID.
|
|
//go:linkname callInterruptHandler runtime.callInterruptHandler
|
|
func callInterruptHandler(id int)
|