add support for CPU interrupts for ESP32-C3

Этот коммит содержится в:
Dmitriy 2021-10-22 21:13:24 -04:00 коммит произвёл Ayke
родитель b5b2600b7b
коммит 43efe94041
5 изменённых файлов: 167 добавлений и 1 удалений

Просмотреть файл

@ -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

114
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)

Просмотреть файл

@ -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

Просмотреть файл

@ -15,6 +15,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"]
}

Просмотреть файл

@ -141,6 +141,9 @@ SECTIONS
*/
.text : ALIGN(4)
{
. = ALIGN (256);
*(.text.exception_vectors)
. = ALIGN (4);
*(.text .text.*)
} >IROM