github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/runtime_k210.go (about) 1 //go:build k210 2 3 // This file implements target-specific things for the K210 chip as used in the 4 // MAix Bit with Mic. 5 6 package runtime 7 8 import ( 9 "device/kendryte" 10 "device/riscv" 11 "machine" 12 "runtime/volatile" 13 "unsafe" 14 ) 15 16 type timeUnit int64 17 18 //export main 19 func main() { 20 21 // Both harts should disable all interrupts on startup. 22 initPLIC() 23 24 // Only use one hart for the moment. 25 if riscv.MHARTID.Get() != 0 { 26 abort() 27 } 28 29 // Reset all interrupt source priorities to zero. 30 for i := 0; i < kendryte.IRQ_max; i++ { 31 kendryte.PLIC.PRIORITY[i].Set(0) 32 } 33 34 // Zero MCAUSE, which is set to the reset reason on reset. It must be zeroed 35 // to make interrupt.In() work. 36 // This would also be a good time to save the reset reason, but that hasn't 37 // been implemented yet. 38 riscv.MCAUSE.Set(0) 39 40 // Set the interrupt address. 41 // Note that this address must be aligned specially, otherwise the MODE bits 42 // of MTVEC won't be zero. 43 riscv.MTVEC.Set(uintptr(unsafe.Pointer(&handleInterruptASM))) 44 45 // Reset the MIE register and enable external interrupts. 46 // It must be reset here because it not zeroed at startup. 47 riscv.MIE.Set(1 << 11) // bit 11 is for machine external interrupts 48 49 // Enable global interrupts now that they've been set up. 50 riscv.MSTATUS.SetBits(1 << 3) // MIE 51 52 preinit() 53 initPeripherals() 54 run() 55 exit(0) 56 } 57 58 func initPLIC() { 59 hartId := riscv.MHARTID.Get() 60 61 // Zero the PLIC enable bits at startup. 62 for i := 0; i < ((kendryte.IRQ_max + 32) / 32); i++ { 63 kendryte.PLIC.TARGET_ENABLES[hartId].ENABLE[i].Set(0) 64 } 65 66 // Zero the PLIC threshold bits to allow all interrupts. 67 kendryte.PLIC.TARGETS[hartId].THRESHOLD.Set(0) 68 } 69 70 //go:extern handleInterruptASM 71 var handleInterruptASM [0]uintptr 72 73 //export handleInterrupt 74 func handleInterrupt() { 75 cause := riscv.MCAUSE.Get() 76 code := uint64(cause &^ (1 << 63)) 77 if cause&(1<<63) != 0 { 78 // Topmost bit is set, which means that it is an interrupt. 79 switch code { 80 case 7: // Machine timer interrupt 81 // Signal timeout. 82 timerWakeup.Set(1) 83 // Disable the timer, to avoid triggering the interrupt right after 84 // this interrupt returns. 85 riscv.MIE.ClearBits(1 << 7) // MTIE bit 86 case 11: // Machine external interrupt 87 hartId := riscv.MHARTID.Get() 88 89 // Claim this interrupt. 90 id := kendryte.PLIC.TARGETS[hartId].CLAIM.Get() 91 // Call the interrupt handler, if any is registered for this ID. 92 kendryte.HandleInterrupt(int(id)) 93 // Complete this interrupt. 94 kendryte.PLIC.TARGETS[hartId].CLAIM.Set(id) 95 } 96 } else { 97 // Topmost bit is clear, so it is an exception of some sort. 98 // We could implement support for unsupported instructions here (such as 99 // misaligned loads). However, for now we'll just print a fatal error. 100 handleException(code) 101 } 102 103 // Zero MCAUSE so that it can later be used to see whether we're in an 104 // interrupt or not. 105 riscv.MCAUSE.Set(0) 106 } 107 108 // initPeripherals configures periperhals the way the runtime expects them. 109 func initPeripherals() { 110 // Enable APB0 clock. 111 kendryte.SYSCTL.CLK_EN_CENT.SetBits(kendryte.SYSCTL_CLK_EN_CENT_APB0_CLK_EN) 112 113 // Enable FPIOA peripheral. 114 kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_FPIOA_CLK_EN) 115 116 machine.InitSerial() 117 } 118 119 func putchar(c byte) { 120 machine.Serial.WriteByte(c) 121 } 122 123 func getchar() byte { 124 for machine.Serial.Buffered() == 0 { 125 Gosched() 126 } 127 v, _ := machine.Serial.ReadByte() 128 return v 129 } 130 131 func buffered() int { 132 return machine.Serial.Buffered() 133 } 134 135 var timerWakeup volatile.Register8 136 137 func ticks() timeUnit { 138 highBits := uint32(kendryte.CLINT.MTIME.Get() >> 32) 139 for { 140 lowBits := uint32(kendryte.CLINT.MTIME.Get() & 0xffffffff) 141 newHighBits := uint32(kendryte.CLINT.MTIME.Get() >> 32) 142 if newHighBits == highBits { 143 return timeUnit(lowBits) | (timeUnit(highBits) << 32) 144 } 145 highBits = newHighBits 146 } 147 } 148 149 func sleepTicks(d timeUnit) { 150 target := uint64(ticks() + d) 151 kendryte.CLINT.MTIMECMP[0].Set(target) 152 riscv.MIE.SetBits(1 << 7) // MTIE 153 for { 154 if timerWakeup.Get() != 0 { 155 timerWakeup.Set(0) 156 // Disable timer. 157 break 158 } 159 riscv.Asm("wfi") 160 } 161 } 162 163 // handleException is called from the interrupt handler for any exception. 164 // Exceptions can be things like illegal instructions, invalid memory 165 // read/write, and similar issues. 166 func handleException(code uint64) { 167 // For a list of exception codes, see: 168 // https://content.riscv.org/wp-content/uploads/2019/08/riscv-privileged-20190608-1.pdf#page=49 169 print("fatal error: exception with mcause=") 170 print(code) 171 print(" pc=") 172 print(riscv.MEPC.Get()) 173 println() 174 abort() 175 }