SAMD51 DFLL in STANDBY

Hi,

I have reviewed the SAMD51 datasheet regarding the DFLL operating during STANDBY and I am a bit uncertain regarding an ambiguity. Specifically, the DFLLCTRLA register description reads as follows:

**Bit 6 – RUNSTDBY **Run in Standby This bit controls how the DFLL behaves during standby sleep mode: 0: The DFLL is not running in standby sleep mode if no peripheral requests the clock. 1: The DFLL is running in standby sleep mode. If ONDEMAND is one, the DFLL will be running when a peripheral is requesting the clock. If ONDEMAND is zero, the clock source will always be running in standby sleep mode.

The ambiguity arises because it is not clear what happens during standby sleep mode when RUNSTDBY = 0. From the description, it appears the DFLL will be on when the device enters STANDBY mode. I had the understanding that in STANDBY mode, all clocks are suspended. I am familiar with using the SAMD21 sleep and the SAMD21 datasheet describes RUNSTDBY a little different:

**Bit 6 – RUNSTDBY **Run in Standby This bit controls how the DFLL behaves during standby sleep mode: 0: The oscillator is disabled in standby sleep mode. 1: The oscillator is not stopped in standby sleep mode. If DFLLCTRL.ONDEMAND is one, the clock source will be running when a peripheral is requesting the clock. If DFLLCTRL.ONDEMAND is zero, the clock source will always be running in standby sleep mode.

The SAMD21 description is pretty clear that the clock is disabled during standby which has been my experience when using the SAMD21. The SAMD51 description appears to disable the DFLL in standby with the caveat "if no peripheral requests the clock". This seems similar to the "ONDEMAND" functionality but appears to apply even when "ONDEMAND" is not set.

My experience with the SAMD51 is that I think I am not disabling it in STANDBY mode. I am experiencing quite high current during STANDBY (1.3mA) than I expect which suggests the DFLL is still running. I have been unable to determine otherwise any peripheral or I/O that is responsible for the additional sleep current. I have connected a debugger and inspected the CTRL registers for all the peripherals and they are either not enabled or do not use a GCLK where the DFLL is the source. In my use case, the DFLL is the source for GCLK0 (main clock) and GCLK2.

I read some earlier posts here regarding disabling the USB and I have confirmed that the USB is disabled (enable = 0). The RTC clocks from the OSC32KULP. SERCOM1, 2, 4 and 5 are enabled but RUNSTDBY is disabled. SERCOM3 is disabled. SERCOM0 is enabled as an I2C slave with RUNSTDBY on. The I2C bus was disconnected so there was no clocking or address match on that port. Moreover, I also did a test where I disabled SERCOM0 and it did not reduce the current.

So, I am wondering if by design, the DFLL it is not intended to stop during STANDBY? Any suggestions for why the sleep current is so high?

Some additional information.

I checked the RUNSTDBY software configuration for all the peripherals and the only places RUNSTDBY is set are the XOSC32K, I2C and USB drivers. As mentioned earlier, I have turned off the USB and confirmed with debugger that it is not enabled (and RUNSTDBY is disabled). I2C RUNSTDBY enables the Wake on Address Match during standby. As mentioned earlier, I disabled this SERCOM port.

DFLL is running in open loop mode. There is little to do to initialize the DFLL since the state of the mcu coming out of reset is to use the DFLL in open loop mode. Here is the initialization:

    uint32_t reg = OSCCTRL_DFLLCTRLB_QLDIS
#ifdef OSCCTRL_DFLLCTRLB_WAITLOCK
             | OSCCTRL_DFLLCTRLB_WAITLOCK
#endif
    ;

    OSCCTRL->DFLLCTRLB.reg = reg;
    while (OSCCTRL->DFLLSYNC.bit.DFLLCTRLB) {
    }

    OSCCTRL->DFLLCTRLA.reg = OSCCTRL_DFLLCTRLA_ENABLE;
    while (OSCCTRL->DFLLSYNC.bit.ENABLE) {
    }

    while (!OSCCTRL->STATUS.bit.DFLLRDY) {
    }

