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.

CCSv6 Beta4, MSP430 GCC 4.8, Wolverine and Interrupts

Other Parts Discussed in Thread: MSP430FR5969, MSP430F5438A

Hi folks!  Trying to play with the new GCC 4.8 included with CCSv6 beta, and I'm having some issues with interrupts.  If this is something not yet "stable" or implemented let me know...

Current objective is to produce a small program that blinks an LED on the Wolverine LaunchPad via the WDT in interval mode, and for that I'm using:

/*
 * main.c
 */
#include <msp430.h>


int main(void) {
	WDTCTL = WDTPW | WDTHOLD;

	// GPIO Setup
	P1OUT = 0;
	P1DIR = 0xFF;

	P2OUT = 0;
	P2DIR = 0xFF;

	P3OUT = 0;
	P3DIR = 0xFF;

	P4OUT = 0;
	P4DIR = 0xFF;
	
	PJOUT = 0;
	PJSEL0 = BIT4 | BIT5;                     // For XT1
	PJDIR = 0xFFFF;


	// Disable the GPIO power-on default high-impedance mode to activate
	// previously configured port settings
	PM5CTL0 &= ~LOCKLPM5;

	// Clock System Setup
	CSCTL0_H = CSKEY >> 8;                    // Unlock CS registers
	CSCTL1 = DCOFSEL_6;                       // Set DCO to 8MHz
	CSCTL2 = SELA__LFXTCLK | SELS__DCOCLK | SELM__DCOCLK;
	CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1;     // Set all dividers
	CSCTL4 &= ~LFXTOFF;                       // Enable LFXT1
	do
	{
		CSCTL5 &= ~LFXTOFFG;                    // Clear XT1 fault flag
		SFRIFG1 &= ~OFIFG;
	} while (SFRIFG1&OFIFG);                  // Test oscillator fault flag
	CSCTL0_H = 0;                             // Lock CS registers

	WDTCTL = WDT_ADLY_250;
	while (1) {
		__delay_cycles(4000000);
	}

	return 0;
}

void __attribute__((interrupt(WDT_VECTOR))) WDT_ISR(void)
{
	P4OUT ^= BIT6;
}

So while the main() routine generally works as I've tested the P4OUT ^= BIT6; inside that while(1) loop, when trying to declare my WDT ISR I get the following warning from the compiler:

../main.c:54:1: warning: numeric argument of 'interrupt' attribute must be in range 0..31 [-Wattributes]

From what I gather, WDT_VECTOR is defined as (50) in msp430fr5969.h, and there is a definition for __interrupt_vector_50 in the linker script:

__interrupt_vector_50  : { KEEP (*(__interrupt_vector_50)) KEEP (*(__interrupt_vector_wdt)) } > VECT50

with VECT50 defined higher up in the linker script as:

  VECT50           : ORIGIN = 0xFFF2, LENGTH = 0x0002

