TeensyTimerTool

Hm.. not sure if this is the case here - and i can't find the document.
NXP adds this to every interrupt (and maybe more places - don't remember and I havn't the code anymore)
Code:
[B]/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F, Cortex-M7, Cortex-M7F Store immediate overlapping[/B]
[B] exception return operation might vector to incorrect interrupt */[/B]
[B]#if defined __CORTEX_M && (__CORTEX_M == 4U || __CORTEX_M == 7U)[/B]
[B] __DSB();[/B]
[B]#endif
[/B]
Anyway - it does not help here.
 
I'll check tomorrow, I stumbled over this when adding the dsb, didn't do a lot of experiments with dsb on the others.
 
Here the disassembly of the isr. Doesn't look suspicious at a first glance...

Code:
void isr()
{
    TMR1_CSCTRL0 &= ~TMR_CSCTRL_TCF1; // clear the timer flag
      60:	ldr	r2, [pc, #28]	; (80 <isr()+0x20>)
      62:	ldrh	r3, [r2, #20]
      64:	bic.w	r3, r3, #16
      68:	lsls	r3, r3, #16
      6a:	lsrs	r3, r3, #16
      6c:	strh	r3, [r2, #20]
    asm volatile("dsb"); // needed without Serial.print() in _isr
      6e:	dsb	sy

    loopCntIsr = loopCnt;
      72:	ldr	r3, [pc, #16]	; (84 <isr()+0x24>)
      74:	ldr	r1, [r3, #0]
      76:	ldr	r2, [pc, #16]	; (88 <isr()+0x28>)
      78:	str	r1, [r2, #0]
    loopCnt = 0;
      7a:	movs	r2, #0
      7c:	str	r2, [r3, #0]

                         // asm volatile("dmb");
                         // asm volatile("isb"); // xxx
}
      7e:	bx	lr
      80:	.word	0x401dc000
      84:	.word	0x20000d5c
      88:	.word	0x20000d60

0000008c <setup>:
 
e9005: Core: Store immediate overlapping exception return operation might vector to incorrect interrupt
Description:Arm Errata 838869:
Store immediate overlapping exception return operation might vector to
incorrect interrupt
Affects: Cortex-M4, Cortex-M4F
Fault Type: Programmer Category B Rare
Fault
Status: Present in: r0p0, r0p1 Open.

The Cortex-M4 includes a write buffer that permits execution to continue while a store is
waiting on the bus. Under specific timing conditions, during an exception return while thisbuffer is still in use by a store instruction, a late change in selection of the next interrupt to betaken might result in there being a mismatch between the interrupt acknowledged by theinterrupt controller and the vector fetched by the processor.Configurations AffectedThis erratum only affects systems where writeable memory locations can exhibit more thanone wait state.Workaround:For software not using the memory protection unit, this erratum can be worked around bysetting DISDEFWBUF in the Auxiliary Control Register.In all other cases, the erratum can be avoided by ensuring a DSB occurs between the storeand the BX instruction. For exception handlers written in C, this can be achieved by insertingthe appropriate set of intrinsics or inline assembly just before the end of the interrupt function,for example:

ARMCC:
...
__schedule_barrier();
__asm{DSB};
__schedule_barrier();}

GCC:
...__asm volatile (“dsb 0xf” ::: “memory”);

This is from an old document, whicht mentions M4 only - but it's the same errata.
From: https://www.nxp.com.cn/docs/en/errata/IMX8MDQLQ_0N14W.pdf
 
It is stable - for me - under the following condition:

1. Tims code with dsb
2. Connect Teensy with button pressed - upload occurs
-> data stable

Not with:
3. Re-Upload during program run
-> data UNstable

Tim, Luni, can you confirm this? If yes, it might a problem with the bootloader?

 
Can confirm this (err... no... sometimes it helps, sometimes not

I give up for today and get me some beer :)
 
Spamming here... :)
Code:
  TMR1_COMP10 = 29'296; // 25ms
                TMR1_CMPLD10 = 29'296[COLOR=#ff0000]+1[/COLOR];
Does help greatly - in most cases.

But.. it in one of 10 resets the result is even more weird. The loopcount looks almost like random, then.
 
The systick does not have a "dsb", too" :)

Tims, code gets pretty stable with the DSB immedately after
TMR1_CSCTRL0 &= ~TMR_CSCTRL_TCF1; // clear the timer flag


Code:
void isr()
{
  TMR1_CSCTRL0 &= ~TMR_CSCTRL_TCF1; // clear the timer flag
   asm volatile("dsb");   // needed without Serial.print() in _i

So, yes, dsb helps - but it should be near resetting the timer-clear-flag

EDIT: NO ?!?! It worked ONE time - after the reset, all is as before ?!?

I did that too - moved the dsb around - moving BEFORE The timer clear results in NO PRINT from while(1).

The problem is the non-atomic INCREMENT of the Count in loop() and then the ZERO in the _isr().

If the _isr() just sets the FLAG value: loopCntIsr = loopCnt[0];
It works well and right after multiple resets.

The increment interrupted mid process ends up overwriting the _isr()'s ZERO with the in process count increment

Flagging in the _isr() and having the loop() ZERO when seen might lose ONE count - but it will complete properly as long as the _isr doesn't fire before the flag is checked and acted on.

Below is current code with added copies showing what numbers are where - uncomment the indicated _isr() line and disparity in the various elements is shown as the process is interrupted.

Code:
volatile uint32_t loopCnt[3];
volatile uint32_t loopCntIsr = 0;

void isr()
{
	TMR1_CSCTRL0 &= ~TMR_CSCTRL_TCF1; // clear the timer flag
	//Serial.print(".");

	loopCntIsr = loopCnt[0];
	loopCnt[2] = loopCnt[0];
[B][COLOR="#FF0000"]	//loopCnt[0] = 0;   // UNCOMMENT THIS LINE TO SEE various NUMBERS as this _ISR interrupts the process[/COLOR][/B]

	asm volatile("dsb");   // needed without Serial.print() in _isr
	//asm volatile("dmb");
	// asm volatile("isb"); // xxx
}

void setup()
{
	while (!Serial && millis() < 4000 );
	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
	attachInterruptVector(IRQ_QTIMER1, isr);

	CCM_CCGR6 |= CCM_CCGR6_QTIMER1(CCM_CCGR_ON);
	TMR1_CTRL0 = 0x0000;
	TMR1_LOAD0 = 0x0000;
	TMR1_COMP10 = 29'296; // 25ms
	              TMR1_CMPLD10 = 29'296;
	TMR1_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(0b1111) | TMR_CTRL_LENGTH;
	TMR1_CSCTRL0 &= ~(TMR_CSCTRL_TCF1);
	TMR1_CSCTRL0 |= TMR_CSCTRL_TCF1EN;
	NVIC_ENABLE_IRQ(IRQ_QTIMER1);

	while (1)
	{
		loopCnt[0]++;
		loopCnt[1] = loopCnt[0];
		if ( 0!=loopCntIsr ) {
			Serial.printf("%lu\t %lu\t#2=%lu\t", loopCnt[0], loopCnt[1], loopCnt[2]);
			Serial.printf("%lu\t %lu\n", loopCntIsr, micros());
			loopCntIsr = 0;
	[B]loopCnt[0] = 0;[/B] // doing this here might miss a count - but it will make the right thing happen as long as the counter _isr() doesn't hit.
		}
	}
}

void loop()
{
}
 
Last edited:
More ODDNESS? Not helpful - just shows there is something unhandled or errata going on?

When writing the micros() using the systick millis and ARM_DWT_CYCCNT combined Frank pointed to this that worked ( in delay.c ) - it clearly made the right diff and worked where both of the enclosed values are updated on millisecond tick:
Code:
	do {
		__LDREXW(&systick_safe_read);
		smc = systick_millis_count;
		scc = systick_cycle_count;
	} while ( __STREXW(1, &systick_safe_read));

It resolves out to: If you do code inside the do{}while and an _isr fires, it gets redone. And reading indicated the var "systick_safe_read" isn't really related it is just a var for ref.

Great so the same should apply here - so I can move the CNT zero back to the _isr - but fails in two ways.

This leaves the count ever increasing - it never gets zeroed by the _isr() - where : callback_safe_update is a dummy var like in micros()
Code:
		do {
			__LDREXW(&callback_safe_update);
			loopCnt++;
		} while ( __STREXW(1, &callback_safe_update));

Doing this shows the CNT as 1 with dsb in _isr {or occasionally 2 when the dsb is not used}:
Code:
		do {
			__LDREXW(&loopCnt);
			loopCnt++;
		} while ( __STREXW(1, &loopCnt));

That is the way ATOMIC code is to be handled? In testing the micros() code before it was used it seemed to work as it does now.

Here is the code with above extra values and prints gone since I expected it to work after adding it:
Code:
volatile uint32_t loopCnt;
volatile uint32_t loopCntIsr = 0;
[B]#include "arm_math.h"	// micros() synchronization
uint32_t callback_safe_update;
[/B]
void isr()
{
	TMR1_CSCTRL0 &= ~TMR_CSCTRL_TCF1; // clear the timer flag
	//Serial.print(".");

	loopCntIsr = loopCnt;

	asm volatile("dsb");   // needed without Serial.print() in _isr
	//asm volatile("dmb");
	// asm volatile("isb"); // xxx
}

void setup()
{
	while (!Serial && millis() < 4000 );
	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
	attachInterruptVector(IRQ_QTIMER1, isr);

	CCM_CCGR6 |= CCM_CCGR6_QTIMER1(CCM_CCGR_ON);
	TMR1_CTRL0 = 0x0000;
	TMR1_LOAD0 = 0x0000;
	TMR1_COMP10 = 29'296; // 25ms
	              TMR1_CMPLD10 = 29'296;
	TMR1_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(0b1111) | TMR_CTRL_LENGTH;
	TMR1_CSCTRL0 &= ~(TMR_CSCTRL_TCF1);
	TMR1_CSCTRL0 |= TMR_CSCTRL_TCF1EN;
	NVIC_ENABLE_IRQ(IRQ_QTIMER1);

	while (1)
	{
[B]		do {
			__LDREXW(&loopCnt);
			loopCnt++;
		} while ( __STREXW(1, &loopCnt));
[/B]/*
		do {
			__LDREXW(&callback_safe_update);
			loopCnt++;
		} while ( __STREXW(1, &callback_safe_update));
*/
		if ( 0 != loopCntIsr ) {
			Serial.printf("%lu\t %lu\n", loopCntIsr, micros());
			loopCntIsr = 0;
		}
	}
}

void loop()
{
}

And a really funny thing if these refs "__LDREXW(&loopCnt);" use __LDREXW(&loopCntIsr); AND the "." print in _isr is turned on the '.' does not print, but it does with the other code as shown. ALSO the TyComm USB output goes VERY jumpy rather than streaming out.
 
The problem is the non-atomic INCREMENT of the Count and the ZERO in the _isr().

You probably meant to say 'The problem is the non-atomic INCREMENT... in the while

Because, fortunately, this seems to be the reason. The problem is not the ISR at all, it does what it is supposed to, but due to the non atomic increment in the while loop the ++loopCnt can get interrupted and then of cause it produces nonsense. So, all in all this is just a pathological example where both the ISR and the main code try to change the same variable which of course is calling for trouble.... Wow, doing that stuff since the early 80ties and still stumble over such beginner bugs :-(

This code works as expected.
Code:
volatile uint32_t loopCnt = 0;
volatile uint32_t loopCntIsr = 0;

void isr()
{
    TMR1_CSCTRL0 &= ~TMR_CSCTRL_TCF1; // clear the timer flag

    loopCntIsr = loopCnt;
    loopCnt = 0;

    asm volatile("dsb"); // needed without Serial.print() in _isr
}

void setup()
{   
    attachInterruptVector(IRQ_QTIMER1, isr);

    CCM_CCGR6 |= CCM_CCGR6_QTIMER1(CCM_CCGR_ON);
    TMR1_CTRL0 = 0x0000;
    TMR1_LOAD0 = 0x0000;
    TMR1_COMP10 = 29'296; // 25ms
    TMR1_CMPLD10 = 29'296;
    TMR1_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(0b1111) | TMR_CTRL_LENGTH;
    TMR1_CSCTRL0 &= ~(TMR_CSCTRL_TCF1);
    TMR1_CSCTRL0 |= TMR_CSCTRL_TCF1EN;
    NVIC_ENABLE_IRQ(IRQ_QTIMER1);

    while (1)
    {
        noInterrupts(); //<================
        ++loopCnt;
        interrupts()    //<================

        if (0 != loopCntIsr)
        {
            Serial.printf("%d\t %d\n", loopCntIsr, micros());
            loopCntIsr = 0;
        }
    }
}

void loop()
{
}

Thanks for helping sorting that out
 
Last edited:
So, since this is sorted out, here the TeensyTimerTool way to do it. (more or less back to #3 where the discussion started).
Tested it for TCK, TMR1 and GPT1, works without issues now.

Code:
#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;

volatile uint32_t loopCnt = 0;

void isr()
{    
    Serial.println(loopCnt);
    loopCnt = 0;    
}

Timer t1 (TMR1);
//Timer t1 (GPT1);
//Timer t1 (TCK);

void setup()
{
    t1.beginPeriodic(isr,25'000);
}

void loop()
{
    noInterrupts();  // not needed for the TCK timer of course
    ++loopCnt;
    interrupts()
}
 
You probably meant to say 'The problem is the non-atomic INCREMENT... in the while
...

Thanks for helping sorting that out

I certainly meant for it to be read that way :) - it's the only inc in the code … post updated

Looks like very good work, and a great example of TeensyTimerTool#callback-functions sending in params! Also good info and examples in the README.md!

This line isn't needed/used in p#39: volatile uint32_t loopCntIsr = 0;

Since it was leftover set loopCntIsr in _isr() - moved the print into the loop() and the cnt dif is 124742 _isr print versus 115915 test loopCntIsr and loop print - UNLESS noInterrupts() pair removed then : 130401

Grabbed updated github with only count print in loop() printing well:
151475 with Timer t1 (TMR1);
and
136339 with Timer t1 (GPT1);


The TCK and GPT1 creep up on us when micros() printed - the TMR1 is rock steady with this in loop: Serial.printf("%d\t %d\n", loopCntIsr, micros());

Still puzzled what is going on with the __LDREXW do/while not fixing the increment in the while(1) not being atomic? - and how the second instance prevents any increment ? Also no inc++ using loopCnt - maybe because they are volatile?
 
Still puzzled what is going on with the __LDREXW do/while not fixing the increment in the while(1) not being atomic? - and how the second instance prevents any increment ?

While reading up what that __LDREXW is supposed to do (__LDREXW seems to be made for multitasking stuff?), I stumbled over the standard c++ 'atomic' header which seems to be more suitable. With this you can easily define atomic variables like this:

Code:
#include <atomic>

//volatile uint32_t loopCnt;
volatile std::atomic<uint32_t> loopCnt;   // variable which can be changed atomically 

void isr()
{
    TMR1_CSCTRL0 &= ~TMR_CSCTRL_TCF1; // clear the timer flag

    Serial.println(loopCnt);
    loopCnt = 0;

    asm volatile("dsb");  // not really needed here since the println is slow enough to make sure the flag is deleted....
}

void setup()
{
    attachInterruptVector(IRQ_QTIMER1, isr);

    CCM_CCGR6 |= CCM_CCGR6_QTIMER1(CCM_CCGR_ON);
    TMR1_CTRL0 = 0x0000;
    TMR1_LOAD0 = 0x0000;
    TMR1_COMP10 = 29'296; // 25ms
    TMR1_CMPLD10 = 29'296;
    TMR1_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(0b1111) | TMR_CTRL_LENGTH;
    TMR1_CSCTRL0 &= ~(TMR_CSCTRL_TCF1);
    TMR1_CSCTRL0 |= TMR_CSCTRL_TCF1EN;
    NVIC_ENABLE_IRQ(IRQ_QTIMER1);   

    while(1) 
    {          
        loopCnt++;     // no interrupt disabling required...
    }
}

void loop()
{

}

Might come in handy for some real life applications.
 
Something odd on your end ? T_4.0 at 600 MHz?

This code - need to add volatile given _isr() calling:
Code:
volatile unsigned loopCnt = 0; 

void callback()
{
    Serial.print("loop()/sec=");
    Serial.print(loopCnt);
    Serial.print(" us=");
    Serial.println(micros());
    loopCnt = 0;
}

IntervalTimer t; 

void setup()
{
    t.begin(callback, 1'000'000);
}

void loop()
{
    loopCnt++;
}
...

Back in post https://forum.pjrc.com/threads/59112-TeensyTimerTool?p=227079&viewfull=1#post227079 the code above added volatile.
I thought when accessing or changing a volatile variable in the main loop() one needs to disable interrupts like:

Code:
void loop()
{
  noInterrupts();
  loopCnt++;
  interrupts();
}

what gives?
 
Last edited:
(__LDREXW seems to be made for multitasking stuff?), I stumbled over the standard c++ 'atomic' header which seems to be more suitable.

Wow, the c++'atomic' header is a good find - I should read more about c++ and use it.. :)

It uses ldrex/strex , too (see the disassembly) - but is easier to use.
 
Sorry I am late to the party here. I've used c++ atomic's when I was using teensythreads and they worked great. I did try the following this morning:
Code:
#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;

#include <atomic>

//volatile uint32_t loopCnt;
volatile std::atomic<uint32_t> loopCnt;   // variable which can be changed atomically 

void isr()
{    
    Serial.println(loopCnt);
    loopCnt = 0;    
}

Timer t1 (TMR1);
//Timer t1 (GPT1);
//Timer t1 (TCK);

void setup()
{
    t1.beginPeriodic(isr, 25'000);
}

void loop()
{
    ++loopCnt;
}
and got the following results:
Code:
131556
131556
131556
with or without adding "dsb" to the ISR.

With the example in post #41 I get:
Code:
833195
833193
833194

With the nointerrupt example I get:
Code:
137590
137591
137591

So my question is what should the results be for the timer? or What I am doing missing/doing wrong here?
 
Wow, the c++'atomic' header is a good find
Yes, that seems to be very useful. Would be interesting to see if there is a performance penalty. Good thing is that it works locally and doesn't stop the whole interrupt system if it needs to wait for an unlock.
 
@mjs513: The example in #41 uses a tight while(1) loop to increment the counter. This will be much quicker of course than doing it in loop. To compare performance it might be best to do the tests with the while(1) loop. I'll give it a try in a minute
 
Code:
TMR & std::atomic               833188  
TMR & disabled interrupts      1153647

GPT & disabled interrupts      1153644
GPT & std::atomic               833184

Direct Register version         833195
Direct Register version        1153658

All measured with the tight while(1) loop. Results are very consistent (which is expected since the ISRs are called only once per 25ms). In this simple example the atomic version is significantly slower (generates a lot of code) But this may(?) be different in a real life application.
 
@luni
Thanks for checking. As you said at the results are consistent when you use the tight while loop.

By the way it really is a great lib you put together. One question I have is if there is a way for it to know if a timer is already in use by another library so the user knows it has to be fixed?
 
Back
Top