tinygo/src/runtime/runtime_k210.go
Ayke van Laethem f41b6a3b96 runtime: check for heap allocations inside interrupts
This is unsafe and should never be done.
2023-02-19 11:33:24 +01:00

175 строки
4,4 КиБ
Go

//go:build k210
// This file implements target-specific things for the K210 chip as used in the
// MAix Bit with Mic.
package runtime
import (
"device/kendryte"
"device/riscv"
"machine"
"runtime/volatile"
"unsafe"
)
type timeUnit int64
//export main
func main() {
// Both harts should disable all interrupts on startup.
initPLIC()
// Only use one hart for the moment.
if riscv.MHARTID.Get() != 0 {
abort()
}
// Reset all interrupt source priorities to zero.
for i := 0; i < kendryte.IRQ_max; i++ {
kendryte.PLIC.PRIORITY[i].Set(0)
}
// Zero MCAUSE, which is set to the reset reason on reset. It must be zeroed
// to make interrupt.In() work.
// This would also be a good time to save the reset reason, but that hasn't
// been implemented yet.
riscv.MCAUSE.Set(0)
// Set the interrupt address.
// Note that this address must be aligned specially, otherwise the MODE bits
// of MTVEC won't be zero.
riscv.MTVEC.Set(uintptr(unsafe.Pointer(&handleInterruptASM)))
// Reset the MIE register and enable external interrupts.
// It must be reset here because it not zeroed at startup.
riscv.MIE.Set(1 << 11) // bit 11 is for machine external interrupts
// Enable global interrupts now that they've been set up.
riscv.MSTATUS.SetBits(1 << 3) // MIE
preinit()
initPeripherals()
run()
exit(0)
}
func initPLIC() {
hartId := riscv.MHARTID.Get()
// Zero the PLIC enable bits at startup.
for i := 0; i < ((kendryte.IRQ_max + 32) / 32); i++ {
kendryte.PLIC.TARGET_ENABLES[hartId].ENABLE[i].Set(0)
}
// Zero the PLIC threshold bits to allow all interrupts.
kendryte.PLIC.TARGETS[hartId].THRESHOLD.Set(0)
}
//go:extern handleInterruptASM
var handleInterruptASM [0]uintptr
//export handleInterrupt
func handleInterrupt() {
cause := riscv.MCAUSE.Get()
code := uint64(cause &^ (1 << 63))
if cause&(1<<63) != 0 {
// Topmost bit is set, which means that it is an interrupt.
switch code {
case 7: // Machine timer interrupt
// Signal timeout.
timerWakeup.Set(1)
// Disable the timer, to avoid triggering the interrupt right after
// this interrupt returns.
riscv.MIE.ClearBits(1 << 7) // MTIE bit
case 11: // Machine external interrupt
hartId := riscv.MHARTID.Get()
// Claim this interrupt.
id := kendryte.PLIC.TARGETS[hartId].CLAIM.Get()
// Call the interrupt handler, if any is registered for this ID.
kendryte.HandleInterrupt(int(id))
// Complete this interrupt.
kendryte.PLIC.TARGETS[hartId].CLAIM.Set(id)
}
} 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(code)
}
// Zero MCAUSE so that it can later be used to see whether we're in an
// interrupt or not.
riscv.MCAUSE.Set(0)
}
// initPeripherals configures periperhals the way the runtime expects them.
func initPeripherals() {
// Enable APB0 clock.
kendryte.SYSCTL.CLK_EN_CENT.SetBits(kendryte.SYSCTL_CLK_EN_CENT_APB0_CLK_EN)
// Enable FPIOA peripheral.
kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_FPIOA_CLK_EN)
machine.InitSerial()
}
func putchar(c byte) {
machine.Serial.WriteByte(c)
}
func getchar() byte {
for machine.Serial.Buffered() == 0 {
Gosched()
}
v, _ := machine.Serial.ReadByte()
return v
}
func buffered() int {
return machine.Serial.Buffered()
}
var timerWakeup volatile.Register8
func ticks() timeUnit {
highBits := uint32(kendryte.CLINT.MTIME.Get() >> 32)
for {
lowBits := uint32(kendryte.CLINT.MTIME.Get() & 0xffffffff)
newHighBits := uint32(kendryte.CLINT.MTIME.Get() >> 32)
if newHighBits == highBits {
return timeUnit(lowBits) | (timeUnit(highBits) << 32)
}
highBits = newHighBits
}
}
func sleepTicks(d timeUnit) {
target := uint64(ticks() + d)
kendryte.CLINT.MTIMECMP[0].Set(target)
riscv.MIE.SetBits(1 << 7) // MTIE
for {
if timerWakeup.Get() != 0 {
timerWakeup.Set(0)
// Disable timer.
break
}
riscv.Asm("wfi")
}
}
// handleException is called from the interrupt handler for any exception.
// Exceptions can be things like illegal instructions, invalid memory
// read/write, and similar issues.
func handleException(code uint64) {
// For a list of exception codes, see:
// https://content.riscv.org/wp-content/uploads/2019/08/riscv-privileged-20190608-1.pdf#page=49
print("fatal error: exception with mcause=")
print(code)
print(" pc=")
print(riscv.MEPC.Get())
println()
abort()
}