add support for GPIO interrupts on esp32c3

Update interrupt_esp32c3.go:
make callHandler inline
save and restore MSTATUS along with MEPC
save and restore actual threshold value and call fence
print additional data during exception
This commit is contained in:
Dmitriy
2022-01-30 19:04:27 -05:00
committed by Ron Evans
parent 0243a5b8be
commit b176494e44
2 changed files with 225 additions and 14 deletions
+140 -14
View File
@@ -34,9 +34,9 @@ func (i Interrupt) Enable() error {
// Set pulse interrupt type (rising edge detection)
esp.INTERRUPT_CORE0.CPU_INT_TYPE.SetBits(1 << i.num)
// Set default threshold to 5
// Set default threshold to defaultThreshold
reg := (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.INTERRUPT_CORE0.CPU_INT_PRI_0)) + uintptr(i.num)*4)))
reg.Set(5)
reg.Set(defaultThreshold)
// Reset interrupt before reenabling
esp.INTERRUPT_CORE0.CPU_INT_CLEAR.SetBits(1 << i.num)
@@ -47,6 +47,118 @@ func (i Interrupt) Enable() error {
return nil
}
// Adding pseudo function calls that is replaced by the compiler with the actual
// functions registered through interrupt.New.
//go:linkname callHandlers runtime/interrupt.callHandlers
func callHandlers(num int)
const (
IRQNUM_1 = 1 + iota
IRQNUM_2
IRQNUM_3
IRQNUM_4
IRQNUM_5
IRQNUM_6
IRQNUM_7
IRQNUM_8
IRQNUM_9
IRQNUM_10
IRQNUM_11
IRQNUM_12
IRQNUM_13
IRQNUM_14
IRQNUM_15
IRQNUM_16
IRQNUM_17
IRQNUM_18
IRQNUM_19
IRQNUM_20
IRQNUM_21
IRQNUM_22
IRQNUM_23
IRQNUM_24
IRQNUM_25
IRQNUM_26
IRQNUM_27
IRQNUM_28
IRQNUM_29
IRQNUM_30
IRQNUM_31
)
const (
defaultThreshold = 5
disableThreshold = 10
)
//go:inline
func callHandler(n int) {
switch n {
case IRQNUM_1:
callHandlers(IRQNUM_1)
case IRQNUM_2:
callHandlers(IRQNUM_2)
case IRQNUM_3:
callHandlers(IRQNUM_3)
case IRQNUM_4:
callHandlers(IRQNUM_4)
case IRQNUM_5:
callHandlers(IRQNUM_5)
case IRQNUM_6:
callHandlers(IRQNUM_6)
case IRQNUM_7:
callHandlers(IRQNUM_7)
case IRQNUM_8:
callHandlers(IRQNUM_8)
case IRQNUM_9:
callHandlers(IRQNUM_9)
case IRQNUM_10:
callHandlers(IRQNUM_10)
case IRQNUM_11:
callHandlers(IRQNUM_11)
case IRQNUM_12:
callHandlers(IRQNUM_12)
case IRQNUM_13:
callHandlers(IRQNUM_13)
case IRQNUM_14:
callHandlers(IRQNUM_14)
case IRQNUM_15:
callHandlers(IRQNUM_15)
case IRQNUM_16:
callHandlers(IRQNUM_16)
case IRQNUM_17:
callHandlers(IRQNUM_17)
case IRQNUM_18:
callHandlers(IRQNUM_18)
case IRQNUM_19:
callHandlers(IRQNUM_19)
case IRQNUM_20:
callHandlers(IRQNUM_20)
case IRQNUM_21:
callHandlers(IRQNUM_21)
case IRQNUM_22:
callHandlers(IRQNUM_22)
case IRQNUM_23:
callHandlers(IRQNUM_23)
case IRQNUM_24:
callHandlers(IRQNUM_24)
case IRQNUM_25:
callHandlers(IRQNUM_25)
case IRQNUM_26:
callHandlers(IRQNUM_26)
case IRQNUM_27:
callHandlers(IRQNUM_27)
case IRQNUM_28:
callHandlers(IRQNUM_28)
case IRQNUM_29:
callHandlers(IRQNUM_29)
case IRQNUM_30:
callHandlers(IRQNUM_30)
case IRQNUM_31:
callHandlers(IRQNUM_31)
}
}
//export handleInterrupt
func handleInterrupt() {
mcause := riscv.MCAUSE.Get()
@@ -54,12 +166,17 @@ func handleInterrupt() {
interruptNumber := uint32(mcause & 0x1f)
if !exception && interruptNumber > 0 {
// save mepc, which could be overwritten by another CPU interrupt
// save MSTATUS & MEPC, which could be overwritten by another CPU interrupt
mstatus := riscv.MSTATUS.Get()
mepc := riscv.MEPC.Get()
// Useing threshold to temporary disable this interrupts.
// FYI: using CPU interrupt enable bit make runtime to loose interrupts.
reg := (*volatile.Register32)(unsafe.Pointer((uintptr(unsafe.Pointer(&esp.INTERRUPT_CORE0.CPU_INT_PRI_0)) + uintptr(interruptNumber)*4)))
thresholdSave := reg.Get()
reg.Set(disableThreshold)
riscv.Asm("fence")
// 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 {
@@ -72,23 +189,22 @@ func handleInterrupt() {
}
// enable CPU interrupts
riscv.MSTATUS.SetBits(0x8)
riscv.MSTATUS.SetBits(1 << 3)
// Call registered interrupt handler(s)
esp.HandleInterrupt(int(interruptNumber))
callHandler(int(interruptNumber))
// disable CPU interrupts
riscv.MSTATUS.ClearBits(0x8)
riscv.MSTATUS.ClearBits(1 << 3)
// mpie must be set to 1 to resume interrupts after 'MRET'
riscv.MSTATUS.SetBits(0x80)
// restore interrupt threshold to enable interrupt again
reg.Set(thresholdSave)
riscv.Asm("fence")
// restore MEPC
// restore MSTATUS & MEPC
riscv.MSTATUS.Set(mstatus)
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)
@@ -104,6 +220,16 @@ func handleException(mcause uintptr) {
println("*** Exception: pc:", riscv.MEPC.Get())
println("*** Exception: code:", uint32(mcause&0x1f))
println("*** Exception: mcause:", mcause)
switch uint32(mcause & 0x1f) {
case 1:
println("*** virtual addess:", riscv.MTVAL.Get())
case 2:
println("*** opcode:", riscv.MTVAL.Get())
case 5:
println("*** read address:", riscv.MTVAL.Get())
case 7:
println("*** write address:", riscv.MTVAL.Get())
}
for {
riscv.Asm("wfi")
}