This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430: how to define interrupt routine as weak

Other Parts Discussed in Thread: CC1101, SIMPLICITI, CC430F5137

I placed port and interrupt related to that port into a library. On Keil compiler I can declare interrupt routine as weak, which causes routine to be linked only if any of other functions are referred by application code.

However, MSP430 compiler links interrupt routine even if I enabled GCC extensions and declared it as weak. Linking interrupt routine also adds other port related stuff, which causes a lot of extra unused binary section..


How can solve that issue?

  • sorry for misinformation, on Keil I even didn't declare interrupt routine as weak. It is linked automatically if any of the routines, variables in that module is referred. It is not linked otherwise..

  • Regarding functions defined in libraries, the TI compiler acts in a similar manner.  A function is brought in from a library only if it is needed.  Keep in mind that, while the call to the library function typically comes from the non-library main application code, it is possible for that call to appear in some other library module.

    Thanks and regards,

    -George

  • The modules that does not have any ISR routine is linked as you described.. But that's not the case if the module has ISR. ISR is only called by hardware, not from any code called from application code or other library code.. If it were like what you said, I mean if the ISR is brought because it is referred somewhere, when I removed/renamed the ISR code, the linker would be annoyed? Wouldn't it?

    My problem is: ISR is not called anywhere but linker adds it to final binary, and also adds what it uses. I encapsulated all ports, SPI, I2C, UART, CC1101 radio, RTC.. So, even I do not use I2C, my final code has I2C's ISR related parts.. On other compilers, such as Keil's Arm compiler  this is not the case.. ISR is only linked when any other function  (in my case method) in that module is referred..

    Do you want me create a simple workspace?

  • Deniz Can Cigsar said:
    Do you want me create a simple workspace?

    I presume you mean a CCS project.  Yes, please.  That would help me understand the details of your case.

    Thanks and regards,

    -George

  • Yes, CCS project.. However, as I am talking about at least one library and one application project, I meant a workspace.. It's pity Eclipse does not have a "solution" approach..

  • I collected the problems into one example.. Two projects, one library and the application using it.. I also added comments..
    The issues:
    - ISR routine in a library is always linked causing other unused parts of that module to be linked as well
    - Return Value Optimization is not working for virtual methods.
    - If a method is virtual, it is always linked..

    Thank you for your time..

    6472.CppAndIsrIssues.zip

  • Hello, could you have time to look at my example?

    ISR caused link is major problem in extending my library. Think about this, I have two classes, and instances for uscia0:

    1- uscia0uart instance that is Uscia0Uart: If you use this class Uscia is configured as uart..

    2- uscia0spi instance that is Uscia0Spi: If you use this class Uscia is configured as spi..

    For instance:

    #include <Acore/Spi.h>
    
    int main()
    {
      uscia0spi.setMisoPin(P1_0);
      ..
      uscia0spi.open(); // uscia0 is configured as SPI
    }
    

    in other compilers/platforms ISR is linked with other functions that are used.. I only have problem with your compiler..

  • I apologize for the delay.

    Deniz Can Cigsar said:
    ISR routine in a library is always linked causing other unused parts of that module to be linked as well

    Your linker command file contains this line ...

       USCI_A0      : { * ( .int58 ) } > INT58 type = VECT_INIT

    The key part is the type = VECT_INIT.  Read about that in the section named Special Section Types in the MSP430 assembly tools manual.  You'll discover that this means the linker will look for a section named .int58 and use it for this interrupt vector slot.  You create that section when you write ...

    #pragma vector=USCI_A0_VECTOR
    __interrupt void processUsciA0Interrupt()
    {
    // interrupt code here
    }
    

    That it appears in a library, and not in the main application code, changes nothing.  That line in the linker command file causes the linker to look for a section named .int58, and the library you supply provides it.  If you don't supply an .int58 section, then the linker gets one from the standard RTS library.

    The solution I recommend is that you create a stub ISR in your main application code ...

    #pragma vector=USCI_A0_VECTOR
    __interrupt void stub_isr()
    { }
    

    Another possible solution is to change the link order so that the compiler RTS library is seen before your custom library.  I presume this solution is more complicated and thus not worth the trouble.

    Deniz Can Cigsar said:
    Return Value Optimization is not working for virtual methods.

    This optimization is usually realized by inlining the call to the function which returns the object.  But you cannot inline a call to a virtual function.  Even so, because of the function calling convention of the TI compiler, objects are returned in an efficient manner.  See the sections titled How a Function Makes a Call and How a Called Function Responds in the MSP430 compiler manual.  Focus on the parts about how a structure is returned.  Note structure and object are different names for the same thing.

    Deniz Can Cigsar said:
    If a method is virtual, it is always linked..

    In your main application code you have this line ...

    SampleGenerator sampleGenerator;
    

    Code later in the main function makes use of this object.  The constructor for this object refers to the virtual function table for the class SampleGenerator.  The virtual function table for the class SampleGenerator is generated from the source file ReturnValueGenerator.cpp, a member of the library.  Expressed as pseudo-code, this table looks like ...

    Virtual_function_table_for_SampleGenerator:
        pointer to function SampleGenerator::getValue();
        pointer to function ReturnValueGenerator::notCalledAnywhereButLinkedAnyway();
    

    This table is all collected together in one input section.  Because this table gets referenced, everything referred to in the table is brought in to the build, including the function ReturnValueGenerator::notCalledAnywhereButLinkedAnyway().  I would be very surprised if any C++ compiler handled this differently.

    Thanks and regards,

    -George

  • The key part is the type = VECT_INIT.  Read about that in the section named Special Section Types in the MSP430 assembly tools manual.  You'll discover that this means the linker will look for a section named .int58 and use it for this interrupt vector slot.  You create that section when you write ...


    I already know that. But this behavior is ridiculous and not acceptable. This way you prevent self organizing libraries. If you have ISR's in library, you include all of the library or not.. You cannot declare two copies of that ISR in a library and let linker to select which ISR to be linked..

    On Keil compiler, if you use timer0, it imports Timer0ISR as well, if you do not refer to any timer0, you are not referring to any of Timer0 related stuff.. You do not see anything starting with Timer:: in final image etc.. This is expected..

    Changing link order prevents other ISR in that library to be linked as well, and cannot be excepted.. With your implementation, the application developer has to deal with hardware stuff as well.. My aim is: Write Once, Use Everywhere, don't deal with hardware abstraction.. For instance my SimpliciTI implementation works on CC430F5137, with it's core radio and on LPC1768+CC1101 without any low level stuff. On CC430..

    #include <Arf/Radio.h>
    #include <Asimpliciti/Network.h> // CC430 has coreRadio Asimpliciti::Network network; int main() { coreRadio.setConfiguration(Arf::CC1101::configuration868MHz250KBit); network.setRadio(coreRadio); network.open(); .. }

    On LPC1768 + CC1101

    #include <Acore/Spi.h>
    #include <Arf/Radio.h>
    #include <Asimpliciti/Network.h>
    
    Arf::CC1101Spi radio;
    Asimpliciti::Network network;
    
    int main()
    {
      spi0.open();
      radio.setPort(spi0);
      radio.setCSPin(P2_1); // the CS is connected to that pin on that implementation
      radio.setGDO0Pin(P2_2);
      radio.setGDO1Pin(P2_3);
      radio.setConfiguration(Arf::CC1101::configuration868MHz250KBit);
      network.setRadio(radio);
      network.open();
      ..
    }
    

    As you can see, no thought about ISR's, no "UCB0CTL0 = UCMST + UCMODE_0 + UCSYNC + UCMSB" cryptic annoyance.. You just include what you use, it's all done by library and linker.. On your compiler my Acore library's all port abstraction is linked because of that problem. Arm code does more than MSP430 code but it is a lot, I mean a lot, leaner than TI's..



    This optimization is usually realized by inlining the call to the function which returns the object.  But you cannot inline a call to a virtual function.

    You look at the problem as inlining. Return Value Optimization IS NOT INLINING. It is: If a function returns an object, caller creates that object's space in its stack and passes a pointer to it.. Callie uses given reference to fill in..

    It is rewriting Object f() to f(Object& o); (& is better than *, & means you cannot pass NULL)

    http://en.wikipedia.org/wiki/Return_value_optimization

    To do that you do not need to INLINE. You can optimize the virtual functions as well. Actually your compiler does the first stage correctly, it allocates memory on stack and passes reference to virtual function, except if caller is not using return value. But virtual function implementation is totally wrong. It reallocates space in stack, fills it with new data, than tests if given reference is not NULL, than calls memcpy to copy created objects data to given reference. Return Value Optimization is to perevent this. I think you didn't read my notes.. Believe me you totally misunderstood the problem..

    Dropping virtual functions is a SUGGESTION. And also I know why it is linked.

    In any implementation, as far as I understand, compiler designers for computers do not care resource deficient platforms. They just care how ugly they can make a compiler, they invent more cryptic syntax in every iteration.. But you are dealing with 1 KB RAM, and charge us dollars for a few KBs of ROM. So, you have to do better on optimization. If a virtual function is not referred at all stages, why do you link it? Just drop, and write NULL at the slot of VMT..

    #include <Asimpliciti/Network.h>
  • My role here is to help people solve problems.  This ...

    Deniz Can Cigsar said:
    this behavior is ridiculous and not acceptable.

    ... is not a problem, its an opinion.  All opinions are welcome in this forum.

    If a particular opinion gains wide support from the community, then there is a good chance we will change the tools to accommodate it.  If a particular opinion is not widely shared, we tend to not make any changes in regards to it.

    Thanks and regards,

    -George

  • It is a problem for me George.. Not an opinion..



    If a particular opinion gains wide support from the community, then there is a good chance we will change the tools to accommodate it.  If a particular opinion is not widely shared, we tend to not make any changes in regards to it.


    You know, the translation of this is: We do not care the improvements unless there is a wide support.. So, customers shall not expect any revolution from TI's development team..

    Thanks..

  • Deniz Can Cigsar said:
    I collected the problems into one example.. Two projects, one library and the application using it.. I also added comments..
    The issues:
    - ISR routine in a library is always linked causing other unused parts of that module to be linked as well

    I imported that example into CCS v6, with MSP430 compiler v4.2.7 installed (the same version as used in the example).

    GCC extensions was enabled in the project options, and the weak attribute was added to the processUsciA0Interrupt function:

    #pragma vector=USCI_A0_VECTOR
    __interrupt  __attribute__((weak)) void processUsciA0Interrupt()
    

    With those changes, inspecting the linker map showed that linkedByISR and processUsciA0Interrupt were no longer linked. i.e. the weak attribute does appear to work. Can you re-try using the weak attribute?

  • Thank you for your time..

    Did you change anything else? Mine still links processUsciA0Interrupt..

    Which platform are you using, Windows/Linux? Mine is Windows..

  • Deniz Can Cigsar said:
    Which platform are you using, Windows/Linux?

    I was using Linux.

    Deniz Can Cigsar said:
    Did you change anything else? Mine still links processUsciA0Interrupt..

    I also did change the compiler version used a few times - since initially didn't have v4.2.7 installed.

    I have had another go:

    1) Create a new empty workspace in a CCS 6.0.1.00040 Linux installation, which has MSP430 compiler versions 4.2.7 and 4.3.5 installed (among others)

    2) Import your unmodified Application and Library projects from CppAndIsrIssues.zip into the empty workspace.

    3) Build the Application project in CCS, which also builds the Library project, using the v4.2.7 compiler. Inspecting the Application.map file shows that linkedByISR and processUsciA0Interrupt (mangled name _Z22processUsciA0Interruptv) have been linked into the image.

    [as expected as have not yet modified the project]

    4) Edit  the LinkedByISR.cpp source file to add __attribute__((weak)) to the processUsciA0Interrupt function:

    __interrupt  __attribute__((weak)) void processUsciA0Interrupt()

    Change the Library Project Properties to enable support for gcc extensions. Build the Application project in CCS, which re-builds the Library project.

    Running nm430 on the modified Library.lib confirms that _Z22processUsciA0Interruptv has been declared as a weak symbol as a result of adding the weak attribute to the source file.

    However, the Application.map still reports that  linkedByISR and processUsciA0Interrupt have been linked in the image.

    5) In the project properties for the Application project change the compiler version from 4.2.7 to 4.3.5, accepting the change but NOT re-building at this point.

    6) In the project properties for the Application project change the compiler version from 4.3.5 back to 4.2.7. Build the Application project. This time inspecting the Application file shows that linkedByISR and processUsciA0Interrupt haven't been linked to the image.

    I have repeated the above sequence 3 times on the Linux CCS installation. Not sure why the change of compiler version makes a difference - will also try the exact same sequence on a Windows CCS v6 installation.

  • Chester Gillon said:
    I have repeated the above sequence 3 times on the Linux CCS installation. Not sure why the change of compiler version makes a difference - will also try the exact same sequence on a Windows CCS v6 installation.

    Have repeated the same sequence with CCS  6.0.1.00040 running under Windows, i.e. the Linux and Windows variants of CCS and the MSP430 compiler operate in the same way.

  • George Mock said:
    This table is all collected together in one input section.  Because this table gets referenced, everything referred to in the table is brought in to the build, including the function ReturnValueGenerator::notCalledAnywhereButLinkedAnyway().  I would be very surprised if any C++ compiler handled this differently.

    Today I see that Keil does that optimization:

        Removing serialport.o(i._ZN5Acore10SerialPort14getBytesToReadEv), (24 bytes).
        Removing serialport.o(i._ZN5Acore10SerialPort15getBytesToWriteEv), (28 bytes).
        Removing serialport.o(i._ZN5Acore10SerialPort16waitForDataReadyEi), (94 bytes).
        Removing serialport.o(i._ZN5Acore10SerialPort18waitForDataWrittenEi), (94 bytes).

    In my library SerialPort extends from Stream and  SerialPort::getBytesToRead etc are all virtual's overriding Stream counterparts. Apllication code only uses serialPort.onDataReady() signal to read bytes in the FIFO, so it does not refer any of virtual methods, or methods that refer any of those.. The linker removes all unused virtual methods..

    So my suggestion was solid.. Right? I think you should change your decision making process. It misses the 'value' right now..

  • Chester Gillon said:

    I have repeated the above sequence 3 times on the Linux CCS installation. Not sure why the change of compiler version makes a difference - will also try the exact same sequence on a Windows CCS v6 installation.

    Have repeated the same sequence with CCS  6.0.1.00040 running under Windows, i.e. the Linux and Windows variants of CCS and the MSP430 compiler operate in the same way.

    [/quote]


    Thank you.. I'll try clean install and test..