I had a FRAM experimenter’s board that I lent to a student for use with mspgcc4 and mspdebug. After a few hours of work, my student informed me that the board quit working and mspdebug presented an error about the fuse being blown.

It seemed improbable that the fuse had become blown (which you do by carefully flashing specific values to special addresses in the flash memory), since we were not experimenting with anything remotely related to fuse setting. A few Google searches turned up this thread, http://www.mail-archive.com/mspgcc-users@lists.sourceforge.net/msg10446.html,and TI tech support suggested that I use the Boot-Strap Loader (BSL) to reset the chip http://www.ti.com/lit/ug/slau319a/slau319a.pdf

Since I’ve worked through UART communications with the Launchpad at MSP430 Launchpad, it seemed obvious that the way to recover my FRAM board is to program a Launchpad to access the Flash BSL (detailed in Chapter 3 of the pdf) and give the Mass Erase command.

#include <msp430g2211.h>

/* FRAM experimenter Mass Erase application.  Invokes the FRAM
 * boot-strap loader and gives the the Mass Erase command via
 * 9600 bps UART communication, 8 bit data, even parity, 1 stop bit.
 * This code requires an external clock crystal for calibration
 * of 4 Mhz clock speed, unless you change HAVE_CRYSTAL below.
 */

#define   RED_LED   BIT0
#define   GRN_LED   BIT6

#define   HAVE_CRYSTAL 1  // Or 0 if you don't have a clock crystal.

#define   FRAM_RST  BIT4  // Connect Launchpad 1.4 to FRAM RST pin.
#define   FRAM_TEST BIT5  // Connect Launchpad 1.5 to FRAM TEST pin.
#define   TXD       BIT1  // Connect Launchpad 1.1 to FRAM RXD pin.
#define   RXD       BIT2  // Connect Launchpad 1.2 to FRAM TXD pin.

/* Ticks per bit, and ticks per half.  Use the following values
 * based on speed: 9600 bps ->  52
 */
#define   TPB      52
#define   TPH      TPB/2

volatile unsigned int parity = 0;
volatile unsigned int TXWord = 0;
volatile unsigned int RXWord = 0;
volatile unsigned int rxbitcnt = 0;
volatile unsigned int txbitcnt = 0;

/* circular buffers for characters received/to send */
#define BSIZE 16                // must be power of 2
volatile unsigned char send_buffer[BSIZE];
volatile unsigned char recv_buffer[BSIZE];
volatile unsigned int sbhead=0, sbtail=0, rbhead=0, rbtail=0,
             bytestosend=0, bytesreceived=0;

/* function prototypes */
unsigned char set_dco_c(unsigned int delta);
void initUart( void );
inline void RX_Start( void );
unsigned char RX_Byte( void );
void TX_Byte( unsigned char c );

/* A crude delay function.  Tune by changing the constant. */
inline void delay( unsigned int n ) {
    volatile unsigned int i = n<<2;
    while( i-- ) ;
}

void main(void) {
    int i, n=32;
    unsigned char erase[] = {
        0x80, 0x01, 0x00, 0x15, /* command */
        0x64, 0xA3 /* checksum */ };
    unsigned char c;

    /* stop the watchdog timer */
    WDTCTL = WDTPW + WDTHOLD;

    /* Reset and Test pins start low */
    P1OUT &= ~ (FRAM_RST + FRAM_TEST);
    P1DIR |= FRAM_RST + FRAM_TEST;
    delay(n);

    /* LEDs off, but we can use them for debugging if we want */
    P1DIR |= RED_LED+GRN_LED;
    P1OUT &= ~ (RED_LED + GRN_LED );

    /* set clock speed and initialize the timers and data pins */
    initUart();

    /* Toggle Test pin. */
    P1OUT |= FRAM_TEST;
    delay(n);
    P1OUT &= ~FRAM_TEST;
    delay(n);

    /* Transition Test then Reset in order */
    P1OUT |= FRAM_TEST;
    delay(n);
    P1OUT |= FRAM_RST;
    delay(n);
    P1OUT &= ~FRAM_TEST;        // final transition starts the BSL

    delay(1500);

    /* Start listening for data, and enable interrupts, 9600 8E1. */
    RX_Start();
    /**** Serial is listening from here forward. ****/
    __bis_SR_register( GIE );

    /* Send the Mass Erase command. */
    P1OUT |= RED_LED;
    for( i = 0; i < sizeof( erase ); i++ ) {
        TX_Byte( erase[i] );
    }

    /* Wait for acknowledgement. */
    while( ! bytesreceived ) ;

    P1OUT |= GRN_LED;

    c = RX_Byte();
    if( c == 0x00 ) {   // Response of 0x00 is success
        P1OUT &= ~RED_LED;
    }

    for( ; ; ) {
        /* go to sleep and wait for data */
        __bis_SR_register( LPM0_bits + GIE );
    }
}

