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-CC1350: Interrupts fail to fire when executing application from SRAM

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

I am going to create a new thread for this as the discussion is beginning to stray from the original topic.

The original thread is located here CC1310 and CC2640 flash programming via JTAG port.

I am attempting to execute code directly from SRAM in an attempt to create a FLASH boot loader for the CC1310/CC1350 that can be loaded through JTAG (SWD) from another ARM based MCU to allow firmware update/recovery of the CC1310/CC1350 regardless of what state it might be in while requiring a minimum number of connections.

I have been able to, more or less, to accomplish this, however, interrupts are not working correctly.

Basically I created a simple LED blink application that uses a timer interrupt to blink the red and green LEDs on the CC1350.

The application works without any issues when executed from FLASH.

I relocated it to SRAM, and it compiles/links correctly, however, interrupts do not fire.

What I have been able to confirm so far...

I do know interrupts flags are being set correctly by adding this code to the main function:

    while(1) {
        if(TimerIntStatus(GPT0_BASE, TIMER_TIMA_TIMEOUT)) {
            ISR_TimerInterrupt_Handler();
        }
    }

The complete code is provided below along with the linker script used.

In the thread referenced above I have also provided the map file and other related information.  It is a little long so I am trying to avoid duplicating it here.

However, I still don't know why the interrupt is not called when the flag is set.

This is going to be a little on the technical side but I can confirm some registers are set properly...

Register 0xE000ED08 (CPU_SCS_VTOR, the vector relocation register) has a correct value of 0x20001000.

If I pause execution I can bring up these registers, which are associated with the timer, which shows that the interrupt is properly enabled (TATOIM = 1), the interrupt has occurred (TATOMIS = 1), however, the function registered with the interrupt is not being called. PRIMASK is 0x00000000 (global interrupt mask disabled).

Everything seems to be setup correctly, but no interrupt occurs.

Suggestions of where to go from here to try to narrow down this issue would be highly appreciated.

/*
 * main.c
 */
#include <stdint.h>
#include <stdbool.h>
#include <boot_loader.h>
#include "driverlib/gpio.h"
#include "driverlib/ioc.h"
#include "driverlib/prcm.h"
#include "driverlib/timer.h"
#include "driverlib/sys_ctrl.h"
#include "inc/hw_memmap.h"
#include "inc/hw_ints.h"

void led_setup(void);
void ISR_TimerInterrupt_Handler(void);
void timer_setup(void);

/*
 *  ======== main ========
 *  Main function
 */
int main(void) {
    // setup the LEDs
    led_setup();

    // setup the timer
    timer_setup();

    // enable the master interrupt
    IntMasterEnable();

    // loop forever waiting for an interrupt
    for(;;);

    if (bootloaderOpened())
    {
      // Choose between SPI and UART, and do baud rate detection for UART
      PickInterface();

      // Boot loader control loop. Does not return.
      Update();
    }
    else
    {
      // Change the below constant to match the application image intvec start.
      //             vvvvvv
      asm(" MOV R0, #0x1010 ");     // The .resetVecs or .intvecs for the app are
                                    // are placed at the constant #0xXXXX address
      asm(" LDR SP, [R0, #0x0] ");  // Load the initial stack pointer
      asm(" LDR R1, [R0, #0x4] ");  // Load the Reset vector address
      asm(" BX R1 ");               // Jump to the application Reset vector
    }
    return 0;
}

/*
 *  ======== led_setup ========
 *  Setup the red and green LEDs
 */
void led_setup(void)
{
    // Power on periperal domain
    PRCMPowerDomainOn(PRCM_DOMAIN_PERIPH);
    while(PRCMPowerDomainStatus(PRCM_DOMAIN_PERIPH) != PRCM_DOMAIN_POWER_ON);

    // Power on the GPIO peripheral
    PRCMPeripheralRunEnable(PRCM_PERIPH_GPIO);
    PRCMLoadSet();
    while ( !PRCMLoadGet() );

    // enable output for the red LED
    GPIO_setOutputEnableDio(IOID_6, GPIO_OUTPUT_ENABLE);
    // turn on the red LED
    GPIO_setDio(IOID_6);

    // enable output for the green LED
    GPIO_setOutputEnableDio(IOID_7, GPIO_OUTPUT_ENABLE);
    //turn off the green LED
    GPIO_clearDio(IOID_7);
}

/*
 *  ======== ISR_TimerInterrupt_Handler ========
 *  Interrupt handler to service the TIMER_TIMA_TIMEOUT interrupt.
 */
void ISR_TimerInterrupt_Handler(void)
{
    // clear interrupt flag
    TimerIntClear(GPT0_BASE, TIMER_TIMA_TIMEOUT);
    // toggle the red LED
    GPIO_toggleDio(IOID_6);
    // toggle the green LED
    GPIO_toggleDio(IOID_7);
}

/*
 *  ======== timer_setup ========
 *  Function to setup the GPT0 timer.
 *  Using GPT0 timer A, periodic mode
 *  Interrupt callback to ISR_TimerInterrupt_Handler
 */
