Skip to content

Implementing a high resolution Teensy clock

luni64 edited this page Oct 23, 2020 · 3 revisions

General

As shown here it is easy to set up the chrono::system_clock for using any convenient time base. Instead of the millis() based example we could as well use the cycle counter as underlying time base. Here we already discussed how to use it to define a custom duration type which we could use for the high resolution clock. However, we need to take the following into account:

  1. The 32bit cycle counter of a T4.x @600MHz quickly rolls over (2^32 / 600MHz = 7.6s).
  2. The chrono::system_clock class uses nanosecond based uint64_t time ticks. the cycle counter runs at 1/F_BUS = 1.667ns (T4.x, 600MHz). Mapping this to nanoseconds is possible of course but would introduce a significant rounding error for small times.
  3. To be able to track absolute times a synchronization to the Teensy RTC would useful.

Extending the cycle counter to 64bit

To achieve useful rollover times we first need to extend the 32bit cycle counter to 64bit. This will give us a roll over time of 2^64 / 600MHz = 975 years which seems to be sufficient. Extending is not difficult:

uint32_t oldLow = ARM_DWT_CYCCNT;
uint32_t curHigh = 0;

uint64_t getCnt()
{
    uint32_t curLow = ARM_DWT_CYCCNT;
    if (curLow < oldLow) // we had a roll over
    {
        curHigh++;
    }
    oldLow = curLow;
    uint64_t curVal = ((uint64_t)curHigh << 32) | curLow;

    return curVal;
}

The code above checks if the current value of the cycle counter is smaller than the last one we have seen. This is only possible if the counter rolled over in between and we have to increment the high word its high word by 1 before we return the combined 64bit value.

Obviously, this pattern only works if we can ensure that getCnt() is called at least once per ARM_DWT_CYCCNT overflow period (7.6s @600MHz). We could use one of the Interval timers to call it periodically but, using one of the 4 timers would be too expensive.

Back | Fun with modern c++

The periodic timer of the real time clock

Fortunately there is a seldom/never used periodic timer in the real time clock module of the T4.x processors which is perfectly suited to call getCnt() say once per second. Since the RTC registers are already set up by Teensyduino enabling the perioding interrupt is straight forward:

// disable periodic interrupt
SNVS_HPCR &= ~SNVS_HPCR_PI_EN;
while ((SNVS_HPCR & SNVS_HPCR_PI_EN)){}   // spin until PI_EN is reset...

// set interrupt frequency to 1Hz
SNVS_HPCR = SNVS_HPCR_PI_FREQ(0b1111);

// enable periodic interrupt
SNVS_HPCR |= SNVS_HPCR_PI_EN;
while (!(SNVS_HPCR & SNVS_HPCR_PI_EN)){}  // spin until PI_EN is set...

// attach a callback
attachInterruptVector(IRQ_SNVS_IRQ, SNVS_isr);
NVIC_SET_PRIORITY(IRQ_SNVS_IRQ, 255);     // lowest priority
NVIC_ENABLE_IRQ(IRQ_SNVS_IRQ);

And here the simple callback we attached to the interrupt:

void SNVS_isr(void)
{
   SNVS_HPSR |= 0b11; // reset interrupt flag
   getCnt()           // dummy call to check for overflow of the cycle counter
   asm("dsb");        // wait until flag is synced on busses to prevent double calls of the isr
}

Back | Fun with modern c++

Implementing the Teensy clock

TBD

Usage examples

TBD

Clone this wiki locally