luni
Well-known member
Yes, but beware, looking at the STL code with unprotected eyes may cause serious injury as they sayI should read more about c++ and use it..
Last edited:
Yes, but beware, looking at the STL code with unprotected eyes may cause serious injury as they sayI should read more about c++ and use it..
But this may(?) be different in a real life application.
Thanks!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?
Timer t1(TMR1), t2(TMR1), t3(TMR1), t4(TMR1), t5(TMR1);
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.
136315 8750038 loopstrex=1
136308 8775039
136315 8800039 loopstrex=1
[B] 272624 8825040
[/B]136311 8850039
136315 8875039 loopstrex=1
[B] 272625 8900040[/B]
136311 8925039
136315 8950039
136316 8975039
136316 9000039
136315 9025040 loopstrex=1
136308 9050040
136315 9075040
136316 9100040
136316 9125040
136315 9150040 loopstrex=1
136308 9175040
#include "arm_math.h" // micros() synchronization
uint32_t callback_safe_update;
volatile uint32_t loopCnt;
#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;
volatile uint32_t loopCntIsr = 0;
uint32_t loopstrex = 0;
void isr()
{
loopCntIsr = loopCnt;
//Serial.print(".");
loopCnt = 0;
asm volatile("dsb"); // xxx
}
Timer t1 (TMR1);
//Timer t1 (GPT1); // stable us
//Timer t1 (TCK);
void setup()
{
t1.beginPeriodic(isr, 25000);
}
void loop()
{
#if 0
loopCnt++;
#else
do {
__LDREXW(&callback_safe_update);
loopCnt++;
loopstrex++;
} while ( __STREXW(1, &callback_safe_update));
#endif
if ( loopCntIsr ) {
if ( loopCntIsr >= 160000 ) Serial.printf("\t");
Serial.printf("%d\t%d", loopCntIsr, micros());
if ( loopstrex > loopCntIsr ) Serial.printf("\tloopstrex=%d",loopstrex-loopCntIsr);
Serial.println();
loopstrex=0;
loopCntIsr = 0;
}
}
That's the important part I suppose - the ldrex/strex blocks work - BUT any direct interaction with an _isr() and shared vars [even volatile] requires critical thought - as with any _isr() data.
The ldrex/strex block generally has low/minimal overhead UNLESS interrupted ( check out micros() it runs in average 38 or less cycles thanks to Paul saving 2-4 cycles in TD 1.49 ).
[U]Similar code (prior post?) with [B]volatile std::atomic<uint32_t> loopCnt;[/B] runs at only 104K versus 133K here[/U] when using even this complex ldrex/strex block.
And as noted that 'atomic' covers only one variable - not a series of operations. BTW: ldrex/strex is supported on M4/T_3.x but NOT M0/T_LC
loopstrex = 0;
do {
__LDREXW(&callback_safe_update);
if ( loopstrex != 0 && loopCntIsr > 1 ) { // repeat entry and the _isr that fired was
Serial.printf("\t\t[[loopCnt=%lu__%lu]]", loopCnt, loopCntIsr);
loopCnt = 1;
}
else
loopCnt++;
loopstrex++;
} while ( __STREXW(1, &callback_safe_update));
133880 61725265
133879 61750265
133879 61775265
133879 61800265 [[loopCnt=0__133879]]
133879 61825269 loopstrex=2 [[loopCnt=1__133853]]
133853 61850269 loopstrex=2
133853 61875265
133879 61900266
133879 61925266
133879 61950266
133879 61975266 [[loopCnt=0__133879]]
133879 62000269 loopstrex=2 [[loopCnt=1__133853]]
133853 62025269 loopstrex=2
133853 62050266
133879 62075266
133879 62100266
133879 62125266
133879 62150266 [[loopCnt=0__133879]]
133879 62175270 loopstrex=2
133853 62200267 [[loopCnt=133879__133878]]
133878 62225272 loopstrex=2
133845 62250267
133879 62275267
#include "arm_math.h" // micros() synchronization
uint32_t callback_safe_update;
volatile uint32_t loopCnt;
uint32_t newValue;
#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;
volatile uint32_t loopCntIsr = 0;
uint32_t loopstrex = 0;
void isr()
{
loopCntIsr = loopCnt;
//Serial.print(".");
loopCnt = 0;
asm volatile("dsb"); // xxx
}
//Timer t1(TMR1);
Timer t1 (GPT1); // stable us
//Timer t1 (TCK);
void setup()
{
t1.beginPeriodic(isr, 25000);
}
void loop()
{
#define VER 3
#if (VER == 1)
loopCnt++;
#elif (VER == 2)
do
{
__LDREXW(&callback_safe_update);
loopCnt++;
loopstrex++;
} while (__STREXW(1, &callback_safe_update));
#elif (VER == 3)
do
{
newValue = __LDREXW(&loopCnt) + 1;
} while (__STREXW(newValue, &loopCnt));
#endif
if (loopCntIsr)
{
if (loopCntIsr >= 160000)
Serial.printf("\t");
Serial.printf("%d\t%d", loopCntIsr, micros());
if (loopstrex > loopCntIsr)
Serial.printf("\tloopstrex=%d", loopstrex - loopCntIsr);
Serial.println();
loopstrex = 0;
loopCntIsr = 0;
}
}
uint16_t reload = t > 0xFFFF ? 0xFFFF : (uint16_t)t - 1;
Checked it with 32ms which should give a even tick count. It still creeps a little bit. I found that the calculated reload ticks are off by one, fixed it, now it gives exactly the right frequency. I'll update the git repo this evening.
(line 16 of TMRchannel.h should read (note the -1)
Code:uint16_t reload = t > 0xFFFF ? 0xFFFF : (uint16_t)t - 1;
Thanks for spotting this
do
{
newValue = __LDREXW(&loopCnt) + 1;
} while (__STREXW(newValue, &loopCnt));
The c++ atomic adds code to the reads, too. In the ISR there are some "dmb".
You might want to take a look at the disassembly (can't post it now, I'm away from my arduino workplace at home)
I'm not sure if they are really needed - But the gcc folks for sure had a reason to do it this way.
Shortest execution time for dmb is 0(zero) cycles. It depends on the queue.
edit: sorry, not reads - I think it was resetting the count to 0
149943 446151905
149943 446176906
299886 446201907
449825 446226907
149938 446251906
149943 446276906
void isr()
{
loopCntIsr = loopCnt; // This var is vulnerable - iff the isr() can fire before it is tested and used in loop()
loopCnt = 0;
//asm volatile("dsb"); // xxx
}
void loop()
{
#if 0
loopCnt++;
#else
loopstrex = 0;
do {
__LDREXW(&callback_safe_update);
if ( loopstrex != 0 && loopCntIsr > 1 ) { // repeat entry and the _isr that fired was ours
loopCnt = 1;
}
else
loopCnt++;
loopstrex++;
} while ( __STREXW(1, &callback_safe_update));
#endif
if ( loopCntIsr>1 ) {
Serial.println();
if ( loopCntIsr >= 160000 ) Serial.printf("\t");
Serial.printf("%lu\t%lu", loopCntIsr, micros());
loopCntIsr = 1;
}
}
Read that multicore is the main use case for it. Wondering if in that case fiddling around on machine instruction level is a good idea? Probably better to use proven higher level approaches like atomic.h or some threading?
Read that multicore is the main use case for it. Wondering if in that case fiddling around on machine instruction level is a good idea? Probably better to use proven higher level approaches like atomic.h or some threading?
Tim:
played a few minutes with ldrex/strex:
It just detects interruptsCode:#include "arm_math.h" #include "core_cmInstr.h" void setup() { delay(1000); } void loop() { static uint32_t a = 0; static uint32_t b = 0; uint32_t c,d, dummy; do { __LDREXW(&dummy); c = a; d = b; if (c==10) {delay(2);a++;}//<- interrupt happens most likely here } while ( __STREXW(0, &dummy)); Serial.printf("c: %d, c:%d\n",c,d); delay(500); a++;b++; }
so.. if an interrupt is detected, it repeats the loop.
simple.
{2, M(1, 0), 0, 1}, // QuadTimer1_0 10 // B0_00
{2, M(1, 2), 0, 1}, // QuadTimer1_2 11 // B0_02
{2, M(1, 1), 0, 1}, // QuadTimer1_1 12 // B0_01
{2, M(2, 0), 0, 1}, // QuadTimer2_0 13 // B0_03
{2, M(3, 2), 0, 1}, // QuadTimer3_2 14 // AD_B1_02
{2, M(3, 3), 0, 1}, // QuadTimer3_3 15 // AD_B1_03
{2, M(3, 1), 0, 1}, // QuadTimer3_1 18 // AD_B1_01
{2, M(3, 0), 0, 1}, // QuadTimer3_0 19 // AD_B1_00
Thanks!@luni - Great stuff.
This morning I was hacking on the Teensy_I2C_Sniffer sketch (different thread) and wondered if it would work better if the timer ran at 2mhz... He was/is using the Timeer1 library. I made a hacked up version of the constructor which allowed me to pass in a floating point for number of microseconds, so passed in 0.5 and was able to get the timer to run at that speed.
Wonder if it makes sense to add that capability here?
f:100.0 kHz Load: 41.6 (w/o interrupts: 6500010 with interrupts 11125203)
f: 50.0 kHz Load: 17.3 (w/o interrupts: 6500010 with interrupts 7858722)
f: 25.0 kHz Load: 8.5 (w/o interrupts: 6500010 with interrupts 7105767)
f: 12.5 kHz Load: 4.3 (w/o interrupts: 6500010 with interrupts 6789252)
f: 6.2 kHz Load: 2.2 (w/o interrupts: 6500010 with interrupts 6644030)
f: 3.1 kHz Load: 1.1 (w/o interrupts: 6500010 with interrupts 6571456)
f: 1.6 kHz Load: 0.6 (w/o interrupts: 6500010 with interrupts 6536124)
Yes I know, same problem when I did TeensyDelay (which uses the FTMs) a couple of years ago. Therefore, this time I tried to make it more easy for the user to choose which resource to use.A assume you already know it, but on T4, the Quad Timer is already used now in a few places.
For dSb: I now read again (completely forgot about that - it was several month ago) that it does not help in any case. It just helps because it adds execution time. A ARM employee wrote this.
Better is to read the interrupt flag again, after resetting it.
Edit: this way, it is guaranteed that the flag got reset and will not trigger again the same interrupt.
Then - an additional dsb prevents the issue mentioned in the errata I posted above.
if (callback != nullptr && regs->CSCTRL & TMR_CSCTRL_TCF1)
{
regs->CSCTRL &= ~TMR_CSCTRL_TCF1;
callback();
regs->CSCTRL = regs->CSCTRL; //<----------
}
....
asm volatile("dsb");
...
callback();
regs->CSCTRL;
}
...