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