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.

CC2652: Changing the Vector Table Offset Register and jumping to an application

I am currently writing a bootloader for the CC2652 and I am new to this so my apologies in case I missed something very obvious.

As far as I know in order to jump to another application you have to do three things

  1. Change the Vector Table Offset Register
  2. Change the Stack Pointer
  3. Change the Program counter

However when trying to write to the Vector Table Offset Register (VTOR) I end up in FaultISR() from which I can see in the CFSR (Configurable Fault Status Register) that the IMPRECISERR bit is set indicating an imprecise buss error. I change the register from an SVCall handler which means I should be in Privileged mode, right? I added my code for jumping to the other application, a description of the IMPRECISERR bit and the paragraph about changing the VTOR from the Technical Manual below.

Code:

void svcallIRQ(void) {
    typedef void (*p_function)(void);
    uint32_t jump_address = *(volatile uint32_t*)(FIRM_OFFSET + 4); // FIRM_OFFSET = 0x2E000
    p_function p_jump_function = (p_function) jump_address;

    __disable_irq();

    uint32_t *vtor = (uint32_t*)CPU_SCS_O_VTOR; // CPU_SCS_O_VTOR = 0x00000D08
    *vtor = ((uint32_t )FIRM_OFFSET); // change vector table offset to first bytes of firmware

    __NOP(); // The error occurs after this NOP
    __NOP();
    __NOP();

    __DSB();

    __set_MSP(*(volatile uint32_t*) FIRM_OFFSET);

    p_jump_function();
}

IMPRECISERR:

Imprecise data bus error. It is a BusFault, but the Return PC is not related to the causing instruction. This is not a synchronous fault. So, if detected when the priority of the current activation is higher than the Bus Fault, it only pends. Bus fault activates when returning to a lower priority activation. If a precise fault occurs before returning to a lower priority exception, the handler detects both IMPRECISERR set and one of the precise fault status bits set at the same time. BFAR is not written.

VTOR Description:

