I helped someone with a project yesterday and had a look at TeensyThreads and found some flaws in it. I hope this can help.
1. The way FPU registers are saved and restored unconditionally are very inefficient. Cortex uses lazy stacking and all you have to do is: (google it for technical info)
// save
tst r14, #0x10
it eq
vstmdbeq r0!, {s16-s31}
// restore
tst r14, #0x10
it eq
vldmiaeq r0!, {s16-s31}
2. The threads_svcall_isr interrupt should have the lowest priority (0xff) on the system. Or it’s impossible to yield from any interrupt with lower or same priority.
3. The use of noinline attributes makes no sens. An advice: Always develop and test you code the optimizer on full throttle -O3 (don’t use lto as default). You can always turn it off if you want to debug. It’s always easier discover and correct the errors right away and the optimizer is normally not destroying anything if done correctly. (Be carefull, the Internet is floating with a lot of corrupt examples on inline asm)
4. Interrupts are disabled longer than necessary, it is recommended to use BASEPRI instead of disabling all interrupts. But that change is up to PJRC or it will break compatibility. Freertos has done a decent job, look at a small file under Source/portable/GCC/ARM_CM4F/port.c
threads.setSliceMicros(100);
const int instrumentUpdateThread = threads.addThread(updataInstValuesThreadFunction);
threads.setTimeSlice(threads.id(), 5);// This is the main loop as thread
threads.setTimeSlice(instrumentUpdateThread, 10);// This is an additional thread
…..
void loop()
{
…..do things here (costs approx. 400 µsec)
threads.delay(20); // Will give 20 msec to other threads ?
}
void updataInstValuesThreadFunction()
{
int i = 0;
do
{
i++;
}while(true);
}
#include <TeensyThreads.h>
volatile int count = 0;
void pattern(void* arg[]){
while(1){
count = count + arg[0]*arg[1];
}
}
void setup() {
Serial.begin(9600);
int a[2] = {10, 20};
threads.addThread(pattern, a);
}
void loop() {
Serial.println(count);
}
Start by not passning in a pointer to an array that is local to the function. I Have a feeling that might be your problem.
int Threads::setMicroTimer(int tick_microseconds)
{
// lowest priority so we don't interrupt other interrupts
context_timer.priority(255);
// start timer with dummy fuction
if (context_timer.begin(context_pit_empty, tick_microseconds) == 0) {
// failed to set the timer!
return 0;
}
currentUseSystick = 0; // disable Systick calls
#ifdef __IMXRT1062__
attachInterruptVector(IRQ_PIT, context_switch_pit_isr);
#else
// get the PIT number [0-3] (IntervalTimer overrides IRQ_NUMBER_t op)
int number = (IRQ_NUMBER_t)context_timer - IRQ_PIT_CH0;
// calculate number of uint32_t per PIT; should be 4.
// Not hard-coded in case this changes in future CPUs.
const int width = (PIT_TFLG1 - PIT_TFLG0) / sizeof(int);
// get the right flag to ackowledge PIT interrupt
context_timer_flag = &PIT_TFLG0 + (width * number);
attachInterruptVector(context_timer, context_switch_pit_isr);
#endif
return 1;
}
@ftrias
You may want to read this post in Beta thread on Interval Timer: https://forum.pjrc.com/threads/54711-Teensy-4-0-First-Beta-Test?p=195605&viewfull=1#post195605, the fix though is in post 884 of the same thread a few posts down. Seems like while you have 1 PIT interrupt you can test on the channel which defines the one of the 4 available interval timers. I think
EDIT: PS. Thanks for compliment but I didn't do anything except try to figure out interval timer - the code is all yours. There is no way I could I wrote teensythreads.
Never realized that - thanks for the explanation.The problem is the the context_switch_isr() function as written must be called directly as an interrupt. It will unroll the special interrupt call stack frame to figure out where it was called from in order to know where to jump to on subsequent context switches. It can't be called in the normal way from within another function. This is why I have to take over the isr function instead of using EventTimer or something like that.
Glad you found the stuff that @manitou put together - he has done a lot with timers.EDIT: I found this code: https://github.com/manitou48/teensy4...er/gpt_isr.ino that has all the setup laid out for me so this should be relatively easy. So I think the plan is to not use systick and IntervalTimer on the T4, but rather to simply take over one of the GPT timers.
I committed this to my github: https://github.com/ftrias/TeensyThreads/commit/d61579a76428e59e9548204312a7b36da85df59c
For Teensy 3, it works the same as before. For Teensy 4, it uses an unused GPT timer to schedule context switches. Thus, it removes the dependency on SysTick and IntervalTimer. If both GPT timers are in use, it will fail. Perhaps in the future it should default to the old method if that's the case. But for now, I just wanted to put something out there that works.
int Threads::setMicroTimer(int tick_microseconds)
{
#ifdef __IMXRT1062__
gtp1_init(tick_microseconds);
#else