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.

CCS/CC2652: Enabling SVCall handler in Vector Table

I'm reading through the CC26xx Driver Library documentation where in the documentation for interrupt.h it says the following for statically configuring the interrupt vector table:

Static registration of interrupt handlers is accomplished by editing the interrupt handler table in the startup code of the application. Texas Instruments provides startup files for each supported compiler ( startup_<compiler>.c ) and these startup files include a default static interrupt vector table. All entries, except ResetISR, are declared as extern with weak assignment to a default interrupt handler. This allows the user to declare and define a function (in the user's code) with the same name as an entry in the vector table. At compile time, the linker then replaces the pointer to the default interrupt handler in the vector table with the pointer to the interrupt handler defined by the user.

Statically configuring the interrupt table provides the fastest interrupt response time because the stacking operation (a write to SRAM on the data bus) is performed in parallel with the interrupt handler table fetch (a read from Flash on the instruction bus), as well as the prefetch of the interrupt handler (assuming it is also in Flash).

I want to handle SVCalls and according to this paragraph from the documentation I have to define a function with the same name as an entry in the vector table. But when I look in the startup_ccs.c file found in ti/simplelink_cc13x2_26x2_sdk_3_40_00_02/sources/ti/devices/cc13x2_c26x2/startup_files I don't see any names.

I do see an IntDefaultHandler entry with a comment after it saying "// 11 Supervisor Call (SVCall)", but most of the other interrupts also use the IntDefaultHandler. So if that statement in the documentation means I have to define my own IntDefaultHandler function won't I be handling all those other interrupts as well? Also the documentation says that the entries are declared as extern, but in the file I see IntDefaultHandler declared as "static void IntDefaultHandler( void );", so not as an extern.

How do I add my own SVCall handler to this vector table statically? Or is it only possible to do this dynamically?

The code below shows the contents of the startup_ccs.c file.

//*****************************************************************************
//
// Check if compiler is CCS
//
//*****************************************************************************
#if !(defined(__TI_COMPILER_VERSION__))
#error "startup_ccs.c: Unsupported compiler!"
#endif

#include "../inc/hw_types.h"
#include "../driverlib/setup.h"


//*****************************************************************************
//
//! Forward declaration of the reset ISR and the default fault handlers.
//
//*****************************************************************************
void        ResetISR( void );
static void NmiSR( void );
static void FaultISR( void );
static void IntDefaultHandler( void );
extern int  main(void);


//*****************************************************************************
//
//! The entry point for the application startup code and device trim fxn.
//
//*****************************************************************************
extern void _c_int00(void);


//*****************************************************************************
//
// CCS: Linker variable that marks the top of the stack.
//
//*****************************************************************************
extern unsigned long __STACK_END;


//! The vector table. Note that the proper constructs must be placed on this to
//! ensure that it ends up at physical address 0x0000.0000 or at the start of
//! the program if located at a start address other than 0.
//
//*****************************************************************************
#pragma DATA_SECTION(g_pfnVectors, ".intvecs")
void (* const g_pfnVectors[])(void) =
{
    (void (*)(void))((unsigned long)&__STACK_END),
                                            //  0 The initial stack pointer
    ResetISR,                               //  1 The reset handler
    NmiSR,                                  //  2 The NMI handler
    FaultISR,                               //  3 The hard fault handler
    IntDefaultHandler,                      //  4 Memory Management (MemManage) Fault
    IntDefaultHandler,                      //  5 The bus fault handler
    IntDefaultHandler,                      //  6 The usage fault handler
    0,                                      //  7 Reserved
    0,                                      //  8 Reserved
    0,                                      //  9 Reserved
    0,                                      // 10 Reserved
    IntDefaultHandler,                      // 11 Supervisor Call (SVCall)
    IntDefaultHandler,                      // 12 Debug monitor handler
    0,                                      // 13 Reserved
    IntDefaultHandler,                      // 14 The PendSV handler
    IntDefaultHandler,                      // 15 The SysTick handler
    //--- External interrupts ---
    IntDefaultHandler,                      // 16 AON edge detect
    IntDefaultHandler,                      // 17 I2C
    IntDefaultHandler,                      // 18 RF Core Command & Packet Engine 1
    IntDefaultHandler,                      // 19 PKA Interrupt event
    IntDefaultHandler,                      // 20 AON RTC
    IntDefaultHandler,                      // 21 UART0 Rx and Tx
    IntDefaultHandler,                      // 22 AUX software event 0
    IntDefaultHandler,                      // 23 SSI0 Rx and Tx
    IntDefaultHandler,                      // 24 SSI1 Rx and Tx
    IntDefaultHandler,                      // 25 RF Core Command & Packet Engine 0
    IntDefaultHandler,                      // 26 RF Core Hardware
    IntDefaultHandler,                      // 27 RF Core Command Acknowledge
    IntDefaultHandler,                      // 28 I2S
    IntDefaultHandler,                      // 29 AUX software event 1
    IntDefaultHandler,                      // 30 Watchdog timer
    IntDefaultHandler,                      // 31 Timer 0 subtimer A
    IntDefaultHandler,                      // 32 Timer 0 subtimer B
    IntDefaultHandler,                      // 33 Timer 1 subtimer A
    IntDefaultHandler,                      // 34 Timer 1 subtimer B
    IntDefaultHandler,                      // 35 Timer 2 subtimer A
    IntDefaultHandler,                      // 36 Timer 2 subtimer B
    IntDefaultHandler,                      // 37 Timer 3 subtimer A
    IntDefaultHandler,                      // 38 Timer 3 subtimer B
    IntDefaultHandler,                      // 39 Crypto Core Result available
    IntDefaultHandler,                      // 40 uDMA Software
    IntDefaultHandler,                      // 41 uDMA Error
    IntDefaultHandler,                      // 42 Flash controller
    IntDefaultHandler,                      // 43 Software Event 0
    IntDefaultHandler,                      // 44 AUX combined event
    IntDefaultHandler,                      // 45 AON programmable 0
    IntDefaultHandler,                      // 46 Dynamic Programmable interrupt
                                            //    source (Default: PRCM)
    IntDefaultHandler,                      // 47 AUX Comparator A
    IntDefaultHandler,                      // 48 AUX ADC new sample or ADC DMA
                                            //    done, ADC underflow, ADC overflow
    IntDefaultHandler,                      // 49 TRNG event
    IntDefaultHandler,                      // 50 Combined event from Oscillator control
    IntDefaultHandler,                      // 51 AUX Timer2 event 0
    IntDefaultHandler,                      // 52 UART1 combined interrupt
    IntDefaultHandler                       // 53 Combined event from battery monitor
};


//*****************************************************************************
//
//! This is the code that gets called when the processor first starts execution
//! following a reset event. Only the absolutely necessary set is performed,
//! after which the application supplied entry() routine is called. Any fancy
//! actions (such as making decisions based on the reset cause register, and
//! resetting the bits in that register) are left solely in the hands of the
//! application.
//
//*****************************************************************************
void
ResetISR(void)
{
    //
    // Final trim of device
    //
    SetupTrimDevice();

    //
    // Jump to the CCS C Initialization Routine.
    //
    __asm("    .global _c_int00\n"
            "    b.w     _c_int00");

    //
    // If we ever return signal Error
    //
    FaultISR();
}

//*****************************************************************************
//
//! This is the code that gets called when the processor receives a NMI. This
//! simply enters an infinite loop, preserving the system state for examination
//! by a debugger.
//
//*****************************************************************************
static void
NmiSR(void)
{
    //
    // Enter an infinite loop.
    //
    while(1)
    {
    }
}

//*****************************************************************************
//
//! This is the code that gets called when the processor receives a fault
//! interrupt. This simply enters an infinite loop, preserving the system state
//! for examination by a debugger.
//
//*****************************************************************************
static void
FaultISR(void)
{
    //
    // Enter an infinite loop.
    //
    while(1)
    {
    }
}


//*****************************************************************************
//
//! This is the code that gets called when the processor receives an unexpected
//! interrupt. This simply enters an infinite loop, preserving the system state
//! for examination by a debugger.
//
//*****************************************************************************
static void
IntDefaultHandler(void)
{
    //
    // Go into an infinite loop.
    //
    while(1)
    {
    }
}

  • Greetings,

    Perhaps this (past successful) set-up of the Start-Up file assists:   (this an extract from a "TM4C123 (other) MCU" application.)

    Note the color coding - which strives to display the linkage between the Declarations & Vector Table.   For compactness/simplicity - just 2 such linkages are shown.

    //*****************************************************************************
    //
    // External declarations for the interrupt handlers used by the application.
    //
    //*****************************************************************************
    extern void IntDefaultHandler(void);
    extern void ADC0IntHandler(void); // Normal pgm opn.
    extern void GPIOBIntHandler(void); // Hall-Sense
    extern void FaultISR(void);
    extern void NmiSR(void);
    extern void PWM0IntHandler(void); // PWM Generator
    extern void SysTickIntHandler(void); // Switch, Spd Pot
    extern void WTimer2BIntHandler(void); // Stall Detect (2 Sec)
    extern void WatchdogIntHandler(void);

    // The vector table. Note that the proper constructs must be placed on this to
    // ensure that it ends up at physical address 0x0000.0000.
    //
    //*****************************************************************************
    __root const uVectorEntry __vector_table[] @ ".intvec" =
    {
    { .ulPtr = (unsigned long)pulStack + sizeof(pulStack) },
    // The initial stack pointer
    ResetISR, // The reset handler
    NmiSR, // The NMI handler
    FaultISR, // The hard fault handler
    IntDefaultHandler, // The MPU fault handler
    IntDefaultHandler, // The bus fault handler
    IntDefaultHandler, // The usage fault handler
    0, // Reserved
    0, // Reserved
    0, // Reserved
    0, // Reserved
    IntDefaultHandler, // SVCall handler
    IntDefaultHandler, // Debug monitor handler
    0, // Reserved
    IntDefaultHandler, // The PendSV handler
    SysTickIntHandler, // The SysTick handler
    IntDefaultHandler, // GPIO Port A
    GPIOBIntHandler, // GPIO Port B
    IntDefaultHandler, // GPIO Port C

    Note that we "edited away, "IntDefaultHandler" to the "more descriptive ISR names" as twice shown, here.

  • But the file is located in C:\ti\simplelink_cc13x2_26x2_sdk_3_40_00_02\source\ti\devices\cc13x2_cc26x2\startup_files so I can not just change it right?

    Because then when I move to another computer or when my coworkers want to use this project that would have to change this file again.

  • My group has no reasonably recent  experience w/your CC26xx devices.

    We've shown you the content of our "IAR Start-Up file" which reveals how the specified "ISRs" are rendered (both) Static & External.   In the case of the (superior) IAR IDE - all such files are properly linked - and easily, "shared by others."

    Indeed the Start-Up file - once modified (as I've shown) - proves unique to that "Specific Project!"    The original Start-Up file - under IAR (& we assume CCS) remains "intact/unchanged."   (and is "no longer" a part of - nor included with - this specific project!)

  • How did you do that in IAR?

    In CCS I made a copy of the file and moved it to my own project, there I made some changes and added my own SVCHandler. I changed the Include options and the File Search Path options for the linker in my project settings so that my file could be found during compilation. But it still goes to the old IntDefaultHandler from the other file when I run my program.

  • Vincent Kenbeek said:
    How did you do that in IAR?

    Feel your pain - yet under "Pro IDE" IAR - the process is (entirely) automated!    We simply altered the, "Provided Start-Up file (which is a "COPY" of the Base Start-Up file).   As my "brief code extract" revealed - we placed each (newly named) ISR w/in their appropriate project file.   (our project contained roughly 15 files.)

    Again - under IAR (& I expect under CCS too) should not (ONLY) those program files - contained w/in the specified project - be rendered operational?    When we launch a "new project" - the IDE is clever enough to (always) include (a COPY of) the Base Start-Up file - which it is understood - is almost always "modified to our program's requirements!"  

    The "Base Start-Up file" is "SAFE from (unwanted) modification" ...  its "COPY" is what is brought into our project - and then "manipulated to suit our objectives!"

  • You can use the Interrupt API to register an IRQ for SVCALL.

    #include <ti/devices/DeviceFamily.h>
    #include DeviceFamily_constructPath(inc/hw_ints.h)         // for INT_SVCALL
    #include DeviceFamily_constructPath(driverlib/interrupt.h) // for Int* API
    
    void svcall_irq(void) { /* impl */ }
    
    ...
    
    IntRegister(INT_SVCALL, svcall_irq);

  • But wouldn't this be a dynamic registration of an interrupt rather than a static one?

    Even if that is the case I think I will have to use that one because I can not find any other information about how to do it statically other than that one paragraph in the driver documentation.

    I have now tried to make a copy of the startup file in my own project but it still uses the other one. Then I tried to move it to a startup_files directory in my project and added that directory to the include options and file search path of my project but it continues to use the other file.

  • Yes, but why would that matter? The static NVIC table is always taken ownership by the interrupt API regardless of what you put there, as that is an integral part of how TI-RTOS (or NoRTOS for that matter) is implemented.

  • According to the documentation the static registration of an interrupt would be slightly fast according to this line.

    Statically configuring the interrupt table provides the fastest interrupt response time because the stacking operation (a write to SRAM on the data bus) is performed in parallel with the interrupt handler table fetch (a read from Flash on the instruction bus), as well as the prefetch of the interrupt handler (assuming it is also in Flash)

    Right now it is not very crucial to have the fastest option, but it is useful to know how to do this for when it does. So I was hoping to get information about how to do it statically now so I can already experiment with it.

  • The runtime "penalty" for dynamically registering the SVCall IRQ is negligible compared to other runtime overhead with IRQ handlers. I don't see any scenario where this would be required to statically register the SVCall IRQ, so feel free to enlighten me if this is the case.

    Are you using any specific stack? BLE stack for instance?

  • -May it be asked, "What you deem (value wise) to prove negligible?"    As the MCU manual makes the effort to identify this (penalty/weakness) - several times in fact - our group sensed this as likely (beyond) negligible.   

    In addition - we were advised that such "Dynamic Registration" proved more vulnerable to disruption than those Static.   We'd appreciate your (vendor/insider) opinion in that regard.   (this gleaned @ a TI Tech Show - several years back...)

    Thanks your time & attention - both appreciated.

    [edit/added]:   Should it not be additionally noted that such interrupts are (likely) to occur in multiples?    (most of my firm's program code extends to 10+ interrupts - even though we strive to limit that number.)    Now - with each of those interrupts subject to "penalty" (due to the use of Interrupt Registration) the cascade effect is almost certain to rise beyond "negligible!"

    And - the determination of "negligible" is normally/customarily best determined by the client/user - who alone knows the demands of his/her application...

    Caveat Emptor...

  • I guess we have different definitions of negligible runtime cost. So feel free to tell me what is acceptable and unacceptable runtime cost for you.

    If you are using TI-RTOS or any of the TI drivers, you are already paying a runtime cost of relocating the NVIC table to RAM, which means registering the SVCall ISR at runtime is already "payed" for in a sense. 

    There is not really any other "penalty" or "weakness" to register an ISR at runtime, the only one being that you need to relocate the NVIC to RAM the first time registering an ISR. Note that this is already taken care of by the API.

    And what do you mean by registering an ISR at runtime is "more vulnerable" to disruption than static configuration? That doesn't make much sense. The API that takes care of this ensures that any modifications done to the NVIC, that being relocation or registering an ISR, is done in a critical section, and as such no HWIs can trigger during such operations. You can take a look at the implementation yourselves if you want to double check this.

    Now if you are concerned about missing any "svc" instructions before being able to register the SVCall ISR, then I can only recommend to register the SVCall ISR at the beginning of your application, either in startup or first call in main.c.

    Now this begs me to ask you the question: why use SVCall? I guess this must be related to your code base, as SVCall instructions are not used by TI code at all.

  • Thank you - while we must, "Agree to Disagree" - your response is appreciated.

    Severin Suveren said:
    So feel free to tell me what is acceptable and unacceptable runtime cost for you.

    This retreats from the requested, "specification of 'negligible'" - does it not?    And  as stated - the impact of cascaded delays upon the code's execution loop is (or should be) the, "Province of the client-user."

    Severin Suveren said:
    And what do you mean by registering an ISR at runtime is "more vulnerable" to disruption than static configuration? That doesn't make much sense.

    Had you - somehow - missed the fact that I simply "relayed/reported" that offered by a T.I. programmer/instructor - at a T.I. Tech Seminar?    Is  the "miss" of that clear fact a far better model of, "Not making much sense?"

    Severin Suveren said:
    Now if you are concerned about missing any "svc" instructions before being able to register the SVCall ISR, then I can only recommend to register the SVCall ISR at the beginning of your application, either in startup or first call in main.c.

    Here - we are in total agreement.    (Good that!)    However - no case has been presented for the, "Adoption of "Interrupt Registration" over "Static Interrupts."    (the possible ability to, "Alter those Registered 'on the fly' - is all that my group has been able to (again possibly) identify.)

    Severin Suveren said:
    Now this begs me to ask you the question: why use SVCall? I guess this must be related to your code base, as SVCall instructions are not used by TI code at all.

    Should you not direct this question to the original poster - not my group - as a "proper read" of this thread reveals?   He alone employed SVCall.    Our objective was to provide related, "Static Interrupt Tech input to this poster" (via our long & successful use of Static Interrupts) which we detailed...