T 4.1 nanosecond counter?

ekartgo

Member
I'm converting C code from a PI 4 to a Teensy 4.1. For the PI I use this to get the full time to nanoseconds:

Code:
struct timespec gettime_now;
long long  f1_on_time;

clock_gettime(CLOCK_REALTIME, &gettime_now);
f1_on_time = (((long long)gettime_now.tv_sec*1000000000)+(long long)gettime_now.tv_nsec);

Is there a nanosecond resolution counter I can access that i can combine with others to get a rollover proof time counter?

Although the above is given in unixtime, I don't need any reference to real world time, just a continuous counter with high resolution.
 
Last edited:
ARM processors have a running counter of clock cycles. For T4.x, all you have to do is read ARM_DWT_CYCCNT. Here is an example program that shows how to use it. Cycles are converted to nanoseconds using macro F_CPU, which is nominally 600M for T4.x, but changes when you modify clock frequency via the Arduino IDE.

Code:
#define CYCLES_TO_NS(cycles)  ((cycles)*(1E9/F_CPU))

uint32_t ms;
uint32_t cycles;

void setup() {
  Serial.begin( 115200 );
  while (!Serial) {}
  ms = millis();
  cycles = ARM_DWT_CYCCNT;        // ARM cycles at F_CPU
}

// every 1000 ms, read cycle counter and compute delta cycles and ns
void loop() {
  uint32_t new_ms = millis();
  if ((new_ms - ms) >= 1000) {
    uint32_t new_cycles = ARM_DWT_CYCCNT;
    uint32_t delta_cycles = new_cycles - cycles;
    uint32_t delta_ns = CYCLES_TO_NS(delta_cycles); 
    Serial.printf( "cycles = %1lu   ns = %1lu\n", delta_cycles, delta_ns );
    ms = new_ms;
    cycles = new_cycles;
  }
}
 
Awesome - thanks everyone. I had found bits and pieces but not that reference to put it all together.

Moving to a micro-controller environment with a single program running is a bit like going back to my early programming on my TI-99/4A in K&R C (super macro assembler).
One program with nothing (much) to get in the way and you have to be very efficient.

This is to avoid the Pi system interrupt issues I was facing, even when the code was running on its own core giving me mostly regular timings - but enough slow loops above 25uS to cause issues at high speed (motor):

< 500 ns count is 0
< 750 ns count is 987494
< 1000 ns count is 666
< 1500 ns count is 29
< 2000 ns count is 738
< 2500 ns count is 3
< 5000 ns count is 204
< 10 us count is 68
< 25 us count is 9910
< 50 us count is 848
< 100 uS count is 40
< 250 uS count is 0
< 500 uS count is 0

Bryan
 
Below is the code that resolves micros() given the last milli second timer update, and the cycle count stored then.

If the math were bumped to all 64 bit and the scaling to nano adjusted from scale_cpu_cycles_to_microseconds it would track with millis, and not roll over after 71 minutes.

With only 600 cpu cycles per 1,000 ns it won't be able to count/see every one.

The two values in the do{}while(); are an interrupt safe/accepting read to get the last millis count and the ARM_cyccnt at that time.

As written this code takes ~36 to ~38 cpu cycles to run, adding other 64 bit math will extend that a bit.

Code:
// Returns the number of microseconds since your program started running.
// This 32 bit number will roll back to zero after about 71 minutes and
// 35 seconds.  For a simpler way to build delays or timeouts, consider
// using elapsedMicros.
uint32_t micros(void)
{
	uint32_t smc, scc;
	do {
		__LDREXW(&systick_safe_read);
		smc = systick_millis_count;
		scc = systick_cycle_count;
	} while ( __STREXW(1, &systick_safe_read));
	uint32_t cyccnt = ARM_DWT_CYCCNT;
	asm volatile("" : : : "memory");
	uint32_t ccdelta = cyccnt - scc;
	uint32_t frac = ((uint64_t)ccdelta * scale_cpu_cycles_to_microseconds) >> 32;
	if (frac > 1000) frac = 1000;
	uint32_t usec = 1000*smc + frac;
	return usec;
}
 
Thanks, that looks like it will work great. I've integrated the code changes and will give it a test and post the results after the board gets here next week.

I also have a second version of the code now using both hardware interrupts and timer interrupts instead of running a hard loop. It will be interesting to see which works better.

Bryan
 
Board arrived today!

The timing looks much better. Pleased so far.

< 500 ns count is 999999
< 750 ns count is 1
< 1000 ns count is 0
< 1500 ns count is 0
....
Sum of counts is 1000000

Bryan
 
Back
Top