Forum Rule: Always post complete source code & details to reproduce any issue!
Page 2 of 4 FirstFirst 1 2 3 4 LastLast
Results 26 to 50 of 83

Thread: TeensyTimerTool

  1. #26
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,209
    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:
    /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F, Cortex-M7, Cortex-M7F Store immediate overlapping
     exception return operation might vector to incorrect interrupt */
    #if defined __CORTEX_M && (__CORTEX_M == 4U || __CORTEX_M == 7U)
     __DSB();
    #endif
    
    Anyway - it does not help here.

  2. #27
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,209
    Luni - is this with all timers (that use interrupts)?

  3. #28
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    723
    I'll check tomorrow, I stumbled over this when adding the dsb, didn't do a lot of experiments with dsb on the others.

  4. #29
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    723
    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>:

  5. #30
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,209
    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/errat...DQLQ_0N14W.pdf

  6. #31
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,209
    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?


  7. #32
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,209
    err... no... sometimes it helps, sometimes not.

  8. #33
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    723
    Can confirm this (err... no... sometimes it helps, sometimes not

    I give up for today and get me some beer :-)

  9. #34
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,209
    Spamming here...
    Code:
      TMR1_COMP10 = 29'296; // 25ms
                    TMR1_CMPLD10 = 29'296+1;
    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.

  10. #35
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,209
    Prost - hau wech Ich hole mir auch ein Bier. Feierabend!

  11. #36
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,598
    Quote Originally Posted by Frank B View Post
    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];
    	//loopCnt[0] = 0;   // UNCOMMENT THIS LINE TO SEE various NUMBERS as this _ISR interrupts the process
    
    	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;
    	loopCnt[0] = 0; // 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 by defragster; 01-22-2020 at 07:14 AM.

  12. #37
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,598
    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;
    #include "arm_math.h"	// micros() synchronization
    uint32_t callback_safe_update;
    
    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)
    	{
    		do {
    			__LDREXW(&loopCnt);
    			loopCnt++;
    		} while ( __STREXW(1, &loopCnt));
    /*
    		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.

  13. #38
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    723
    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 by luni; 01-22-2020 at 06:26 AM.

  14. #39
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    723
    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()
    }

  15. #40
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,598
    Quote Originally Posted by luni View Post
    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?

  16. #41
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    723
    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.

  17. #42
    Senior Member
    Join Date
    Dec 2015
    Location
    LA
    Posts
    153
    Quote Originally Posted by defragster View Post
    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...l=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 by bicycleguy; 01-22-2020 at 06:54 PM. Reason: spelling

  18. #43
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    723
    I would say, you should not attempt to do a non atomic change of a variable in the main code when you access the same variable in an ISR. Disabling interrupts or using an 'atomic variable' (see #41) solves the originally observed issue.
    You probably missed https://forum.pjrc.com/threads/59112...l=1#post227237 which implements exactly your suggestion :-)

  19. #44
    Senior Member
    Join Date
    Dec 2015
    Location
    LA
    Posts
    153
    luni, well that was embarrassing

    thanks

  20. #45
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,209
    Quote Originally Posted by luni View Post
    (__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.

  21. #46
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    4,589
    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?

  22. #47
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    723
    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.

  23. #48
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    723
    @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

  24. #49
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    723
    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.

  25. #50
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    4,589
    @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?

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •