Electronic – MPLAB X XC32 – Peculiar build issue involving volatile variables

mplabxpicvariablexc32

I am struggling with an issue that is already costing me 2 days of productive work.

The code of main.c at the end of the question is a stripped-down version of the entire project, and contains the relevant code related to the issue discussed.

A brief summary of the entire setup:
Microcontroller: PIC32MX250F128B
PICkit3
MPLAB X v4.20
XC32 compiler v1.34
Windows 10 64-bit

I have a project that involves receiving data over the UART and performing certain functions based on the data received. A software FIFO buffer is used to temporarily store the received data for processing (since the hardware FIFO is too small for the rate at which data is received). The soft FIFO gets populated with the received data by using an interrupt routine. I am experiencing a very peculiar issue when attempting to build the project (both in debug and release modes).

At first I thought that the problem described below is due to a hardware issue (faulty PICkit3), but the problem persists even if I use the simulator instead of the PICkit3. Then I investigated into possible MPLAB X issues, but the same issue also persists in multiple versions of MPLAB X (I have tested this with v4.05, v4.15 and v4.20). I have also tested this in both Windows 7 and Windows 10, with exactly the same results. Upon further investigation, I have narrowed it down to the soft FIFO and the main() function in main.c.

The soft FIFO is defined as follows (relevant snippets from main.c):

/* Software FIFO buffer size */
#define FIFO_BUFFER_SIZE (2100u)

/* Software FIFO buffer type */
typedef struct
{
    char dataBuffer[FIFO_BUFFER_SIZE];
    int firstByteIndex;
    int lastByteIndex;
    int numBytes;
} SOFT_FIFO;

/* Variables used with FIFO */
static SOFT_FIFO rxFifo = {{0}, 0, 0, 0};
static volatile BOOL fifoFlagBufEmpty = TRUE;
static volatile BOOL fifoFlagFull = FALSE;
static volatile BOOL fifoFlagOverflow = FALSE;

Here is the part in main.c that I have further narrowed down to be related to the issue:

void main(void)
{ 
    char c;

    /* ... initialisation code is here (see full main.c file)... */

    /* ... higher-level infinite loop is here (see full main.c file)... */

    /* While there is data in the software FIFO buffer */
    while (fifoFlagBufEmpty == FALSE)
    {
        /* Process data in the FIFO buffer */
        c = getByte();
        processGeneralMessage(&c);
    } /* while */

} /* main() */

Whenever I build the project for debugging (for both PICKit3 and the simulator), the entire build process just hangs and never completes. It has to be killed manually in the Windows Task Manager by terminating the "cc1.exe" process. After leaving it to try and build for more than 2 hours, I think it is pretty safe to come to the conclusion that it will not complete the build at all.

The apparent cause of the issue seems to be related to how the variable fifoFlagBufEmpty is defined and declared. If I remove the "volatile" specifier from its definition:

//static volatile BOOL fifoFlagBufEmpty = TRUE; /* Old definition */
static BOOL fifoFlagBufEmpty = TRUE;

then the project builds perfectly and the debugger starts executing the code. If I put the "volatile" specifier back, the build hangs again and never completes.

What is even more bizzare is that when I comment out any of the following two lines in the while block:

c = getByte();
processGeneralMessage(&c);

the project builds completely and start executing normally (just like when I remove the "volatile" specifier)!

I have absolutely no explanation for why this is happening. The most frustrating issue is that I have this exact same definition and use of the soft FIFO in another project using another PIC (PIC32MX664F128H) and it works perfectly there.
Can anybody please help me with this and identify why this is happening?

Here is the full main.c file:

/*------------------------------------------------------------------------------

    Module           : main.c

    Microcontroller  : PIC32MX250F128B

------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------
                              CONFIGURATION BITS
------------------------------------------------------------------------------*/
// DEVCFG3
// USERID = No Setting
#pragma config PMDL1WAY = ON            // Peripheral Module Disable Configuration (Allow only one reconfiguration)
#pragma config IOL1WAY = ON             // Peripheral Pin Select Configuration (Allow only one reconfiguration)
#pragma config FUSBIDIO = ON            // USB USID Selection (Controlled by the USB Module)
#pragma config FVBUSONIO = ON           // USB VBUS ON Selection (Controlled by USB Module)

