github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/runtime_atsamd21.go (about) 1 //go:build sam && atsamd21 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 "unsafe" 13 ) 14 15 type timeUnit int64 16 17 //export Reset_Handler 18 func main() { 19 preinit() 20 run() 21 exit(0) 22 } 23 24 func init() { 25 initClocks() 26 initRTC() 27 initSERCOMClocks() 28 initUSBClock() 29 initADCClock() 30 31 cdc.EnableUSBCDC() 32 machine.USBDev.Configure(machine.UARTConfig{}) 33 machine.InitSerial() 34 } 35 36 func putchar(c byte) { 37 machine.Serial.WriteByte(c) 38 } 39 40 func getchar() byte { 41 for machine.Serial.Buffered() == 0 { 42 Gosched() 43 } 44 v, _ := machine.Serial.ReadByte() 45 return v 46 } 47 48 func buffered() int { 49 return machine.Serial.Buffered() 50 } 51 52 func initClocks() { 53 // Set 1 Flash Wait State for 48MHz, required for 3.3V operation according to SAMD21 Datasheet 54 sam.NVMCTRL.CTRLB.SetBits(sam.NVMCTRL_CTRLB_RWS_HALF << sam.NVMCTRL_CTRLB_RWS_Pos) 55 56 // Turn on the digital interface clock 57 sam.PM.APBAMASK.SetBits(sam.PM_APBAMASK_GCLK_) 58 // turn off RTC 59 sam.PM.APBAMASK.ClearBits(sam.PM_APBAMASK_RTC_) 60 61 // Enable OSC32K clock (Internal 32.768Hz oscillator). 62 // This requires registers that are not included in the SVD file. 63 // This is from samd21g18a.h and nvmctrl.h: 64 // 65 // #define NVMCTRL_OTP4 0x00806020 66 // 67 // #define SYSCTRL_FUSES_OSC32K_CAL_ADDR (NVMCTRL_OTP4 + 4) 68 // #define SYSCTRL_FUSES_OSC32K_CAL_Pos 6 /** (NVMCTRL_OTP4) OSC32K Calibration */ 69 // #define SYSCTRL_FUSES_OSC32K_CAL_Msk (0x7Fu << SYSCTRL_FUSES_OSC32K_CAL_Pos) 70 // #define SYSCTRL_FUSES_OSC32K_CAL(value) ((SYSCTRL_FUSES_OSC32K_CAL_Msk & ((value) << SYSCTRL_FUSES_OSC32K_CAL_Pos))) 71 // u32_t fuse = *(u32_t *)FUSES_OSC32K_CAL_ADDR; 72 // u32_t calib = (fuse & FUSES_OSC32K_CAL_Msk) >> FUSES_OSC32K_CAL_Pos; 73 fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4)) 74 calib := (fuse & uint32(0x7f<<6)) >> 6 75 76 // SYSCTRL_OSC32K_CALIB(calib) | 77 // SYSCTRL_OSC32K_STARTUP(0x6u) | 78 // SYSCTRL_OSC32K_EN32K | SYSCTRL_OSC32K_ENABLE; 79 sam.SYSCTRL.OSC32K.Set((calib << sam.SYSCTRL_OSC32K_CALIB_Pos) | 80 (0x6 << sam.SYSCTRL_OSC32K_STARTUP_Pos) | 81 sam.SYSCTRL_OSC32K_EN32K | 82 sam.SYSCTRL_OSC32K_EN1K | 83 sam.SYSCTRL_OSC32K_ENABLE) 84 // Wait for oscillator stabilization 85 for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_OSC32KRDY) { 86 } 87 88 // Software reset the module to ensure it is re-initialized correctly 89 sam.GCLK.CTRL.Set(sam.GCLK_CTRL_SWRST) 90 // Wait for reset to complete 91 for sam.GCLK.CTRL.HasBits(sam.GCLK_CTRL_SWRST) && sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) { 92 } 93 94 // Put OSC32K as source of Generic Clock Generator 1 95 sam.GCLK.GENDIV.Set((1 << sam.GCLK_GENDIV_ID_Pos) | 96 (0 << sam.GCLK_GENDIV_DIV_Pos)) 97 waitForSync() 98 99 // GCLK_GENCTRL_ID(1) | GCLK_GENCTRL_SRC_OSC32K | GCLK_GENCTRL_GENEN; 100 sam.GCLK.GENCTRL.Set((1 << sam.GCLK_GENCTRL_ID_Pos) | 101 (sam.GCLK_GENCTRL_SRC_OSC32K << sam.GCLK_GENCTRL_SRC_Pos) | 102 sam.GCLK_GENCTRL_GENEN) 103 waitForSync() 104 105 // Use Generic Clock Generator 1 as source for Generic Clock Multiplexer 0 (DFLL48M reference) 106 sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_DFLL48 << sam.GCLK_CLKCTRL_ID_Pos) | 107 (sam.GCLK_CLKCTRL_GEN_GCLK1 << sam.GCLK_CLKCTRL_GEN_Pos) | 108 sam.GCLK_CLKCTRL_CLKEN) 109 waitForSync() 110 111 // Remove the OnDemand mode, Bug http://avr32.icgroup.norway.atmel.com/bugzilla/show_bug.cgi?id=9905 112 sam.SYSCTRL.DFLLCTRL.Set(sam.SYSCTRL_DFLLCTRL_ENABLE) 113 // Wait for ready 114 for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_DFLLRDY) { 115 } 116 117 // Handle DFLL calibration based on info learned from Arduino SAMD implementation, 118 // using value stored in fuse. 119 // #define SYSCTRL_FUSES_DFLL48M_COARSE_CAL_ADDR (NVMCTRL_OTP4 + 4) 120 // #define SYSCTRL_FUSES_DFLL48M_COARSE_CAL_Pos 26 /**< \brief (NVMCTRL_OTP4) DFLL48M Coarse Calibration */ 121 // #define SYSCTRL_FUSES_DFLL48M_COARSE_CAL_Msk (0x3Fu << SYSCTRL_FUSES_DFLL48M_COARSE_CAL_Pos) 122 // #define SYSCTRL_FUSES_DFLL48M_COARSE_CAL(value) ((SYSCTRL_FUSES_DFLL48M_COARSE_CAL_Msk & ((value) << SYSCTRL_FUSES_DFLL48M_COARSE_CAL_Pos))) 123 coarse := (fuse >> 26) & 0x3F 124 if coarse == 0x3f { 125 coarse = 0x1f 126 } 127 128 sam.SYSCTRL.DFLLVAL.SetBits(coarse << sam.SYSCTRL_DFLLVAL_COARSE_Pos) 129 sam.SYSCTRL.DFLLVAL.SetBits(0x1ff << sam.SYSCTRL_DFLLVAL_FINE_Pos) 130 131 // Write full configuration to DFLL control register 132 // SYSCTRL_DFLLMUL_CSTEP( 0x1f / 4 ) | // Coarse step is 31, half of the max value 133 // SYSCTRL_DFLLMUL_FSTEP( 10 ) | 134 // SYSCTRL_DFLLMUL_MUL( (48000) ) ; 135 sam.SYSCTRL.DFLLMUL.Set(((31 / 4) << sam.SYSCTRL_DFLLMUL_CSTEP_Pos) | 136 (10 << sam.SYSCTRL_DFLLMUL_FSTEP_Pos) | 137 (48000 << sam.SYSCTRL_DFLLMUL_MUL_Pos)) 138 139 // disable DFLL 140 sam.SYSCTRL.DFLLCTRL.Set(0) 141 waitForSync() 142 143 sam.SYSCTRL.DFLLCTRL.SetBits(sam.SYSCTRL_DFLLCTRL_MODE | 144 sam.SYSCTRL_DFLLCTRL_CCDIS | 145 sam.SYSCTRL_DFLLCTRL_USBCRM | 146 sam.SYSCTRL_DFLLCTRL_BPLCKC) 147 // Wait for ready 148 for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_DFLLRDY) { 149 } 150 151 // Re-enable the DFLL 152 sam.SYSCTRL.DFLLCTRL.SetBits(sam.SYSCTRL_DFLLCTRL_ENABLE) 153 // Wait for ready 154 for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_DFLLRDY) { 155 } 156 157 // Switch Generic Clock Generator 0 to DFLL48M. CPU will run at 48MHz. 158 sam.GCLK.GENDIV.Set((0 << sam.GCLK_GENDIV_ID_Pos) | 159 (0 << sam.GCLK_GENDIV_DIV_Pos)) 160 waitForSync() 161 162 sam.GCLK.GENCTRL.Set((0 << sam.GCLK_GENCTRL_ID_Pos) | 163 (sam.GCLK_GENCTRL_SRC_DFLL48M << sam.GCLK_GENCTRL_SRC_Pos) | 164 sam.GCLK_GENCTRL_IDC | 165 sam.GCLK_GENCTRL_GENEN) 166 waitForSync() 167 168 // Modify PRESCaler value of OSC8M to have 8MHz 169 sam.SYSCTRL.OSC8M.SetBits(sam.SYSCTRL_OSC8M_PRESC_0 << sam.SYSCTRL_OSC8M_PRESC_Pos) 170 sam.SYSCTRL.OSC8M.ClearBits(1 << sam.SYSCTRL_OSC8M_ONDEMAND_Pos) 171 // Wait for oscillator stabilization 172 for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_OSC8MRDY) { 173 } 174 175 // Use OSC8M as source for Generic Clock Generator 3 176 sam.GCLK.GENDIV.Set((3 << sam.GCLK_GENDIV_ID_Pos)) 177 waitForSync() 178 179 sam.GCLK.GENCTRL.Set((3 << sam.GCLK_GENCTRL_ID_Pos) | 180 (sam.GCLK_GENCTRL_SRC_OSC8M << sam.GCLK_GENCTRL_SRC_Pos) | 181 sam.GCLK_GENCTRL_GENEN) 182 waitForSync() 183 184 // Use OSC32K as source for Generic Clock Generator 2 185 // OSC32K/1 -> GCLK2 at 32KHz 186 sam.GCLK.GENDIV.Set(2 << sam.GCLK_GENDIV_ID_Pos) 187 waitForSync() 188 189 sam.GCLK.GENCTRL.Set((2 << sam.GCLK_GENCTRL_ID_Pos) | 190 (sam.GCLK_GENCTRL_SRC_OSC32K << sam.GCLK_GENCTRL_SRC_Pos) | 191 sam.GCLK_GENCTRL_GENEN) 192 waitForSync() 193 194 // Use GCLK2 for RTC 195 sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_RTC << sam.GCLK_CLKCTRL_ID_Pos) | 196 (sam.GCLK_CLKCTRL_GEN_GCLK2 << sam.GCLK_CLKCTRL_GEN_Pos) | 197 sam.GCLK_CLKCTRL_CLKEN) 198 waitForSync() 199 200 // Set the CPU, APBA, B, and C dividers 201 sam.PM.CPUSEL.Set(sam.PM_CPUSEL_CPUDIV_DIV1) 202 sam.PM.APBASEL.Set(sam.PM_APBASEL_APBADIV_DIV1) 203 sam.PM.APBBSEL.Set(sam.PM_APBBSEL_APBBDIV_DIV1) 204 sam.PM.APBCSEL.Set(sam.PM_APBCSEL_APBCDIV_DIV1) 205 206 // Disable automatic NVM write operations 207 sam.NVMCTRL.CTRLB.SetBits(sam.NVMCTRL_CTRLB_MANW) 208 } 209 210 func initRTC() { 211 // turn on digital interface clock 212 sam.PM.APBAMASK.SetBits(sam.PM_APBAMASK_RTC_) 213 214 // disable RTC 215 sam.RTC_MODE0.CTRL.Set(0) 216 waitForSync() 217 218 // reset RTC 219 sam.RTC_MODE0.CTRL.SetBits(sam.RTC_MODE0_CTRL_SWRST) 220 waitForSync() 221 222 // set Mode0 to 32-bit counter (mode 0) with prescaler 1 and GCLK2 is 32KHz/1 223 sam.RTC_MODE0.CTRL.Set((sam.RTC_MODE0_CTRL_MODE_COUNT32 << sam.RTC_MODE0_CTRL_MODE_Pos) | 224 (sam.RTC_MODE0_CTRL_PRESCALER_DIV1 << sam.RTC_MODE0_CTRL_PRESCALER_Pos)) 225 waitForSync() 226 227 // re-enable RTC 228 sam.RTC_MODE0.CTRL.SetBits(sam.RTC_MODE0_CTRL_ENABLE) 229 waitForSync() 230 231 rtcInterrupt := interrupt.New(sam.IRQ_RTC, func(intr interrupt.Interrupt) { 232 flags := sam.RTC_MODE0.INTFLAG.Get() 233 if flags&sam.RTC_MODE0_INTENSET_CMP0 != 0 { 234 // The timer (for a sleep) has expired. 235 timerWakeup.Set(1) 236 } 237 if flags&sam.RTC_MODE0_INTENSET_OVF != 0 { 238 // The 32-bit RTC timer has overflowed. 239 rtcOverflows.Set(rtcOverflows.Get() + 1) 240 } 241 // Mark this interrupt has handled for CMP0 and OVF. 242 sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0 | sam.RTC_MODE0_INTENSET_OVF) 243 }) 244 sam.RTC_MODE0.INTENSET.Set(sam.RTC_MODE0_INTENSET_OVF) 245 rtcInterrupt.SetPriority(0xc0) 246 rtcInterrupt.Enable() 247 } 248 249 func waitForSync() { 250 for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) { 251 } 252 } 253 254 var rtcOverflows volatile.Register32 // number of times the RTC wrapped around 255 256 var timerWakeup volatile.Register8 257 258 // ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds. 259 func ticksToNanoseconds(ticks timeUnit) int64 { 260 // The following calculation is actually the following, but with both sides 261 // reduced to reduce the risk of overflow: 262 // ticks * 1e9 / 32768 263 return int64(ticks) * 1953125 / 64 264 } 265 266 // nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz). 267 func nanosecondsToTicks(ns int64) timeUnit { 268 // The following calculation is actually the following, but with both sides 269 // reduced to reduce the risk of overflow: 270 // ns * 32768 / 1e9 271 return timeUnit(ns * 64 / 1953125) 272 } 273 274 // sleepTicks should sleep for d number of microseconds. 275 func sleepTicks(d timeUnit) { 276 for d != 0 { 277 ticks := uint32(d) 278 if !timerSleep(ticks) { 279 // Bail out early to handle a non-time interrupt. 280 return 281 } 282 d -= timeUnit(ticks) 283 } 284 } 285 286 // ticks returns the elapsed time since reset. 287 func ticks() timeUnit { 288 // For some ways of capturing the time atomically, see this thread: 289 // https://www.eevblog.com/forum/microcontrollers/correct-timing-by-timer-overflow-count/msg749617/#msg749617 290 // Here, instead of re-reading the counter register if an overflow has been 291 // detected, we simply try again because that results in smaller code. 292 for { 293 mask := interrupt.Disable() 294 counter := readRTC() 295 overflows := rtcOverflows.Get() 296 hasOverflow := sam.RTC_MODE0.INTFLAG.Get()&sam.RTC_MODE0_INTENSET_OVF != 0 297 interrupt.Restore(mask) 298 299 if hasOverflow { 300 // There was an overflow while trying to capture the timer. 301 // Try again. 302 continue 303 } 304 305 // This is a 32-bit timer, so the number of timer overflows forms the 306 // upper 32 bits of this timer. 307 return timeUnit(overflows)<<32 + timeUnit(counter) 308 } 309 } 310 311 func readRTC() uint32 { 312 // request read of count 313 sam.RTC_MODE0.READREQ.Set(sam.RTC_MODE0_READREQ_RREQ) 314 waitForSync() 315 316 return sam.RTC_MODE0.COUNT.Get() 317 } 318 319 // ticks are in microseconds 320 // Returns true if the timer completed. 321 // Returns false if another interrupt occured which requires an early return to scheduler. 322 func timerSleep(ticks uint32) bool { 323 timerWakeup.Set(0) 324 if ticks < 7 { 325 // Due to around 6 clock ticks delay waiting for the register value to 326 // sync, the minimum sleep value for the SAMD21 is 214us. 327 // For related info, see: 328 // https://community.atmel.com/comment/2507091#comment-2507091 329 ticks = 7 330 } 331 332 // request read of count 333 sam.RTC_MODE0.READREQ.Set(sam.RTC_MODE0_READREQ_RREQ) 334 waitForSync() 335 336 // set compare value 337 cnt := sam.RTC_MODE0.COUNT.Get() 338 sam.RTC_MODE0.COMP0.Set(uint32(cnt) + ticks) 339 waitForSync() 340 341 // enable IRQ for CMP0 compare 342 sam.RTC_MODE0.INTENSET.Set(sam.RTC_MODE0_INTENSET_CMP0) 343 344 wait: 345 waitForEvents() 346 if timerWakeup.Get() != 0 { 347 return true 348 } 349 if hasScheduler { 350 // The interurpt may have awoken a goroutine, so bail out early. 351 // Disable IRQ for CMP0 compare. 352 sam.RTC_MODE0.INTENCLR.Set(sam.RTC_MODE0_INTENSET_CMP0) 353 return false 354 } else { 355 // This is running without a scheduler. 356 // The application expects this to sleep the whole time. 357 goto wait 358 } 359 } 360 361 func initUSBClock() { 362 // Turn on clock for USB 363 sam.PM.APBBMASK.SetBits(sam.PM_APBBMASK_USB_) 364 365 // Put Generic Clock Generator 0 as source for Generic Clock Multiplexer 6 (USB reference) 366 sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_USB << sam.GCLK_CLKCTRL_ID_Pos) | 367 (sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) | 368 sam.GCLK_CLKCTRL_CLKEN) 369 waitForSync() 370 } 371 372 func initADCClock() { 373 // Turn on clock for ADC 374 sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_ADC_) 375 376 // Put Generic Clock Generator 0 as source for Generic Clock Multiplexer for ADC. 377 sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_ADC << sam.GCLK_CLKCTRL_ID_Pos) | 378 (sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) | 379 sam.GCLK_CLKCTRL_CLKEN) 380 waitForSync() 381 } 382 383 func waitForEvents() { 384 arm.Asm("wfe") 385 }