Possible Priority Inversion with Serial1

duff

Well-known member
Hi all,

in the following code i think that the Serial1 "serial_write" handler function is still not handling "Priority Inversion" correctly. It's my understanding that Teensyduino 1.17 Release Candidate #1 should of fixed this but i'm not sure. The code below sets up the Interval Timer CH0 with a priority that is equal to Serial1 and then prints using Serial1 in the interrupt handler routine for the Interval Timer. It runs for a few cycles then quits printing as far as I can tell. It also makes it so you have to push the upload code button on the teensy to reprogram it.
Code:
//example 1
#define NVIC_IABR1		*(volatile uint32_t *)0xE000E300
#define NVIC_IABR2		*(volatile uint32_t *)0xE000E304

IntervalTimer timer;

void PriorityInversionISR(void) {
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  [COLOR="#FF0000"][B]Serial1.println("Hello, World!!!");[/B][/COLOR]
  int priority = nvic_execution_priority();
  Serial.printf("current priority: %i | NVIC_IABR1: %#X | NVIC_IABR2: %#X\n", priority, NVIC_IABR1, NVIC_IABR2);
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(0);
  Serial1.begin(1200);
  while(!Serial);
  delay(100);
  [COLOR="#FF0000"][B]NVIC_SET_PRIORITY(IRQ_PIT_CH0, 64);//<-- same priority as serial1[/B][/COLOR]
  timer.begin(PriorityInversionISR, 100000);
}

void loop() {}

This might be a issue with the Interval Timer class not handling the inversion also, but if i leave the priority of Interval Timer at 64 and remove the Serial1.print code it works normally. The code below shows this.
Code:
example 2
#define NVIC_IABR1		*(volatile uint32_t *)0xE000E300
#define NVIC_IABR2		*(volatile uint32_t *)0xE000E304

IntervalTimer timer;

void PriorityInversionISR(void) {
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  [COLOR="#FF0000"][B]//Serial1.println("Hello, World!!!");<--- commented out, now works![/B][/COLOR]
  int priority = nvic_execution_priority();
  Serial.printf("current priority: %i | NVIC_IABR1: %#X | NVIC_IABR2: %#X\n", priority, NVIC_IABR1, NVIC_IABR2);
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(0);
  Serial1.begin(1200);
  while(!Serial);
  delay(100);
  [COLOR="#FF0000"][B]NVIC_SET_PRIORITY(IRQ_PIT_CH0, 64); //<-- same priority as serial1[/B][/COLOR]
  timer.begin(PriorityInversionISR, 100000);
}

void loop() {}

The weird thing is it seems as if the interval timer works still even if the teensy is "sudo" locked up using example 1. The work around i found is to change PIT CH0 priority to 128 before calling Serial1.print and then change it back after shown below.
Code:
//example 3
#define NVIC_IABR1		*(volatile uint32_t *)0xE000E300
#define NVIC_IABR2		*(volatile uint32_t *)0xE000E304

IntervalTimer timer;

void PriorityInversionISR(void) {
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
[COLOR="#FF0000"]  
[B]  
  NVIC_SET_PRIORITY(IRQ_PIT_CH0, 128);//<--- Lower priority
  Serial1.println("Hello, World!!!");
  NVIC_SET_PRIORITY(IRQ_PIT_CH0, 64);//<--- increase priority
[/B]
[/COLOR]
  int priority = nvic_execution_priority();
  Serial.printf("current priority: %i | NVIC_IABR1: %#X | NVIC_IABR2: %#X\n", priority, NVIC_IABR1, NVIC_IABR2);
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(0);
  Serial1.begin(1200);
  while(!Serial);
  delay(100);
  [COLOR="#FF0000"][B]NVIC_SET_PRIORITY(IRQ_PIT_CH0, 64); //<-- same priority as serial1[/B][/COLOR]
  timer.begin(PriorityInversionISR, 100000);
}

void loop() {}

The reason i think i have to change the priority to 128 in the above example is that the USB is at priority 112 so that where the inversion i think is happening. Anyway i just wanted to share this case someone in counters the same problem. This probably won't be a issue with 99% of project but it is a problem for those setting priorities in thier code.

duff
 
Back
Top