ftrias / TeensyThreads

MIT License
178 stars 26 forks source link

Specific timing of thread start causes hard fault (invalid EXC_RETURN) in Teensy core #42

Open Merlin04 opened 1 year ago

Merlin04 commented 1 year ago

There's a very weird bug I've encountered while using TeensyThreads - I'm not sure if it's an issue with this library or with the Teensyduino core, so I'm reporting the issue to both places.

I'm not entirely sure exactly what triggers it, but it seems that certain timing between the end of a thread and the start of another causes a hard fault. This sketch is the most reliable way I've found to cause the issue:

#include <TeensyThreads.h>
#include <CrashReport.h>

volatile unsigned int d = 1;

void threadFn() {
  next();
}

IntervalTimer t;

void a() {
  threads.addThread(threadFn);
  t.end();
}

void next() {
  d += 1;
  threads.addThread(threadFn);
  t.begin(a, d);
}

void setup() {
  // put your setup code here, to run once:
  delay(5000);
  Serial.println(CrashReport);
  delay(5000);
  threads.setSliceMicros(400);

  threads.addThread(threadFn);

  // we're going to scan through a list of possible delays to figure out what triggers the fault
  next();
}

void loop() {}

The output from it looks something like this:

19:23:49.492 -> CrashReport: 19:23:49.492 -> A problem occurred at (system time) 19:23:35 19:23:49.492 -> Code was executing from address 0x22BA 19:23:49.492 -> CFSR: 40000 19:23:49.492 -> (INVPC) Usage fault: invalid EXC_RETURN 19:23:49.492 -> Temperature inside the chip was 56.82 °C 19:23:49.492 -> Startup CPU clock speed is 600MHz 19:23:49.492 -> Reboot was caused by auto reboot after fault or bad interrupt detected

The address changes a bit per run, but from using addr2line I've determined it always points to somewhere inside the yield function in packages/teensy/hardware/avr/1.58.1/cores/teensy4/yield.cpp.

I actually found this happening and being a very big problem in a real application (took me forever to try to figure out the root cause) - I have a thread which does some debouncing stuff and is triggered by an interrupt on some pins connected to switches, and repeatedly pressing the switches would eventually cause this exact fault. I've put together a simplified example of this in case it helps with testing:

Interrupt-triggered thread example ```cpp #include #include void removeI() { detachInterrupt(41); } void addI() { attachInterrupt(41, []() { removeI(); threads.addThread(threadFn); }, LOW); } void threadFn() { Serial.println("hi!"); addI(); } void setup() { delay(5000); // give time for serial monitor to auto-reconnect Serial.println(CrashReport); delay(5000); pinMode(41, INPUT_PULLUP); threads.setSliceMicros(400); threads.addThread(threadFn); } void loop() {} ```

Specifically, I've found the fault occurs after threads.addThread was run, but before the thread function itself runs.

Let me know if there's any way I can help get to the bottom of this, I'm really curious what the actual issue is (and I also really need it to be fixed so I can get my hardware project working reliably).