// DEVCFG2
#pragma config FPLLIDIV = DIV_2         // PLL Input Divider (2x Divider)
#pragma config FPLLMUL = MUL_20         // PLL Multiplier (20x Multiplier)
#pragma config UPLLIDIV = DIV_12        // USB PLL Input Divider (12x Divider)
#pragma config UPLLEN = OFF             // USB PLL Enable (Disabled and Bypassed)
#pragma config FPLLODIV = DIV_2         // System PLL Output Clock Divider (PLL Divide by 2)

// DEVCFG1
#pragma config FNOSC = FRCPLL           // Oscillator Selection Bits (Fast RC Osc with PLL)
#pragma config FSOSCEN = OFF            // Secondary Oscillator Enable (Disabled)
#pragma config IESO = ON                // Internal/External Switch Over (Enabled)
#pragma config POSCMOD = OFF            // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = OFF           // CLKO Output Signal Active on the OSCO Pin (Disabled)
#pragma config FPBDIV = DIV_1           // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/1)
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576        // Watchdog Timer Postscaler (1:1048576)
#pragma config WINDIS = OFF             // Watchdog Timer Window Enable (Watchdog Timer is in Non-Window Mode)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (WDT Disabled (SWDTEN Bit Controls))
#pragma config FWDTWINSZ = WINSZ_25     // Watchdog Timer Window Size (Window Size is 25%)

// DEVCFG0
#pragma config JTAGEN = OFF             // JTAG Enable (JTAG Disabled)
#pragma config ICESEL = ICS_PGx1        // ICE/ICD Comm Channel Select (Communicate on PGEC1/PGED1)
#pragma config PWP = OFF                // Program Flash Write Protect (Disable)
#pragma config BWP = OFF                // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF                 // Code Protect (Protection Disabled)

/*------------------------------------------------------------------------------
                                   INCLUDES
------------------------------------------------------------------------------*/
#define _SUPPRESS_PLIB_WARNING /* Suppress PLIB warnings */
#define _DISABLE_OPENADC10_CONFIGPORT_WARNING /* Suppress ADC warning */

#include <p32xxxx.h>
#include <plib.h>

/*------------------------------------------------------------------------------
                                    DEFINES
------------------------------------------------------------------------------*/
#define SYS_CLOCK               (40000000u) /* 40 MHz */
#define SYS_CLOCK_MHZ           (SYS_CLOCK / 1000000)
#define PB_CLOCK                (SYS_CLOCK) /* Use system clock for peripherals */
#define BAUD_RATE_COMMS         (19200u)
#define UART_MODULE             (1u)
#define BRGH_BIT                (FALSE)  

/* Missing interrupt defines in int_1xx_2xx_legacy.h */
#define mU1RXClearIntFlag() (IFS1CLR = _IFS1_U1RXIF_MASK)
#define mU1RXGetIntFlag() (IFS1bits.U1RXIF)
#define mU1RXGetIntEnable() (IEC1bits.U1RXIE)
#define mU1RXIntEnable(enable) (IEC1CLR = _IEC1_U1RXIE_MASK, IEC1SET = ((enable) << _IEC1_U1RXIE_POSITION))
#define mU1RXSetIntPriority(priority) (IPC8CLR = _IPC8_U1IP_MASK, IPC8SET = ((priority) << _IPC8_U1IP_POSITION))

/* Logic state definitions */
#define HIGH (1)
#define LOW  (0)

/* Software FIFO buffer size */
#define FIFO_BUFFER_SIZE        (2100u)

/* LED pin */
#define LED_PIN (LATBbits.LATB7)

/*------------------------------------------------------------------------------
                                LOCAL DATA TYPES
------------------------------------------------------------------------------*/
/* Software FIFO buffer type */
typedef struct
{
    char dataBuffer[FIFO_BUFFER_SIZE];
    int firstByteIndex;
    int lastByteIndex;
    int numBytes;
} SOFT_FIFO;

/*------------------------------------------------------------------------------
                            LOCAL FUNCTION PROTOTYPES
------------------------------------------------------------------------------*/
static unsigned int calcBRG(unsigned int baudRate, 
                            BOOL BRGH_bit, 
                            unsigned int clk);
static void initUART(unsigned int channel,
                     unsigned int baudRate, 
                     BOOL BRGH_bit, 
                     unsigned int clk);
static void initMain(void);
static void configureInterrupts(void);
static char getByte(void);
static void processGeneralMessage(unsigned char *c);

