PDA

View Full Version : Teensy-3 Clock Settings



spacewrench
12-24-2012, 07:22 AM
I'm trying to ensure that my clock settings are working as expected (I've stolen Paul's setup code and moved things around to fit nicely into the FreeRTOS framework). Things look good setting the PLL to 96MHz and then dividing down to 24MHz or 48MHz core & bus clocks, but when I execute the following code at the end of the clock setup, the core clock appears to jump to 96MHz. (I'm checking with a NOP delay loop: 2400 delay cycles followed by a pin toggle, so I expect 100uS pulses at 24MHz, 50uS at 48MHz.) However, if I execute the code in red at the bottom of the function, I get 25uS pulses --> 96MHz.


extern "C" void
startup_late_hook( void )
{
// start in FEI mode (default mode: FLL Engaged Internal)
// C1[CLKS] = 00, C1[IREFS] = 1, C6[PLLS] = 0

// enable internal load capacitors for crystal
OSC_CR = ((1 << OSC_CR_SC8P_SHIFT) | (1 << OSC_CR_SC2P_SHIFT));

// enable osc, 8-32 MHz range, low power mode
MCG_C2 = (MCG_C2_RANGE(2) |
(1 << MCG_C2_EREFS_SHIFT));

// switch to crystal as clock source, FLL input = 16 MHz / 512 == 31.25kHz
MCG_C1 = (MCG_C1_CLKS(2) |
MCG_C1_FRDIV(4));

// wait for crystal oscillator to begin
while ((MCG_S & (1 << MCG_S_OSCINIT_SHIFT)) == 0) ;

// wait for FLL to use oscillator
while ((MCG_S & (1 << MCG_S_IREFST_SHIFT)) != 0) ;

// wait for MCGOUT to use oscillator
while ((MCG_S & MCG_S_CLKST_MASK) != MCG_S_CLKST(2)) ;

// now we're in FBE (FLL-Bypassed External) mode
// config PLL input for 16 MHz Crystal / 4 = 4 MHz
MCG_C5 = MCG_C5_PRDIV(3);

// config PLL for 96 MHz output
MCG_C6 = ((1 << MCG_C6_PLLS_SHIFT) |
MCG_C6_VDIV(0));

// wait for PLL to start using xtal as its input
while (!(MCG_S & (1 << MCG_S_PLLST_SHIFT))) ;

// wait for PLL to lock
while (!(MCG_S & (1 << MCG_S_LOCK_SHIFT))) ;

// now we're in PBE (PLL-Bypassed External) mode
#if F_CPU == 96000000
// config divisors: 96 MHz core, 48 MHz bus, 24 MHz flash
SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIV1(0) | SIM_CLKDIV1_OUTDIV2(1) | SIM_CLKDIV1_OUTDIV4(3);
#elif F_CPU == 48000000
// config divisors: 48 MHz core, 48 MHz bus, 24 MHz flash
SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIV1(1) | SIM_CLKDIV1_OUTDIV2(1) | SIM_CLKDIV1_OUTDIV4(3);
#elif F_CPU == 24000000
// config divisors: 24 MHz core, 24 MHz bus, 24 MHz flash
SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIV1(3) | SIM_CLKDIV1_OUTDIV2(3) | SIM_CLKDIV1_OUTDIV4(3);
#else
# error "Error, F_CPU must be 96000000, 48000000, or 24000000"
#endif

// switch to PLL as clock source, FLL input = 16 MHz / 512
MCG_C1 = MCG_C1_CLKS(0) | MCG_C1_FRDIV(4);

// wait for PLL clock to be used
while ((MCG_S & MCG_S_CLKST_MASK) != MCG_S_CLKST(3)) ;

#if 0
// now we're in PEE (PLL-Engaged External) mode
// configure USB for 48 MHz clock
SIM_CLKDIV2 = SIM_CLKDIV2_USBDIV(1); // USB = 96 MHz PLL / 2

//USB uses PLL clock, trace is CPU clock, CLKOUT=OSCERCLK0
SIM_SOPT2 = (0x00000000 | // reserved bits
(1 << SIM_SOPT2_USBSRC_SHIFT) |
(1 << SIM_SOPT2_PLLFLLSEL_SHIFT) |
(1 << SIM_SOPT2_TRACECLKSEL_SHIFT) |
(6 << 5) | // CLKOUTSEL = OSCERCLK0
0);
#endif
}
Any idea what I'm doing wrong? (The RCGC peripheral enabling happens after the clock setting, if it matters. And I'm not actually using the USB peripheral, although I've tried turning it on after setting the clock.)

Thanks!

spacewrench
02-10-2015, 03:21 PM
Rats, I figured this out a couple of years ago, but never wrote down the answer so I had to figure it out again just now. This time, though... ;-)

I think I was confused by monitoring artifacts. I used a short delay loop / toggle GPIO to figure out what the clock speed is. But the Flash clock is slower than the CPU clock. So...if the delay loop fits in CPU cache, it runs at CPU speed. If it doesn't, it runs at Flash speed. I think there are also peripheral-bus-speed effects (for example, if the peripheral bus can't send the toggle commands to the GPIO as quickly as the CPU is generating them.)

Here are examples of delay/toggle loop code that give different electrical signals depending on clock speed (i.e., for a given CPU clock, these routines produce different pin outputs):


for ( ; ; ) {
GPIOC_PTOR = 1 << 5; // toggle LED
for (unsigned i = 0; i < 24000; ++i) __asm__( "nop" );
}


for ( ; ; ) {
GPIOC_PTOR = 1 << 5; // toggle LED
for unsigned i = 0; i < 80; ++i) __asm__( "nop;nop;nop; ...100 NOPs...; nop; nop" );
}

The second one is slower because all the NOPs don't fit in CPU cache, so it runs at Flash speed. (The number of loop iterations is different from the number of NOPs in each case because the one-NOP-per-loop has a decrement, a test and a branch with each NOP. The 100-NOP loop only has dec/test/branch every 100 NOPs.)

PaulStoffregen
02-10-2015, 04:23 PM
There's now a FASTRUN keyword you can put on functions, causing them to be allocated in zero-wait RAM.