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.

Ethernet bootloader (BOOTP request) support forTiva™ C Series TM4C129x

Other Parts Discussed in Thread: EK-TM4C1294XL, TM4C1294NCPDT

Hi, 

Is there any ROM API by which Application can bring TIVA into Bootloader mode ?

and does its BootLoader supports Ethernet BootP Request for ethernet updation of firmware ?

Please Suggest,

Thanks,

Sanchit Mehra

  • Yes, yes and yes.

    Here is what I did...

    1) Started with the EK-TM4C1294XL Launchpad.  Find the enet_io example.

    2) add the TivaWare file in utils\swupdate.c to the project.

    3) create a callback function that will be called when the utility filter finds the magic packet. In my case I just added a flag bit t othe g_ulFlags and set it in this handler.  This is called from interrupts and we want to just set a flag and then start the update from main.  The magic packet is defined in one of the docs.  Either in ROM users guide or Bootloader users guide. Send UDP packet to port 9 of the device.  The byte pattern is basically 6 bytes of 170 decimal.  Then the MAC address repeated 4 times.

    3) add a call to the SoftwareUpdateInit Function after the else statement in the lwipHostTimerIntHandler after the UARTprintf("Open a browser..."); line. This is about line 523 in my file but is likely off by a few lines from the original. Pass in a pointer to the callback function you just created.

    4) In the main function check for the new flag.  If it is set then call SoftwareUpdateBegin( pass in the sys clock freq).

    5) From this point the ROM Ethernet bootloader takes over.  You have handed off control to the ROM bootloader.

    6) ROM bootloader will send a bootp packet.  A bootp server will reply with an IP address and server information for a TFTP server and filename that should be used to update the device.

    7) Once the ROM bootloader gets the reply it will fetch the binary file from the TFTP server and burn it to flash.  Then it will automatically restart the system running the new image.

    Some of my code snippets from enet_io are below.

    // Call the SoftwareUpdateInit this creates a filter that can call the callback when the magic packet is received.

            else
            {
                //
                // Display the new IP address.
                //
                UARTprintf("IP Address: ");
                DisplayIPAddress(ui32NewIPAddress);
                UARTprintf("\n");
                UARTprintf("Open a browser and enter the IP address.\n");
                //
                // SoftwareUpdateInit
                //
                UARTprintf("Calling UpdateInit\n");
                SoftwareUpdateInit(SoftwareUpdateCallback);
            }

     

    // my update callback function.

    void
    SoftwareUpdateCallback(void)
    {
        UARTprintf("UpdateCallback\n");
        HWREGBITW(&g_ulFlags, FLAG_SWUPDATE) = 1;
    }

    // my modification to main.  will start a software update using ROM if button is pressed or the magic packet came in.

          if((g_ui8ButtonState & USR_SW1) ||
                    (HWREGBITW(&g_ulFlags, FLAG_SWUPDATE)))
            {
                UARTprintf("Calling UpdateBegin\n");
                SoftwareUpdateBegin(g_ui32SysClock);
            }

    // this last bit of code goes after the while(!g_ulFlags) loop.

    As you can see the magic packet is optional you can do it from a button or any other software mechanism you want to use.  The biggest thing is to make sure the Ethernet is fully configured and ready before you call SoftwareUpdateBegin.

  • Also, I created a Python BOOTP and Magic packet utility.  The magic packet script formats and sends a packet to the MAC address you specify.  The BOOTP server will listen to BOOTP requests and send back replies with TFTP server information. 

    I used TFTPy (python tftp server) to complete the loop and do my Ethernet updates.

    I will try to clear releasing the python code under a BSD license for the community.  It may take a few days.

  • Hi Dexter,

    Thanks for quick reply to the post.

    But, I have been using the uIP stack for various http , dhcp services.

    The swupdate.c code uses the lwip  stack .

    will it be possible to use the uIP stack?

    Secondly, 

    I asked this question because of this code snippet in swupdate.c

    #if ((defined ROM_UpdateEthernet) && !(defined USE_FLASH_BOOT_LOADER))
    ROM_UpdateEthernet(ROM_SysCtlClockGet());
    #else
    (*((void (*)(void))(*(uint32_t *)0x2c)))();
    #endif

    ROM_UpdateEthernet(ROM_SysCtlClockGet()); => This API is not in the library.

    so, will the ROM API present at this ROM Address => (*((void (*)(void))(*(uint32_t *)0x2c)))();

    will initiate the loading of Ethernet bootloader and therby proceeding with the BOOTP request ?

     

    Please suggest,

    Thanks.

    Sanchit

     

  • It should not matter what stack you use in the application.  The minimum is that you initialize the MAC and PHY properly before calling the SoftwareUpdateBegin function. 

    That may be a bug in swupdate. I think the name changed from UpdateEthernet to UpdateEMAC and one of the references in this file was not changed.  On a 129 part you can call UpdateEMAC and it will transfer to the ROM. 

    SysCtlClockGet is only on 123 class devices.  The 129 class uses SysCtlClockFreqset which returns the system clock speed with out a separate function call.  generally we keep that system clock speed in a global variable and pass it into other APIs as needed.

    I recall (now that you mention it) I had to tinker with exactly this #if and #else statement, I think since I knew I was always (for that project) on a 129 and always wanted to use ROM not flash I just removed the #if and #else went directly to ROM_UpdateEMAC.

    Dexter

  • Hi Dexter,

    This API, ROM_UpdateEMAC is also not supported by the library, not in the file "rom.h"

    so how can we achieve calling the ethernet bootloader ?

     

    Please suggest,

    Thanks,

    Sanchit

  • rom.h line 1678  TivaWare 2.1.0.12573

    #if defined(TARGET_IS_TM4C129_RA1)
    #define ROM_UpdateEMAC                                                        \
            ((void (*)(uint32_t ui32Clock))ROM_EMACTABLE[71])
    #endif

  • Hi Dexter,

    Thanks ...

    actually, I am using TivaWare_C_Series-2.0

    That is the reason , I am unable to find this api.

    So, Can I use TivaWare 2.1.0.12573 version libs for Tiva™ C Series TM4C129x.

     

    Thanks,

    Sanchit

  • Hi Dexter,

    my sysctl registers reads as 

    SYSCTL_DID0 0x100A0000 Device Identification 0 [Memory Mapped]

    SYSCTL_DID1 0x102DC06C Device Identification 1 [Memory Mapped]

    so, by this , is my mcu chip is A0 or A1 ?

    I think only A1 chip supports update by ethernet ?

    Am i right ?

    Please suggest.

    Thanks,

    Sanchit Mehra

  • Datasheet page 255 for the TM4C1294NCPDT shows the DID0 to die revision table mapping.  Yes, if those numbers are right you have an A0.  I do believe you are correct that only after and including A1 is update supported by ROM Ethernet update.  You could still do flash update. 

    My memory is that only the first few hundred DK-TM4C129X boards and none of the EK-TM4C1294XL boards had A0.  Almost all the boards to the public are A1 devices. 

    The simplest answer may be to get a board with a new chip.

    Yes you can use TivaWare 2.1.0.12573 with The TM4C129 series.

  • Hi,

    I'm using the EK-TM4C1294XL. I'd like to bootload software via ethernet. I'm having trouble implementing these changes. Could you provide the code you used?

     

    Thanks,

  • Hello Christopher,
    You can reuse the infrastructure from the DK-TM4C129 to the EK-TM4C129. Is there a specific question as to what needs to be done?

    Regards

    Amit

  • Amit,

    Ok. I've tried to use the DK infrastructure but its not working....I've been using CCS to debug the emac boot example code onto the launch board. Then I I'm trying to use LM Flash programmer to load in a .bin over ethernet. 

    It actually shows a program complete on LM Flash if I reset the launch board before I try to load the .bin......but nothing happens(board visually stays idle instead of my .bin that should flash leds).

  • Hello Christopher,

    The DK ethernet boot loader would require changes as the parts on the DK and EK are different. Have those changes been made? If possible can you attach the CCS project you have for the EK so that we can look into it?

    Regards

    Amit

  • Amit,

    Right, I've tried to make the changes and I'm having lots of problems. This is my first time coding a microcontroller like this...I am not very versed on this sort of thing. I don't think it would be worth it for me to share my cookie cutter code. 

    What I want is to have a simple code that acts like a bootloader so that I can load a .bin over ethernet while its running.  I've tried cutting up boot_demo_emac_flash but there seems to many libraries and links to DK not EK that I cant get it to work. 

    Is there a simple way I can do what I want with a few lines of code/functions? To help my understanding of the bootloader.

    Thanks,

  • Hello Christopher,,

    The EK has the ROM Boot Loader for Ethernet (this is a fixed boot loader) while the Flash Based boot loader on the DK is a customizable boot loader. It should be fairly simple, but as you mentioned that you are very much new to Tiva, you can still go ahead and share the project code (I hope it is CCS)

    Regards

    Amit

  • Hi Amit,

    I followed the steps mentioned above. I am sending .bin file from LMFlash Programmer. In following code it always go to else condition.
    //
    // 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_UpdateEthernet) && !(defined USE_FLASH_BOOT_LOADER))
    ROM_UpdateEMAC(ui32SysClock);
    #else
    (*((void (*)(void))(*(uint32_t *)0x2c)))();
    #endif
    }
    I am using EK-TM4C1294XL board. If I just keep, ROM_UpdateEMAC function it is giving compilation error that ROM_UpdateEMAC() is not defined.

    It goes to SoftwareUpdateRequestCallback() function, but upgrade is not happening.
    Can you please provide any suggestion why its not working?

    Thanks,
    Bhavesh
  • Hi,

    Able to resolve the answer by adding 'TARGET_IS_TM4C129_RA1' in Pre defined symbols and some changes in UDP callback.
    Thanks for help.

    BTW started RnD on using TI-RTOS for the project and currently checking feasibility of all modules which I have added in my project.
    Will post for any queries.

    Thanks,
    Bhavesh
  • Hello Bhavesh

    Do check the revision of the TM4C129. For both RA1 and RA2 revision devices, the existing TivaWare 2.1.0 requires the use of RA1 as the define.

    Regards
    Amit
  • Is their any possibility you remember how you defined FLAG_SWUPDATE ?
    I notice FLAG_TICK is defined as 1 (not sure if this is important or could cause conflicts)

    Also, where do you put
    ROM_UpdateEMAC(g_ui32SysClock);

    is that called from an interrupt handler, at the end of SoftwareUpdateBegin, or somewhere else?
  • Hi Jack,

    - Check continuously for any change in IP address assigned to board (if using DHCP) and call for SoftwareUpdateInit(callback) that time.
    - In my code FLAG_TICK is defined as 1 in one of .h file. Forgot why I have put that but using FLAG_TICK in updating flags to control LEDs.
    - ROM_UpdateEMAC() is defined in SoftwareUpdateBegin() as below at the end:
        

    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;
    
        // 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))   // For Ethernet
              ROM_UpdateEMAC(g_ui32SysClock);
        #else
               (*((void (*)(void))(*(uint32_t *)0x2c)))();
        #endif
    }

    - I have not used FLAG_SWUPDATE in my code. Whenever update call is recieved at UDP than it starts updating code.

    Thanks,
    Bhavesh

  • Hi Bhavesh

    I am getting the same problem.
    Please help me out this,


    I followed the steps mentioned above. I am sending .bin file from LMFlash Programmer. In following code it always go to else condition.
    //
    // 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_UpdateEthernet) && !(defined USE_FLASH_BOOT_LOADER))
    ROM_UpdateEMAC(ui32SysClock);
    #else
    (*((void (*)(void))(*(uint32_t *)0x2c)))();
    #endif
    }
    I am using EK-TM4C1294XL board. If I just keep, ROM_UpdateEMAC function it is giving compilation error that ROM_UpdateEMAC() is not defined.

    It goes to SoftwareUpdateRequestCallback() function, but upgrade is not happening.
    Can you please provide any suggestion why its not working?
  • Hi,

    Have you provided Predefined symbol "TARGET_IS_TM4C129_RA1" and "PART_TM4C1294NCPDT" in Project Settings?

    These were the setting I have done and I was able to upload Firmware via Ethernet using LM Flash Programmer. 

    I have attached swupdate.c for reference. Hope this helps.

    swupdate.c
    //*****************************************************************************
    //
    // swupdate.c - A module wrapping the Ethernet bootloader software update
    //              functionality.
    //
    // Copyright (c) 2008-2014 Texas Instruments Incorporated.  All rights reserved.
    // 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.
    // 
    // This is part of revision 2.1.0.12573 of the Tiva Utility Library.
    //
    //*****************************************************************************
    
    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_nvic.h"
    #include "inc/hw_sysctl.h"
    #include "inc/hw_types.h"
    #include "driverlib/flash.h"
    #include "driverlib/rom.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/systick.h"
    #include "utils/lwiplib.h"
    #include "utils/swupdate.h"
    #include "main.h"
    
    //*****************************************************************************
    //
    //! \addtogroup swupdate_api
    //! @{
    //
    //*****************************************************************************
    
    //*****************************************************************************
    //
    // The UDP port used to send the remote firmware update request signal.  This
    // is the well-known port associated with "discard" function and is also used
    // by some Wake-On-LAN implementations.
    //
    //*****************************************************************************
    #define MPACKET_PORT            9
    
    //*****************************************************************************
    //
    // The length of the various parts of the remote firmware update request magic
    // packet and its total length.  This contains a 6 byte header followed by 4
    // copies of the target MAC address.
    //
    //*****************************************************************************
    #define MPACKET_HEADER_LEN      6
    #define MPACKET_MAC_REP         4
    #define MPACKET_MAC_LEN         6
    #define MPACKET_LEN             (MPACKET_HEADER_LEN +                         \
                                     (MPACKET_MAC_REP * MPACKET_MAC_LEN))
    
    //*****************************************************************************
    //
    // The marker byte used at the start of the magic packet.  This is repeated
    // MPACKET_HEADER_LEN times.
    //
    //*****************************************************************************
    #define MPACKET_MARKER          0xAA
    
    //*****************************************************************************
    //
    // The callback function which is used to determine whether or not the
    // application wants to allow a remotely-requested firmware update.
    //
    //*****************************************************************************
    tSoftwareUpdateRequested g_pfnUpdateCallback = NULL;
    
    //*****************************************************************************
    //
    // A pointer to the remote firmware update signal PCB data structure.
    //
    //*****************************************************************************
    static struct udp_pcb *g_psMagicPacketPCB = NULL;
    
    //*****************************************************************************
    //
    // Receives a UDP port 9 packet from lwIP.
    //
    // \param arg is not used in this implementation.
    // \param pcb is the pointer to the UDB control structure.
    // \param p is the pointer to the PBUF structure containing the packet data.
    // \param addr is the source (remote) IP address for this packet.
    // \param port is the source (remote) port for this packet.
    //
    // This function is called when the lwIP TCP/IP stack has an incoming
    // UDP packet to be processed on the remote firmware update signal port.
    //
    // \return None.
    //
    //*****************************************************************************
    static void
    SoftwareUpdateUDPReceive(void *arg, struct udp_pcb *pcb, struct pbuf *p,
                             struct ip_addr *addr, u16_t port)
    {
        int8_t *pi8Data = p->payload;
        uint32_t ui32Loop, ui32MACLoop;
    
        //
        // Check that the packet length is what we expect.  If not, ignore the
        // packet.
        //
        if(p->len == MPACKET_LEN)
        {
            //
            // The length matches so now look for the 6 byte header
            //
            for(ui32Loop = 0; ui32Loop < MPACKET_HEADER_LEN; ui32Loop++)
            {
                //
                // Does this header byte match the expected marker?
                //
                if((*pi8Data & 0x000000FF)!= MPACKET_MARKER)
                {
                    //
                    // No - free the buffer and return - this is not a packet
                    // we are interested in.
                    //
                    pbuf_free(p);
                    return;
                }
                else
                {
                    //
                    // Byte matched so move on to the next one.
                    //
                    pi8Data++;
                }
            }
        }
        else
        {
            //
            // No - free the buffer and return - this is not a packet
            // we are interested in.
            //
            pbuf_free(p);
            return;
        }
    
        //
        // If we get here, the packet length and header markers indicate
        // that this is a remote firmware update request.  Now check that it
        // is for us and that it contains the required number of copies of
        // the MAC address.
        //
    
        //
        // Loop through each of the expected MAC address copies.
        //
        for(ui32Loop = 0; ui32Loop < MPACKET_MAC_REP; ui32Loop++)
        {
            //
            // Loop through each byte of the MAC address in this
            // copy.
            //
            for(ui32MACLoop = 0; ui32MACLoop < MPACKET_MAC_LEN; ui32MACLoop++)
            {
                //
                // Does the payload MAC address byte match what we expect?
                //
                if((*pi8Data & 0x000000FF) != g_pui8MACAddr[ui32MACLoop])
                {
                    //
                    // No match - free the packet and return.
                    //
                    pbuf_free(p);
                    return;
                }
                else
                {
                    //
                    // Byte matched so move on to the next one.
                    //
                    pi8Data++;
                }
            }
        }
    
        //
        // Free the pbuf since we are finished with it now.
        //
        pbuf_free(p);
    
        //
        // If we get this far, we've received a valid remote firmare update
        // request targetted at this board.  Signal this to the application
        // if we have a valid callback pointer.
        //
        if(g_pfnUpdateCallback)
        {
            g_pfnUpdateCallback();
        }
    }
    
    //*****************************************************************************
    //
    //! Initializes the remote Ethernet software update notification feature.
    //!
    //! \param pfnCallback is a pointer to a function which will be called whenever
    //! a remote firmware update request is received.  If the application wishes
    //! to allow the update to go ahead, it must call SoftwareUpdateBegin() from
    //! non-interrupt context after the callback is received.  Note that the
    //! callback will most likely be made in interrupt context so it is not safe
    //! to call SoftwareUpdateBegin() from within the callback itself.
    //!
    //! This function may be used on Ethernet-enabled parts to support
    //! remotely-signaled firmware updates over Ethernet.  The LM Flash Programmer
    //! (LMFlash.exe) application sends a magic packet to UDP port 9 whenever the
    //! user requests an Ethernet-based firmware update.  This packet consists of
    //! 6 bytes of 0xAA followed by the target MAC address repeated 4 times.
    //! This function starts listening on UDP port 9 and, if a magic packet
    //! matching the MAC address of this board is received, makes a call to the
    //! provided callback function to indicate that an update has been requested.
    //!
    //! The callback function provided here will typically be called in the context
    //! of the lwIP Ethernet interrupt handler.  It is not safe to call
    //! SoftwareUpdateBegin() in this context so the application should use the
    //! callback to signal code running in a non-interrupt context to perform the
    //! update if it is to be allowed.
    //!
    //! UDP port 9 is chosen for this function since this is the well-known port
    //! associated with ``discard'' operation.  In other words, any other system
    //! receiving the magic packet will simply ignore it.  The actual magic packet
    //! used is modeled on Wake-On-LAN which uses a similar structure (6 bytes of
    //! 0xFF followed by 16 repetitions of the target MAC address).  Some
    //! Wake-On-LAN implementations also use UDP port 9 for their signaling.
    //!
    //! \note Applications using this function must initialize the lwIP stack prior
    //! to making this call and must ensure that the lwIPTimer() function is called
    //! periodically.  lwIP UDP must be enabled in lwipopts.h to ensure that the
    //! magic packets can be received.
    //!
    //! \return None.
    //
    //*****************************************************************************
    void
    SoftwareUpdateInit(tSoftwareUpdateRequested pfnCallback)
    {
        // Remember the callback function pointer we have been given.
        g_pfnUpdateCallback = pfnCallback;
    
        // Set up a UDP PCB to allow us to receive the magic packets sent from
        // LMFlash.  These may be sent to port 9 from any port on the source
        // machine so we do not call udp_connect here (since this causes lwIP to
        // filter any packet that did not originate from port 9 too).
        //
    
        if(g_psMagicPacketPCB != NULL) {udp_remove(g_psMagicPacketPCB);}
        g_psMagicPacketPCB = udp_new();
        udp_recv(g_psMagicPacketPCB, SoftwareUpdateUDPReceive, NULL);
        udp_bind(g_psMagicPacketPCB, IP_ADDR_ANY, MPACKET_PORT);
    }
    
    //*****************************************************************************
    //
    //! Passes control to the bootloader and initiates a remote software update
    //! over Ethernet.
    //!
    //! This function passes control to the bootloader and initiates an update of
    //! the main application firmware image via BOOTP across Ethernet.  This
    //! function may only be used on parts supporting Ethernet and in cases where
    //! the Ethernet boot loader is in use alongside the main application image.
    //! It must not be called in interrupt context.
    //!
    //! Applications wishing to make use of this function must be built to
    //! operate with the bootloader.  If this function is called on a system
    //! which does not include the bootloader, the results are unpredictable.
    //!
    //! \note It is not safe to call this function from within the callback
    //! provided on the initial call to SoftwareUpdateInit().  The application
    //! must use the callback to signal a pending update (assuming the update is to
    //! be permitted) to some other code running in a non-interrupt context.
    //!
    //! \return Never returns.
    //
    //*****************************************************************************
    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(g_ui32SysClock);
    #else
        (*((void (*)(void))(*(uint32_t *)0x2c)))();
    #endif
    }
    
    //*****************************************************************************
    //
    // Close the Doxygen group.
    //! @}
    //
    //*****************************************************************************
    

    Thanks,
    Bhavesh