The xosc32k is enabled and set to run during standby:

    OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_ENABLE | OSC32KCTRL_XOSC32K_XTALEN
                | OSC32KCTRL_XOSC32K_EN32K | OSC32KCTRL_XOSC32K_RUNSTDBY
                | OSC32KCTRL_XOSC32K_STARTUP(6);

    while (!OSC32KCTRL->STATUS.bit.XOSC32KRDY) {
    }

    GCLK->GENCTRL[1].reg = GCLK_GENCTRL_SRC(GCLK_SOURCE_XOSC32K)
                 | GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN;

GCLK2 is connected to the DFLL. GCLK2 is intended to be the source for the USB peripheral when it is enabled.

I believe some peripheral is keeping the clock running but I have been unable to track it down.

Hi sslupsky,

I remember a while back that there was an issue with Adafruit Sleepydog library on SAMD51. The problem was that the processor wouldn't stay in deep sleep (STANDBY) and kept waking up. The culprit back then, was that the SAMD51's RUNSTDBY bit was set in the native USB peripheral.

The solution was to clear the RUNSTDBY bit in the USB module, before putting the processor to sleep:

// On the SAMD51 it's necessary to deactivate the RUNSTDBY (Run Standby) bit on the native USB
USB->DEVICE.CTRLA.bit.ENABLE = 0;         // Disable the USB peripheral
while(USB->DEVICE.SYNCBUSY.bit.ENABLE);   // Wait for synchronization
USB->DEVICE.CTRLA.bit.RUNSTDBY = 0;       // Deactivate run on standby
USB->DEVICE.CTRLA.bit.ENABLE = 1;         // Enable the USB peripheral
while(USB->DEVICE.SYNCBUSY.bit.ENABLE);   // Wait for synchronization

__DSB();       // Complete outstanding memory operations - not required for SAMD21 ARM Cortex M0+
__WFI();       // Put the SAMD51 into deep sleep, Zzzzzzzz...

I believe all other RUNSTDBY peripheral bits must be disabled, otherwise I would imagine that Adafruit's Sleepydog library for the SAMD51 wouldn't work.

I went back to the issue on Github. The problem was caused by the Adafruit's M4 bootloader setting the USB peripheral's RUNSTDBY bit in the "board_driver_usb.c" file.

The subsequent SAMD51 core code library doesn't overwrite this bootloader setting.

I checked Adafruit bootloader source code for their latest build (currently 1.6.5) and the "board_driver_usb.c" file still sets the RUNSTDBY bit:

/* Enable Run in Standby */
USB->HOST.CTRLA.bit.RUNSTDBY = true;

Hi MartinL,

Thank you for the feedback. I did review that thread where you originally described the problem. So I did ensure that the USB peripheral is disabled. I also confirmed by analyzing the USB registers with a debugger that USB is not enabled and the RUNSTDBY bit is 0.

I am not using the Adafruit sleepy dog library but I did have a look at the startup.c code in their Arduino repo. They initialize more GCLK's but otherwise, the DFLL is running open loop.

There was a strange line in the startup code:

OSCCTRL->DFLLVAL.reg = OSCCTRL->DFLLVAL.reg;
while( OSCCTRL->DFLLSYNC.bit.DFLLVAL );

I saw an earlier thread where you referred to this and mentioned it was the result of an errata. Does this refer to errata "2.8.3 DFLLVAL.FINE Value When DFLL48M Re-enabled"?

Tried closed loop mode with the DFLL. I confirmed with a debugger that the DFLL is locked and ready. The change caused the frequency to decrease a small amount which resulted in a slightly lower current.

Hi sslupsky,

Does this refer to errata "2.8.3 DFLLVAL.FINE Value When DFLL48M Re-enabled"

Yes it does.

The Microchip SAMD5x/SAMDEx Family Errata, section 2.8.3 here: https://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D5x_E5x_Family_Silicon_Errata_DS80000748L.pdf.

It appears to be the main hardware bug in the DFLL, (along with a number of status register glitches).

That line of code is certainly strange. I'm not sure what getting the DFLLVAL register to load itself is doing? Although according to the errata, it apparently works around the issue of the DFLL not reading the DFLLVAL.FINE portion of the register.

