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