void timer_setup(void)
{
    // Power on periperal domain
    PRCMPowerDomainOn(PRCM_DOMAIN_PERIPH);
    PRCMLoadSet();
    while ( !PRCMLoadGet() );

    // Power on the TIMER0 peripheral
    PRCMPeripheralRunEnable(PRCM_PERIPH_TIMER0);
    PRCMLoadSet();
    while ( !PRCMLoadGet() );

    // Enable TIMER0 to continue counting while the MCU sleeps
    PRCMPeripheralSleepEnable(PRCM_PERIPH_TIMER0);

    // Configure the TIMER0
    TimerConfigure(GPT0_BASE, TIMER_CFG_A_PERIODIC);
    // Set initial timer value
    TimerLoadSet(GPT0_BASE, TIMER_A, 0xFFFFFF);
    // Set prescaler
    TimerPrescaleSet(GPT0_BASE, TIMER_A, 0x000000FF);//255
    // Timer to count on positive clock edge
    TimerEventControl(GPT0_BASE,TIMER_A,TIMER_EVENT_POS_EDGE);
    // Be sure the interrupt is clear to start
    TimerIntClear(GPT0_BASE,TIMER_TIMA_TIMEOUT);
    // Assign the interrupt handler
    TimerIntRegister(GPT0_BASE, TIMER_A, ISR_TimerInterrupt_Handler);
    // Enable the interrupt
    TimerIntEnable(GPT0_BASE,TIMER_TIMA_TIMEOUT);
    // Enable the timer
    TimerEnable(GPT0_BASE,TIMER_A);
}

This is the linker file (relevant parts only)

//*****************************************************************************
//! @file       cc13x0f128.cmd
//! @brief      CC13x0F128 rev2 linker file for Code Composer Studio.
...
...
... /* Section allocation in memory */ SECTIONS { .intvecs : > RAM_BASE .text : > SRAM .const : > SRAM .constdata : > SRAM .rodata : > SRAM .binit : > SRAM .cinit : > SRAM .pinit : > SRAM .init_array : > SRAM .emb_text : > SRAM .ccfg : > FLASH (HIGH) .vtable : > SRAM .vtable_ram : > SRAM vtable_ram : > SRAM .data : > SRAM .bss : > SRAM .sysmem : > SRAM .stack : > SRAM (HIGH) .nonretenvar : > SRAM .TI.noinit : > SRAM .gpram : > GPRAM #ifdef __TI_COMPILER_VERSION__ #if __TI_COMPILER_VERSION__ >= 15009000 /* Hide section from older compilers not supporting the "ramfunc" attribute. See processors.wiki.ti.com/.../Placing_functions_in_RAM */ .TI.ramfunc : {} load=FLASH, run=SRAM, table(BINIT) #endif #endif } /* Create global constant that points to top of stack */ /* CCS: Change stack size under Project Properties */ __STACK_TOP = __stack + __STACK_SIZE;

  • Hi,

    You also have to tell the NVIC that it should care about interrupts from the various vectors. driverlib/interrupt.c :: IntEnable( vector );

    You have enabled interrupt generation from the peripheral, which is good, and enabled interrupts globally, also good, but I think that last step is also needed.

    Best regards,
    Aslak

  • Hi Aslak,

    Sorry I have not had time to get back to this issue lately...

    I have tried as suggested but still encounter the issue.

    Inside the driverlib/timer.c :: TimerIntEnable() function this is also called.

    However, I modified my source to call IntEnable, but still encounter the issue.

    Is it possible that I need to setup the NVIC to work off of the new table?

    I am most likely having an issue because of the vector table relocation, however, as I understand it the vector table is moved anyway to SRAM as soon as any interrupt is registered...

    As far as I can tell, interrupt flags are being set as expected, but the NVIC is not interrupting the CPU as expected, is there some setup for the NVIC that I am missing?

    Is the NVIC possibly using a vector table located elsewhere? If this is the cause I would think the CPU would fault, which it is not.

    Further assistance is greatly appreciated! I will continue investigating on my end.

    //*****************************************************************************
    //
    //! Registers an interrupt handler for the timer interrupt
    //
    //*****************************************************************************
    void
    TimerIntRegister(uint32_t ui32Base, uint32_t ui32Timer, void (*pfnHandler)(void))
    {
        uint32_t ui32Int;
    
        //
        // Check the arguments.
        //
        ASSERT(TimerBaseValid(ui32Base));
        ASSERT((ui32Timer == TIMER_A) || (ui32Timer == TIMER_B) ||
               (ui32Timer == TIMER_BOTH));
    
        //
        // Get the interrupt number for this timer module.
        //
        ui32Int = TimerIntNumberGet(ui32Base);
    
        //
        // Register an interrupt handler for timer A if requested.
        //
        if(ui32Timer & TIMER_A)
        {
            //
            // Register the interrupt handler.
            //
            IntRegister(ui32Int, pfnHandler);
    
            //
            // Enable the interrupt.
            //
            IntEnable(ui32Int);
        }
    
        //
        // Register an interrupt handler for timer B if requested.
        //
        if(ui32Timer & TIMER_B)
        {
            //
            // Register the interrupt handler.
            //
            IntRegister(ui32Int + 1, pfnHandler);
    
            //
            // Enable the interrupt.
            //
            IntEnable(ui32Int + 1);
        }
    }
    

  • As a temporary workaround I can use this following code and just pull the interrupt, but this is not ideal...

    This will allow me to continue development until I can resolve the issue with the NVIC controller.

        while(1) {
            if(TimerIntStatus(GPT0_BASE, TIMER_TIMA_TIMEOUT)) {
                ISR_TimerInterrupt_Handler();
            }
        }