Please note as of Wednesday, August 15th, 2018 this wiki has been set to read only. If you are a TI Employee and require Edit ability please contact x0211426 from the company directory.

Invoke a C++ Class Member Function from an Interrupt

From Texas Instruments Wiki
Jump to: navigation, search

Problem Statement

How do you have an interrupt call a function that is a member of a C++ class?

Why Its Tricky

An interrupt function can take no arguments and return no result. A C++ class member function, however, is always passed an implicit "this" pointer argument. So, a C++ class member function cannot be directly invoked from an interrupt.

Solutions

One solution could look like this:

class myclass { public: void member_fxn(); ... };  // define myclass
myclass g;                                         // myclass global object
interrupt void called_from_interrupt()  // problem here addressed later
   { g.member_fxn(); }

Notice the class member function is not called directly, but from another function entirely outside the class. Also notice how you must invoke the member function on an actual object of that class, "g" in this case. Here is an attempt to fix these issues.

// doesn't quite work!
class myclass { public: static interrupt void called_from_interrupt(); ...};

Static member functions do not require a object on which to be invoked. They are not passed a "this" pointer. But there is another problem here. How do you associate this function with the interrupt? Details vary by system. One simple scenario is the function name is entered in a table in assembly language:

.sect "vectors"
.word myclass::called_from_interrupt        ; doesn't work!

Is the problem clear now? This solution comes closer.

// still not quite right!
class myclass { public: static void member_fxn(); ... };
interrupt void called_from_interrupt()
   { myclass::member_fxn(); }

The problem here is that you still can't associate the function "called_from_interrupt" with the interrupt. Note the first example also has this problem. Why? By C++ convention, all function names are mangled. An internet search on "C++ name mangling" gives more detail. To defeat the name mangling:

// this works!
class myclass { public: static void member_fxn(); ... };
extern "C" void called_from_interrupt();                  // new stuff here
interrupt void called_from_interrupt()
   { myclass::member_fxn(); }

Now you can enter the name "_called_from_interrupt" in the interrupt vector table. Note you may not need that leading underscore. Consult your compiler manual.

Limitations of Static Member Functions

The main limitation is that static member functions cannot access ordinary data members, but only static data members. There are other limitations which are less likely to cause any issues. If you are affected by this limitation, then your interrupt handling function must invoke the class member function on an actual object of the class, like the first example does.

About the Keyword interrupt

The above examples use the interrupt keyword. This is an extension to C++ supported by the TI compilers. It changes conventions on saving registers and function return. If the affected function is directly invoked by the interrupt, then you must use the interrupt keyword for correct operation. Note this is not the case with BIOS interrupts. BIOS interrupts are handled differently. It is an error to use the interrupt keyword on a function invoked by a BIOS interrupt.

Make it Fast

The key to good performance in this situation is to inline the call to the class member_fxn(). Two steps are required: 1) Request inlining when defining the member_fxn() 2) Build with optimization. There are two ways to request inlining. You can use the inline keyword, or you can supply the definition of member_fxn() within the definition of myclass. Consult your favorite C++ book for details. Even though inlining is requested, the compiler does not actually inline the function unless optimization options (--opt_level=2 or higher) are used.