/* Pass a delta value to set the DCO speed.  Values for delta:
 *  244 -> 1 MHz   1220 -> 5 Mhz   2197 ->  9 Mhz   3173 -> 13 Mhz
 *  488 -> 2 Mhz   1464 -> 6 Mhz   2441 -> 10 Mhz   3417 -> 14 Mhz
 *  732 -> 3 Mhz   1708 -> 7 Mhz   2685 -> 11 Mhz   3662 -> 15 Mhz
 *  976 -> 4 Mhz   1953 -> 8 Mhz   2929 -> 12 Mhz   3906 -> 16 Mhz
 * General formula:
 *  floor(x hz / 4096) -> delta
 * Return values:
 *  0   - DCO set
 *  255 - Timeout
 * Adapted from code by J. B. Remnant.
 */
unsigned char set_dco_c(unsigned int delta) {
  unsigned int Compare, Oldcapture = 0;
  int direction=0;
  volatile int i;
  unsigned char signchg=0;

  /* set auxiliary clock to use /8 divider (requires external crystal) */
  BCSCTL1 |= DIVA_3;
  /* Timer A0 capture positive edge, select input 1, capture mode */
  TACCTL0 = CM_1 + CCIS_1 + CAP;
  /* Timer A use submain clock, continuous up mode, clear timer */
  TACTL = TASSEL_2 + MC_2 + TACLR;          // SMCLK, cont-mode, clear

  /* loop 10000 times to set clock */
  for ( i = 10000; i && (signchg<3); i-- ) {
    while (!(CCIFG & TACCTL0)) ;            // Wait until capture occurred
    TACCTL0 &= ~CCIFG;                      // Capture occurred, clear flag
    Compare = TACCR0;                       // Get current captured SMCLK
    Compare = Compare - Oldcapture;         // SMCLK difference
    Oldcapture = TACCR0;                    // Save current captured SMCLK

    if (delta == Compare)
      break;                                // If equal, leave the loop
    else if (delta < Compare)
    {
      DCOCTL--;                             // DCO is too fast, slow it down
      if (DCOCTL == 0xFF)                   // Did DCO roll under?
        if (BCSCTL1 & 0x0f)
          BCSCTL1--;                        // Select lower RSEL

      if( direction > 0 ) {                 // catch for successive direction changes
        signchg++;                          // and increment count when one happens
      } else {
        signchg=0;
      }
      direction = -1;
    }
    else
    {
      DCOCTL++;                             // DCO is too slow, speed it up
      if (DCOCTL == 0x00)                   // Did DCO roll over?
        if ((BCSCTL1 & 0x0f) != 0x0f)
          BCSCTL1++;                        // Sel higher RSEL

      if( direction < 0 ) {                 // catch for successive direction changes
        signchg++;                          // and increment count when one happens
      } else {
        signchg=0;
      }
      direction = +1;
    }
  }

  TACCTL0 = 0;                              // Stop TACCR0
  TACTL = 0;                                // Stop Timer_A
  BCSCTL1 &= ~DIVA_3;                       // ACLK = LFXT1CLK

  /* i>0 means that DCO is set correctly -- set return value accordingly */
  Compare = (i) ? 0 : 255;

  /* delay loop */
  for (i = 0; i < 0x4000; i++) ;

  return Compare;
}

void initUart( void ) {
    /* Set clock speed to 4 Mhz, no divider. */
#if HAVE_CRYSTAL
    set_dco_c( 976 );
#else
    /* If no crystal, this setting will probably work. */
    BCSCTL1 = CALBC1_1MHZ + 4;
    DCOCTL = CALDCO_1MHZ;
#endif
    BCSCTL2 &= ~(DIVS_3);

    /* Set timer A to use continuous mode 4 Mhz / 8 = 500 khz. */
    TACTL = TASSEL_2 + MC_2 + ID_3;

    /* When TXD isn't being used, it should be set to binary 1. */
    TACCTL0 = OUT;

    /* TXD and RXD set for timer function, RXD input, TXD output */
    P1SEL |= TXD + RXD;
    P1DIR &= ~ RXD;
    P1DIR |= TXD;
}

/* This continuously sends bits of the TXWord starting from the
 * least significant bit (the 0 start bit).  One bit is sent every
 * time the handler is activated.  When the bits run out, a new
 * byte is loaded from the data pointer, until bytestosend equals 0.
 */
void TimerA0 (void) __attribute__((interrupt(TIMERA0_VECTOR)));
void TimerA0(void) {
    int i;

    if( txbitcnt ) {
        /* Send least significant bit, by changing output mode to
         * Reset (OUTMOD0|OUTMOD2) for binary 0 and Set (OUTMOD0) for 1. */
        if( TXWord & 0x01 ) {
            TACCTL0 &= ~OUTMOD2;
        } else {
            TACCTL0 |= OUTMOD2;
        }
        TXWord >>= 1;
        txbitcnt --;
    }

    /* If there are no bits left, load the next byte */
    if( !txbitcnt ) {
        if( bytestosend ) {
            TXWord = send_buffer[sbtail++];
            sbtail &= BSIZE-1;
            bytestosend --;

            /* Get the parity bit to make even parity. */
            parity = 0;
            for( i = 0; i < 8; i++ ) {
                parity = ( parity ^ (TXWord>>i));
            }
            parity &= 1;

            /* Load next byte with even parity, 1 stop bit 0x200, and shifted
             * left to make the start bit. */
            TXWord = ( 0x200 | (parity<<8) | TXWord ) << 1;

            /* 1 start bit + 8 data bits + 1 parity + 1 stop bit */
            txbitcnt = 11;
        } else {
            /* turn off interrupts if not receiving */
            if( ! rxbitcnt ) TACCTL0 &= ~ CCIE;
        }
    }

    /* add ticks per bit to trigger again on next bit in stream */
    CCR0 += TPB;
    /* reset the interrupt flag */
    TACCTL0 &= ~CCIFG;
}