So I'm not sure what's going on here.  Incomplete implementation for the higher-end chips perhaps?

  • update-

    After removing -Werror, i.e. treat warnings as errors, I noticed the program actually did compile correctly with __interrupt_vector_50 at the correct location, pointing to my WDT_ISR function.  So this warning is probably a compiler bug but it does the right thing at the end.  The LED blinks (and tried changing the WDT_ADLY_ macro with correct results).

  • I'm running into a the same problem for a different interrupt.

    I'm trying to run an ISR for the USCI_A1 (this is the serial port). I'm using the msp430f5438a, and CCSv6.0 in linux.

    When I run the code compiled using the TI compiler, interrupts are generated, but when I compiled it in GNU v4.8.0 (Red Hat) it doesn't work.

    This is the relevant bit of code:

    #ifdef __TI_COMPILER_VERSION__

    #pragma vector=USCI_A1_VECTOR

    __interrupt void USCI_A1_ISR(void)

    #else

    static void
    __attribute__((__interrupt__(USCI_A1_VECTOR))) USCI_A1_ISR(void)

    #endif

    {
    halUsbReceiveBuffer[bufferSize++] = UCA1RXBUF;
    __bic_SR_register_on_exit(LPM3_bits);
    }

    I get the same warning code as you said before: numeric argument of '__interrupt__' attribute must be in range 0..31 [-Wattributes]

    Could you give me some hints as to how to fix this?

    thanks

  • I did forget to put _EINT(); in my example up above, that fixed it for me.  (Same as _BIS_SR(GIE);)

  • thank you for your quick reply.

    However, I did add __bis_SR_register(GIE);                 // Enable Interrupts

    in my code. 

    I'm compiling exactly the same code for the TI compiler and the GNU compiler, except for the bit where the ISR is defined, and for TI it works.

  • Fwiw, my interrupt functions are declared-

    void __attribute__((interrupt(...))) and not static void __attribute__((__interrupt__(...))) ... maybe something to try?

    I'm not too familiar with CCS's code analysis tools, but, I am somewhat familiar with GCC's commandline utilities and I use the msp430-elf-objdump command (with -x to view sections & symbols, -dS to view disassembled output w/ source shown inline) to analyze the resulting .out file.

  • Hi Eric,

    well the compiler didn't complain about removing static, but it didn't help either, unfortunately.

    I'll have a go at that objdump command then, thanks for the tip.

    cheers, Michael

  • here's an example of me doing objdump from Windows, using a Cygwin terminal (so C: is /cygdrive/c)-

    cd /cygdrive/c/Users/spirilis/workspace_v6_0/wolv_rhgcc_test/Debug

    ls -l wolv_rhgcc_test.out

    -rwx------+ 1 spirilis None 15004 Apr 15 13:44 wolv_rhgcc_test.out

    Running objdump -x to show the sections & symbols:

    /cygdrive/c/ccsv6/ccsv6/tools/compiler/gcc_msp430_4.8/bin/msp430-elf-objdump.exe -x wolv_rhgcc_test.out | less

    wolv_rhgcc_test.out:     file format elf32-msp430
    wolv_rhgcc_test.out
    architecture: msp:430X, flags 0x00000112:
    EXEC_P, HAS_SYMS, D_PAGED
    start address 0x00004410
    
    Program Header:
        LOAD off    0x00000000 vaddr 0x0000434c paddr 0x0000434c align 2**2
             filesz 0x0000033e memsz 0x0000033e flags rwx
        LOAD off    0x00000340 vaddr 0x00001c00 paddr 0x0000468a align 2**2
             filesz 0x00000002 memsz 0x00000014 flags rw-
        LOAD off    0x00000342 vaddr 0x0000fff2 paddr 0x0000fff2 align 2**2
             filesz 0x00000002 memsz 0x00000002 flags r-x
        LOAD off    0x00000346 vaddr 0x0000fffe paddr 0x0000fffe align 2**2
             filesz 0x00000002 memsz 0x00000002 flags r--
    
    Sections:
    Idx Name          Size      VMA       LMA       File off  Algn
      0 __interrupt_vector_50 00000002  0000fff2  0000fff2  00000342  2**0
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
      1 __reset_vector 00000002  0000fffe  0000fffe  00000346  2**0
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      2 .rodata       00000010  00004400  00004400  000000b4  2**2
                      CONTENTS, ALLOC, LOAD, DATA
      3 .text         0000027a  00004410  00004410  000000c4  2**1
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
      4 .data         00000002  00001c00  0000468a  00000340  2**1
                      CONTENTS, ALLOC, LOAD, DATA
      5 .bss          00000012  00001c02  0000468c  00000342  2**1
                      ALLOC
      6 .MP430.attributes 000000e6  00000000  00000000  00000348  2**0
                      CONTENTS, READONLY
      7 .comment      00000064  00000000  00000000  0000042e  2**0
                      CONTENTS, READONLY
      8 .debug_aranges 00000120  00000000  00000000  00000498  2**3
                      CONTENTS, READONLY, DEBUGGING
      9 .debug_info   000007cd  00000000  00000000  000005b8  2**0
                      CONTENTS, READONLY, DEBUGGING
     10 .debug_abbrev 000001fa  00000000  00000000  00000d85  2**0
                      CONTENTS, READONLY, DEBUGGING
    

    (cut the paste short)

    Note __interrupt_vector_50, that was listed in the linker and vector #50 is the constant that WDT_VECTOR resolves to.

    Disassembling the output:

     /cygdrive/c/ccsv6/ccsv6/tools/compiler/gcc_msp430_4.8/bin/msp430-elf-objdump.exe -dS wolv_rhgcc_test.out | less

    wolv_rhgcc_test.out:     file format elf32-msp430
    
    
    Disassembly of section __interrupt_vector_50:
    
    0000fff2 <__interrupt_vector_50>:
        fff2:       da 45           interrupt service routine at 0x45da
    
    Disassembly of section .text:
    
    00004410 <__start>:
        4410:       31 40 00 24     mov     #9216,  r1      ;#0x2400
    
    00004414 <.Loc.56.1>:
        4414:       b2 40 80 5a     mov     #23168, &0x015c ;#0x5a80
        4418:       5c 01
    
    0000441a <__crt0_init_bss>:
        441a:       3c 40 02 1c     mov     #7170,  r12     ;#0x1c02
    

    (cut paste short)

    The first entry says it all.  0x45da is:

    000045da <wdtisr>:
            return 0;
    }
    
    void __attribute__((interrupt(WDT_VECTOR))) wdtisr()
    {
            P4OUT ^= BIT6;
        45da:       f2 e0 40 00     xor.b   #64,    &0x0223 ;#0x0040
        45de:       23 02
    
    000045e0 <.Loc.58.1>:
            __bic_SR_register_on_exit(LPM3_bits);
        45e0:       b1 c0 d0 00     bic     #208,   0(r1)   ;#0x00d0
        45e4:       00 00
    
    000045e6 <.Loc.59.1>:
    }
        45e6:       00 13           reti
    

    I kinda think the source-inlining feature is a bit glitchy here since it implies the return 0; from the prior function is at 0x45da, when it isn't, but it's evident anyhow that 45da is in fact the first instruction of the ISR.

  • ok I did what you suggested. Thanks for that nugget, it was very interesting, although there are a lot of entries I do not quite understand yet.

    I looked at the output of both the gnu and ti compiler for the same program.

    The origin of the interrupt is correct in both disassembly outputs: 0XFFDC, and also 00005e68 occurs in the output of both compilers, that is where the isr is located.

    Conclusion: the debug shows that there is actually an ISR that lives at the correct address. 

    So I dove a bit deeper:

    in my program there is a loop that checks if new characters have been added to the buffer, and if that is the case, it sends a * character for each received character. Also I've added some debug values.

    globals.h: 

    extern char halUsbReceiveBuffer[255];
    extern unsigned char bufferSize;
    extern unsigned char gVar1;
    extern unsigned char gVar2;

    globals.c:

    char halUsbReceiveBuffer[255];
    unsigned char bufferSize = 0;
    unsigned char gVar1;
    unsigned char gVar2;

    main.c:

    #include "globals.h"
    
    unsigned char quit;
    volatile unsigned char i;
    volatile unsigned char gVar1Copy;
    volatile unsigned char gVar2Copy;
    volatile unsigned char bSizeCopy;

    // snipped //

    quit = 0;
        while (quit == 0)
        {
        	P1OUT ^= 0x01;            // toggle pin
        	gVar1Copy = gVar1;      // gVar1Copy is a local var, gVar1 = global var
        	gVar2++;                       // gVar2 = global var
        	gVar2Copy= gVar2;      // gVar2Copy = local var
        	bSizeCopy = bufferSize;   // local var (I'm doing this for a reason).
    
            if (bufferSize > 0)
            {
            	halUsbSendChar('*');
                for (i = 0; i < bufferSize; i++)
                    halUsbReceiveBuffer[i] = '\0';
                bufferSize  = 0;
            }
        }

    Also in the ISR I have added a bit of debug:

    #ifdef __TI_COMPILER_VERSION__
    
    #pragma vector=USCI_A1_VECTOR
    __interrupt void USCI_A1_ISR(void)
    
    #else
    
    void
    __attribute__((__interrupt__(USCI_A1_VECTOR))) USCI_A1_ISR(void)
    #endif
    
    {
    	    halUsbSendChar(UCA1RXBUF);
    	    halUsbSendChar(gVar2);
    	    halUsbSendChar(0x30+bufferSize);
    	    bufferSize+=1;
    	    gVar1 +=1;
    	    halUsbSendChar(0x30+bufferSize);
    	    halUsbSendChar(0x20);
    	halUsbReceiveBuffer[bufferSize] = UCA1RXBUF;
        __bic_SR_register_on_exit(LPM3_bits);
    }
    

    And this is the output:

    a¢01 b¢12 c¢23 d¢34 e¢45 f¢56 g¢67 h¢78 i¢89

    (I'm typing a b c d e f g h i)

    So there you have it. The program does receive interrupts, and the bufferSize is increased at each interrupt. However, then things turn strange.

    On the first run, the program enters the if(bufferSize>0) statement, even though bSizecopy = 0, implying that bufferSize should also be 0 (and is indeed explicitly initialised at 0). This does not happen if bufferSize is again set to 0 just before while (quit ==0).

    Then, when the program is just running without an interrupt, and the program is paused every now and then, gVar2Copy is changing, indicating that within the main program, global variables can be changed without problems.

    Also, the led is toggling as expected, at 840 KHz.

    When I send one character, bufferSize is increased in the ISR: witnessed from the terminal output. However, in the main loop, the if(bufferSize>0) is never entered. Also, gVar1Copy is never increased. So as far as the main program is concerned, bufferSize was never increased.

    Conclusion:

    Global variables that are accessible in the main program can only be manipulated in main, and global variables that are accessible in an interrupt can for some reason only be manipulated in the interrupt. Reading those those variables locally only gives the correct values when they have been assigned locally.

    How do I fix that?? This code works fine for the TI compiler, but do I have the wrong syntax for defining globals for the gnu compiler? I've removed "extern" but with no success. It looks like a bug to me, but perhaps I overlooked something or made a mistake somewhere.

    I have added the debug code of the original interrupt routine:

    __attribute__((__interrupt__(USCI_A1_VECTOR))) USCI_A1_ISR(void)
    #endif
    
    {
        5e68:	1d 15       	pushm	#2,	r13	;16-bit words
    
    00005e6a <.LCFI1>:
    	halUsbReceiveBuffer[bufferSize++] = UCA1RXBUF;
        5e6a:	5c 42 90 1c 	mov.b	&0x1c90,r12	;0x1c90
        5e6e:	4d 4c       	mov.b	r12,	r13	;
        5e70:	5d 53       	inc.b	r13		;
        5e72:	c2 4d 90 1c 	mov.b	r13,	&0x1c90	;
        5e76:	dc 42 0c 06 	mov.b	&0x060c,7314(r12);0x060c, 0x1c92
        5e7a:	92 1c 
    
    00005e7c <.Loc.144.1>:
        __bic_SR_register_on_exit(LPM3_bits);
        5e7c:	b1 c0 d0 00 	bic	#208,	4(r1)	;#0x00d0
        5e80:	04 00 
    
    00005e82 <.Loc.145.1>:
    }
        5e82:	1c 17       	popm	#2,	r13	;16-bit words
        5e84:	00 13       	reti			

    Unfortunately, I can't compare this with the -dS output of the TI compiler, since that generated a segmentation fault..

  • I have checked this behaviour for the WDT timer too, and for the TI (v4.3.1) this example works, whilst for the GNU compiler (v4.8.0) it doesn't.

    I'm using CCSv6 on linux (CCS6.0.0.00156_linux.tar.gz), using the standard installed compilers. I'm using the MSP-EXP430F5438 board and the MSP-FET430UIF V1.4A with the latest firmware.

    Behaviour:

    the TI compiled example, both leds are toggling, obviously led 1.0 much faster. Both gVar1Copy and gVar2Copy are changed: indicating that both the main loop is running, as well as the ISR. 

    In the GNU compiled example, both leds are toggling too, however only gVar1Copy is increased and not gVar2Copy. So the ISR is running, but values changed in the ISR are not picked up in the main program.

    #include <msp430.h>
    
    unsigned char gVar1=0;
    unsigned char gVar2=0;
    
    int main(void)
    {
    
      volatile unsigned char gVar1Copy;
      volatile unsigned char gVar2Copy;
    
      WDTCTL = WDT_MDLY_32;                     // WDT 32ms, SMCLK, interval timer
      SFRIE1 |= WDTIE;                          // Enable WDT interrupt
      P1DIR |= 0x03;                            // Set P1.0 and P1.1 to output direction
      gVar1Copy = gVar1;
      gVar2Copy = gVar2;
      __bis_SR_register( GIE);       // enable interrupts
    
      while(1)
      {
    	  gVar1+=1;
    	  gVar1Copy = gVar1;
    	  gVar2Copy = gVar2;
    	  P1OUT ^= 0x01;                            // Toggle P1.0 (LED)
      }
      __no_operation();                         // For debugger
    }
    
    // Watchdog Timer interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=WDT_VECTOR
    __interrupt void WDT_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(WDT_VECTOR))) WDT_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
    	gVar2+=1;
      P1OUT ^= 0x02;                            // Toggle P1.1 (LED)
    }
    

  • Shouldn't it be the gVar variables that are volatile, not the copies in main()?  gVar2 at least... it's the one being modified by an ISR.

  • Eric is right. Your ISR uses and alters variables from globals.h that are not declared volatile. This allows the compiler to make assumptions about these variables, like updating them in memory (from a local register copy) can be delayed until next function call, or keeping a copy in a register is okay, as the code flow doesn’t alter it (but the ISR does).

    This might explain the oddities you noticed.

    In the last version, it makes no sense to declare the copie sin main as volatile. They are auto storage, that means the compiler decides where to plae them (in a register or on stack) and therefore knows exactly that nobody else can alter them, So the volatile keyword is ignored. This may be different if you pass a reference to a local variable to a function or a global. But as long as you don't, the volatile keyword for locals is futile.

  • Hi Eric and Jens-Michael,

    thanks for the help. I have declared the global variables as volatile now, and the code works.

    Some questions/remarks:

    1) Jens-Michael: if I don't declare gVar2Copy as volatile, then it will not show up in the "variables" output of the debugger, i.e. I cannot read out their value during debugging.

    2) Why does the TI compiler not require volatile to be declared for global variables? (For the TI compiler, the code works either way). Is that compiler less strict, or perhaps less efficient in weeding out 'unused' statements?

    -----------Correct code, declaring globals in main:--------------

    #include <msp430.h>
    //#include "globals.h"
    
    volatile unsigned char gVar1=0;                // global variables always volatile
    volatile unsigned char gVar2=0;                // global variables always volatile
    
    int main(void)
    {
    
     volatile unsigned char gVar1Copy;          // declare as volatile only during debugging.
     volatile unsigned char gVar2Copy;          // declare as volatile only during debugging.
    
      WDTCTL = WDT_MDLY_32;                     // WDT 32ms, SMCLK, interval timer
      SFRIE1 |= WDTIE;                          // Enable WDT interrupt
      P1DIR |= 0x03;                            // Set P1.0 and P1.1 to output direction
      gVar1Copy = gVar1;
      gVar2Copy = gVar2;
      __bis_SR_register( GIE);       // enable interrupts
    
      while(1)
      {
    	  gVar1+=1;
    	  gVar1Copy = gVar1;
    	  gVar2Copy = gVar2;
    	  P1OUT ^= 0x01;                            // Toggle P1.0 (LED)
      }
      __no_operation();                         // For debugger
    }
    
    // Watchdog Timer interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=WDT_VECTOR
    __interrupt void WDT_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(WDT_VECTOR))) WDT_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
    	gVar2+=1;
        P1OUT ^= 0x02;                            // Toggle P1.1 (LED)
    }
    

    ---------- correct code, but now declaring variables in global.h -----------------

    /*
     * globals.h
     */
    
    #ifndef GLOBALS_H_
    #define GLOBALS_H_
    
    volatile unsigned char gVar1=0;      // global variables always volatile
    volatile unsigned char gVar2=0;      // global variables always volatile 
    
    #endif
    

    ------------ Correct code for the main of this example --------

    #include <msp430.h>
    #include "globals.h"
    
    //volatile unsigned char gVar1=0;
    //volatile unsigned char gVar2=0;
    
    int main(void)
    {
    
     volatile unsigned char gVar1Copy;          // declare as volatile only during debugging.
     volatile unsigned char gVar2Copy;          // declare as volatile only during debugging.
    
      WDTCTL = WDT_MDLY_32;                     // WDT 32ms, SMCLK, interval timer
      SFRIE1 |= WDTIE;                          // Enable WDT interrupt
      P1DIR |= 0x03;                            // Set P1.0 and P1.1 to output direction
      gVar1Copy = gVar1;
      gVar2Copy = gVar2;
      __bis_SR_register( GIE);                  // enable interrupts
    
      while(1)
      {
    	  gVar1+=1;
    	  gVar1Copy = gVar1;
    	  gVar2Copy = gVar2;
    	  P1OUT ^= 0x01;                        // Toggle P1.0 (LED)
      }
      __no_operation();                         // For debugger
    }
    
    // Watchdog Timer interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=WDT_VECTOR
    __interrupt void WDT_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(WDT_VECTOR))) WDT_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
    	gVar2+=1;
        P1OUT ^= 0x02;                          // Toggle P1.1 (LED)
    }
    

  • I'm making an assumption here as I'm not familiar with the TI compiler, but I bet the TI compiler is smart enough to understand that variables referenced in an ISR should be implicitly treated as "volatile" whereas GCC might not have that sort of feature.  I guess the GCC folks are mostly used to compiling code on higher-level O/S's.

  • michael mentink said:
    Some questions/remarks:

    1)     Apparently, the compiler has detected the usage of gVar2Copy as superfluous or redundant (which is actually is, as its only purpose apparently is to be there for debugger inspection) Same should apply to gVar1Copy. It’s also possible that the compiler has put both into a register. Who knows what a compiler does when optimizing. Apparently the volatile keyword doe shave an effect on local vars on your compiler, but it isn’t guaranteed to have on others. (in general, the exact effect of the volatile keyword is not defined by the C standard). Each compiler, even each compiler revision can handle it differently.

    2)     Again, each compiler handles it differently. Optimization algorithms are a well-kept secret of a compiler manufacturer. Also, the default optimization levels (and their meaning) may vary between different toolchains. General experience is that IAR does a slightly better job optimizing code size - if you let it. So using volatile likely (and apparently) has a greater impact.

    Eric: the compiler cannot know this. Imagine that x is a global variable. Main() is in main.c and the ISR is in isr.c.
    That measn whiel compiling main.c, the compiler doesn't know that it will later be asked to compiler isr.c and that it will contain an ISR that will access the global var.
    The linker would know, but then it is far too late.
    An optimization like this coudl only be done when it is sure that the compile will compaile the whole program, including all library code, in a single, monolithic block of code. And all ISRs are before any other code.

    Writing an optimization for this more than unlikely case is a waste of resources.

**Attention** This is a public forum