diff options
author | Dmitriy <[email protected]> | 2021-10-22 21:13:24 -0400 |
---|---|---|
committer | Ayke van Laethem <[email protected]> | 2021-10-23 03:31:53 +0200 |
commit | 909b5ebae479461ef8dca1028c6e5c61d645554d (patch) | |
tree | e8f50829ac6f644db625bfecb9210019d807a023 | |
parent | 426c1935402d0b7357d31161dafd22eff729fe51 (diff) | |
download | tinygo-909b5ebae479461ef8dca1028c6e5c61d645554d.tar.gz tinygo-909b5ebae479461ef8dca1028c6e5c61d645554d.zip |
add support for CPU interrupts for ESP32-C3espnet
-rw-r--r-- | src/device/esp/esp32c3.S | 18 | ||||
-rw-r--r-- | src/runtime/interrupt/interrupt_esp32c3.go | 114 | ||||
-rw-r--r-- | src/runtime/runtime_esp32c3.go | 30 | ||||
-rw-r--r-- | targets/esp32c3.json | 3 | ||||
-rw-r--r-- | targets/esp32c3.ld | 3 |
5 files changed, 167 insertions, 1 deletions
diff --git a/src/device/esp/esp32c3.S b/src/device/esp/esp32c3.S index 707339824..0395d73bc 100644 --- a/src/device/esp/esp32c3.S +++ b/src/device/esp/esp32c3.S @@ -47,3 +47,21 @@ call_start_cpu0: // (It appears that the linker relaxes this jump and instead inserts the // _start function right after here). j _start + +.section .text.exception_vectors +.global _vector_table +.type _vector_table,@function + +_vector_table: + + .option push + .option norvc + + .rept 32 + j handleInterruptASM /* interrupt handler */ + .endr + + .option pop + +.size _vector_table, .-_vector_table + diff --git a/src/runtime/interrupt/interrupt_esp32c3.go b/src/runtime/interrupt/interrupt_esp32c3.go new file mode 100644 index 000000000..20b055f8d --- /dev/null +++ b/src/runtime/interrupt/interrupt_esp32c3.go @@ -0,0 +1,114 @@ +// +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) diff --git a/src/runtime/runtime_esp32c3.go b/src/runtime/runtime_esp32c3.go index 0f769c0fc..2b73222ad 100644 --- a/src/runtime/runtime_esp32c3.go +++ b/src/runtime/runtime_esp32c3.go @@ -5,6 +5,8 @@ package runtime import ( "device/esp" "device/riscv" + "runtime/volatile" + "unsafe" ) // This is the function called on startup after the flash (IROM/DROM) is @@ -47,6 +49,9 @@ func main() { clearbss() + // Configure interrupt handler + interruptInit() + // Initialize main system timer used for time.Now. initTimer() @@ -63,3 +68,28 @@ func abort() { riscv.Asm("wfi") } } + +// interruptInit initialize the interrupt controller and called from runtime once. +func interruptInit() { + mie := riscv.DisableInterrupts() + + // Reset all interrupt source priorities to zero. + priReg := &esp.INTERRUPT_CORE0.CPU_INT_PRI_1 + for i := 0; i < 31; i++ { + priReg.Set(0) + priReg = (*volatile.Register32)(unsafe.Pointer(uintptr(unsafe.Pointer(priReg)) + uintptr(4))) + } + + // default threshold for interrupts is 5 + esp.INTERRUPT_CORE0.CPU_INT_THRESH.Set(5) + + // Set the interrupt address. + // Set MODE field to 1 - a vector base address (only supported by ESP32C3) + // Note that this address must be aligned to 256 bytes. + riscv.MTVEC.Set((uintptr(unsafe.Pointer(&_vector_table))) | 1) + + riscv.EnableInterrupts(mie) +} + +//go:extern _vector_table +var _vector_table [0]uintptr diff --git a/targets/esp32c3.json b/targets/esp32c3.json index 7bca64c49..1ef709225 100644 --- a/targets/esp32c3.json +++ b/targets/esp32c3.json @@ -14,6 +14,7 @@ "serial-port": ["acm:303a:1001"], "openocd-interface": "esp_usb_jtag", "openocd-target": "esp32c3", - "openocd-commands": ["gdb_memory_map disable"] + "openocd-commands": ["gdb_memory_map disable"], + "gdb": ["riscv32-esp-elf-gdb"] } diff --git a/targets/esp32c3.ld b/targets/esp32c3.ld index 4f6058efc..3cef55ba4 100644 --- a/targets/esp32c3.ld +++ b/targets/esp32c3.ld @@ -146,6 +146,9 @@ SECTIONS */ .text : ALIGN(4) { + . = ALIGN (256); + *(.text.exception_vectors) + . = ALIGN (4); *(.text .text.*) *(.wifislpiram .wifislpiram.*) } >IROM |