Skip to content

ESP32C3 interrupts, GPIO and UART support #2167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 223 additions & 0 deletions src/device/esp/esp32c3_intr.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
#define REGSIZE 4
#define SAVE_REGS 36
#define CONTEXT_SIZE (SAVE_REGS * 4)
#define CPU_INT_THRESH (0x600c2000 + 0x194)
#define CPU_INT_PRI_0_REG (0x600c2000 + 0x114)

.macro save_regs
addi sp, sp, -CONTEXT_SIZE
sw ra, 0*REGSIZE(sp)
sw tp, 1*REGSIZE(sp)
sw t0, 2*REGSIZE(sp)
sw t1, 3*REGSIZE(sp)
sw t2, 4*REGSIZE(sp)
sw s0, 5*REGSIZE(sp)
sw s1, 6*REGSIZE(sp)
sw a0, 7*REGSIZE(sp)
sw a1, 8*REGSIZE(sp)
sw a2, 9*REGSIZE(sp)
sw a3, 10*REGSIZE(sp)
sw a4, 11*REGSIZE(sp)
sw a5, 12*REGSIZE(sp)
sw a6, 13*REGSIZE(sp)
sw a7, 14*REGSIZE(sp)
sw s2, 15*REGSIZE(sp)
sw s3, 16*REGSIZE(sp)
sw s4, 17*REGSIZE(sp)
sw s5, 18*REGSIZE(sp)
sw s6, 19*REGSIZE(sp)
sw s7, 20*REGSIZE(sp)
sw s8, 21*REGSIZE(sp)
sw s9, 22*REGSIZE(sp)
sw s10, 23*REGSIZE(sp)
sw s11, 24*REGSIZE(sp)
sw t3, 25*REGSIZE(sp)
sw t4, 26*REGSIZE(sp)
sw t5, 27*REGSIZE(sp)
sw t6, 28*REGSIZE(sp)
.endm

.macro save_mepc
csrr t0, mepc
sw t0, 29*REGSIZE(sp)
.endm

.macro restore_regs
lw ra, 0*REGSIZE(sp)
lw tp, 1*REGSIZE(sp)
lw t0, 2*REGSIZE(sp)
lw t1, 3*REGSIZE(sp)
lw t2, 4*REGSIZE(sp)
lw s0, 5*REGSIZE(sp)
lw s1, 6*REGSIZE(sp)
lw a0, 7*REGSIZE(sp)
lw a1, 8*REGSIZE(sp)
lw a2, 9*REGSIZE(sp)
lw a3, 10*REGSIZE(sp)
lw a4, 11*REGSIZE(sp)
lw a5, 12*REGSIZE(sp)
lw a6, 13*REGSIZE(sp)
lw a7, 14*REGSIZE(sp)
lw s2, 15*REGSIZE(sp)
lw s3, 16*REGSIZE(sp)
lw s4, 17*REGSIZE(sp)
lw s5, 18*REGSIZE(sp)
lw s6, 19*REGSIZE(sp)
lw s7, 20*REGSIZE(sp)
lw s8, 21*REGSIZE(sp)
lw s9, 22*REGSIZE(sp)
lw s10, 23*REGSIZE(sp)
lw s11, 24*REGSIZE(sp)
lw t3, 25*REGSIZE(sp)
lw t4, 26*REGSIZE(sp)
lw t5, 27*REGSIZE(sp)
lw t6, 28*REGSIZE(sp)
addi sp, sp, CONTEXT_SIZE
.endm

.macro restore_mepc
lw t0, 29*REGSIZE(sp)
csrw mepc, t0
.endm

/* This is the vector table. MTVEC points here.
*
* Use 4-byte intructions here. 1 instruction = 1 entry of the table.
* The CPU jumps to MTVEC (i.e. the first entry) in case of an exception,
* and (MTVEC & 0xfffffffc) + (mcause & 0x7fffffff) * 4, in case of an interrupt.
*
* Note: for our CPU, we need to place this on a 256-byte boundary, as CPU
* only uses the 24 MSBs of the MTVEC, i.e. (MTVEC & 0xffffff00).
*/

.section .text.exception_vectors
.global _vector_table
.type _vector_table,@function
_vector_table:

.option push
.option norvc

j _panic_handler /* exception handler, entry 0 */
.rept 24
j _interrupt_handler /* 24 identical entries, all pointing to the interrupt handler */
.endr
j _panic_handler /* Call panic handler for ETS_T1_WDT_INUM interrupt (soc-level panic)*/
j _panic_handler /* Call panic handler for ETS_CACHEERR_INUM interrupt (soc-level panic)*/
.rept 5
j _interrupt_handler /* 6 identical entries, all pointing to the interrupt handler */
Comment on lines +107 to +108
Copy link
Member

Choose a reason for hiding this comment

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

Are there 5 or 6 identical entries? The comment and .rept statement disagree.

.endr

.option pop

.size _vector_table, .-_vector_table


/* Exception handler */
.type _panic_handler, @function
_panic_handler:
/* save general registers */
save_regs
save_mepc

csrr t0, mstatus
sw t0, 30*REGSIZE(sp)
csrr t0, mtvec
sw t0, 31*REGSIZE(sp)
csrr t0, mtval
sw t0, 32*REGSIZE(sp)
csrr t0, mhartid
sw t0, 33*REGSIZE(sp)