On system reset, the vector table is fixed at address 0x00000000. Privileged software can write to the Vector Table Offset register (CPU_SCS:VTOR) to relocate the vector table start address to a different memory location, in the range 0x00000200 to 0x3FFFFE00. When configuring the CPU_SCS:VTOR register, the offset must be aligned on a 512-byte boundary.

  • Hi Vincent,

    Let's start by clarifying some misconceptions you seem to have.

    All TI code from the SDK are assumed to always be running in privileged mode. In the device, execution restarts as privileged execution in thread mode, and since the TI ecosystem never enters unprivileged mode the device stays in privileged mode. This means that there is no need to use SVCall to access privileged mode, and you can write to control registers from any context.

    Regarding jumping to an application entry point from the bootloader, you should not relocate the NVIC. Leave this as-is! What is required is the following:

    1. Find the program entry for the application (reset ISR function pointer).
    2. Reset the stack pointer (SP, not MSP).
    3. Jump to the program entry.

    I highly recommend taking look at the BIM examples in the SDK (<SDK>/examples/nortos/CC1312R1_LAUNCHXL/bim), which are the bootloader examples from TI. You should take a look at the jumpToPrgEntry() function, which implements the jumping routine.

  • Hi Severin,

    Thank you for your reply. I took a look at the example and copied it over to my own project, but changing the program entry point to 0x2E000. My code now looks like this:

    void startApp(uint32_t prgEntry) {
        static uint32_t temp;
        temp = prgEntry;
        // Reset the stack pointer,
        temp +=4;
        asm(" LDR SP, [R0, #0x0] ");
        ((void (*)(void))(*((uint32_t*)temp)))();
    }

    prgEntry Is 0x2E000 in this case. I still end up in the FaultISR() but this time with the STKERR and the IBUSERR bits set. So something is still wrong.

  • And what is prgEntry defined to? This is usually taken care of by the linker file, and should point the interrupt vector table in your application. Could you upload the map file here?

    Could you please elaborate the memory regions allocated for the bootloader and the application, and where the interrupt vector for both the bootloader and the application are located within those memory regions.

  • I call the startApp function like this:

    startApp(FIRM_OFFSET);

    Where FIRM_OFFSET = 0x2E000

    The program I am trying to jump to is the pwmled2 example from the nortos examples for the CC2652 but I modified the .cmd file to look like this:

    #define BOOT_BASE		0x0
    #define BOOT_SIZE		0x8000
    #define FLASH_BASE              0x2E000
    #define FLASH_SIZE              0x2A000
    #define RAM_BASE                0x20000000
    #define RAM_SIZE                0x14000
    #define GPRAM_BASE              0x11000000
    #define GPRAM_SIZE              0x2000
    
    
    /* System memory map */
    
    MEMORY
    {
        /* Application stored in and executes from internal flash */
        FLASH (RX) : origin = FLASH_BASE, length = FLASH_SIZE
        /* Application uses internal RAM for data */
        SRAM (RWX) : origin = RAM_BASE, length = RAM_SIZE
        /* Application can use GPRAM region as RAM if cache is disabled in the CCFG
        (DEFAULT_CCFG_SIZE_AND_DIS_FLAGS.SET_CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM = 0) */
        GPRAM (RWX): origin = GPRAM_BASE, length = GPRAM_SIZE
    }
    
    /* Section allocation in memory */
    
    SECTIONS
    {
        .intvecs        :   > FLASH_BASE
        .text           :   > FLASH
        .TI.ramfunc     : {} load=FLASH, run=SRAM, table(BINIT)
        .const          :   > FLASH
        .constdata      :   > FLASH
        .rodata         :   > FLASH
        .binit          :   > FLASH
        .cinit          :   > FLASH
        .pinit          :   > FLASH
        .init_array     :   > FLASH
        .emb_text       :   > FLASH
        .ccfg           :   > FLASH (HIGH)
    
        .vtable         :   > SRAM
        .vtable_ram     :   > SRAM
         vtable_ram     :   > SRAM
        .data           :   > SRAM
        .bss            :   > SRAM
        .sysmem         :   > SRAM
        .stack          :   > SRAM (HIGH)
        .nonretenvar    :   > SRAM
    
        .gpram          :   > GPRAM
    }
    

    The .cmd file for the bootloader looks the same but with the sections pointing to BOOT_BASE.

    #define BOOT_BASE				0x0
    #define BOOT_SIZE				0x8000
    #define FLASH_BASE              0x2E000
    #define FLASH_SIZE              0x2A000
    #define RAM_BASE                0x20000000
    #define RAM_SIZE                0x14000
    #define GPRAM_BASE              0x11000000
    #define GPRAM_SIZE              0x2000
    
    
    /* System memory map */
    
    MEMORY
    {
    	/* The bootloader will be stored and executed from this location in internal flash */
    	BOOT (RX)  : origin = BOOT_BASE, length = BOOT_SIZE
        /* Application stored in and executes from internal flash */
        FLASH (RX) : origin = FLASH_BASE, length = FLASH_SIZE
        /* Application uses internal RAM for data */
        SRAM (RWX) : origin = RAM_BASE, length = RAM_SIZE
        /* Application can use GPRAM region as RAM if cache is disabled in the CCFG
        (DEFAULT_CCFG_SIZE_AND_DIS_FLAGS.SET_CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM = 0) */
        GPRAM (RWX): origin = GPRAM_BASE, length = GPRAM_SIZE
    }
    
    
    /* Section allocation in memory */
    
    SECTIONS
    {
        .intvecs        :   > BOOT_BASE
        .text           :   > BOOT
        .TI.ramfunc     : {} load=BOOT, run=SRAM, table(BINIT)
        .const          :   > BOOT
        .constdata      :   > BOOT
        .rodata         :   > BOOT
        .binit          :   > BOOT
        .cinit          :   > BOOT
        .pinit          :   > BOOT
        .init_array     :   > BOOT
        .emb_text       :   > BOOT
        .ccfg           :   > BOOT (HIGH)
    
        .vtable         :   > SRAM
        .vtable_ram     :   > SRAM
         vtable_ram     :   > SRAM
        .data           :   > SRAM
        .bss            :   > SRAM
        .sysmem         :   > SRAM
        .stack          :   > SRAM (HIGH)
        .nonretenvar    :   > SRAM
    
        .gpram          :   > GPRAM
    }
  • Vincent,

    I'm right away seeing that the memory regions you've allocated for your bootloader and application has not taken the CCFG into consideration. The CCFG is required to be located in the upper 88 bytes of the flash, i.e the last flash page, regardless of where your application (or bootloader) is placed. Right now, neither your bootloader nor application is allocated for the last flash page, and hence the CCFG is therefore never programmed which makes the device mostly useless. Also, both the application and bootloader image cannot configure the CCFG, as they both can't overlap on the same memory region. TI recommends to bundle the bootloader with the CCFG, and let the bootloader occupy at least the last flash page.

    What's also important is that the IMAGE_VALID_CONF configuration in CCFG is configured, as that register must point to the NVIC of the bootloader. If the bootloader is placed in the first flash page, and subsequently the reset vector is placed at address 0x0000_0000, then IMAGE_VALID_CONF does not need to be reconfigured. However, if the reset vector is not at address 0x0000_0000, then you will have to configure IMAGE_VALID_CONF.

  • My apologies, I have never modified the CCFG before or developed a bootloader so I am quite new to all of this. I looked at the linker command files for the BIM examples and modified mine to look like this for the bootloader:

    --stack_size=1024   /* C stack is also used for ISR stack */
    
    --heap_size=256
    
    /* Retain interrupt vector table variable                                    */
    --retain=g_pfnVectors
    /* Override default entry point.                                             */
    --entry_point resetISR
    /* Allow main() to take args                                                 */
    --args 0x8
    /* Suppress warnings and errors:                                             */
    /* - 10063: Warning about entry point not being _c_int00                     */
    /* - 16011, 16012: 8-byte alignment errors. Observed when linking in object  */
    /*   files compiled using Keil (ARM compiler)                                */
    --diag_suppress=10063,16011,16012
    
    #define FLASH_BASE 				0x0
    #define FLASH_SIZE				0x58000
    #define RAM_BASE                0x20000000
    #define RAM_SIZE                0x14000
    #define GPRAM_BASE              0x11000000
    #define GPRAM_SIZE              0x2000
    
    #define FLASH_START				FLASH_BASE
    
    #define PAGE_SIZE				0x2000
    
    #define BOOT_BASE				0x50000
    #define BOOT_END				CERT_START - 1
    #define BOOT_SIZE				((BOOT_END) - (BOOT_BASE) + 1)
    
    #define APP_BASE				0x0
    #define APP_END					BOOT_BASE
    #define APP_SIZE				BOOT_BASE
    
    #define FLASH_CCFG_START		0x00057FA8
    #define FLASH_CCFG_END			(FLASH_START + FLASH_SIZE - 1)
    #define FLASH_CCFG_SIZE			((FLASH_CCFG_END) - (FLASH_CCFG_START) + 1)
    
    #define FLASH_FNPTR_START		FLASH_CCFG_START - 8
    #define FLASH_FNPTR_END			FLASH_FNPTR_START + 3
    #define FLASH_FNPTR_SIZE		4
    
    #define CERT_END				FLASH_FNPTR_START - 1
    #define CERT_SIZE				0x4C
    #define CERT_START				CERT_END - CERT_SIZE + 1
    
    
    /* System memory map */
    
    MEMORY
    {
    
    	BOOT (RX)  : origin = BOOT_BASE, length = BOOT_SIZE
    
        APP (RX) : origin = APP_BASE, length = APP_SIZE
    
        FLASH_CCFG (RX) : origin = FLASH_CCFG_START, length = FLASH_CCFG_SIZE
    	FLASH_FNPTR (RX) : origin = FLASH_FNPTR_START, length = FLASH_FNPTR_SIZE
    	FLASH_CERT (RX) : origin = CERT_START, length = CERT_SIZE
    
        /* Application uses internal RAM for data */
        SRAM (RWX) : origin = RAM_BASE, length = RAM_SIZE
        /* Application can use GPRAM region as RAM if cache is disabled in the CCFG
        (DEFAULT_CCFG_SIZE_AND_DIS_FLAGS.SET_CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM = 0) */
        GPRAM (RWX): origin = GPRAM_BASE, length = GPRAM_SIZE
    }
    
    
    
    /* Create global constant that points to top of stack */
    /* CCS: Change stack size under Project Properties    */
    __STACK_TOP = __stack + __STACK_SIZE;
    
    
    /* Section allocation in memory */
    
    SECTIONS
    {
        .intvecs        :   > BOOT_BASE
        .text           :   > BOOT
        .TI.ramfunc     : {} load=BOOT, run=SRAM, table(BINIT)
        .const          :   > BOOT
        .constdata      :   > BOOT
        .rodata         :   > BOOT
        .binit          :   > BOOT
        .cinit          :   > BOOT
        .pinit          :   > BOOT
        .init_array     :   > BOOT
        .emb_text       :   > BOOT
        .cert_element   :   > FLASH_CERT
    	.fnPtr          :   > FLASH_FNPTR
    	.ccfg           :   > FLASH_CCFG (HIGH)
    
        .vtable         :   > SRAM
        .vtable_ram     :   > SRAM
         vtable_ram     :   > SRAM
        .data           :   > SRAM
        .bss            :   > SRAM
        .sysmem         :   > SRAM
        .stack          :   > SRAM (HIGH)
        .nonretenvar    :   > SRAM
    
        .gpram          :   > GPRAM
    }

    And like this for the application:

    --stack_size=1024   /* C stack is also used for ISR stack */
    
    --heap_size=256
    
    /* Retain interrupt vector table variable                                    */
    --retain=g_pfnVectors
    /* Override default entry point.                                             */
    --entry_point resetISR
    /* Allow main() to take args                                                 */
    --args 0x8
    /* Suppress warnings and errors:                                             */
    /* - 10063: Warning about entry point not being _c_int00                     */
    /* - 16011, 16012: 8-byte alignment errors. Observed when linking in object  */
    /*   files compiled using Keil (ARM compiler)                                */
    --diag_suppress=10063,16011,16012
    
    /* The starting address of the application.  Normally the interrupt vectors  */
    /* must be located at the beginning of the application.                      */
    #define FLASH_BASE 				0x0
    #define FLASH_SIZE				0x58000
    #define RAM_BASE                0x20000000
    #define RAM_SIZE                0x14000
    #define GPRAM_BASE              0x11000000
    #define GPRAM_SIZE              0x2000
    
    #define FLASH_START				FLASH_BASE
    
    #define PAGE_SIZE				0x2000
    
    #define BOOT_BASE				0x50000
    #define BOOT_END				CERT_START - 1
    #define BOOT_SIZE				((BOOT_END) - (BOOT_BASE) + 1)
    
    #define APP_BASE				0x0
    #define APP_END					BOOT_BASE
    #define APP_SIZE				BOOT_BASE
    
    #define FLASH_CCFG_START		0x00057FA8
    #define FLASH_CCFG_END			(FLASH_START + FLASH_SIZE - 1)
    #define FLASH_CCFG_SIZE			((FLASH_CCFG_END) - (FLASH_CCFG_START) + 1)
    
    #define FLASH_FNPTR_START		FLASH_CCFG_START - 8
    #define FLASH_FNPTR_END			FLASH_FNPTR_START + 3
    #define FLASH_FNPTR_SIZE		4
    
    #define CERT_END				FLASH_FNPTR_START - 1
    #define CERT_SIZE				0x4C
    #define CERT_START				CERT_END - CERT_SIZE + 1
    
    
    /* System memory map */
    
    MEMORY
    {
    
    	BOOT (RX)  : origin = BOOT_BASE, length = BOOT_SIZE
    
        APP (RX) : origin = APP_BASE, length = APP_SIZE
    
        FLASH_CCFG (RX) : origin = FLASH_CCFG_START, length = FLASH_CCFG_SIZE
    	FLASH_FNPTR (RX) : origin = FLASH_FNPTR_START, length = FLASH_FNPTR_SIZE
    	FLASH_CERT (RX) : origin = CERT_START, length = CERT_SIZE
    
        /* Application uses internal RAM for data */
        SRAM (RWX) : origin = RAM_BASE, length = RAM_SIZE
        /* Application can use GPRAM region as RAM if cache is disabled in the CCFG
        (DEFAULT_CCFG_SIZE_AND_DIS_FLAGS.SET_CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM = 0) */
        GPRAM (RWX): origin = GPRAM_BASE, length = GPRAM_SIZE
    }
    
    
    /* Section allocation in memory */
    
    SECTIONS
    {
        .intvecs        :   > APP_BASE
        .text           :   > APP
        .TI.ramfunc     : {} load=APP, run=SRAM, table(BINIT)
        .const          :   > APP
        .constdata      :   > APP
        .rodata         :   > APP
        .binit          :   > APP
        .cinit          :   > APP
        .pinit          :   > APP
        .init_array     :   > APP
        .emb_text       :   > APP
        .cert_element   :   > FLASH_CERT, TYPE=NOLOAD
    	.fnPtr          :   > FLASH_FNPTR, TYPE=NOLOAD
    	.ccfg           :   > FLASH_CCFG (HIGH), TYPE=NOLOAD
    
        .vtable         :   > SRAM
        .vtable_ram     :   > SRAM
         vtable_ram     :   > SRAM
        .data           :   > SRAM
        .bss            :   > SRAM
        .sysmem         :   > SRAM
        .stack          :   > SRAM (HIGH)
        .nonretenvar    :   > SRAM
    
        .gpram          :   > GPRAM
    }

    I did not have a ccfg.c file yet so I created on in the bootloader and added a single #define SET_CCFG_IMAGE_VALID_CONF_IMAGE_VALID 0x50000. But I am still going to the FaultISR() with still the same bits being set.

  • Since your bootloader includes the CCFG, you should no longer include the CCFG in the application. Hence, you should remove the .ccfg section in the application linker file.

    In the CCFG configuration, you will have to set CCFG_IMAGE_VALID_CONF to the address ot the reset vector table in the bootloader.

    What I am not completely certain about is what the FLASH_FNPTR region is used for. Is this related to the reset vector table, or some other dynamic dispatch table used by the application?