This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Need to disabled missing clock detect in Piccolo PLL_setup() function in pll.c

Other Parts Discussed in Thread: MOTORWARE

Hi Trey,

In the Piccolo LaunchPad F28027 and F28069 code, the newer Motorware style pll.c module contains a function called PLL_setup().  This function seems to emulate much of the behavior of the older style code in the InitPll() function from F2806x_SysCtrl.c.  The PLL_setup() function behaves well when used after setting up the CLK module IF an internal oscillator 1 is selected as the clock source.  However, when we use code to set an EXTERNAL oscillator clock source using the XCLKIN pin (GPIO19 on out F28069), then the subsequent PLL_setup() call will crash when it invokes the final call to PLL_setDivideSelect().

Our older code for configuring the clock and PLL for an External clock source using ExtOscSel() function in the F2806x_SysCtrl.c module works just fine. 

//---------------------------------------------------------------------------
// Example: ExtOscSel:
//---------------------------------------------------------------------------
// This function switches to External oscillator and turns off all other clock
// sources to minimize power consumption.

void ExtOscSel (void)  {
     EALLOW;
     SysCtrlRegs.XCLK.bit.XCLKINSEL = 1;       // 1-GPIO19 = XCLKIN, 0-GPIO38 = XCLKIN
     SysCtrlRegs.CLKCTL.bit.XTALOSCOFF = 1;    // Turn on XTALOSC
     SysCtrlRegs.CLKCTL.bit.XCLKINOFF = 0;     // Turn on XCLKIN
     SysCtrlRegs.CLKCTL.bit.OSCCLKSRC2SEL = 0; // Switch to external clock
     SysCtrlRegs.CLKCTL.bit.OSCCLKSRCSEL = 1;  // Switch from INTOSC1 to INTOSC2/ext clk
     SysCtrlRegs.CLKCTL.bit.WDCLKSRCSEL = 0;   // Clock Watchdog off of INTOSC1 always
     SysCtrlRegs.CLKCTL.bit.INTOSC2OFF = 1;    // Turn off INTOSC2
     SysCtrlRegs.CLKCTL.bit.INTOSC1OFF = 0;     // Leave INTOSC1 on
     EDIS;
}

I suspect that the issue we encounter when PLL_setDivideSelect() crashes when called from PLL_setup() is that the code to temporarily disable the missing clock detect circuit while waiting for the PLL to lock up was not added to the new PLL_setup() function.

Here is the existing PLL_setup() code:

void PLL_setup(PLL_Handle pllHandle, const PLL_ClkFreq_e clkMult, const PLL_DivideSelect_e divSelect)
{

    if(PLL_getClkStatus(pllHandle) == PLL_ClkStatus_Missing)
    {
        // The clock is missing so we cannot setup the PLL correctly
        asm(" ESTOP0");
    }
    
    // Set divider to max value (/4) for safety
    PLL_setDivideSelect(pllHandle, PLL_DivideSelect_ClkIn_by_4);
    
    // Set the desired multiplier
    PLL_setClkFreq(pllHandle, clkMult);
    
    while(PLL_getLockStatus(pllHandle) != PLL_LockStatus_Done)
    {
    }
    
    // Set the desired divider
    PLL_setDivideSelect(pllHandle, divSelect);
    
} // end of PLL_setup() function

I suggest that this function needs to have the PLL_disableClkDetect() and PLL_enableClkDetect() calls added as below:

void PLL_setup(PLL_Handle pllHandle, const PLL_ClkFreq_e clkMult, const PLL_DivideSelect_e divSelect)
{

    if(PLL_getClkStatus(pllHandle) == PLL_ClkStatus_Missing)
    {
        // The clock is missing so we cannot setup the PLL correctly
        asm(" ESTOP0");
    }
    
    // Set divider to max value (/4) for safety
    // DIVSEL MUST be 0 before PLLCR can be changed from
    // 0x0000. It is set to 0 by an external reset XRSn
    PLL_setDivideSelect(pllHandle, PLL_DivideSelect_ClkIn_by_4);
    
    // Before setting PLLCR turn off missing clock detect logic
    PLL_disableClkDetect(pllHandle);

    // Set the desired multiplier
    PLL_setClkFreq(pllHandle, clkMult);
    
    // Wait for the PLL lock bit to be set.
    while(PLL_getLockStatus(pllHandle) != PLL_LockStatus_Done)
    {
    }
    
    // After the PLL has locked, we reenable missing clock detect logic
    PLL_enableClkDetect(pllHandle);

    // Set the desired divider
    PLL_setDivideSelect(pllHandle, divSelect);
    
} // end of PLL_setup() function

