aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDmitriy <[email protected]>2021-10-22 21:13:24 -0400
committerAyke van Laethem <[email protected]>2021-10-23 03:31:53 +0200
commit909b5ebae479461ef8dca1028c6e5c61d645554d (patch)
treee8f50829ac6f644db625bfecb9210019d807a023
parent426c1935402d0b7357d31161dafd22eff729fe51 (diff)
downloadtinygo-909b5ebae479461ef8dca1028c6e5c61d645554d.tar.gz
tinygo-909b5ebae479461ef8dca1028c6e5c61d645554d.zip
add support for CPU interrupts for ESP32-C3espnet
-rw-r--r--src/device/esp/esp32c3.S18
-rw-r--r--src/runtime/interrupt/interrupt_esp32c3.go114
-rw-r--r--src/runtime/runtime_esp32c3.go30
-rw-r--r--targets/esp32c3.json3
-rw-r--r--targets/esp32c3.ld3
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