Arduino Zero Boot time

Hi,

I'm trying to speed up the arduino zero boot times. I'm using a custom board based on the SAMD21, and using the Arduino IDE for development. I normally upload my code via USB, but I also have an Atmel-ICE programmer.

So far, with a simple blink sketch (no serial or anything in it), I get a boot time of ~1.7s from power on to first light out.

int laserPin = 8;
// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(laserPin, OUTPUT);
  digitalWrite(laserPin, HIGH);   // turn the LED on (HIGH is the voltage level)
}

// the loop function runs over and over again forever
void loop() {
  for (int aux = 0; aux < 10; aux++){
    digitalWrite(laserPin, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(500);                       // wait for a second
    digitalWrite(laserPin, LOW);    // turn the LED off by making the voltage LOW
    delay(500); 
  }
  for (int aux = 0; aux < 10; aux++){
    digitalWrite(laserPin, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(50);                       // wait for a second
    digitalWrite(laserPin, LOW);    // turn the LED off by making the voltage LOW
    delay(50); 
  }
// wait for a second
}

I checked on an oscilloscope, power on the microcontroller is instant, and although there might be some propagation delay (I use a driver to power a laser) it is in the microseconds range, not 1.7s. Again, I checked on an oscilloscope, and the delay is from power on to first activity on the pins of the SAMD

I think I read that removing the bootloader I might achieve faster (instant??) boot. I followed the advice of the great AloyseTech here ([Solved] Programming a SAMD21 with the ATMEL-ICE from the IDE - Arduino Zero - Arduino Forum) to remove the bootloader of my boards, and program them straight from the programmer, and I have the same boot times if I program it from the IDE using the no bootloader option, if I export the binary from the IDE and flash it with Atmel Studio and if I flash the bootloader, and export the binary and I flash it from Atmel studio without erasing the chip and starting in address 0x2000.

So for me, I have the same boot times with and without the bootloader and I was wondering if it's just the time it takes the M0 to boot. Does anyone have any idea about this?

With no bootloader on the chip it should start running instantly. Are you using the same crystal and capacitors as the Arduino Zero?

The bootloader if compiled with the double tap functionality should take at least 500ms. If you don't need the double-tap-on-reset-to-stay-in-bootloader feature, you can remove it. You'll have to look for the remaining 1.2 seconds though.

And of course, as dlabun said, if you can always use the ICE, you could remove the bootloader completely to get almost instant start.

Well, then I don't get it. It might be that I didn't add as many decoupling caps as in the Zero

I have a 10uF in the 3.3V (although is not very close to VDDA :S), plus the 1uF in the VDDA/GNDA pair, and one 100nF in one of the VDDIO pins (I didn't know that I needed one on each "pair". The crystal is this one (Crystal), and I added the standard 22p as it appears (as an indication) in the SAMD21 datasheet. Sure, it's quite lacking compared to the Xplained and Zero boards, but Adafruit's Feather has even less and it seems to be working good.

Do you think that this might be the issue? Is there a way to check that the crystal is working as expected?

The 1.7sec needed for booting are not an issue. I may have been a bit unclear sorry.
The boot time is around the same for my board. What I as saying is that there are 500ms taken for sure by the bootloader to check if a double tap occurred (see here). The remaining 1.2 sec might be a lot of USB stuff. Have you measured the boot time with or without USB connection? Is there a difference?

Well, there is no USB connections on the board, or any USB-dependant code, it's just a blink sketch. I guess that that doesn't mean that the USB activity that you mention doesn't happen, but I can't see how to make it "not happen".

What puzzles me even more is that I'm generating the binary file for a "with bootloader" board and a "without bootloader" board, and flashing it with atmel studio (offsetting it to 0x2000 for the bootloader version), and both take the same amount of time, although when I plug a development board that has a USB socket, the one with bootloader appears as a com port in windows, and the one without doesn't (so I think that means I managed to get rid of the bootloader).

Can you guys, with your original arduino zero boards, get rid of the bootloader, and start a blink sketch instantly from power off?

I actually don't use the Arduino Zero, but rather the Sparkfun SAMD21 dev board. Without the boot loader my sketches do launch instantly, with it they launch in less than a second. I'd venture to guess you have a timing / crystal issue.

Hi Dlabun,

I'm not saying I don't have a HW issue somewhere, but the thing is: I CAN start it instantly, if I use Atmel Studio for the development, and don't use the Arduino libraries. I attached the Atmel Studio project in a previous message, if you want to check it. If I use the Arduino IDE, then 1.7 seconds is the fastest I can make it. Regarding adding or removing the bootloader, I think I'm doing correctly, but I might be doing it wrong and not removing it. In any case, I see no differences by adding it or removing it. Sadly, I don't have an official board at hand to check. What puzzles me is that I can start it instantly with a certain code, but not with the IDE. Is not a deal breaker, but I would like to know why. I also understand that my board is custom, and hence what applies to the zero doesn't have to apply to my board.

In the next revision, I will do some more tests to get the HW in a "better" state, closer to what the Xplore and Zero boards are.

Is there a way to test if the crystal is not working as intended? In some μC there is an option to output the clocks in an IO pin. Does the SAM D21 has this option?

It would be great if someone with a Adafruit Feather board can tell us what their times are too, because they have a setup that is closer to what I have.

Try to comment the following line in the main.cpp of the SAMD core file :

USBDevice.init();
USBDevice.attach();

(and keep USBCON defined)

I do have a Feather M0 Datalogger, but unfortunately it has the Adafruit boot loader still on it.

There's a way to get the SAMD21 to output its clock signals but I haven't been able to make it work. Below is a chunk of code I use to initiate a pulse output on pin 12... you should see a 400Hz pulse with this code if your clocks are working right.

REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK4
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                     GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Enable the port multiplexer for the 1 PWM channel: timer TCC0 output
  const uint8_t CHANNELS = 1;
  const uint8_t pwmPins[] = { 12 };
  for (uint8_t i = 0; i < CHANNELS; i++)
  {
     PORT->Group[g_APinDescription[pwmPins[i]].ulPort].PINCFG[g_APinDescription[pwmPins[i]].ulPin].bit.PMUXEN = 1;
  }
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  
  PORT->Group[g_APinDescription[12].ulPort].PMUX[g_APinDescription[12].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;

  // Feed GCLK4 to TCC0 and TCC1
  /*REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC0 and TCC1
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed GCLK4 to TCC0 and TCC1*/
  GCLK->CLKCTRL.reg = 0x441A;                     // Added for compilation problem with Arduino M0
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC0_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTTOM;    // Setup dual slope PWM on TCC0
  while (TCC0->SYNCBUSY.bit.WAVE);               // Wait for synchronization

  // Each timer counts up to a maximum or TOP value set by the PER register,
  // this determines the frequency of the PWM operation:
  // 20000 = 50Hz, 10000 = 100Hz, 2500  = 400Hz, 2702 = 370Hz



   REG_TCC0_PER = 2500;      
   
  while(TCC0->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  REG_TCC0_CCB3 = 33000;       // TCC0 CCB3 - center the servo on D7
  while(TCC0->SYNCBUSY.bit.CCB3);

  // Divide the 16MHz signal by 8 giving 2MHz (0.5us) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV8 |    // Divide GCLK4 by 8
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

You can also use the Arduino PWM : if clocked right, the frequency should be 732.4Hz when using analogWrite(pin,value)

Oh.. Duh, Forgot about the built in PWM feature... Either way, test a pulsing output to see how accurate it is.

OK, I am going to do some thread necromancy, since I ended up here with a similar issue but saw no resolution & I made progress on my case of this issue today.

I am quite certain that the culprit, at least in my case, is a very long start-up time for the external 32Khz crystal oscillator.
The ATSAMD21G18A datasheet states that it will take 28K-30K cycles (!) for the 32Khz crystal osc to stabilize, which would account for ~1s of delay as the startup code will wait for this clock to be stable before continuing.
However, the fix detailed below reduced the total startup delay until the entry to setup() to 8.8ms, including waiting for 48MHz PLL lock and other clocks to sync. However, the board will be running from the internal 32Khz oscillator after this, which will be a big compromise in accuracy.

I am using an m0 based "Feather" board from Adafruit in a solar-powered application where we do not want to burn a bunch of power during startup.
I measured the startup delay from application of power to the entry to setup() (signaled by a pin write) as between 1.7-2.2s. I could not figure out where that delay was coming from. I erased the bootloader by unprotecting the bootloader[1] area and erasing the whole chip from within Atmel Studio 7. Even with a simple sketch flashed on, the delay remained.
The issue did not show up with a debugger attached. I assume this was since the board was already powered and had been through one clock start-up sequence, so the OSC32 peripheral was still running or is running by default and the clock was ready instantly.

If you look into the startup.c file for the SAMD core, you'll see that there is code for both an external 32Khz crystal and one for using the internal 32Khz oscillator. By defining the symbol CRYSTALLESS, the internal clock will be used instead, which greatly shortens the startup time. This was done at the project properties level in Atmel Studio 7, under Toolchain -> ARM/GNU C Compiler -> Symbols , and also the same for the ARM/GNU C++ Compiler section, for both the 'ArduinoCore' project and the sketch project.

Also note that Arduino sketch import is not exactly working with AS7 right now, although the workaround that I used [2] appears to resolve things, even for builds that pull in referenced libraries.

Of course, there is no need for the core to busy-wait while the clocks are starting up, so a more nuanced approach to bring-up should be totally possible, one where the core clock is switched after it becomes stable or the core is in sleep until that clock is ready.

[1] https://roamingthings.de/use-j-link-to-change-the-boot-loader-protection-of-a-sam-d21/
[2] http://www.avrfreaks.net/comment/2190156#comment-2190156

Hi All

I'm having the same issue using a raw samd21g18 chip. If I use the arduino libraries (arduino IDE or AS7) I get a 2sec startup delay but if I add CRYSTALLESS to the symbols, startup is immediate (which is great) but...
If I use CRYSTALLESS my USB is not recognized, whereas simply removing this the USB is recognized????
Creating a simple atmel.start project, startup is immediate but I'm not able to test the USB (still relatively new and rely on the arduino framework).

So I've got a raw samd21G18 chip working, even with a bootloader but I can't have a 2 second start delay, it needs to be under 500ms. I'm happy to use CRYSTALLESS as long as I can get windows to recognize the device if using an external crystal is going to take 1-2sec to stabilize?

Please help

Thanks,
Mark

I managed to get the USB to talk correctly with the USB using CRYSTALLESS.

I removed the following line in "startup.c" "SystemInit" routine of the arduino library
So I now have a zero second start up and full USB connectivity.

//#if defined(CRYSTALLESS)
//
 //#define NVM_SW_CALIB_DFLL48M_COARSE_VAL 58
 //#define NVM_SW_CALIB_DFLL48M_FINE_VAL   64
//
 //// Turn on DFLL
 //uint32_t coarse =( *((uint32_t *)(NVMCTRL_OTP4) + (NVM_SW_CALIB_DFLL48M_COARSE_VAL / 32)) >> (NVM_SW_CALIB_DFLL48M_COARSE_VAL % 32) )
                  //& ((1 << 6) - 1);
 //if (coarse == 0x3f) {
   //coarse = 0x1f;
 //}
 //uint32_t fine =( *((uint32_t *)(NVMCTRL_OTP4) + (NVM_SW_CALIB_DFLL48M_FINE_VAL / 32)) >> (NVM_SW_CALIB_DFLL48M_FINE_VAL % 32) )
                //& ((1 << 10) - 1);
 //if (fine == 0x3ff) {
   //fine = 0x1ff;
 //}
//
 //SYSCTRL->DFLLVAL.bit.COARSE = coarse;
 //SYSCTRL->DFLLVAL.bit.FINE = fine;
 ///* Write full configuration to DFLL control register */
 //SYSCTRL->DFLLCTRL.reg =  SYSCTRL_DFLLCTRL_USBCRM | /* USB correction */
                          //SYSCTRL_DFLLCTRL_CCDIS |
                          //SYSCTRL_DFLLCTRL_WAITLOCK |
                          //SYSCTRL_DFLLCTRL_QLDIS ; /* Disable Quick lock */
//
 //while ( (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY) == 0 )
 //{
   ///* Wait for synchronization */
 //}
//
 ///* Enable the DFLL */
 //SYSCTRL->DFLLCTRL.reg |= SYSCTRL_DFLLCTRL_ENABLE ;
//
//#else   // has crystal