Interrupt safe read.

samm_flynn

Well-known member
I'm using a Teensy 4.1, and I have a float variable (tgtfr) that's updated in serialEvent() based on serial input. This same variable is read inside a timer interrupt that fires every 1 ms.

The main loop is running very fast — over 7 MHz, based on measurements (just incrementing a counter per loop and checking it in the ISR).

My concern is about a simple assignment like:

a = b; // where both are float

Since a float is 4 bytes, is it possible that the interrupt triggers in the middle of this assignment — meaning the variable might only be partially updated when the ISR runs and reads it?

Does the Teensy 4.1 guarantee that a float assignment is atomic? Or do I need to protect access (e.g., disable interrupts briefly, which I dont want to do because it will screw up my high performance control algorithm, which expects bang-on timing to work) when reading or writing shared float variables between the main loop and an ISR?

What's the correct and safe way to handle this on this platform? I want to avoid any possibility of corrupted values — it could cause serious issues in my robot.



C++:
void serialEvent() // measured call rate 7.25 MHz
{
  int bytesAvailable = Serial.available();
  if (Serial.available() < 1) return;
  char buffer[bytesAvailable + 1] = { 0 };

  int bytesRead = Serial.readBytes(buffer, bytesAvailable);
  buffer[bytesRead] = '\0';  // Null terminate

  String input(buffer);
  input.trim();

  float val = input.toFloat();
  if (val >= 0 && val <= 100)
  {
    tgtfr = val;
  }
}
 
Since a float is 4 bytes, is it possible that the interrupt triggers in the middle of this assignment — meaning the variable might only be partially updated when the ISR runs and reads it?

As long as the variable is in RAM1 or RAM2, and perhaps it must also be 4-byte aligned, I'm pretty sure the answer is no, the assignment cannot be interrupted. Hopefully others with deeper understanding of the processor will confirm.

Does the Teensy 4.1 guarantee that a float assignment is atomic? Or do I need to protect access (e.g., disable interrupts briefly, which I dont want to do because it will screw up my high performance control algorithm, which expects bang-on timing to work) when reading or writing shared float variables between the main loop and an ISR?

What's the correct and safe way to handle this on this platform? I want to avoid any possibility of corrupted values — it could cause serious issues in my robot.

Use an interrupt-safe queue. See the link below for an explanation, and if you want to see an example, look at the FreqMeasureMulti library. In its ISR, it posts data to a queue, and that queue can be read at non-interrupt level via the available() and read() methods. It's more common to want to safely transfer data read in an ISR to task level, but the same thing will work in the other direction, so you could post to a queue from your serial event handler, which runs at "task" level, and read from the queue in your ISR.

 
Your existing code is fine, it's a 32-bit CPU so any direct memory assignment of that size or below will happen as a single operation.
 
I would argue that it's only atomic if the value is correctly aligned to a 32 bit boundary. But the arm FPU provides a very handy way to verify whether that's the case. It generates a hard fault if you try using a float that isn't correctly aligned.

So if you code isn't crashing then it's aligned.
 
I would argue that it's only atomic if the value is correctly aligned to a 32 bit boundary. But the arm FPU provides a very handy way to verify whether that's the case. It generates a hard fault if you try using a float that isn't correctly aligned.

So if you code isn't crashing then it's aligned.
Hi @AndyA. The ARM docs do specify that alignment is necessary for atomicity.
 
Back
Top