github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/runtime_atsamd51.go (about) 1 //go:build (sam && atsamd51) || (sam && atsame5x) 2 3 package runtime 4 5 import ( 6 "device/arm" 7 "device/sam" 8 "machine" 9 "machine/usb/cdc" 10 "runtime/interrupt" 11 "runtime/volatile" 12 ) 13 14 type timeUnit int64 15 16 //export Reset_Handler 17 func main() { 18 arm.SCB.CPACR.Set(0) // disable FPU if it is enabled 19 preinit() 20 run() 21 exit(0) 22 } 23 24 func init() { 25 initClocks() 26 initRTC() 27 initSERCOMClocks() 28 initUSBClock() 29 initADCClock() 30 enableCache() 31 32 cdc.EnableUSBCDC() 33 machine.USBDev.Configure(machine.UARTConfig{}) 34 machine.InitSerial() 35 } 36 37 func putchar(c byte) { 38 machine.Serial.WriteByte(c) 39 } 40 41 func getchar() byte { 42 for machine.Serial.Buffered() == 0 { 43 Gosched() 44 } 45 v, _ := machine.Serial.ReadByte() 46 return v 47 } 48 49 func buffered() int { 50 return machine.Serial.Buffered() 51 } 52 53 func initClocks() { 54 // set flash wait state 55 sam.NVMCTRL.CTRLA.SetBits(0 << sam.NVMCTRL_CTRLA_RWS_Pos) 56 57 // software reset 58 sam.GCLK.CTRLA.SetBits(sam.GCLK_CTRLA_SWRST) 59 for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_SWRST) { 60 } 61 62 // Set OSCULP32K as source of Generic Clock Generator 3 63 // GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_XOSC32K].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_OSCULP32K) | GCLK_GENCTRL_GENEN; //generic clock gen 3 64 sam.GCLK.GENCTRL[3].Set((sam.GCLK_GENCTRL_SRC_OSCULP32K << sam.GCLK_GENCTRL_SRC_Pos) | 65 sam.GCLK_GENCTRL_GENEN) 66 for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK3) { 67 } 68 69 // Set OSCULP32K as source of Generic Clock Generator 0 70 sam.GCLK.GENCTRL[0].Set((sam.GCLK_GENCTRL_SRC_OSCULP32K << sam.GCLK_GENCTRL_SRC_Pos) | 71 sam.GCLK_GENCTRL_GENEN) 72 for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK0) { 73 } 74 75 // Enable DFLL48M clock 76 sam.OSCCTRL.DFLLCTRLA.Set(0) 77 sam.OSCCTRL.DFLLMUL.Set((0x1 << sam.OSCCTRL_DFLLMUL_CSTEP_Pos) | 78 (0x1 << sam.OSCCTRL_DFLLMUL_FSTEP_Pos) | 79 (0x0 << sam.OSCCTRL_DFLLMUL_MUL_Pos)) 80 for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_DFLLMUL) { 81 } 82 83 sam.OSCCTRL.DFLLCTRLB.Set(0) 84 for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_DFLLCTRLB) { 85 } 86 87 sam.OSCCTRL.DFLLCTRLA.SetBits(sam.OSCCTRL_DFLLCTRLA_ENABLE) 88 for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_ENABLE) { 89 } 90 91 sam.OSCCTRL.DFLLVAL.Set(sam.OSCCTRL.DFLLVAL.Get()) 92 for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_DFLLVAL) { 93 } 94 95 sam.OSCCTRL.DFLLCTRLB.Set(sam.OSCCTRL_DFLLCTRLB_WAITLOCK | 96 sam.OSCCTRL_DFLLCTRLB_CCDIS | 97 sam.OSCCTRL_DFLLCTRLB_USBCRM) 98 for !sam.OSCCTRL.STATUS.HasBits(sam.OSCCTRL_STATUS_DFLLRDY) { 99 } 100 101 // set GCLK7 to run at 2MHz, using DFLL48M as clock source 102 // GCLK7 = 48MHz / 24 = 2MHz 103 sam.GCLK.GENCTRL[7].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) | 104 (24 << sam.GCLK_GENCTRL_DIV_Pos) | 105 sam.GCLK_GENCTRL_GENEN) 106 for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK7) { 107 } 108 109 // Set up the PLLs 110 111 // Set PLL0 to run at 120MHz, using GCLK7 as clock source 112 sam.GCLK.PCHCTRL[1].Set(sam.GCLK_PCHCTRL_CHEN | 113 (sam.GCLK_PCHCTRL_GEN_GCLK7 << sam.GCLK_PCHCTRL_GEN_Pos)) 114 115 // multiplier = 59 + 1 + (0/32) = 60 116 // PLL0 = 2MHz * 60 = 120MHz 117 sam.OSCCTRL.DPLL[0].DPLLRATIO.Set((0x0 << sam.OSCCTRL_DPLL_DPLLRATIO_LDRFRAC_Pos) | 118 (59 << sam.OSCCTRL_DPLL_DPLLRATIO_LDR_Pos)) 119 for sam.OSCCTRL.DPLL[0].DPLLSYNCBUSY.HasBits(sam.OSCCTRL_DPLL_DPLLSYNCBUSY_DPLLRATIO) { 120 } 121 122 // MUST USE LBYPASS DUE TO BUG IN REV A OF SAMD51, via Adafruit lib. 123 sam.OSCCTRL.DPLL[0].DPLLCTRLB.Set((sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_GCLK << sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_Pos) | 124 sam.OSCCTRL_DPLL_DPLLCTRLB_LBYPASS) 125 126 sam.OSCCTRL.DPLL[0].DPLLCTRLA.Set(sam.OSCCTRL_DPLL_DPLLCTRLA_ENABLE) 127 for !sam.OSCCTRL.DPLL[0].DPLLSTATUS.HasBits(sam.OSCCTRL_DPLL_DPLLSTATUS_CLKRDY) || 128 !sam.OSCCTRL.DPLL[0].DPLLSTATUS.HasBits(sam.OSCCTRL_DPLL_DPLLSTATUS_LOCK) { 129 } 130 131 // Set PLL1 to run at 100MHz, using GCLK7 as clock source 132 sam.GCLK.PCHCTRL[2].Set(sam.GCLK_PCHCTRL_CHEN | 133 (sam.GCLK_PCHCTRL_GEN_GCLK7 << sam.GCLK_PCHCTRL_GEN_Pos)) 134 135 // multiplier = 49 + 1 + (0/32) = 50 136 // PLL1 = 2MHz * 50 = 100MHz 137 sam.OSCCTRL.DPLL[1].DPLLRATIO.Set((0x0 << sam.OSCCTRL_DPLL_DPLLRATIO_LDRFRAC_Pos) | 138 (49 << sam.OSCCTRL_DPLL_DPLLRATIO_LDR_Pos)) 139 for sam.OSCCTRL.DPLL[1].DPLLSYNCBUSY.HasBits(sam.OSCCTRL_DPLL_DPLLSYNCBUSY_DPLLRATIO) { 140 } 141 142 // // MUST USE LBYPASS DUE TO BUG IN REV A OF SAMD51 143 sam.OSCCTRL.DPLL[1].DPLLCTRLB.Set((sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_GCLK << sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_Pos) | 144 sam.OSCCTRL_DPLL_DPLLCTRLB_LBYPASS) 145 146 sam.OSCCTRL.DPLL[1].DPLLCTRLA.Set(sam.OSCCTRL_DPLL_DPLLCTRLA_ENABLE) 147 // for !sam.OSCCTRL.DPLLSTATUS1.HasBits(sam.OSCCTRL_DPLLSTATUS_CLKRDY) || 148 // !sam.OSCCTRL.DPLLSTATUS1.HasBits(sam.OSCCTRL_DPLLSTATUS_LOCK) { 149 // } 150 151 // Set up the peripheral clocks 152 // Set 48MHZ CLOCK FOR USB 153 sam.GCLK.GENCTRL[1].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) | 154 sam.GCLK_GENCTRL_IDC | 155 sam.GCLK_GENCTRL_GENEN) 156 for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK1) { 157 } 158 159 // // Set 100MHZ CLOCK FOR OTHER PERIPHERALS 160 // sam.GCLK.GENCTRL2.Set((sam.GCLK_GENCTRL_SRC_DPLL1 << sam.GCLK_GENCTRL_SRC_Pos) | 161 // sam.GCLK_GENCTRL_IDC | 162 // sam.GCLK_GENCTRL_GENEN) 163 // for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL2) { 164 // } 165 166 // // Set 12MHZ CLOCK FOR DAC 167 sam.GCLK.GENCTRL[4].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) | 168 sam.GCLK_GENCTRL_IDC | 169 (4 << sam.GCLK_GENCTRL_DIVSEL_Pos) | 170 sam.GCLK_GENCTRL_GENEN) 171 for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK4) { 172 } 173 174 // // Set up main clock 175 sam.GCLK.GENCTRL[0].Set((sam.GCLK_GENCTRL_SRC_DPLL0 << sam.GCLK_GENCTRL_SRC_Pos) | 176 sam.GCLK_GENCTRL_IDC | 177 sam.GCLK_GENCTRL_GENEN) 178 for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK0) { 179 } 180 181 sam.MCLK.CPUDIV.Set(sam.MCLK_CPUDIV_DIV_DIV1) 182 183 // Use the LDO regulator by default 184 sam.SUPC.VREG.ClearBits(sam.SUPC_VREG_SEL) 185 186 // Start up the "Debug Watchpoint and Trace" unit, so that we can use 187 // it's 32bit cycle counter for timing. 188 //CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; 189 //DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; 190 191 // Disable automatic NVM write operations 192 sam.NVMCTRL.SetCTRLA_WMODE(sam.NVMCTRL_CTRLA_WMODE_MAN) 193 } 194 195 func initRTC() { 196 // turn on digital interface clock 197 sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_RTC_) 198 199 // disable RTC 200 sam.RTC_MODE0.CTRLA.ClearBits(sam.RTC_MODE0_CTRLA_ENABLE) 201 //sam.RTC_MODE0.CTRLA.Set(0) 202 for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_ENABLE) { 203 } 204 205 // reset RTC 206 sam.RTC_MODE0.CTRLA.SetBits(sam.RTC_MODE0_CTRLA_SWRST) 207 for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_SWRST) { 208 } 209 210 // set to use ulp 32k oscillator 211 sam.OSC32KCTRL.OSCULP32K.SetBits(sam.OSC32KCTRL_OSCULP32K_EN32K) 212 sam.OSC32KCTRL.RTCCTRL.Set(sam.OSC32KCTRL_RTCCTRL_RTCSEL_ULP32K) 213 214 // set Mode0 to 32-bit counter (mode 0) with prescaler 1 and GCLK2 is 32KHz/1 215 sam.RTC_MODE0.CTRLA.Set((sam.RTC_MODE0_CTRLA_MODE_COUNT32 << sam.RTC_MODE0_CTRLA_MODE_Pos) | 216 (sam.RTC_MODE0_CTRLA_PRESCALER_DIV1 << sam.RTC_MODE0_CTRLA_PRESCALER_Pos) | 217 (sam.RTC_MODE0_CTRLA_COUNTSYNC)) 218 219 // re-enable RTC 220 sam.RTC_MODE0.CTRLA.SetBits(sam.RTC_MODE0_CTRLA_ENABLE) 221 for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_ENABLE) { 222 } 223 224 irq := interrupt.New(sam.IRQ_RTC, func(interrupt.Interrupt) { 225 flags := sam.RTC_MODE0.INTFLAG.Get() 226 if flags&sam.RTC_MODE0_INTENSET_CMP0 != 0 { 227 // The timer (for a sleep) has expired. 228 timerWakeup.Set(1) 229 } 230 if flags&sam.RTC_MODE0_INTENSET_OVF != 0 { 231 // The 32-bit RTC timer has overflowed. 232 rtcOverflows.Set(rtcOverflows.Get() + 1) 233 } 234 // Mark this interrupt has handled for CMP0 and OVF. 235 sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0 | sam.RTC_MODE0_INTENSET_OVF) 236 }) 237 sam.RTC_MODE0.INTENSET.Set(sam.RTC_MODE0_INTENSET_OVF) 238 irq.SetPriority(0xc0) 239 irq.Enable() 240 } 241 242 func waitForSync() { 243 for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_COUNT) { 244 } 245 } 246 247 var rtcOverflows volatile.Register32 // number of times the RTC wrapped around 248 249 var timerWakeup volatile.Register8 250 251 // ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds. 252 func ticksToNanoseconds(ticks timeUnit) int64 { 253 // The following calculation is actually the following, but with both sides 254 // reduced to reduce the risk of overflow: 255 // ticks * 1e9 / 32768 256 return int64(ticks) * 1953125 / 64 257 } 258 259 // nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz). 260 func nanosecondsToTicks(ns int64) timeUnit { 261 // The following calculation is actually the following, but with both sides 262 // reduced to reduce the risk of overflow: 263 // ns * 32768 / 1e9 264 return timeUnit(ns * 64 / 1953125) 265 } 266 267 // sleepTicks should sleep for d number of microseconds. 268 func sleepTicks(d timeUnit) { 269 for d != 0 { 270 ticks := uint32(d) 271 if !timerSleep(ticks) { 272 return 273 } 274 d -= timeUnit(ticks) 275 } 276 } 277 278 // ticks returns the elapsed time since reset. 279 func ticks() timeUnit { 280 // For some ways of capturing the time atomically, see this thread: 281 // https://www.eevblog.com/forum/microcontrollers/correct-timing-by-timer-overflow-count/msg749617/#msg749617 282 // Here, instead of re-reading the counter register if an overflow has been 283 // detected, we simply try again because that results in smaller code. 284 for { 285 mask := interrupt.Disable() 286 counter := readRTC() 287 overflows := rtcOverflows.Get() 288 hasOverflow := sam.RTC_MODE0.INTFLAG.Get()&sam.RTC_MODE0_INTENSET_OVF != 0 289 interrupt.Restore(mask) 290 291 if hasOverflow { 292 // There was an overflow while trying to capture the timer. 293 // Try again. 294 continue 295 } 296 297 // This is a 32-bit timer, so the number of timer overflows forms the 298 // upper 32 bits of this timer. 299 return timeUnit(overflows)<<32 + timeUnit(counter) 300 } 301 } 302 303 func readRTC() uint32 { 304 waitForSync() 305 return sam.RTC_MODE0.COUNT.Get() 306 } 307 308 // ticks are in microseconds 309 // Returns true if the timer completed. 310 // Returns false if another interrupt occured which requires an early return to scheduler. 311 func timerSleep(ticks uint32) bool { 312 timerWakeup.Set(0) 313 if ticks < 8 { 314 // due to delay waiting for the register value to sync, the minimum sleep value 315 // for the SAMD51 is 260us. 316 // For related info for SAMD21, see: 317 // https://community.atmel.com/comment/2507091#comment-2507091 318 ticks = 8 319 } 320 321 // request read of count 322 waitForSync() 323 324 // set compare value 325 cnt := sam.RTC_MODE0.COUNT.Get() 326 327 sam.RTC_MODE0.COMP[0].Set(uint32(cnt) + ticks) 328 329 // enable IRQ for CMP0 compare 330 sam.RTC_MODE0.INTENSET.Set(sam.RTC_MODE0_INTENSET_CMP0) 331 332 wait: 333 waitForEvents() 334 if timerWakeup.Get() != 0 { 335 return true 336 } 337 if hasScheduler { 338 // The interurpt may have awoken a goroutine, so bail out early. 339 // Disable IRQ for CMP0 compare. 340 sam.RTC_MODE0.INTENCLR.Set(sam.RTC_MODE0_INTENSET_CMP0) 341 return false 342 } else { 343 // This is running without a scheduler. 344 // The application expects this to sleep the whole time. 345 goto wait 346 } 347 } 348 349 func initUSBClock() { 350 // Turn on clock(s) for USB 351 //MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB; 352 //MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB; 353 sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_USB_) 354 sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_USB_) 355 356 // Put Generic Clock Generator 1 as source for USB 357 //GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); 358 sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_USB].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) | 359 sam.GCLK_PCHCTRL_CHEN) 360 } 361 362 func initADCClock() { 363 // Turn on clocks for ADC0/ADC1. 364 sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_ADC0_) 365 sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_ADC1_) 366 367 // Put Generic Clock Generator 1 as source for ADC0 and ADC1. 368 sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_ADC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) | 369 sam.GCLK_PCHCTRL_CHEN) 370 sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_ADC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) | 371 sam.GCLK_PCHCTRL_CHEN) 372 } 373 374 func enableCache() { 375 sam.CMCC.CTRL.SetBits(sam.CMCC_CTRL_CEN) 376 } 377 378 func waitForEvents() { 379 arm.Asm("wfe") 380 }