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.

EK-TM4C129EXL: ROM Bootloader Image requirements

Part Number: EK-TM4C129EXL
Other Parts Discussed in Thread: TM4C129ENCPDT,

Hi All,
I am working on a project using GCC with a TM4C129EXL, and I am trying to enable remote firmware update (via Ethernet) using LMFlash Programmer and the ROM Bootloader. When I initially flash my firmware using CCS and the onboard debugger, I can do an Ethernet update on the Board and place the Blinky example (from Tivaware), which starts and run without any issues. So my firmware seems to recognize the "magic packets" from the LMFlash programmer and transfer the control to the ROM Bootloader, which loads and executes the new images. So far, so good.


However, when I try to re-flash my firmware with LMFlash Programmer to place a new version of my firmware (instead of blinky), the Board either ends up in a FaultISR() (and the flashing process stalls); or the flashing process completes, but the new firmware does not start.


So my questions are:
- Is there a particular setting to change on CCS to export images to be able to do remote firmware updates
- Is there a size limitation on the images?


If anyone could give me some input about these topics, I would greatly appreciate it!
Best,
Pierrick

  • Hello Pierrick,

    There wouldn't be size limitations on the image. Are you trying to load a new image at location 0x0000 of Flash memory then to override the prior program? Is your new firmware set to have an application start address of 0x0000 in that case?

    Can you walk through the steps you are taking throughout this process? I understand the high level but hope that with another layer of detail maybe something will stick out to me as a possible incorrect step.

    Best Regards,

    Ralph Jacobi

  • Hello Ralph, 

    Yes, I am writing at 0x000, or at least I want to bc I am using the ROM bootloader. Here is my tm4c129ncpdt_startup_css_gcc.c

    //*****************************************************************************
    //
    // Startup code for use with TI's Code Composer Studio and GNU tools.
    //
    // Copyright (c) 2011-2014 Texas Instruments Incorporated.  All rights reserved.
    // Software License Agreement
    //
    // Software License Agreement
    //
    // Texas Instruments (TI) is supplying this software for use solely and
    // exclusively on TI's microcontroller products. The software is owned by
    // TI and/or its suppliers, and is protected under applicable copyright
    // laws. You may not combine this software with "viral" open-source
    // software in order to form a larger program.
    //
    // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
    // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
    // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
    // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
    // DAMAGES, FOR ANY REASON WHATSOEVER.
    //
    //*****************************************************************************
    
    #include <stdint.h>
    
    //*****************************************************************************
    //
    // Forward declaration of the default fault handlers.
    //
    //*****************************************************************************
    void ResetISR(void);
    static void NmiSR(void);
    static void FaultISR(void);
    static void IntDefaultHandler(void);
    
    #ifndef HWREG
    #define HWREG(x) (*((volatile uint32_t*)(x)))
    #endif
    
    //*****************************************************************************
    //
    // The entry point for the application.
    //
    //*****************************************************************************
    extern int main(void);
    
    //*****************************************************************************
    //
    // Reserve space for the system stack.
    //
    //*****************************************************************************
    
    //static uint32_t pui32Stack[4096];
    static uint32_t pui32Stack[2048];
    
    //*****************************************************************************
    //
    // External declarations for the interrupt handlers used by the application.
    //
    //*****************************************************************************
    // registering the ADC interrupt to the NVIC
    extern void _c_int00(void);
    extern void ADCSeq0Handler(void);
    extern void UDMAErrorHandler(void);
    // registering the Ethernet interrupt to the NVIC
    extern void lwIPEthernetIntHandler(void);
    extern void SysTickIntHandler(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 or at the start of
    // the program if located at a start address other than 0.
    //
    //*****************************************************************************
    __attribute__((section(".intvecs"))) void (*const g_pfnVectors[])(void) = {
        (void (*)(void))((uint32_t)pui32Stack + sizeof(pui32Stack)),
        // 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
        // IntDefaultHandler,      // The SysTick handler
        SysTickIntHandler,  // The SysTick handler
        IntDefaultHandler,  // GPIO Port A
        IntDefaultHandler,  // GPIO Port B
        IntDefaultHandler,  // GPIO Port C
        IntDefaultHandler,  // GPIO Port D
        IntDefaultHandler,  // GPIO Port E
        IntDefaultHandler,  // UART0 Rx and Tx
        IntDefaultHandler,  // UART1 Rx and Tx
        IntDefaultHandler,  // SSI0 Rx and Tx
        IntDefaultHandler,  // I2C0 Master and Slave
        IntDefaultHandler,  // PWM Fault
        IntDefaultHandler,  // PWM Generator 0
        IntDefaultHandler,  // PWM Generator 1
        IntDefaultHandler,  // PWM Generator 2
        IntDefaultHandler,  // Quadrature Encoder 0
        ADCSeq0Handler,     // TODO: ADC Sequence 0
        IntDefaultHandler,  // ADC Sequence 1
        IntDefaultHandler,  // ADC Sequence 2
        IntDefaultHandler,  // ADC Sequence 3
        IntDefaultHandler,  // Watchdog timer
        IntDefaultHandler,  // Timer 0 subtimer A
        IntDefaultHandler,  // Timer 0 subtimer B
        IntDefaultHandler,  // Timer 1 subtimer A
        IntDefaultHandler,  // Timer 1 subtimer B
        IntDefaultHandler,  // Timer 2 subtimer A
        IntDefaultHandler,  // Timer 2 subtimer B
        IntDefaultHandler,  // Analog Comparator 0
        IntDefaultHandler,  // Analog Comparator 1
        IntDefaultHandler,  // Analog Comparator 2
        IntDefaultHandler,  // System Control (PLL, OSC, BO)
        IntDefaultHandler,  // FLASH Control
        IntDefaultHandler,  // GPIO Port F
        IntDefaultHandler,  // GPIO Port G
        IntDefaultHandler,  // GPIO Port H
        IntDefaultHandler,  // UART2 Rx and Tx
        IntDefaultHandler,  // SSI1 Rx and Tx
        IntDefaultHandler,  // Timer 3 subtimer A
        IntDefaultHandler,  // Timer 3 subtimer B
        IntDefaultHandler,  // I2C1 Master and Slave
        IntDefaultHandler,  // CAN0
        IntDefaultHandler,  // CAN1
        // IntDefaultHandler, // Ethernet
        lwIPEthernetIntHandler,  // Ethernet
        IntDefaultHandler,       // Hibernate
        IntDefaultHandler,       // USB0
        IntDefaultHandler,       // PWM Generator 3
        IntDefaultHandler,       // uDMA Software Transfer
        UDMAErrorHandler,   // TODO: error handler defined in the ping pong example
        IntDefaultHandler,  // ADC1 Sequence 0
        IntDefaultHandler,  // ADC1 Sequence 1
        IntDefaultHandler,  // ADC1 Sequence 2
        IntDefaultHandler,  // ADC1 Sequence 3
        IntDefaultHandler,  // External Bus Interface 0
        IntDefaultHandler,  // GPIO Port J
        IntDefaultHandler,  // GPIO Port K
        IntDefaultHandler,  // GPIO Port L
        IntDefaultHandler,  // SSI2 Rx and Tx
        IntDefaultHandler,  // SSI3 Rx and Tx
        IntDefaultHandler,  // UART3 Rx and Tx
        IntDefaultHandler,  // UART4 Rx and Tx
        IntDefaultHandler,  // UART5 Rx and Tx
        IntDefaultHandler,  // UART6 Rx and Tx
        IntDefaultHandler,  // UART7 Rx and Tx
        IntDefaultHandler,  // I2C2 Master and Slave
        IntDefaultHandler,  // I2C3 Master and Slave
        IntDefaultHandler,  // Timer 4 subtimer A
        IntDefaultHandler,  // Timer 4 subtimer B
        IntDefaultHandler,  // Timer 5 subtimer A
        IntDefaultHandler,  // Timer 5 subtimer B
        IntDefaultHandler,  // FPU
        0,                  // Reserved
        0,                  // Reserved
        IntDefaultHandler,  // I2C4 Master and Slave
        IntDefaultHandler,  // I2C5 Master and Slave
        IntDefaultHandler,  // GPIO Port M
        IntDefaultHandler,  // GPIO Port N
        0,                  // Reserved
        IntDefaultHandler,  // Tamper
        IntDefaultHandler,  // GPIO Port P (Summary or P0)
        IntDefaultHandler,  // GPIO Port P1
        IntDefaultHandler,  // GPIO Port P2
        IntDefaultHandler,  // GPIO Port P3
        IntDefaultHandler,  // GPIO Port P4
        IntDefaultHandler,  // GPIO Port P5
        IntDefaultHandler,  // GPIO Port P6
        IntDefaultHandler,  // GPIO Port P7
        IntDefaultHandler,  // GPIO Port Q (Summary or Q0)
        IntDefaultHandler,  // GPIO Port Q1
        IntDefaultHandler,  // GPIO Port Q2
        IntDefaultHandler,  // GPIO Port Q3
        IntDefaultHandler,  // GPIO Port Q4
        IntDefaultHandler,  // GPIO Port Q5
        IntDefaultHandler,  // GPIO Port Q6
        IntDefaultHandler,  // GPIO Port Q7
        IntDefaultHandler,  // GPIO Port R
        IntDefaultHandler,  // GPIO Port S
        IntDefaultHandler,  // SHA/MD5 0
        IntDefaultHandler,  // AES 0
        IntDefaultHandler,  // DES3DES 0
        IntDefaultHandler,  // LCD Controller 0
        IntDefaultHandler,  // Timer 6 subtimer A
        IntDefaultHandler,  // Timer 6 subtimer B
        IntDefaultHandler,  // Timer 7 subtimer A
        IntDefaultHandler,  // Timer 7 subtimer B
        IntDefaultHandler,  // I2C6 Master and Slave
        IntDefaultHandler,  // I2C7 Master and Slave
        IntDefaultHandler,  // HIM Scan Matrix Keyboard 0
        IntDefaultHandler,  // One Wire 0
        IntDefaultHandler,  // HIM PS/2 0
        IntDefaultHandler,  // HIM LED Sequencer 0
        IntDefaultHandler,  // HIM Consumer IR 0
        IntDefaultHandler,  // I2C8 Master and Slave
        IntDefaultHandler,  // I2C9 Master and Slave
        IntDefaultHandler,  // GPIO Port T
        IntDefaultHandler,  // Fan 1
        0,                  // Reserved
    };
    
    //*****************************************************************************
    //
    // The following are constructs created by the linker, indicating where the
    // the "data" and "bss" segments reside in memory.  The initializers for the
    // for the "data" segment resides immediately following the "text" segment.
    //
    //*****************************************************************************
    extern uint32_t __data_load__;
    extern uint32_t __data_start__;
    extern uint32_t __data_end__;
    extern uint32_t __bss_start__;
    extern uint32_t __bss_end__;
    
    //*****************************************************************************
    //
    // 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) {
      uint32_t *pui32Src, *pui32Dest;
    
      //
      // Copy the data segment initializers from flash to SRAM.
      //
      pui32Src = &__data_load__;
      for (pui32Dest = &__data_start__; pui32Dest < &__data_end__;) {
        *pui32Dest++ = *pui32Src++;
      }
    
      //
      // Zero fill the bss segment.
      //
      __asm(
          "    ldr     r0, =__bss_start__\n"
          "    ldr     r1, =__bss_end__\n"
          "    mov     r2, #0\n"
          "    .thumb_func\n"
          "zero_loop:\n"
          "        cmp     r0, r1\n"
          "        it      lt\n"
          "        strlt   r2, [r0], #4\n"
          "        blt     zero_loop");
    
      //
      // Enable the floating-point unit.  This must be done here to handle the
      // case where main() uses floating-point and the function prologue saves
      // floating-point registers (which will fault if floating-point is not
      // enabled).  Any configuration of the floating-point unit using DriverLib
      // APIs must be done here prior to the floating-point unit being enabled.
      //
      // Note that this does not use DriverLib since it might not be included in
      // this project.
      //
      HWREG(0xE000ED88) = ((HWREG(0xE000ED88) & ~0x00F00000) | 0x00F00000);
    
      //
      // Call the application's entry point.
      //
      main();
    }
    
    //*****************************************************************************
    //
    // 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) {
      }
    }
    

    The way I am testing my code is the following: 

    1- Compile the code using CCS and make a copy of the generated binaries (.out) and transfer them to Computer2 (that will be used later for Ethernet Reflash), 

    2- Launch a debug session with Code Composer Studio with the MCU connected to Computer1 -> the code starts and executes normally

    3- With Computer2, start LMFlash Programmer, enter the IP and the MAC address of the board, and select the binaries, then the over the Ethernet Reflash -> at this point, the MCU either:

                a. ends up in a FaultISR() -> I know that as I am still running the debug session on Computer1 

                b. LMFlash Programmer indicates that the transfer is completed, but the MCU does not boot (even after on power ON/OFF cycle) 

    I hope this description help; please let me know if I can provide more details.

    Best,

    Pierrick

  • Hello Pierrick,

    So your start address would be in the .cmd file, the startup file has the stack information.

    Regarding your process... do you have the ROM boot loader invoked via the application code then? Like by a button press?

    If so, do you still have debug capability then? How so? I would think the ROM boot loader would break the Debug connection at that point because unlike with the Flash boot loader which loads into SRAM and can be debugged via symbols, the ROM boot loader would execute in ROM only and I don't think the debugger can access the ROM enough to do any sort of debugging?

    I am guessing you have the Ethernet cable from computer 2 connecting directly onto your board and then a USB cable from computer 1 that connects to your board to access the ICDI debugger, is that a correct understanding of the hardware aspect here? I'm a little confused why you are using two computers for this process honestly.

    Best Regards,

    Ralph Jacobi

  • Hello Ralph, 

    Sorry I meant to include the .lds instead of the startup file (I do not have a .cmd file as I am using GCC).

    Here it is, tm4c129encpdt.lds:

    /******************************************************************************
     *
     * Default Linker script for the Texas Instruments TM4C129ENCPDT
     *
     * This is derived from revision 15071 of the TivaWare Library.
    
     *****************************************************************************/
    
    MEMORY
    {
        FLASH (RX) : ORIGIN = 0x00000000, LENGTH = 0x00100000
        SRAM (WX)  : ORIGIN = 0x20000000, LENGTH = 0x00040000
    }
    
    REGION_ALIAS("REGION_TEXT", FLASH);
    REGION_ALIAS("REGION_BSS", SRAM);
    REGION_ALIAS("REGION_DATA", SRAM);
    REGION_ALIAS("REGION_STACK", SRAM);
    REGION_ALIAS("REGION_HEAP", SRAM);
    REGION_ALIAS("REGION_ARM_EXIDX", FLASH);
    REGION_ALIAS("REGION_ARM_EXTAB", FLASH);
    
    SECTIONS {
    
        PROVIDE (_intvecs_base_address = 0x0);
    
        .intvecs (_intvecs_base_address) : AT (_intvecs_base_address) {
            KEEP (*(.intvecs))
        } > REGION_TEXT
    
        PROVIDE (_vtable_base_address = 0x20000000);
    
        .vtable (_vtable_base_address) : AT (_vtable_base_address) {
            KEEP (*(.vtable))
        } > REGION_DATA
    
        .text : {
            CREATE_OBJECT_SYMBOLS
            *(.text)
            *(.text.*)
            . = ALIGN(0x4);
            KEEP (*(.ctors))
            . = ALIGN(0x4);
            KEEP (*(.dtors))
            . = ALIGN(0x4);
            __init_array_start = .;
            KEEP (*(.init_array*))
            __init_array_end = .;
            *(.init)
            *(.fini*)
        } > REGION_TEXT
    
        PROVIDE (__etext = .);
        PROVIDE (_etext = .);
        PROVIDE (etext = .);
    
        .rodata : {
            *(.rodata)
            *(.rodata*)
        } > REGION_TEXT
    
        .data : ALIGN (4) {
            __data_load__ = LOADADDR (.data);
            __data_start__ = .;
            *(.data)
            *(.data*)
            . = ALIGN (4);
            __data_end__ = .;
        } > REGION_DATA AT> REGION_TEXT
    
        .ARM.exidx : {
            __exidx_start = .;
            *(.ARM.exidx* .gnu.linkonce.armexidx.*)
            __exidx_end = .;
        } > REGION_ARM_EXIDX
    
        .ARM.extab : {
            *(.ARM.extab* .gnu.linkonce.armextab.*)
        } > REGION_ARM_EXTAB
    
        .bss : {
            __bss_start__ = .;
            *(.shbss)
            *(.bss)
            *(.bss.*)
            *(COMMON)
            . = ALIGN (4);
            __bss_end__ = .;
        } > REGION_BSS
    
        .heap : {
            __heap_start__ = .;
            end = __heap_start__;
            _end = end;
            __end = end;
            KEEP(*(.heap))
            __heap_end__ = .;
            __HeapLimit = __heap_end__;
        } > REGION_HEAP
    
        .stack : ALIGN(0x8) {
            _stack = .;
            __stack = .;
            KEEP(*(.stack))
        } > REGION_STACK
    }
    

    """"Regarding your process... do you have the ROM boot loader invoked via the application code then? Like by a button press?""""

    The software invokes the ROM bootloader in the application; I reproduced the behavior of the Tivaware Example: boot_demo_emac_rom (under ti/SW-TM4C-2.2.0.295/examples/boards/ek-tm4c129exl/boot_demo_emac_rom) where when a "Magic packet" is received from LMFlashProgramer; an "update_requested" flag is set to true which exists the main application loop and calls the `SoftwareUpdateBegin(g_ui32SysClock);` function. 

    """If so, do you still have debug capability then? How so? I would think the ROM boot loader would break the Debug connection at that point because unlike with the Flash boot loader which loads into SRAM and can be debugged via symbols, the ROM boot loader would execute in ROM only and I don't think the debugger can access the ROM enough to do any sort of debugging?""""

    I was also surprised that I still had a connection with the ICDI debugger. I knew that I ended up in a FaultISR() because I had a breakpoint set within the default FaultISR() interrupt handler, and I ended up at the breakpoint.

    """I am guessing you have the Ethernet cable from computer 2 connecting directly onto your board and then a USB cable from computer 1 that connects to your board to access the ICDI debugger, is that a correct understanding of the hardware aspect here? I'm a little confused why you are using two computers for this process honestly."""

    Computer2 is connected via wifi to the router, and I have a wired connection from the board to the router. Computer1 is connected via USB to the ICDI debugger. I am using two computers because computer 1 runs Mac Os; thus cannot run LMFlash Programmer (Computer2 is a Windows).

    Best, 

    Pierrick 

  • Hi,

     I suppose you use boot_demo_emac_rom.c example as a reference, correct? The example will call SoftwareUpdateBegin(g_ui32SysClock) when a magic packet is received. Below is the snippet of SoftwareUpdateBegin function. As you can see the function will first disable all the interrupts before calling the ROM_UpdateEMAC(). Normally, this works quite well. However, very rarely, I have seen people report that the processor will run into fault before calling ROM_UpdateXXX (where XXX is the module to boot, e.g. UART or EMAC). See this post for details e2e.ti.com/.../tm4c123gh6pm-invoke-rom_updateuart-programmatically. The workaround as suggested in this post is to disable any modules that can generate interrupt in your application. For example, instead of calling:

    HWREG(NVIC_DIS0_R) = 0xFFFFFFFF;
    HWREG(NVIC_DIS1_R) = 0xFFFFFFFF;

    Replaced with:

    ROM_IntDisable(INT_GPIOA);
    ROM_IntDisable(INT_GPIOB);
    ROM_IntDisable(INT_GPIOC);
    ROM_IntDisable(INT_GPIOD);
    ROM_IntDisable(INT_GPIOE);
    ROM_IntDisable(INT_GPIOF);
    ROM_IntDisable(INT_UART0);
    ROM_IntDisable(INT_UART1);
    ROM_IntDisable(INT_TIMER0A);
    ROM_IntDisable(INT_TIMER0B);
    ROM_IntDisable(INT_TIMER1A);
    ROM_IntDisable(INT_TIMER1B);
    ROM_IntDisable(INT_TIMER2A);
    ROM_IntDisable(INT_TIMER2B);
    ROM_IntDisable(INT_TIMER3A);
    ROM_IntDisable(INT_TIMER3B);

    If you know exactly what modules you have in your application that can potentially generate interrupts before ROM_UpdateEMAC is called, please try to disable those modules at the module level to see they will resolve the issue. 

    void
    SoftwareUpdateBegin(uint32_t ui32SysClock)
    {
        //
        // Disable all processor interrupts.  Instead of disabling them
        // one at a time (and possibly missing an interrupt if new sources
        // are added), a direct write to NVIC is done to disable all
        // peripheral interrupts.
        //
        HWREG(NVIC_DIS0) = 0xffffffff;
        HWREG(NVIC_DIS1) = 0xffffffff;
        HWREG(NVIC_DIS2) = 0xffffffff;
        HWREG(NVIC_DIS3) = 0xffffffff;
        HWREG(NVIC_DIS4) = 0xffffffff;
    
        //
        // Also disable the SysTick interrupt.
        //
        SysTickIntDisable();
        SysTickDisable();
    
        //
        // Return control to the boot loader.  This is a call to the SVC
        // handler in the flashed-based boot loader, or to the ROM if configured.
        //
    #if ((defined ROM_UpdateEMAC) && !(defined USE_FLASH_BOOT_LOADER))
        ROM_UpdateEMAC(ui32SysClock);
    #else
        (*((void (*)(void))(*(uint32_t *)0x2c)))();
    #endif
    }
    

  • Hi Charles,
    I will try that this morning and let you know how it goes. Thanks for the input.
    Best,
    Pierrick