Compare this against the older InitPll() function:

void InitPll(Uint16 val, Uint16 divsel)
{
   volatile Uint16 iVol;

   // Make sure the PLL is not running in limp mode
   if (SysCtrlRegs.PLLSTS.bit.MCLKSTS != 0)
   {
      EALLOW;
      // OSCCLKSRC1 failure detected. PLL running in limp mode.
      // Re-enable missing clock logic.
      SysCtrlRegs.PLLSTS.bit.MCLKCLR = 1;
      EDIS;
      // Replace this line with a call to an appropriate
      // SystemShutdown(); function.
     __asm("        ESTOP0");     // Uncomment for debugging purposes
   }

   // DIVSEL MUST be 0 before PLLCR can be changed from
   // 0x0000. It is set to 0 by an external reset XRSn
   // This puts us in 1/4
   if (SysCtrlRegs.PLLSTS.bit.DIVSEL != 0)
   {
       EALLOW;
       SysCtrlRegs.PLLSTS.bit.DIVSEL = 0;
       EDIS;
   }

   // Change the PLLCR
   if (SysCtrlRegs.PLLCR.bit.DIV != val)
   {

      EALLOW;
      // Before setting PLLCR turn off missing clock detect logic
      SysCtrlRegs.PLLSTS.bit.MCLKOFF = 1;
      SysCtrlRegs.PLLCR.bit.DIV = val;
      EDIS;

      // Optional: Wait for PLL to lock.
      // During this time the CPU will switch to OSCCLK/2 until
      // the PLL is stable.  Once the PLL is stable the CPU will
      // switch to the new PLL value.
      //
      // This time-to-lock is monitored by a PLL lock counter.
      //
      // Code is not required to sit and wait for the PLL to lock.
      // However, if the code does anything that is timing critical,
      // and requires the correct clock be locked, then it is best to
      // wait until this switching has completed.

      // Wait for the PLL lock bit to be set.

      // The watchdog should be disabled before this loop, or fed within
      // the loop via ServiceDog().

      // Uncomment to disable the watchdog
      DisableDog();

      while(SysCtrlRegs.PLLSTS.bit.PLLLOCKS != 1)
      {
          // Uncomment to service the watchdog
          // ServiceDog();
      }

      EALLOW;
      SysCtrlRegs.PLLSTS.bit.MCLKOFF = 0;
      EDIS;
    }

    // If switching to 1/2
    if((divsel == 1)||(divsel == 2))
    {
        EALLOW;
        SysCtrlRegs.PLLSTS.bit.DIVSEL = divsel;
        EDIS;
    }

    // If switching to 1/1
    // * First go to 1/2 and let the power settle
    //   The time required will depend on the system, this is only an example
    // * Then switch to 1/1
    if(divsel == 3)
    {
        EALLOW;
        SysCtrlRegs.PLLSTS.bit.DIVSEL = 2;
        DELAY_US(50L);
        SysCtrlRegs.PLLSTS.bit.DIVSEL = 3;
        EDIS;
    }
}

Notice that the older code disables and reenables the value of the SysCtrlRegs.PLLSTS.bit.MCLKOFF bit while waiting for the PLL to lock.

Incidently, my new code to setup the internal or external clock source and the PLL looks like the following (if you are interested in testing an external clock). We use a TCXO (temp-compensated external oscillator) on our custom board for an external clock source tied to GPIO19.

// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks

    // Perform basic system initialization    
    WDOG_disable(myWDog);
    CLK_enableAdcClock(myClk);
    (*Device_cal)();

