divisor value in clock_init() call takes no effect!?

CircuitPython on hardware including Adafruit's boards, and CircuitPython libraries using Blinka on host computers.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
opal
 
Posts: 27
Joined: Wed Nov 29, 2017 12:05 pm

divisor value in clock_init() call takes no effect!?

Post by opal »

Hello dear circuitpython maintainers,

I needed to adjust the clock source to RTC and stumbled across a confusing thing in samd21_clocks.c file.

There is this function:

Code: Select all

void clock_init(void)
{
    init_clock_source_osc8m();
    if (board_has_crystal())
        init_clock_source_xosc32k();
    else
        init_clock_source_osc32k();
    enable_clock_generator(0, GCLK_GENCTRL_SRC_DFLL48M_Val, 1);
    enable_clock_generator(1, GCLK_GENCTRL_SRC_DFLL48M_Val, 150);
    init_clock_source_dfll48m();
    if (board_has_crystal())
        enable_clock_generator(2, GCLK_GENCTRL_SRC_XOSC32K_Val, 32);
    else
        enable_clock_generator(2, GCLK_GENCTRL_SRC_OSC32K_Val, 32);
}
So, the last parameter to enable_clock_generator() funciton call is a "divisor" (what you put in GCLK.GENDIV:DIV field, page 115 of the SAMD21 datasheet).

So, I looked what that function does:

Code: Select all

void enable_clock_generator(uint8_t gclk, uint32_t source, uint16_t divisor) {
    uint32_t divsel = 0;
    if (gclk == 2 && divisor > 31) {
        divsel = GCLK_GENCTRL_DIVSEL;
        for (int i = 15; i > 4; i++) {
            if (divisor & (1 << i)) {
                divisor = i - 1;
                break;
            }
        }
    }
    GCLK->GENDIV.reg = GCLK_GENDIV_ID(gclk) | GCLK_GENDIV_DIV(divisor);
    GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(gclk) | GCLK_GENCTRL_SRC(source) | divsel | GCLK_GENCTRL_OE | GCLK_GENCTRL_GENEN;
    while (GCLK->STATUS.bit.SYNCBUSY != 0) {}
}
The divisor parameter will be only considered for the clock generator 2. For all other generators the divisor will be divsel which is 0.

So, at least two of four calls to that function make no effect:

Code: Select all

    enable_clock_generator(0, GCLK_GENCTRL_SRC_DFLL48M_Val, 1);
    enable_clock_generator(1, GCLK_GENCTRL_SRC_DFLL48M_Val, 150);
Am I missing something?
And yes, I checked - that is not another leftover (of which there is tons in this code). clock_init() will be called from supervisor/port.c:port_init()

So, if someone has problems with clock signal being too slow/fast than expected (announced?), RTC drift or timer interval other than set up - check the generator you are driving your peripherals from. Only generator 2 will work adequately.

regards

User avatar
tannewt
 
Posts: 3304
Joined: Thu Oct 06, 2016 8:48 pm

Re: divisor value in clock_init() call takes no effect!?

Post by tannewt »

DIVSEL doesn't enable or disable the divisor. Instead, it changes it from linear to power of two. So, the code works just fine.

GCLK2 has a special case because its divisor is only 5 bits. Most other clocks are 8 bits and therefore a value of 150 works just fine without DIVSEL. (Details under the GENDIV register info in the datasheet.)

User avatar
opal
 
Posts: 27
Joined: Wed Nov 29, 2017 12:05 pm

Re: divisor value in clock_init() call takes no effect!?

Post by opal »

Hi, you are right! Sorry for the confusion. Thanks for the explanations.

I haven't noticed the prescaler for generator 2 to be special.

I wander what is the reason it is the way it is. The only possible reason I could think of is that you prescale very low frequencies with it. But I can't find anything in datasheet saying the generator 2 is dedicated to 32K osc or something like that.

Anyway - it has nothing to do with the firmware and this topic. Thanks once again for taking the time and answering.

Regards

Locked
Please be positive and constructive with your questions and comments.

Return to “Adafruit CircuitPython”