addi t0, sp, CONTEXT_SIZE /* restore sp with the value when trap happened */
sw t0, 34*REGSIZE(sp)

/* Call handleException(sp) or handleException(sp)
Copy link
Member

Choose a reason for hiding this comment

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

handleException or handleException? This doesn't look right.

* depending on whether we have a pseudo exception or not.
* If mcause's highest bit is 1, then an interrupt called this routine,
* so we have a pseudo exception. Else, it is due to a exception, we don't
* have an pseudo exception */
// ??? why we need it? mv a0, sp
csrr a1, mcause
li t0, 0x80000000 /* Branches instructions don't accept immediate values, so use t0 to store our comparator */
bgeu a1, t0, _call_panic_handler
sw a1, 35*REGSIZE(sp)
/* handleException never returns */
la t0, handleException
jalr t0 /* absolute jump, avoid the 1 MiB range constraint */

_call_panic_handler:
/* Remove highest bit from mcause (a1) register and save it in the
* structure */
not t0, t0
and a1, a1, t0
sw a1, 35*REGSIZE(sp)
/* exception_from_isr never returns */
la t0, handleException
jalr t0 /* absolute jump, avoid the 1 MiB range constraint */

.size _panic_handler, .-_panic_handler

/* This is the interrupt handler.
* It saves the registers on the stack,
* prepares for interrupt nesting,
* re-enables the interrupts,
* then jumps to the C dispatcher in interrupt.c.
*/
/*.global _interrupt_handler*/
.type _interrupt_handler, @function
_interrupt_handler:
/* entry */
save_regs
save_mepc

/* Save interrupts registers */
csrr s1, mcause
csrr s2, mstatus

/* Save the interrupt threshold level */
li t0, CPU_INT_THRESH
lw s3, 0(t0)

/* Increase interrupt threshold level */
li t2, 0x7fffffff
and t1, s1, t2 /* t1 = mcause & mask */
slli t1, t1, 2 /* t1 = mcause * 4 */
li t2, CPU_INT_PRI_0_REG
add t1, t2, t1 /* t1 = INTC_INT_PRIO_REG + 4 * mcause */
lw t2, 0(t1) /* t2 = INTC_INT_PRIO_REG[mcause] */
addi t2, t2, 1 /* t2 = t2 +1 */
sw t2, 0(t0) /* CPU_INT_THRESH = t2 */
fence
Comment on lines +182 to +191
Copy link
Member

Choose a reason for hiding this comment

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

You are implementing nested interrupts here. Is this intentional? Did you test nested interrupts?


/* Before dispatch c handler, restore interrupt to enable nested intr */
li t0, 0x8
csrrs t0, mstatus, t0

/* call the interrupt dispatcher */
mv a0, sp /* argument 1, stack pointer */
csrr a1, mcause /* argument 2, interrupt number */
/* mask off the interrupt flag of mcause */
li t0, 0x7fffffff
and a1, a1, t0
call handleInterrupt

/* Disable interrupts */
li t0, 0x8
csrrc t0, mstatus, t0

/* restore the interrupt threshold level */
li t0, CPU_INT_THRESH
sw s3, 0(t0)
fence

/* restore the rest of the registers */
csrw mcause, s1
csrw mstatus, s2
restore_mepc
restore_regs

/* exit, this will also re-enable the interrupts */
mret

.size _interrupt_handler, .-_interrupt_handler
42 changes: 42 additions & 0 deletions src/examples/esp/esp32c3/echo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"machine"
"time"
)

func main() {

uartConfig := &machine.UARTConfig{
BaudRate: 115200,
TX: machine.Pin(2),
RX: machine.Pin(5),
}
Comment on lines +10 to +14
Copy link
Member

Choose a reason for hiding this comment

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

Why does this need a new example?

uart := machine.UART1
uart.Configure(uartConfig)

uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n"))
input := make([]byte, 64)
i := 0
for {
if uart.Buffered() > 0 {
data, _ := uart.ReadByte()

switch data {
case 13:
// return key
uart.Write([]byte("\r\n"))
uart.Write([]byte("You typed: "))
uart.Write(input[:i])
uart.Write([]byte("\r\n"))
i = 0
default:
// just echo the character
uart.WriteByte(data)
input[i] = data
i++
}
}
time.Sleep(10 * time.Millisecond)
}
}
40 changes: 40 additions & 0 deletions src/examples/esp/esp32c3/pininterrupt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"machine"
"time"
)

func main() {
red := machine.Pin(3)
red.Configure(machine.PinConfig{Mode: machine.PinOutput})
red.Low()

green := machine.Pin(4)
green.Configure(machine.PinConfig{Mode: machine.PinOutput})
green.Low()

blue := machine.Pin(5)
blue.Configure(machine.PinConfig{Mode: machine.PinOutput})
blue.Low()

btn := machine.Pin(2)
btn.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
btn.SetInterrupt(machine.PinInterruptToggle, func(p machine.Pin) {
v := btn.Get()
println("button A toggle:", v)
red.Set(!v)
})

btnB := machine.Pin(6)
btnB.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
btnB.SetInterrupt(machine.PinInterruptToggle, func(p machine.Pin) {
v := btnB.Get()
println("button B toggle:", v)
green.Set(!v)
})

for {
time.Sleep(10 * time.Hour)
}
}
Loading