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.

MSP430FR5994: Exception Handlers (and TI vs GCC)

Part Number: MSP430FR5994
Other Parts Discussed in Thread: MSP-EXP430FR5994,

As a newcomer to the TI MSP430 / CCS environment, please forgive a naive question!

Short form:

I have written an app that performs correctly when compiled with the TI C compiler.  But when compiled with GCC it fails, ending up with what I presume is an exception at 0x8000 (JMP 0x0000).  So I have three related questions:

  • What is is about the GCC compiler that would cause a problem where the TI compiler wouldn't?
  • Is 0x80000 a special address, or has it simply run off the end of memory (and why?)
  • Where do I learn more about exception processing (and debugging it)?

Some details:

It appears to be failing within a delay loop, which is (effectively):

Fullscreen
1
2
3
4
5
set_led_on();
while (get_counter() < desired) {
asm("nop");
}
set_led_off();
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

If the delay is short, I don't get the exception.  And once the exception happens, I see the LED flicker periodically and the debugger is no longer in control, which would suggest the processor is resetting continually.  I have disabled the WDT and verified that WDTHOLD is set to 1 prior to entering my loop, so that's not the culprit.

I'm using the GNU v9.3.1.11 (Mitto Systems Limited) compiler in CCS 10.3.1.00003.  And as I mentioned, I don't see this problem when using TI v20.2.5LTS.

  • There is nothing in that short bit of code that would cause trouble. So it is probably in some detail which you haven't shown.

    I use the GCC compiler and have never used the TI compiler or this CCS thing.

  • Hi,

    I doubted that you used a wrong link file. For CCS, when you use GCC and TI compiler, it will use different link file format. Usually, you need not change the default. Please check the setting and import a example project from resource explorer and compare the compiler settings. 

  • Hawken Li:

    I noticed that if you build a project using one compiler, you cannot (successfully) switch to the other compiler via the Project => Property settings.

    Instead, it appears to be necessary to stay with the compiler the project was built with.

    I will try creating a stripped down version of my app -- starting from an example project -- so we can better understand what's happening.

    Thank you for the info.  I'll let you know what happens.

  • @David Schutlz36: Good point.  I'm beginning to think this may be a CCS issue rather than a code issue.  (See Hawken Li's reply below.)

  • I'm including a complete test program to demonstrate the issue.

    To Replicate:

    First verify under the TI compiler:

    1. In CCS 10.3.1, File => New => CCS Project
    2. Name the project timer_test_ti, and select TI v20.5.2LTS as the compiler
    3. Replace the contents of main.c with the code provided below
    4. Compile and run under the debugger on an MSP-EXP430FR5994 dev board

    Observe: the LED turns on and off every 5 seconds.

    Now test under the GCC compiler:

    1. In CCS 10.3.1, File => New => CCS Project
    2. Name the project timer_test_gcc and select GNU v9.3..1.11 as the compiler
    3. Replace the contents of main.c with the code provided below
    4. Compile and run under the debugger on an MSP-EXP430FR5004 dev board

    Observe: the LED turns on, but before it turns off, the program crashes.  Pausing the debugger displays the message "Break at address "0x4" with no debug information available, or outside of program code."

    What's interesting is that if you place a breakpoint at the call to set_led(false) at line 81, you'll see that it crashes before it arrives there.  But if you place a breakpoint on the preceding NOP (inside the while() loop), it executes the NOP an indeterminate number of times before crashing.

    The test code:

    (As an aside, attempting to insert this as code resulted in a server error: You don't have permission ..., so I'm inserting it inline)

    /**
     * @file timer_test.c
     *
     * Verify a simple program for the MSP430FR5994, compiled under the TI compiler
     * and the GCC compiler.
     *
     * rdp May 2021
     *
     * This file is to be included as a part of two Code Composer Studio projects:
     * one compiled using the TI compiler and the other using the GCC compiler.
     * Compile and test the code on a MSP-EXP430FR5994 development board.  You
     * should see the LED turn on and off every 5 seconds.
     *
     * However, it appears to work under the TI compiler, but crashes when compiled
     * under the GCC compiler.
     */

    // ============================================================================
    // Includes

    #include <msp430.h>
    #include <stdbool.h>
    #include <stdint.h>
    #include <stdio.h>

    // ============================================================================
    // Definitions and types

    #define RTC_FREQUENCY 32768L
    #define MS_PER_SECOND 1000L
    #define MS_TO_DURATION(ms) ((uint32_t)((ms * RTC_FREQUENCY) / MS_PER_SECOND))

    // ============================================================================
    // Forward declarations

    static void set_led(bool on);
    static void init_gpios(void);

    static void init_buttons(void);
    static void on_button1_press(void);
    static void on_button2_press(void);

    static void init_timer(void);
    uint32_t get_time(void);          // get current time as a 32-bit value
    static uint16_t get_timer_l(void);
    static void on_timer_overflow(void);


    // ============================================================================
    // Local storage

    /**
     * @brief s_timer_h holds the high-order 16 bits of a 32 bit timer/counter.
     * It is incremented every time the 16 bit TA0 timer overflows.
     */
    static uint16_t s_timer_h;

    // ============================================================================
    // Main code

    /**
     * main.c
     */
    int main(void) {
      WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
      init_gpios();
      init_buttons();
      init_timer();
      __enable_interrupt();

      while (true) {
        uint32_t until;

        set_led(true);
        printf("LED should be on for 5 seconds:\n");
        until = get_time() + MS_TO_DURATION(5000);
        while (get_time() < until) {
          asm(" nop");
        }

        set_led(false);
        printf("LED should be off for 5 seconds:\n");
        until = get_time() + MS_TO_DURATION(5000);
        while (get_time() < until) {
          asm(" nop");
        }
      }
      return 0;
    }

    // ============================================================================
    // Local code

    // ====================================
    // Managing GPIOs

    /**
     * @brief Initialize the GPIOs
     */
    static void init_gpios() {
      // Configure all unused GPIOs as outputs to save power.
      P1OUT = (0x00);
      P1DIR = (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);
      // P2.0 configured as UART TX
      // P2.1 configured as UART RX
      P2OUT = (0x00);
      P2DIR = (BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);
      P2SEL0 &= ~(BIT0 | BIT1);
      P2SEL1 |= (BIT0 | BIT1);
      P3OUT = (0x00);
      P3DIR = (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);
      P4OUT = (BIT5);
      P4DIR = (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);
      // P5.5 configured as SW2, Input pull-up
      // P5.6 configured as SW1, Input pull-up
      P5OUT = (BIT5 | BIT6);
      P5DIR = (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT7);
      P5REN |= (BIT5 | BIT6);
      P6OUT = (0x00);
      P6DIR = (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);
      P7OUT = (0x00);
      P7DIR = (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);
      P8OUT = (0x00);
      P8DIR = (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);
      PJOUT = (0x00);
      PJDIR = (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);

      // Disable the GPIO power-on default high-impedance mode to activate
      // previously configured port settings (not necessary since we re-
      // initialize all GPIOs on startup...)
      PM5CTL0 &= ~LOCKLPM5;
    }

    /**
     * @brief Set the demo LED on or off.
     */
    static void set_led(bool on) {
      if (on) {
        P1OUT |= BIT0;
      } else {
        P1OUT &= ~BIT0;
      }
    }

    // ====================================
    // Managing the buttons

    static void init_buttons(void) {
      // Configure SW1 and SW2 for interrupts
      P5IES = (BIT5 | BIT6); // Hi/Low edge
      P5IFG = 0;             // Clear flags
      P5IE = (BIT5 | BIT6);  // interrupt enabled
    }

    static void on_button1_press(void) { asm(" nop"); }

    static void on_button2_press(void) { asm(" nop"); }

    // Port 4 interrupt service routine for buttons
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = PORT5_VECTOR
    __interrupt void Port_5(void)
    #elif defined(__GNUC__)
    void __attribute__((interrupt(PORT5_VECTOR))) Port_5(void)
    #else
    #error Compiler not supported!
    #endif
    {
      if (P5IFG & BIT6) {
        on_button1_press();
        P5IFG &= ~BIT6; // Clear IFG
      }
      if (P5IFG & BIT5) {
        on_button2_press();
        P5IFG &= ~BIT5; // Clear IFG
      }
      __bic_SR_register_on_exit(LPM3_bits); // Exit LPM3
    }

    // ====================================
    // Time Management.

    /**
     *  @brief TA0 is configured in continuous count up mode, clocked at 32KHz. As a
     *  16-bit counter, it will overflow once every (2^16)/32768 = 2 seconds.  The
     *  overflow interrupt will increment s_timer_h, which we use as the high-order
     *  16 bits of a composite 32-bit counter.
     */

    static void init_timer() {
      TA0CTL = TASSEL__ACLK |   // 32768 Hz source
               MC__CONTINUOUS | // continuous mode
               TAIE |           // Interrupt on overflow
               0;               // TACLR;
      s_timer_h = 0;
    }

    /**
     * @brief Return the number of 32KHz ticks since reboot as a 32 bit value.
     */
    uint32_t get_time(void) {
      uint16_t timer_h;
      uint16_t timer_l;

      do {
        // take snapshot of high order and low order bits of timer
        timer_h = s_timer_h;
        timer_l = get_timer_l();
        // snapshots are valid only if high order word hasn't changed
      } while (timer_h != s_timer_h);

      // assemble snapshots of high and low 16 bits into a 32 bit value.
      return ((uint32_t)timer_h << 16) | (timer_l);
    }

    /**
     * @brief Read the 16 bit timer
     */
    static uint16_t get_timer_l(void) { return TA0R; }

    /**
     * @brief On timer overflow, increment the high-order 16 bits (at interrupt lvl)
     */
    static void on_timer_overflow(void) { s_timer_h += 1; }

    // Timer overflow interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = TIMER0_A1_VECTOR
    __interrupt void Timer(void)
    #elif defined(__GNUC__)
    void __attribute__((interrupt(TIMER0_A1_VECTOR))) Timer_A0(void)
    #else
    #error Compiler not supported!
    #endif
    {
      on_timer_overflow();
      TA0CTL &= ~TAIFG;                     // clear interrupt flag
      __bic_SR_register_on_exit(LPM3_bits); // Exit LPM3
    }

  • Two updates:

    I realized that static uint16_t s_timer_h; should be marked volatile.  After making that change, there was no change in behavior -- the GCC version still crashed.

    I compared the two projects.  As expected (and as appropriate), the GCC project had a linker file named msp430fr5994.ld, and the TI project had a linker file named lnk_msp430fr5994.cmd.

    So I believe the linker files are correct for both projects.

  • I don't see anything that might cause your trouble. Other problems (see the note at 25.2.1 on reading TAxR) but no red flags.

    Use the debugger to examine the system state when things fall apart. Perhaps the stack frame will provide a clue. If all else fails, compare the assembly code generated by the two compilers.

  • @David Schultz36: Thank you for the suggestions.  As I'm coming from the ARM world, which registers should I examine when things fall apart (and what would you look for in those registers)?

    In the meantime, I'll figure out how to get CCS to emit assembly code...

  • It isn't so much registers you want to look at as it is the stack frame. This provides a record of where the program has been.

    I use objdump (msp430-elf-objdump -S) to look at what is in a .elf file output by gcc. I did that with your code and didn't see any obvious trouble. (I don't have a fr5994 to run it on.) Except for that volatile issue you mentioned. The gratuitous use of static complicates things as this tends to make gcc not use labels. Optimization can also obscure program flow in many ways including inlining functions. When I get really confused, I remove that switch. (My default is "-O2".)

  • I don't often use GCC on the MSP430, but running this program with CCS v8.3 (GCC v7.3.2.154 [Mitto]) seems to work fine.

    Some shots in the dark:

    1) Does it change anything if you remove the printf() calls? I vaguely recall that CCS and GCC use different mechanisms.

    2) Check your stack size. I think the stack extends from __heap_end__ to __stack (see .map file).

  • Well, for what its worth, I'm running a newer CCS and a newer GCC.  Regarding the shots in the dark:

    What's really odd is that it dies inside the while() loop, after the printf().  And by putting a breakpoint on the NOP instruction inside the loop, it appears to run the loop anywhere between 30 and 50 times (random) before failing.

    When it fails, it ends up at address 0x4, which is a "jmp ." loop.  And the stack is at the top level, so there's nothing interesting to see what it called before getting jumping there.

    From the .map file:

    • __heap_end = 0x1dc2
    • __stack = 0x2c00

    Inside the while() loop (and when it fails), SP is 0x2bf4, so there's no evidence of a stack overflow.

  • Please remove the printf() then try again. I highly doubted the printf calling in gcc may not include the special command'\n'. 

  • I removed the calls to printf().  The program still fails in the same way.

  • Here is a substantially stripped down version of the code.  When run, it fails by jumping off to uninitialized memory:

    • PC = 0x0A0058 (=> 0x3FFF, or "JMP .")
    • SP = 0x002BF4 (looks reasonable)
    • SR = 0x0

    (Aside: When I try to insert </> code, I still get the error message: You don't have permission to access "">e2e.ti.com/.../configure on this server., so I'm inserting inline.)

    And setting a breakpoint with a skip count on the NOP, it shows that the NOP was executed a random number of times before hanging: 28 times in my most recent test.

    /**
     * @file timer_test.c
     *
     * Verify a simple program for the MSP430FR5994, compiled under the TI compiler
     * and the GCC compiler.
     *
     * rdp May 2021
     *
     * This file is to be included as a part of two Code Composer Studio projects:
     * one compiled using the TI compiler and the other using the GCC compiler.
     * Compile and test the code on a MSP-EXP430FR5994 development board.  You
     * should see the LED turn on and off every 5 seconds.
     *
     * However, it appears to work under the TI compiler, but crashes when compiled
     * under the GCC compiler.
     */

    // ============================================================================
    // Includes

    #include <msp430.h>
    #include <stdint.h>

    // ============================================================================
    // Definitions and types

    #define RTC_FREQUENCY 32768L
    #define MS_PER_SECOND 1000L
    #define MS_TO_DURATION(ms) ((uint32_t)((ms * RTC_FREQUENCY) / MS_PER_SECOND))

    // ============================================================================
    // Local storage

    /**
     * @brief Incremented at interrupt level when the 16 bit TA0 timer overflows,
     * s_timer_h represents the high-order 16 bits of a 32 bit timer/counter.
     */
    volatile uint16_t s_timer_h;

    // ====================================
    // Time Management.

    /**
     *  @brief TA0 is configured in continuous count up mode, clocked at 32KHz. As a
     *  16-bit counter, it will overflow once every (2^16)/32768 = 2 seconds.  The
     *  overflow interrupt will increment s_timer_h, which we use as the high-order
     *  16 bits of a composite 32-bit counter.
     */

    void init_timer() {
      TA0CTL = TASSEL__ACLK |   // 32768 Hz source
               MC__CONTINUOUS | // continuous mode
               TAIE |           // Interrupt on overflow
               0;               // TACLR;
      s_timer_h = 0;
    }

    /**
     * @brief Return the number of 32KHz ticks since reboot as a 32 bit value.
     */
    uint32_t get_time(void) {
      uint16_t timer_h;
      uint16_t timer_l;

      do {
        // take snapshots of high order and low order bits of timer
        timer_h = s_timer_h; // s_timer_h incremented at interrupt level
        timer_l = TA0R;      // get_timer_lo()
        // snapshots are valid only if high order word hasn't changed
      } while (timer_h != s_timer_h);

      // assemble snapshots of high and low words into a 32 bit word.
      return ((uint32_t)timer_h << 16) | timer_l;
    }

    // Timer overflow interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = TIMER0_A1_VECTOR
    __interrupt void Timer(void)
    #elif defined(__GNUC__)
    void __attribute__((interrupt(TIMER0_A1_VECTOR))) Timer_A0(void)
    #else
    #error Compiler not supported!
    #endif
    {
      // Arrive ere on timer overflow (once every 2 seconds)
      s_timer_h += 1;                       // increment the high-order timer word
      TA0CTL &= ~TAIFG;                     // clear interrupt flag
      __bic_SR_register_on_exit(LPM3_bits); // Exit LPM3
    }

    // ============================================================================
    // Main code

    /**
     * main.c
     */
    int main(void) {
      uint32_t until;

      WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer

        // init_gpios();
        P1OUT = (0x00);  // set P1OUT.0 as output for LED
      P1DIR = (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);
        PM5CTL0 &= ~LOCKLPM5;

      init_timer();
      __enable_interrupt();

      P1OUT |= BIT0; // set_led(true);

      // LED should be on for 5 seconds
      until = get_time() + MS_TO_DURATION(5000);
      while (get_time() < until) {
        asm(" nop");
      }
        // Using GCC compiler, never arrives here.
      P1OUT &= ~BIT0; // set_led(false);

      return 0;
    }

  • First off 0xA0058 is non-existent memory and not uninitialized since there is only 256KB on this part.

    I was wondering if your leaving MCLK at the power on default could be trouble but when reading the docs again I found other trouble since you seem to be assuming a 32KHz crystal controlled clock is available on ACLK. You have to do a little work with the clock system to get that to happen. Here is what I do on a fr5972:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    FRCTL0 = FWPW|NWAITS_1; // set 1 FRAM wait state
    FRCTL0_H = 0; // lock FRAM control
    CSCTL0 = CSKEY; // unlock clock system registers
    CSCTL1 = DCORSEL|DCOFSEL_4; // Set DCO to 16MHz
    CSCTL2 = SELA__LFXTCLK|SELS__DCOCLK|SELM__DCOCLK; // ACLK = XT1, MCLK = DCO
    CSCTL3 = DIVA__1|DIVM__1|DIVS__16; // set clock dividers
    // Startup the LFXT
    PJSEL0 |= BIT4|BIT5; // assign pins to oscillator
    CSCTL4 &= ~LFXTOFF; // LFXT On
    // Wait for oscillator to start
    do {
    CSCTL5 &= ~LFXTOFFG; // clear fault flag
    SFRIFG1 &= ~OFIFG;
    } while(SFRIFG1&OFIFG);
    CSCTL0_L = 0; // lock clock system registers
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

  • @David Schultz36: Improperly initializing (or failing to initialize) the clocks properly certainly could be a source of trouble, but that raises two more questions:

    • How would an improperly initialized clock cause the PC to jump off into nonexistent memory?
    • Why does this code work reliably under the TI compiler and not under the GCC compiler?  (Perhaps the crt0 startup code is different?)

    I've pared down the problem code to even smaller, here in its entirety:

    /**
     * @file timer_test.c
     *
     * This code works when compiled by the TI compiler, but crashes when compiled
     * by the GCC compiler.
     */

    #include <msp430.h>

    int main(void) {

      WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer

      // init_gpios();
      P1OUT = (0x00); // set P1OUT.0 as output for LED
      P1DIR = (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);
      PM5CTL0 &= ~LOCKLPM5;

      // init_timer()
      TA0CTL = TASSEL__ACLK |   // 32768 Hz source
               MC__CONTINUOUS | // continuous mode
               TACLR;           // TACLR;

      // LED should be on for 0x7fff clock ticks
      P1OUT |= BIT0; // set_led(true);
      while (TA0R < 0x7fff) {
        asm(" nop");
      }
      // Using GCC compiler, never arrives here.
      P1OUT &= ~BIT0; // set_led(false);

      return 0;
    }

    The problem almost certainly has to be in the while (TA0R < 0x7fff) statement, since I see the LED go on, and a breakpoint on the NOP with a skip count of 100 only gets called about 30 to 50 times before the PC wanders off into no-no land.

    I checked the generated code.  Under the TI compiler (the working case), the while() loop looks like this:

    39        while (TA0R < 0x7fff) {
    01001c:   90B2 7FFF 0350      CMP.W   #0x7fff,&TA0_TA0R
    010022:   2C05                JHS     (0x002e)
    40          asm(" nop");
    010024:   4303                NOP     
    39        while (TA0R < 0x7fff) {
    010026:   90B2 7FFF 0350      CMP.W   #0x7fff,&TA0_TA0R
    01002c:   2BFB                JLO     (0x0024)
    43        P1OUT &= ~BIT0; // set_led(false);
    01002e:

    The corresponding while() loop compiled under GCC (the failing case) looks like this:

    39        while (TA0R < 0x7fff) {
    00402c:   421C 0350           MOV.W   &TA0_TA0R,R12
    004030:   403D 7FFE           MOV.W   #0x7ffe,R13
    004034:   9C0D                CMP.W   R12,R13
    004036:   2807                JLO     (0x4046)
    40          asm(" nop");
    004038:   4303                NOP     
    39        while (TA0R < 0x7fff) {
    00403a:   421C 0350           MOV.W   &TA0_TA0R,R12
    00403e:   403D 7FFE           MOV.W   #0x7ffe,R13
    004042:   9C0D                CMP.W   R12,R13
    004044:   2FF9                JHS     (0x4038)
    004046:

    I didn't see anything in the errata sheets that would suggest a problem for the GCC code.

    Update:

    I ran the output of the two versions (TI and GCC) through msp430-elf-objdump and it's evident that the TI version does some initialization that the GCC version does not.  This could explain a lot.  This leads to one more (and hopefully final) question: where can I find the startup code for the TI and for the GCC versions?  I'd like to verify that the startup code accounts for the difference.

    Update 2:

    I'm going to file a new question ("where can I find the startup code for the TI and for the GCC versions") since that seems the likely cause, but it's now deeply buried in this thread.  If that does NOT explain the cause of the hang, I'll revisit this thread.

  • @David Schultz36: I ran the GCC compiled code after running the TI compiled code and -- surprise -- it completed without crashing.  This gives weight to your theory about some registers not being correctly initialized.  I'm hoping that comparing the TI startup code against the GCC startup code will show why that's the case.

  • Could you compare it with assembly so that we can analyze the root cause? 

  • Certainly.  I have two files: test-gcc.objdump is the code as assembled by the GCC compiler, test-ti.objdump is the code as assembled by the TI compiler.  I can see that the TI version calls __mpu_init() while the GCC version does not.

    Is there a way to attach files to this thread?  If not, I'll just copy and paste the contents...

  • You can use Insert->File attach the file.

  • I tried attaching a file via Insert => Image/video/file, then clicking on [Upload], selecting the file name, but no filename appeared in the dialog, and clicking [Ok] had no effect.

  • That could explain things.  Unlike the cause described in MSP430-GCC-OPENSOURCE: GCC 9.2.0.50 msp430fr5994.ld .lowtext bugfix, my example isn't using ISRs.

  • : My stripped down example doesn't use interrupts at all, yet still fails.  My reading of the bug reports that you supplied is that they only apply to interrupt handling.  Did I read that incorrectly?

  • Configuring the timer to use ACLK generates an ACLK request which will result in an oscillator fault because the LFXT pins are configured as I/O. This isn't supposed to generate an interrupt unless OFIE is set. Or so the documents say.

    An easy test is to switch the timer to SMCLK. Or provide a user NMI handler.

  • That is correct, other bug report was ISR related.

    I have just tried to compile your minimal example using CCS Cloud https://dev.ti.com/ide/ 

    New CCS project, GNU v9.3.1.11, MSP430FR5994,
    uploaded it on the MSP-EXP430FR5994 launchpad.
    And the red LED blinks as expected. So I cannot reproduce the bug.

**Attention** This is a public forum