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.

LAUNCHXL-CC1310: Adcbuf driver stucks in ISR after disabling ISRs for a while

Part Number: LAUNCHXL-CC1310
Other Parts Discussed in Thread: CC1310

Hi All!

So here is my problem.

I am using the adcbuf driver on the cc1310.

I modified the adcbufcontinuous No RTOS CCS compiler example from here: https://dev.ti.com/

This is the modified adcBufContinuousSampling.c file:

#include <stdint.h>
#include <stdio.h>
/* For sleep() */
#include <unistd.h>

/* Driver Header files */
#include <ti/drivers/ADCBuf.h>
#include <ti/drivers/dpl/HwiP.h>

/* Example/Board Header files */
#include "Board.h"

#define ADCBUFFERSIZE    (50)

uint16_t sampleBufferOne[ADCBUFFERSIZE];
uint16_t sampleBufferTwo[ADCBUFFERSIZE];


/*
 * This function is called whenever an ADC buffer is full.
 */
volatile uint32_t foo;
void adcBufCallback(ADCBuf_Handle handle, ADCBuf_Conversion *conversion,
    void *completedADCBuffer, uint32_t completedChannel)
{
    foo++;
}

/*
 *  ======== mainThread ========
 */
void *mainThread(void *arg0)
{
    ADCBuf_Handle adcBuf;
    ADCBuf_Params adcBufParams;
    ADCBuf_Conversion continuousConversion;

    /* Call driver init functions */
    ADCBuf_init();


    /* Set up an ADCBuf peripheral in ADCBuf_RECURRENCE_MODE_CONTINUOUS */
    ADCBuf_Params_init(&adcBufParams);
    adcBufParams.callbackFxn = adcBufCallback;
    adcBufParams.recurrenceMode = ADCBuf_RECURRENCE_MODE_CONTINUOUS;
    adcBufParams.returnMode = ADCBuf_RETURN_MODE_CALLBACK;
    adcBufParams.samplingFrequency = 2000;
    adcBuf = ADCBuf_open(Board_ADCBUF0, &adcBufParams);

    /* Configure the conversion struct */
    continuousConversion.arg = NULL;
    continuousConversion.adcChannel = Board_ADCBUF0CHANNEL0;
    continuousConversion.sampleBuffer = sampleBufferOne;
    continuousConversion.sampleBufferTwo = sampleBufferTwo;
    continuousConversion.samplesRequestedCount = 20;

    if (adcBuf == NULL){
        /* ADCBuf failed to open. */
        while(1);
    }

    /* Start converting. */
    if (ADCBuf_convert(adcBuf, &continuousConversion, 1) !=
        ADCBuf_STATUS_SUCCESS) {
        /* Did not start conversion process correctly. */
        while(1);
    }


    uintptr_t key = HwiP_disable();
    volatile uint32_t foobar;
    // long isr disable to emulate flash write
    // counting up 100000 times corrupts the adcbuf as it will spin in its interrupt forever
    // counting up 100 times is still safe
    int i;
    for (i = 0 ; i < 100000; i++)
    {
        foobar++;
    }

    HwiP_restore(key);

    /*
     * Go to sleep in the foreground thread forever. The ADC hardware will
     * perform conversions and invoke the callback function when a buffer is
     * full.
     */
    while(1) {
        sleep(1);
    }
}

What it does is basically setting up the adcbuf in callback interrupt mode, and the simulating a long interrupt disabling, which might happen when the internal flash is being written.

If the interrupt disabling takes more time then the adcbuf  would take to fill both buffers, then it gets stuck forever in the ADCBufCC26XX_hwiFxn ISR.

By stuck I mean that it just seems to reenter the ADCBufCC26XX_hwiFxn forever, and nothing else gets run anymore.

When might this happen:

In our application we use the internal flash as an emulated eeprom. This gets written every once in a while. An erase and write cycle takes up to ~10ms (whenever the flash is written or erased all interrupts are to be disabled), which is more than enough to crash the adcbuf as demonstrated in this example.

A possible workaround for me would be to disable the adcbuf before writing to the flash.

I was wondering if you did knew about this, because I don't seem to find any warning in the documentation that it might get locked.

The only thing I found is this:

If the ADCBUF driver is setup in ADCBuf_RECURRENCE_MODE_CONTINUOUS mode, the user must assure that the provided callback function is completed before the next conversion completes. If the next conversion completes before the callback function finishes, the DMA will clobber the previous buffer with new data.

For me this only means an overwritten data, but nut an ISR which deadlocks itself.

Obviously I would be thrilled if you had any inputs on how to solve this issue.

Maybe this is even a bugreport?

Thanks for your help!

Cheers!

  • So I did some research.

    I boiled it down to this line not working:

        /* Clear DMA interrupts */
        UDMACC26XX_clearInterrupt(object->udmaHandle, (1 << UDMA_CHAN_AUX_ADC) | (1 << UDMA_CHAN_TIMER0_A));

    It is from the following function in the ADCBufCC26XX.c file

    static void ADCBufCC26XX_hwiFxn (uintptr_t arg);

    It should clear bit 7 and 9, thus writing 0x280 into the register, but it has no effect, even if I try to do it from the debugger view it does not work.

    If I were to write manually from the debugger perspective 0x280 into the mask register: DONEMASK, then the interrupt does not fire continuously anymore.

    So there is something spooky going on with the udma module it seems.

  • Hi

    I have reached out to the driver team, and the feedback I received was that they would not expect things to function correctly when disabling interrupts as you do. The ISR must be executed in order to reset the uDMA operation since it is in pin-pong mode. When running uDMA transaction in pin-pong mode, it would at best not restart the operation if it timed out, but this is not something we have tested.

    You will need to assure the ISR can run and reset the just completed channel before the currently active one completes.  

    BR

    Siri

  • Thanks Siri!

    In the end I also came to the conclusion that the ping-pong mode is not suitable for me.

    Technical background for those who follow up:

    In the ping-pong mode, the udma is prepared for 2 transactions: primary and alternative. It starts with the primary and continuous with the alternative.

    The udma control structure in SRAM holds the number of transactions which need to be performed. If both primary and alternative finish, the it sets the flag in the REQDONE register.

    The ADCBuf driver is not prepared that both primary and alternative settings were consumed and would only set up one  again, the other remaining in "ready" state, still setting the REQDONE flag high, this is the reason it can not be cleared.

    And from now on a "deadlock" appears.

    I use now the ADCBuf_RECURRENCE_MODE_ONE_SHOT mode, which safely works even with a long interrupt lock.

    Obviously it would be possible to modify the simplelink driver, and recompile it, so it could handle this scenario in the ping-pong mode, but that would increase our effort for code maintenance.

    Thanks a lot Siri for verifying this issue!

  • Glad you found a way to get it working. Thank you for sharing your solution and analysis.

    BR

    Siri