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.

MSPM0G3507: TRM error in SYSPLL setup example [SLAU846B Sec 2.3.1.3.1]

Part Number: MSPM0G3507
Other Parts Discussed in Thread: MSPM0G1507, SYSCONFIG

Tool/software:

There is a mistake in the steps in the Usage Example for the SYSPLL in TRM (SLAU846B) Sec 2.3.1.3.1:

Steps (9)-(10) start the PLL, then Step (11) connects the PLL output to the HSCLK using MCLK2XVCO. But once the PLL is enabled, MCLK2XVCO can't be set [observed behavior], so the HSCLK (and thus MCLK) ends up with no clock source. The symptom is that the MCU hangs, and the debugger can't contact it. The BSL Trick is sufficient to recover it.

The solution is to perform Step (11) prior to Step (9). 

Driverlib does it that way, indeed it combines Steps (7), (8), and (11) into a single step performed prior to Step (9).

Ordinarily I wouldn't post about a simple documentation error, but this seems like a fairly significant (and non-obvious) one.

  • I just posted this to warn others. I have a workaround, so I'm fine.

  • Hi, 

    Steps (9)-(10) start the PLL, then Step (11) connects the PLL output to the HSCLK using MCLK2XVCO. But once the PLL is enabled, MCLK2XVCO can't be set [observed behavior], so the HSCLK (and thus MCLK) ends up with no clock source.

    Do you have this test code?

    I intend to try from my side.

    Regards,

    Helic

  • I don't have the code here; I'll plan to post it this evening. It's really just a step-by-step implementation of the Example. [I've observed that every PLL has something unusual somewhere, so I started simple.]

    It's fairly typical of a PLL (sub)system that some/all of the configuration is locked once the PLL starts ticking, but the TRM doesn't really mention that about SYSPLL, nor (more to the point) which particular fields are locked.

    One clue (I just noticed) is the definition of CLKSTATUS:SYSPLLBLKUPD [Ref Table 2-53], which is described as applying to all of SYSPLLCFG0/1 (and SYSPLLPARAM0/1), which would include MCLK2XVCO. [I suppose it would be "blocked" if the SYSPLL is running, but it doesn't say that either.]

    As I mentioned: I already (now) know the answer; I was just concerned about the next person who tries this.

  • Here is a pared-down version. Setting PLLFAIL to 1 will use the TRM order, and 0 the corrected order.

    Import pretty much any Example (I used gpio_toggle_output) then paste this over the main .c. In particular, avoid calling SYSCFG_DL_init() since this code assumes no one has messed with the clock.

    ///
    //      Start up the PLL
    //      No warranty, no support. I may not even exist.
    //
    #include <ti/devices/msp/msp.h>
    
    #define PLLFAIL 0           // Set to 1 to demonstrate TRM ordering
    
    //  clk.h:
    
    #define HZ  80000000UL // 32000000UL
    extern void clk_init(void);
    extern void clk_mon(void);
    
    //  clk.c:
    
    ///
    //  clk_icache()
    //  Turn the ICache on (>0) or off (=0)
    //
    static inline uint32_t
    clk_icache(uint32_t onoff)
    {
        uint32_t saved = (CPUSS->CTL & CPUSS_CTL_ICACHE_ENABLE);
        if (onoff)              // on?
            CPUSS->CTL |= CPUSS_CTL_ICACHE_ENABLE;
        else                    // off
            CPUSS->CTL &= ~CPUSS_CTL_ICACHE_ENABLE;
        return(saved);
    }
    
    ///
    //  clk_init()
    //  Make MCLK run at (HZ)
    //
    void
    clk_init(void)
    {
    #if (HZ == 32000000UL)
        //  (That was easy)
    #elif (HZ == 80000000UL)||(HZ == 64000000UL)
        uint32_t ic = clk_icache(0);            // Disable ICache for CPU_ERR_01
        SYSCTL->SOCLOCK.SYSPLLCFG0 &= ~SYSCTL_SYSPLLCFG0_SYSPLLREF_MASK;    // (3) PLLREF=0 (SYSOSC)
        SYSCTL->SOCLOCK.SYSPLLCFG1 |= SYSCTL_SYSPLLCFG1_PDIV_REFDIV2;       // (4) PLLREF/2 -> f_LOOPIN=16MHz
        SYSCTL->SOCLOCK.SYSPLLPARAM0 = FACTORYREGION->PLLSTARTUP0_16_32MHZ; // (5) f_LOOPIN = 16MHz
        SYSCTL->SOCLOCK.SYSPLLPARAM1 = FACTORYREGION->PLLSTARTUP1_16_32MHZ; // (5) f_LOOPIN = 16MHz
    #if (HZ == 80000000UL)
        SYSCTL->SOCLOCK.SYSPLLCFG1 |= ((5-1) << SYSCTL_SYSPLLCFG1_QDIV_OFS); // (6) QDIV=5 for (5*16)=80MHz
    #else   //  64MHz
        SYSCTL->SOCLOCK.SYSPLLCFG1 |= ((4-1) << SYSCTL_SYSPLLCFG1_QDIV_OFS); // (6) QDIV=4 for (4*16)=64MHz
    #endif //   HZ
        SYSCTL->SOCLOCK.SYSPLLCFG0 |= SYSCTL_SYSPLLCFG0_RDIVCLK1_CLK1DIV2|SYSCTL_SYSPLLCFG0_RDIVCLK2X_CLK2XDIV2; // (7) CLK1/2, CLK2X/2
        SYSCTL->SOCLOCK.SYSPLLCFG0 |= SYSCTL_SYSPLLCFG0_ENABLECLK1_ENABLE|SYSCTL_SYSPLLCFG0_ENABLECLK2X_ENABLE; // (8) En CLK1 and CLK2X
    #if !PLLFAIL
        SYSCTL->SOCLOCK.SYSPLLCFG0 |= SYSCTL_SYSPLLCFG0_MCLK2XVCO_ENABLE;   // (11) CLK2X->HSCLK
    #endif // PLLFAIL
        SYSCTL->SOCLOCK.HSCLKEN |= SYSCTL_HSCLKEN_SYSPLLEN_ENABLE;          // (9) Enable PLL
        while ((SYSCTL->SOCLOCK.CLKSTATUS & SYSCTL_CLKSTATUS_SYSPLLGOOD_MASK) == 0) {/*EMPTY*/;} // (10) Wait for PLLGOOD
    #if PLLFAIL
        SYSCTL->SOCLOCK.SYSPLLCFG0 |= SYSCTL_SYSPLLCFG0_MCLK2XVCO_ENABLE;   // (11) CLK2X->HSCLK
    #endif // PLLFAIL
        SYSCTL->SOCLOCK.HSCLKCFG &= ~SYSCTL_HSCLKCFG_HSCLKSEL_MASK;         // (12) HSCLKSEL=SYSPLL
        SYSCTL->SOCLOCK.MCLKCFG |= SYSCTL_MCLKCFG_USEHSCLK_ENABLE;          // (13) MCLK = HSCLK
        clk_icache(ic);             // Restore ICache
    #else
    #error "clk_init: HZ?"
    #endif // HZ
        return;
    }
    
    ///
    //  clk_mon()
    //  Put CLK_OUT (PLLOUT1) on PB11 for the scope
    //
    void
    clk_mon(void)
    {
    #define CLKOUT_PORT GPIOB
    #define CLKOUT_PIN  (1u << 11)  //  PB11
    #define CLKOUT_MUX  IOMUX_PINCM28
    #define CLKOUT_PF   4           // CLK_OUT per SLASEX6B Table 6-1
    #define CLKOUT_DIV  0           // /1 or maybe (SYSCTL_GENCLKCFG_EXCLKDIVVAL_DIV2|SYSCTL_GENCLKCFG_EXCLKDIVEN_ENABLE) // /2
        IOMUX->SECCFG.PINCM[CLKOUT_MUX] = (IOMUX_PINCM_PC_CONNECTED | IOMUX_PINCM_INENA_ENABLE | CLKOUT_PF);
        SYSCTL->SOCLOCK.GENCLKCFG |= SYSCTL_GENCLKCFG_EXCLKSRC_SYSPLLOUT1|CLKOUT_DIV;
        SYSCTL->SOCLOCK.GENCLKEN |= SYSCTL_GENCLKEN_EXCLKEN_ENABLE;
        return;
    }
    
    //  main.c:
    
    ///
    //  gpio_powerup()
    //
    void
    gpio_powerup(void)
    {
        //  Power up all the GPIOs
        GPIOA->GPRCM.PWREN = (GPIO_PWREN_KEY_UNLOCK_W | GPIO_PWREN_ENABLE_ENABLE);
        GPIOB->GPRCM.PWREN = (GPIO_PWREN_KEY_UNLOCK_W | GPIO_PWREN_ENABLE_ENABLE);
        return;
    }
    
    ///
    //  main()
    //
    int
    main(void)
    {
        gpio_powerup();
        clk_mon();              // Something to watch on the scope (PB11)
        clk_init();
        while (1)
        {
            __WFI();
        }
        /*NOTREACHED*/
        return(0);
    }
    

  • That's great!

    Of course, if you don't have such a demo, I will try to setup one.

    It's better to have one, this will save more time!

    Thanks for your feedback~

  • Um, are you looking for something different from what I posted (above)?

  • Hi ,

    I tried your suggestion, "Setting PLLFAIL to 1 will use the TRM order," in the DL_SYSCTL_configSYSPLL() function. With this change, I am getting a better result for my MSPM0G1507 frequency out-of-tolerance issue.

    Please check the following link for more details: MSPM0G1507 - Frequency Out of Tolerance Issue

    Could you confirm if this modification is necessary when using SYSPLL, or is there an alternative approach to ensure better accuracy?

  • I'm not quite sure what change you made (and to what), but I think it's interesting that it had an effect on your results. [I haven't commented there since Helic seems to be far ahead of me.]

    One difference I noticed is that in your case Sysconfig decided on QDIV=(10-1) and RDIV2X=(4-1) while I was using QDIV=(5-1) and RDIV2X=(2-1).

    Each of these takes SYSOSC=32MHz and puts out MCLK=80MHz, but yours runs the clocks twice as fast {VCO=160MHz,CLK2X=320MHz} vs {VCO=80MHz, CLK2X=160MHz}. This isn't out of spec, but maybe there's a phenomenon in there somewhere?

  • Hi Bruce,

    Thank you for clarification, 

     I suspect something might be missing or incorrectly sequenced in the "DL_SYSCTL_configSYSPLL()" function implementation, which could be affecting the SYSPLL initialization process.

  • With respect to the topic here, configSYSPLL is using the correct order (different from that in the TRM). 

    What change did you make that seemed to improve your results?

  • He reset the device after device completely powering up, he get a better clock accuracy.

  • We reinitialize the clock function, yeah after soft reset also I am getting better accuracy 

     // for any clock change the SYSOSC must be at 32MHz and PLL should be disabled
        DL_SYSCTL_setSYSOSCFreq(DL_SYSCTL_SYSOSC_FREQ_BASE);
        DL_SYSCTL_enableSYSOSCFCLExternalResistor();
        DL_SYSCTL_disableHFXT();
        DL_SYSCTL_disableSYSPLL();
    
        
     
            static const DL_SYSCTL_SYSPLLConfig SYSPLLConfig_80Mhz = 
            {
                .inputFreq              = DL_SYSCTL_SYSPLL_INPUT_FREQ_16_32_MHZ,
                .rDivClk2x              = 3,
                .rDivClk1               = 0, 
                .rDivClk0               = 0,
                .enableCLK2x            = DL_SYSCTL_SYSPLL_CLK2X_ENABLE,
                .enableCLK1             = DL_SYSCTL_SYSPLL_CLK1_DISABLE, 
                .enableCLK0             = DL_SYSCTL_SYSPLL_CLK0_DISABLE,
                .sysPLLMCLK             = DL_SYSCTL_SYSPLL_MCLK_CLK2X,
                .sysPLLRef              = DL_SYSCTL_SYSPLL_REF_SYSOSC,
                .qDiv                   = 9,
                .pDiv                   = DL_SYSCTL_SYSPLL_PDIV_2
            };
            /*PLL setup and Clock dividers*/
            DL_SYSCTL_configSYSPLL((DL_SYSCTL_SYSPLLConfig *) &SYSPLLConfig_80Mhz);
            DL_SYSCTL_setMCLKSource(SYSOSC, HSCLK, DL_SYSCTL_HSCLK_SOURCE_SYSPLL); 
            DL_SYSCTL_setULPCLKDivider(DL_SYSCTL_ULPCLK_DIV_2);
            DL_SYSCTL_configSYSPLL((DL_SYSCTL_SYSPLLConfig *) &SYSPLLConfig_80Mhz);

    I have attached Code snippet for your reference.

  • Please check the following two scenarios:

    1️⃣ With Default Clock Initialization Code
    Result: Clock is not accurate (Set: 2kHz, get: 2025-2030Hz tested with 5 sample boards).

    void DL_SYSCTL_configSYSPLL(DL_SYSCTL_SYSPLLConfig *config)
    {
        /* PLL configurations are retained in lower reset levels. Set default
         * behavior of disabling the PLL to keep a consistent behavior regardless
         * of reset level. */
        DL_SYSCTL_disableSYSPLL();
    
        /* Check that SYSPLL is disabled before configuration */
        while ((DL_SYSCTL_getClockStatus() & (DL_SYSCTL_CLK_STATUS_SYSPLL_OFF)) !=
               (DL_SYSCTL_CLK_STATUS_SYSPLL_OFF)) {
            ;
        }
    
        // set SYSPLL reference clock
        DL_Common_updateReg(&SYSCTL->SOCLOCK.SYSPLLCFG0,
            ((uint32_t) config->sysPLLRef), SYSCTL_SYSPLLCFG0_SYSPLLREF_MASK);
    
        // set predivider PDIV (divides reference clock)
        DL_Common_updateReg(&SYSCTL->SOCLOCK.SYSPLLCFG1, ((uint32_t) config->pDiv),
            SYSCTL_SYSPLLCFG1_PDIV_MASK);
    
        // save CPUSS CTL state and disable the cache
        uint32_t ctlTemp = DL_CORE_getInstructionConfig();
        DL_CORE_configInstruction(DL_CORE_PREFETCH_ENABLED, DL_CORE_CACHE_DISABLED,
            DL_CORE_LITERAL_CACHE_ENABLED);
    
        // populate SYSPLLPARAM0/1 tuning registers from flash, based on input freq
        SYSCTL->SOCLOCK.SYSPLLPARAM0 =
            *(volatile uint32_t *) ((uint32_t) config->inputFreq);
        SYSCTL->SOCLOCK.SYSPLLPARAM1 =
            *(volatile uint32_t *) ((uint32_t) config->inputFreq + (uint32_t) 0x4);
    
        // restore CPUSS CTL state
        CPUSS->CTL = ctlTemp;
    
        // set feedback divider QDIV (multiplies to give output frequency)
        DL_Common_updateReg(&SYSCTL->SOCLOCK.SYSPLLCFG1,
            ((config->qDiv << SYSCTL_SYSPLLCFG1_QDIV_OFS) &
                SYSCTL_SYSPLLCFG1_QDIV_MASK),
            SYSCTL_SYSPLLCFG1_QDIV_MASK);
    
        // write clock output dividers, enable outputs, and MCLK source to SYSPLLCFG0
        DL_Common_updateReg(&SYSCTL->SOCLOCK.SYSPLLCFG0,
            (((config->rDivClk2x << SYSCTL_SYSPLLCFG0_RDIVCLK2X_OFS) &
                 SYSCTL_SYSPLLCFG0_RDIVCLK2X_MASK) |
                ((config->rDivClk1 << SYSCTL_SYSPLLCFG0_RDIVCLK1_OFS) &
                    SYSCTL_SYSPLLCFG0_RDIVCLK1_MASK) |
                ((config->rDivClk0 << SYSCTL_SYSPLLCFG0_RDIVCLK0_OFS) &
                    SYSCTL_SYSPLLCFG0_RDIVCLK0_MASK) |
                config->enableCLK2x | config->enableCLK1 | config->enableCLK0 |
                (uint32_t) config->sysPLLMCLK),
            (SYSCTL_SYSPLLCFG0_RDIVCLK2X_MASK | SYSCTL_SYSPLLCFG0_RDIVCLK1_MASK |
                SYSCTL_SYSPLLCFG0_RDIVCLK0_MASK |
                SYSCTL_SYSPLLCFG0_ENABLECLK2X_MASK |
                SYSCTL_SYSPLLCFG0_ENABLECLK1_MASK |
                SYSCTL_SYSPLLCFG0_ENABLECLK0_MASK |
                SYSCTL_SYSPLLCFG0_MCLK2XVCO_MASK));
    
        // enable SYSPLL
        SYSCTL->SOCLOCK.HSCLKEN |= SYSCTL_HSCLKEN_SYSPLLEN_ENABLE;
    
        // wait until SYSPLL startup is stabilized
        while ((DL_SYSCTL_getClockStatus() & SYSCTL_CLKSTATUS_SYSPLLGOOD_MASK) !=
               DL_SYSCTL_CLK_STATUS_SYSPLL_GOOD) {
            ;
        }
    }
    

    2️⃣ With Default Code + Your Suggested Changes

    void DL_SYSCTL_configSYSPLL(DL_SYSCTL_SYSPLLConfig *config)
    {
        /* PLL configurations are retained in lower reset levels. Set default
         * behavior of disabling the PLL to keep a consistent behavior regardless
         * of reset level. */
        DL_SYSCTL_disableSYSPLL();
    
        /* Check that SYSPLL is disabled before configuration */
        while ((DL_SYSCTL_getClockStatus() & (DL_SYSCTL_CLK_STATUS_SYSPLL_OFF)) !=
               (DL_SYSCTL_CLK_STATUS_SYSPLL_OFF)) {
            ;
        }
    
        // set SYSPLL reference clock
        DL_Common_updateReg(&SYSCTL->SOCLOCK.SYSPLLCFG0,
            ((uint32_t) config->sysPLLRef), SYSCTL_SYSPLLCFG0_SYSPLLREF_MASK);
    
        // set predivider PDIV (divides reference clock)
        DL_Common_updateReg(&SYSCTL->SOCLOCK.SYSPLLCFG1, ((uint32_t) config->pDiv),
            SYSCTL_SYSPLLCFG1_PDIV_MASK);
    
        // save CPUSS CTL state and disable the cache
        uint32_t ctlTemp = DL_CORE_getInstructionConfig();
        DL_CORE_configInstruction(DL_CORE_PREFETCH_ENABLED, DL_CORE_CACHE_DISABLED,
            DL_CORE_LITERAL_CACHE_ENABLED);
    
        // populate SYSPLLPARAM0/1 tuning registers from flash, based on input freq
        SYSCTL->SOCLOCK.SYSPLLPARAM0 =
            *(volatile uint32_t *) ((uint32_t) config->inputFreq);
        SYSCTL->SOCLOCK.SYSPLLPARAM1 =
            *(volatile uint32_t *) ((uint32_t) config->inputFreq + (uint32_t) 0x4);
    
        // restore CPUSS CTL state
        CPUSS->CTL = ctlTemp;
    
        // set feedback divider QDIV (multiplies to give output frequency)
        DL_Common_updateReg(&SYSCTL->SOCLOCK.SYSPLLCFG1,
            ((config->qDiv << SYSCTL_SYSPLLCFG1_QDIV_OFS) &
                SYSCTL_SYSPLLCFG1_QDIV_MASK),
            SYSCTL_SYSPLLCFG1_QDIV_MASK);
    
        // write clock output dividers, enable outputs, and MCLK source to SYSPLLCFG0
        DL_Common_updateReg(&SYSCTL->SOCLOCK.SYSPLLCFG0,
            (((config->rDivClk2x << SYSCTL_SYSPLLCFG0_RDIVCLK2X_OFS) &
                 SYSCTL_SYSPLLCFG0_RDIVCLK2X_MASK) |
                ((config->rDivClk1 << SYSCTL_SYSPLLCFG0_RDIVCLK1_OFS) &
                    SYSCTL_SYSPLLCFG0_RDIVCLK1_MASK) |
                ((config->rDivClk0 << SYSCTL_SYSPLLCFG0_RDIVCLK0_OFS) &
                    SYSCTL_SYSPLLCFG0_RDIVCLK0_MASK) |
                config->enableCLK2x | config->enableCLK1 | config->enableCLK0 |
                (uint32_t) config->sysPLLMCLK),
            (SYSCTL_SYSPLLCFG0_RDIVCLK2X_MASK | SYSCTL_SYSPLLCFG0_RDIVCLK1_MASK |
                SYSCTL_SYSPLLCFG0_RDIVCLK0_MASK |
                SYSCTL_SYSPLLCFG0_ENABLECLK2X_MASK |
                SYSCTL_SYSPLLCFG0_ENABLECLK1_MASK |
                SYSCTL_SYSPLLCFG0_ENABLECLK0_MASK |
                SYSCTL_SYSPLLCFG0_MCLK2XVCO_MASK));
    
        // enable SYSPLL
        SYSCTL->SOCLOCK.HSCLKEN |= SYSCTL_HSCLKEN_SYSPLLEN_ENABLE;
    
        // wait until SYSPLL startup is stabilized
        while ((DL_SYSCTL_getClockStatus() & SYSCTL_CLKSTATUS_SYSPLLGOOD_MASK) !=
               DL_SYSCTL_CLK_STATUS_SYSPLL_GOOD) {
            ;
        }
         SYSCTL->SOCLOCK.SYSPLLCFG0 |= SYSCTL_SYSPLLCFG0_MCLK2XVCO_ENABLE;   // (11) CLK2X->HSCLK: this is the only change I added extra
    }
    

    Result: Get a better clock accuracy (Set: 2kHz, get: 1994-2015Hz tested with 5 sample boards).

    Based on this, I have doubts about whether the initialization sequence is correct. Could you please review it?

  • Another point from my side.

    Need to confirm whether this is caused by SYSPLL or SYSOSC.

    Could you please test SYSOSC without SYSPLL? Set CPU running at 32MHz directly source from SYSOSC.

    I guess this is caused by SYSPLL configuration sequence, not about SYSOSC accuracy.

    Based on this, I have doubts about whether the initialization sequence is correct. Could you please review it?

    I will review this, but this may take a few days to finish.

    And It's great to receive such feedback from you.

  • guess this is caused by SYSPLL configuration sequence, not about SYSOSC accuracy.

    I also doubt the SYSPLL initialization sequence, not the SYSOSC accuracy.

  • I also doubt the SYSPLL initialization sequence, not the SYSOSC accuracy.

    Agree with you.

  • This is a somewhat surprising result. What I observed -- indeed the premise of this thread -- was that once the PLL was started (PLLEN) the MCLK2XVCO bit (probably all of PLLCFG0) was locked and couldn't be updated. The implication would be that your additional line of code would have no effect. (Also: by then MCLK2XVCO was already set.) Is it possible that the Read (of the Read/Modify/Write) had an effect? [It's not supposed to, but I've been doing this stuff too long to automatically reject this idea.]

    If you look near the end of the code I posted above, there's a clk_mon() function you might want to steal. The CLK_OUT function can monitor SYOSC or PLLOUT1 [you'll have to enable that one in Sysconfig] on PB11.

  • Here are some reference from TRM, but these seems not detailed enough:

    Enabling and Disabling the SYSPLL
    After configuration, enable the SYSPLL by setting the SYSPLLEN bit in the HSCLKEN register. Before enabling
    the SYSPLL, make sure that the SYSPLL is in a disabled state by verifying that the SYSPLLOFF bit in the
    CLKSTATUS register is set. After the SYSPLL is enabled, application software must not disable it until the
    SYSPLLGOOD or SYSPLLOFF bit is set in the CLKSTATUS register, indicating that the SYSPLL transitioned to
    a stable active state or a stable dead state. When the SYSPLL is enabled, the SYSPLL reference clock selection
    must not be changed.

    Quite busy now, but I am sure that I will verify this in next few days.