//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() }