Adafruit run all their M4 boards with the 48MHz DFLL in open-loop mode, as it must be accurate enough to clock the native USB module and good enough for most user's applications. I don't think that running the DFLL in closed-loop mode really changes anything regarding low power modes, apart from a more accurate and precise clock signal.

Are you using any extenal interrupts? These can asychronously wake the processor unexpectedly.

I appreciate that it's most likely not the case, but does your board has a DotStar LED? These can take around 1mA of quiescent current when off. I've seen others have this issue with the Itsy Bitsy M4.

Hi MartinL,

The board does indeed have an LED but the current measurements I am making are during the time the LED is off (it is a discrete LED).

There are no external interrupts.

I think I have narrowed down the main source of the problem. If I disable SERCOM0 I can reduce the power consumption to about 105uA. This SERCOM port is configured to operate as an I2C slave. The slave is configured to wake the mcu on an Address Match event. It appears that this is preventing the DFLL from turning off during STANDBY.

I have a "reference" for comparison. The board I am using can be populated with either a SAMD21 or the SAMD51 MCU. So, I have two side by side with identical PCB's and identical components on the board except the MCU. I am using the same source code for the SAMD21 and the SAMD51 and compiling a different binary depending on the MCU. Both MCU's are configured to operate at 48 MHz. The STANDBY current when running from the SAMD21 is about 55uA with SERCOM0 enabled.

Hi MartinL,

The description of the RUNSTDBY is the same when comparing the SAMD21 and SAMD51 datasheets and the Sleep Mode Operation sections are the same. So, it appears they are intended to operate the same. Are you aware of any errata regarding I2C AMATCH and STANDBY mode?

There is one errata I found "2.18.10 SERCOM-I2C: I2C in Slave Mode" but that errata does not describe any issues related to STANDBY mode.

Hi sslupsky,

Not sure it has anything to do with he SERCOM peripherals. The SAMD21 and SAMD51 SERCOMs from a register and functionality perspective look practically very similar. Looks like a lot of the errata on the SAMD21's SERCOM was carried over as well.

I also had a look at the bootloader's serial equivalent of the USB driver: "board_driver_serial.c", but unlike the USB file, there's no reference to RUNSTDBY.

One way to test if the DFLL is the problem, might be to switch your main clock (MCLK) on GCLK0 over to the OSCULP32K clock source and then disable the DFLL before putting the processor to sleep. Looks like something similar is happening in the "startup.c" file, where the MCLK (GCLK0) is switched over to the OSCULP32K, just before DFLL is configured for open-loop operation.

Hi MartinL,

I think the reason the Adafruit startup.c source for GCLK0 is switched to OSCULP32K is because the DFLL is disabled shortly thereafter. A clock is required to keep the MCU going to make the changes to the DFLL and the other changes to the DPLL. Eventually, the GCLK0 is connected to the DPLL and the MCU runs at a high clock speed again.

I had considered using the OSCULP32K as a source for GCLK0 to help debug this but because of the slow clock, I am not sure it would help much. Total current consumption would go down by about 3 orders of magnitude and make it difficult to actually see the problem. I did make a small change to the frequency of the DFLL (using closed loop mode) and did notice the change in current because of the change in frequency. This was another of my observations that helped me narrow down that the DFLL was still ticking.

By disabling SERCOM0 I was able to reduce current by a little more than 10x (1300uA to 105uA). So, that tells me SERCOM0 is asserting a clock request and the clock request propagates to the DFLL during standby. The clock request keeps both the DFLL and the SERCOM port ticking.

Some old documentation published by ATMEL on the SAMD21 peripheral power consumption indicates a SERCOM port consumes about 28uA at 1 MHz. The DFLL is ticking away at 48 MHz so that actually works out to about 1300uA. So, maybe the other SERCOM ports somehow gate their clock and do not increase the current. But, it appears likely that SERCOM0 is responsible for the current I am observing during standby. So, I think I might have been incorrect to suggest the DFLL is causing the over current directly. Rather, because the DFLL is ticking at 48 MHz which is the source for the SERCOM0 peripheral clock, the port is consuming 1300uA.