void RX_Start( void ) {
    /* Make ready to receive character.  Synchronize, negative edge
     * capture, enable interrupts.
     */
    TACCTL1 = SCS + OUTMOD0 + CM1 + CAP + CCIE;
}

/* Stuffs a byte into the buffer, and schedules it to be sent. */
void TX_Byte( unsigned char c ) {
    if( bytestosend < BSIZE ) {
        send_buffer[sbhead++] = c;
        sbhead &= BSIZE-1;
        bytestosend ++;
    }

    /* Turn on transmitting if needed. */
    if( ! (TACCTL0 & CCIE)) {
        /* Start sending after 1 more bit of mark time. */
        CCR0 = TAR + TPB;
        /* Transmit 1/Mark (OUTMOD0) and enable interrupts */
        TACCTL0 = CCIS0 + OUTMOD0 + CCIE;
    }
}

/* Retrieves a byte from the receive buffer. */
unsigned char RX_Byte( void ) {
    unsigned char c = 0;

    if( bytesreceived ) {
        c = recv_buffer[rbtail++];
        rbtail &= BSIZE-1;
        bytesreceived --;
    }
    return c;
}

void TimerA1 (void) __attribute__((interrupt(TIMERA1_VECTOR)));
void TimerA1(void) {
    /* If we just caught the 0 start bit, then turn off capture
     * mode (it'll be all compares from here forward) and add
     * ticks-per-half so we'll catch signals in the middle of
     * each bit.
     */
    if( TACCTL1 & CAP ) {
        /* 9 bits pending = 8 bits + 1 parity */
        rxbitcnt = 9;
        RXWord = 0;

        /* next interrupt in 1.5 bits (i.e. in middle of next bit) */
        CCR1 += TPH + TPB;

        /* reset capture mode and interrupt flag */
        TACCTL1 &= ~ ( CAP + CCIFG );

        return;
    }

    /* Otherwise we need to catch another bit.  We'll shift right
     * the currently received data, and add new bits on the left.
     */
    RXWord >>= 1;
    if( TACCTL1 & SCCI ) {
        RXWord |= 0x100;
    }
    rxbitcnt --;

    /* last bit received */
    if( ! rxbitcnt ) {
        /* Record this byte and reset for next.
         * Put character in circular recv_buffer (unless full).
         */
        if( bytesreceived < BSIZE ) {
            recv_buffer[rbhead++] = (unsigned char)RXWord;
            rbhead &= BSIZE-1;
            bytesreceived ++;
        }

        /* we're done, reset to capture */
        TACCTL1 = SCS + OUTMOD0 + CM1 + CAP + CCIE;

        return;
    }

    /* add ticks per bit to trigger again on next bit in stream */
    CCR1 += TPB;

    /* reset the interrupt flag */
    TACCTL1 &= ~CCIFG;
}

This code was designed for a Launchpad board that has the clock crystal soldered in place (since it uses that clock to calibrate the main system clock to 4Mhz instead of the default 1Mhz speed). But if you don’t have a crystal, you can change the value of HAVE_CRYSTAL to 0, and the program will try a "best guess" that will probably work.

On your FRAM board, remove the jumpers from TXD, RXD, RST, and TEST. You’re going to connect the "chip side" of these jumpers to your Launchpad board. On your Launchpad board, remove the TXD and RXD jumpers.

These are the connections to make between the boards:

  • FRAM TXD to Launchpad RXD (P1.2)

  • FRAM RXD to Launchpad TXD (P1.1)

  • FRAM RST to Launchpad P1.4

  • FRAM TEST to Launchpad P1.5

  • FRAM Vcc to Launchpad Vcc

  • FRAM GND to Launchpad GND

Connect the USB cable from your computer to your Launchpad card, and load the program.

$ msp430-gcc -O2 -mmcu=msp430g2211 -o fram_bsl.elf fram_bsl.c

$ mspdebug rf2500 "prog fram_bsl.elf"

When the program runs on your Launchpad, you should briefly see the red LED come on. If it was successful, the red LED will go dark and the green LED will remain lit. At that point, your FRAM board should be erased and ready to use again (don’t forget to re-install the jumpers on both boards).

I was asked by someone to provide an elf file, since different compilers can sometimes want slightly different source and syntax. I have generated one and uploaded it. This one is compiled with HAVE_CRYSTAL defined to 0, so it should work on a Launchpad with or without the clock crystal. I believe it will work fine on an msp430g2211, an msp430g2231, or an msp430f2012; perhaps others as well.

Download: fram_bsl.elf

Good luck.