#if CLK_INTERNAL == 1
    //Select the internal oscillator 1 as the clock source
    // CLK_setOscSrc(myClk, CLK_OscSrc_Internal);     // old code
    CLK_enableOsc2(myClk);                            // Turn on INTOSC2                       
    CLK_setOsc2Src(myClk, CLK_Osc2Src_External);      // Switch to INTOSC2                     
    CLK_disableClkIn(myClk);                          // Turn off XCLKIN                       
    CLK_disableCrystalOsc(myClk);                     // Turn off XTALOSC                      
    CLK_setOscSrc(myClk, CLK_OscSrc_Internal);        // Switch to Internal Oscillator 2       
    CLK_setWatchDogSrc(myClk, CLK_WdClkSrc_IntOsc1);  // Clock Watchdog off of INTOSC1 always  
    CLK_enableOsc1(myClk);                            // Leave INTOSC1 on                      
#elif CLK_EXTERNAL == 1
    CLK_setXclkinPin(myClk, XCLKIN_GPIO19);           // Select either GPIO19 or GPIO38 for XCLKIN
    CLK_disableCrystalOsc(myClk);                     // Turn off XTALOSC                         
    CLK_enableClkIn(myClk);                           // Turn on XCLKIN                          
    CLK_setOsc2Src(myClk, CLK_Osc2Src_External);      // Switch to external clock
    CLK_setOscSrc(myClk, CLK_OscSrc_External);        // Switch from INTOSC1 to INTOSC2/ext clk
    CLK_setWatchDogSrc(myClk, CLK_WdClkSrc_IntOsc1);  // Clock Watchdog off of INTOSC1 always    
    CLK_disableOsc2(myClk);                           // Turn off INTOSC2                        
    CLK_enableOsc1(myClk);                            // Leave INTOSC1 on                        
#endif

    // Setup the PLL for x18 /2 which will yield 90Mhz = 10Mhz * 18 / 2
    PLL_setup(myPll, PLL_ClkFreq_90_MHz, PLL_DivideSelect_ClkIn_by_2);
    
    // Disable the PIE and all interrupts
    PIE_disable(myPie);
    PIE_disableAllInts(myPie);
    CPU_disableGlobalInts(myCpu);
    CPU_clearIntFlags(myCpu);
    

Can you perhaps confirm this issue if using an external oscillator source?

Best regards,

Gordon Finlay

  • Hi Trey,

    A quick update. I found out why my call to PLL_Setup is crashing. If the system is configured via preprocessor switch CLK_INTERNAL for an internal clock source (OSCCLK frequency is 10MHz), then the PLLCR multiplier value of 18 is correct for a desired SYSCLKOUT frequency of 90MHz (10MHz *18 / 2) assuming that we use DIVSEL set for divide by 2. However for the external clock case where CLK_EXTERNAL==1,, although all the CLK_xxxx calls for changing the clock source to XCLKIN are correct, the call to PLL_setup() is just plain wrong - and not just the multiplier.

    It is not just a case of using a multiplier value PLLCR = 1 and DIVSEL set for /1.  If the PLL section is enabled, the maximum legal external oscillator frequency value is 30MHz. In our case, the custom board's XCLKIN external oscillator frequency is 90MHz. According to the datasheet for the F28069, if the XCLKIN frequency is between 30MHz and 90MHz, the PLL must be disabled (i.e. PLLSTS.PLLOFF = 1 and the multiplier PLLCR=0x0000 for bypass). I replaced the PLL_Setup code with the correct calls to disable the PLL entirely, and the clock initialization completes the transition to the XCLKIN source properly without crashing. 

    I should note though that the existin PLL_Setup() routine implementation is still incorrect when it fails to disable the Missing Clock Detect by setting/clearing the PLLSTS MCLKOFF bit temporarily while the PLL waits to lock.  So my proposed change to PLL_disable/enableClkDetect() are still applicable (even though my call to PLL_Setup for the external clock case was wrong).

    Best Regards,

    Gordon