/*------------------------------------------------------------------------------
                                 GLOBAL VARIABLES
------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------
                                  LOCAL VARIABLES
------------------------------------------------------------------------------*/
static char rxData;
static BOOL byteReceived = FALSE;
static int rxCount = 0;
static SOFT_FIFO rxFifo = {{0}, 0, 0, 0};
static volatile BOOL fifoFlagBufEmpty = TRUE;
static volatile BOOL fifoFlagFull = FALSE;
static volatile BOOL fifoFlagOverflow = FALSE;

/*------------------------------------------------------------------------------
                           GLOBAL FUNCTION DEFINITIONS
------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------
  Function   : main
  Purpose    : Entry point of the system.
  Parameters : None.
  Returns    : None.
  Notes:     : None.
------------------------------------------------------------------------------*/
void main(void)
{    
    char c;

    /* Initialise the system */
    initMain();

    /* Wait for commands from the user interface */
    while (1)
    {
        /* While there is data in the software FIFO buffer */
        while (fifoFlagBufEmpty == FALSE)
        {
            /* Process data in the FIFO buffer */
            c = getByte();
            processGeneralMessage(&c);
        } /* while */
    } /* while */
} /* main() */

/*------------------------------------------------------------------------------
                            LOCAL FUNCTION DEFINITIONS
------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------
  Function   : initUART
  Purpose    : Initialises the UART peripheral.
  Parameters : channel - which UART channel to initialise.
               baudRate - baud rate used for UART comms
               BRGH_bit - bit to indicate high/low speed baud rate generator
               clk      - clock speed of the PIC
  Returns    : None.
  Notes      : None.
------------------------------------------------------------------------------*/
static void initUART(unsigned int channel,
                     unsigned int baudRate, 
                     BOOL BRGH_bit, 
                     unsigned int clk)
{
    /* Enable UART, BRGH = 0, simplex, RX idle LOW, 1 stop, no parity */
    unsigned int uxMode = 0x8800; 
    /* Enable RX & TX */
    unsigned int uxSta = 0x1400;
    unsigned int brg;

    brg = calcBRG(baudRate, BRGH_bit, clk);
    switch (channel)
    {
        case 1:
            U1BRG = brg;
            U1MODE = uxMode;
            U1STA = uxSta;
            break;
        case 2:
            U2BRG = brg;
            U2MODE = uxMode;
            U2STA = uxSta;
            break; 
#if (UART_COUNT_MAX >= 3)
         case 3:
            U3BRG = brg;
            U3MODE = uxMode;
            U3STA = uxSta;
            break; 
#endif
    } /* switch */
} /* initUART() */

/*------------------------------------------------------------------------------
  Function   : calcBRG
  Purpose    : Calculates the BRG value required for the UART baud rate 
               generator initialisation.
  Parameters : baudRate - baud rate used for UART comms
               BRGH_bit - bit to indicate high/low speed baud rate generator
               clk      - clock speed of the PIC
  Returns    : BRG value
  Notes      : The formula used to calculate the BRG value is obtained from the
               PIC32 Family Reference Manual, sect. 21 (UART) - in section 21.3.
------------------------------------------------------------------------------*/
static unsigned int calcBRG(unsigned int baudRate, 
                            BOOL BRGH_bit, 
                            unsigned int clk)
{
    unsigned int BRGval;    
    if (BRGH_bit == FALSE) /* low speed baud rate generator used */
    {
        BRGval = (clk / (16 * baudRate)) - 1;
    }
    else /* high speed baud rate generator used */
    {
        BRGval = (clk / (4 * baudRate)) - 1;
    }

    return BRGval;
} /* calcBRG() */

/*------------------------------------------------------------------------------
  Function   : initMain
  Purpose    : Main initialisation routine.
  Parameters : None.
  Returns    : None.
  Notes      : None.
------------------------------------------------------------------------------*/
void initMain(void)
{
    /* Initialise the system clock */
    SYSTEMConfigPerformance(SYS_CLOCK);

    /* Initialise the UART peripheral */
    initUART(1, BAUD_RATE_COMMS, FALSE, PB_CLOCK);

    /* Configure interrupts */   
    configureInterrupts();

    /* Disable JTAG */
    DDPCONbits.JTAGEN = 0;

    TRISBbits.TRISB7 = 0; /* LED pin - output */
    U1RXR = 0x00; /* U1RX on RA2 */
    RPB3R = 0x01; /* U1TX on RB3 */    
} /* initMain() */