So now my attention turns to why SERCOM0 is consuming so much current. That is the only port for which RUNSTDBY is enabled. On the SAMD21, the RUNSTDBY allows the Wake on Address Match interrupt to wake up the device. Since the receive register is clocked by SCL, the standby current of the SERCOM port is very low. On the SAMD51, it appears that is not the case. It appears the SERCOM port has not gated its' peripheral clock so the peripheral is consuming more power that it should.

Unfortunately, this may be leading to an outcome that exposes a show stopper limitation for me. If I cannot use the SERCOM as an I2C Slave and operate at low power during STANDBY, I will be unable to use the SAMD51. I avoided changing my MCU vendor in my latest board rev by providing an upgrade path from the SAMD21 to the SAMD51. I did that by taking advantage of the nearly identical pinout so I could gain the benefits of more FLASH and SRAM.

Unfortunately, there doesn't appear to be widespread reports of this happening and I do not think I am the first one to attempt to do this. So that is my conundrum at the moment. My instinct tells me there is a very specific problem with the SAMD51 SERCOM I2C slave Wake on Address Match but I cannot rationalize the fact that no one else appears to have reported this issue.

The other interesting tidbit is since the Microchip acquisition, there doesn't appear to have been any revisions to the SAMD51. SAMD51 has been stuck at Rev D for two or three years now. Many of the errata going back to 2018 have not been addressed. There were a small number of errata addressed between Rev A and Rev D (what happened to Rev B and C?). There are an enormous number of outstanding errata for several years now. Does that suggest Microchip is not paying attention to this family?

Hi sslupsky,

I was wondering if the issue you're encountering has something to do with the SERCOM slow clock?

Looking through the Adafruit SERCOM code in the "SERCOM.cpp" file, it appears that they're configuring the slow clock at the same speed as the core clock, at 48MHz :o :

// SPI DMA speed is dictated by the "slow clock" (I think...maybe) so
// BOTH are set to the same clock source (clk_slow isn't sourced from
// XOSC32K as in prior versions of SAMD core).
// This might have power implications for sleep code.

setClockSource(idx, clockSource, true);  // true  = core clock
setClockSource(idx, clockSource, false); // false = slow clock

Not sure why they're mentioning the SPI, or setting it to 48MHz, as according to the SAMD51 datasheet the slow clock should be from a 32k source, (maximum 12MHz) and is only really relevant to I2C SMBus timing.

Furthermore, I believe that in the SAMD21 core code the SERCOM slow clock isn't configured at all. It might be worth commenting out the second call to setClockSource() above. This is in the SERCOM::initClockNVIC() function, (in the "SERCOM.cpp" file).

Hi MartinL,

Yes, I did configure the slow clock to use the OSCULP32K oscillator.

I tried an experiment where I changed the peripheral clock for SERCOM0 to use the GCLK3 which I have connected to the OSCULP32K. It's weird, I expected to see a reduction in current when I did this but I do not.

Hi MartinL,

I have an update. There was a bug in the i2c driver so my earlier attempt to run SERCOM0 at 32 KHz was not working. I fixed that and was able to confirm the SERCOM0 clock configuration was correct using a debugger. I did notice the expected decrease in current consumption.

I was also able to confirm that the I2C port configured as a slave will operate with a 32 KHz peripheral clock. This required some additional work and my initial tests indicate the port is working. I have not yet undertaken an analysis to determine how the slower clock impacts the speed of the I2C slave bus transaction. I expect that the transaction takes longer because some of the registers (ADDR and DATA) require synchronization with the peripheral clock.

Hi MartinL,

Regarding the SERCOM slow clock, on the SAMD51, the peripheral channel (3) is also used (shared might be a better word here) by the SDHC controller and the lock timer for the DPLL.

I agree with you, the slow clock should not be running at 48 MHz. It is intended to nominally be 32 KHz to ensure correct timings. There is an errata however that indicates the SDA Hold times are incorrect in the datasheet: "2.18.6 SERCOM-I2C: SDAHOLD Timing"