github.com/aykevl/tinygo@v0.5.0/src/runtime/runtime_atsamd21.go (about)

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