/*------------------------------------------------------------------------------
  Function   : configureInterrupts
  Purpose    : Configure the interrupts used in this module.
  Parameters : None.
  Returns    : None.
  Notes      : None.
------------------------------------------------------------------------------*/
static void configureInterrupts(void)
{
    /* Configure UART interrupt */
    mU1RXSetIntPriority(1);    
    INTEnableSystemMultiVectoredInt();
    mU1RXIntEnable(HIGH);
    mU1RXClearIntFlag();
} /* configureInterrupts() */

/*------------------------------------------------------------------------------
  Function   : getByte
  Purpose    : Retrieve the next available data byte from the FIFO buffer.
  Parameters : None.  
  Returns    : Data byte.
  Notes      : None.
------------------------------------------------------------------------------*/
static char getByte(void)
{
    char byteValue = -1;

    /* If data exists in the buffer */
    if (rxFifo.numBytes > 0)
    {
        byteValue = rxFifo.dataBuffer[rxFifo.firstByteIndex];
        rxFifo.firstByteIndex++;
        rxFifo.numBytes--;
        //rxFifo.lastByteIndex--;
    }
    /* NOTE: this condition should actually never be reached */
    else /* If the software FIFO buffer is empty */
    {
        asm("nop"); /* TODO: Doen dalk iets hier? */
        //fifoFlagBufEmpty = TRUE; /* (Now it IS empty) */
        //return byteValue; /* with value = -1, indicating no data */
    }

    /* If the software FIFO buffer is empty, after the above read */
    /* This is the same as (numBytes == 0) */
    if (rxFifo.firstByteIndex == rxFifo.lastByteIndex)
    {
        fifoFlagBufEmpty = TRUE;
        /* Note that numBytes should now also be 0 */
    }

    /* If the index has reached the end of the buffer */
    if (rxFifo.lastByteIndex == FIFO_BUFFER_SIZE)
    {
        rxFifo.lastByteIndex = 0; /* Roll-over the index counter */       
    }

    /* If at the end of the buffer */
    if (rxFifo.firstByteIndex == FIFO_BUFFER_SIZE)
    {
        rxFifo.firstByteIndex = 0; /* Roll-over index counter */
    }

    return byteValue;
} /* getByte() */

/*------------------------------------------------------------------------------
  Function   : processGeneralMessage
  Purpose    : Process general incoming messages from console system.
  Parameters : c - pointer to one byte of the incoming message.
  Returns    : None.
  Notes      : None.
------------------------------------------------------------------------------*/
static void processGeneralMessage(unsigned char *c)
{
    LED_PIN ^= 1; /* Toggle the LED pin */
} /* processGeneralMessage() */

/*------------------------------------------------------------------------------
  Function   : uartRxInterruptHandler
  Purpose    : Interrupt handler for the UART receiver.
  Parameters : None.
  Returns    : None.
  Notes      : None.
------------------------------------------------------------------------------*/
//void __ISR(_UART1_RX_IRQ, ipl1auto)uartRxInterruptHandler(void)
void __ISR(_UART1_VECTOR, ipl1auto)uartRxInterruptHandler(void)
{

    /* If software FIFO buffer is full */
    if (rxFifo.numBytes == FIFO_BUFFER_SIZE)
    {
        fifoFlagOverflow = TRUE;
    } 
    /* If the software FIFO buffer is not full */
    else if (rxFifo.numBytes < FIFO_BUFFER_SIZE)
    {        
        /* Read a byte from the UART receive (hardware) FIFO */
        rxFifo.dataBuffer[rxFifo.lastByteIndex] = U1RXREG;
        rxFifo.lastByteIndex++;
        rxFifo.numBytes++;
    }

    /* If buffer has now been filled up after the byte read above */
    if (rxFifo.numBytes == FIFO_BUFFER_SIZE)
    {
        fifoFlagFull = TRUE;

        /* TODO: Besluit nog wat hier gedoen moet word */
    }
    /* TODO: skuif hierdie iewers anders heen om reg te hanteer */
    fifoFlagBufEmpty = FALSE;
    mU1RXClearIntFlag();

} /* ISR */

Best Answer

It seems that the culprit is the fact that the variable c in main() is not initialised. After initialising it at its declaration, it builds fine as normal.

It would still be enligthening to understand what actually went wrong.