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.

TM4C1294KCPDT: Ethernet bootloader does not get a UIP_NEWDATA flag.

Part Number: TM4C1294KCPDT
Other Parts Discussed in Thread: EK-TM4C1294XL

Recently, I have started to work with TM4C1294KCPDT board and trying to create an Ethernet bootloader for it. I am working on Linux Mint 20.1 Cinnamon using ARM-USB-OCD-H and VS Code for debugging.

After some time of learning basics, I have reached the point, when Ethernet bootloader uses BOOTP and successfully gets IP for the board. Then, it sends a request for a first block of data to TFTP server. Server responses with the first block and here I got a problem. At this point, TFTP server continues retransmitting the first block and bootloader does not process it.

I have noticed that this block of data is actually inside receive descriptor, but the flag is in UIP_POLL state. Moreover, it becomes more weird for me when the connection is aborted(timeout event occurs), bootloader is able to reconnect using BOOTP, where the same UIP_NEWDATA flag is up with a BOOTP data package from the server. The following request from TFTP server is transmitted again, but once again received the first block of data comes without UIP_NEWDATA flag.

My startup.c:

/*
* Copyright (c) 2018, Shawn D'silva <shawn@shawndsilva.com>
* All rights reserved.
*
*  This file is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This file is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
*
* File:			startup.c
* Author:		Shawn D'silva <https://www.shawndsilva.com>.
* Version:		1.0.0.
* Description:	startup file for the TM4C Launchpad board,defines the vector table,
  and most importantly the Reset_Handler enabling the TM4C to execute the main program when the <RESET>
  button is pressed on board
*/
#include "startup.h"

//*****************************************************************************
//
// Reserve space for the system stack.
//
//*****************************************************************************
#ifndef STACK_SIZE
#define STACK_SIZE                              1024
#endif
static unsigned long pulStack[STACK_SIZE];

// +-----------------------------------------------------------------------------------+
// +										            Vector Table                                       +
// +-----------------------------------------------------------------------------------+

__attribute__((section(".vector_table"))) //marks this vector table as a part of the section ".vector_table"
//in the linker script
vector_table_t vectors[] = 
//void (* g_pfnVectors[])(void) __attribute__((section(".vector_table"))) =
{
    //{.stack_top = &_stack_ptr}, // 0    Pointer to top of Stack
    (void (*)(void))((unsigned long)pulStack + sizeof(pulStack)),
    Reset_Handler,              // 1    Reset handler is called when the <RESET> button is pressed
    NMI_Handler,                // 2    Non-Maskable Interrupt handler
    HardFault_Handler,          // 3    Hard Fault Handler
    MemManageFault_Handler,     // 4    Memory management fault Handler
    BusFault_Handler,           // 5    Bus Fault Handler
    UsageFault_Handler,         // 6    Usage Fault Handler
    0,                          // 7    Reserved
    0,                          // 8    Reserved
    0,                          // 9    Reserved
    0,                          // 10   Reserved
    SVC_Handler,                // 11   SuperVisor Call Handler
    DebugMonitor_Handler,       // 12   Debug Monitor Handler
    0,                          // 13   Reserved
    PendSV_Handler,             // 14   Pendeable interrupt driven request
    SysTickIntHandler,          // 15   SysTick Timer handler
    GPIOPortA_ISR,              // 16   GPIO Port A Interrupt Service Routine
    GPIOPortB_ISR,              // 17   GPIO Port B Interrupt Service Routine
    GPIOPortC_ISR,              // 18   GPIO Port C Interrupt Service Routine
    GPIOPortD_ISR,              // 19   GPIO Port D Interrupt Service Routine
    GPIOPortE_ISR,              // 20   GPIO Port E Interrupt Service Routine
    UART0_ISR,                  // 21   UART 0
    UART1_ISR,                  // 22   UART 1
    SSI0_ISR,                   // 23   SSI 0
    I2C0_ISR,                   // 24   I2C0
    PWMFault_ISR,               // 25   PWM Fault
    PWMGenerator0_ISR,          // 26   PWM Generator 0
    PWMGenerator1_ISR,          // 27   PWM Generator 1
    PWMGenerator2_ISR,          // 28   PWM Generator 2
    QEI0_ISR,                   // 29   QEI0
    ADC0Sequence0_ISR,          // 30   ADC0 Sequence 0
    ADC0Sequence1_ISR,          // 31   ADC0 Sequence 1
    ADC0Sequence2_ISR,          // 32   ADC0 Sequence 2
    ADC0Sequence3_ISR,          // 33   ADC0 Sequence 3
    WatchDogTimer_ISR,          // 34   Watchdog Timers 0 and 1
    Timer0A_ISR,                // 35   16/32-Bit Timer 0A
    Timer0B_ISR,                // 36   16/32-Bit Timer 0B
    Timer1A_ISR,                // 37   16/32-Bit Timer 1A
    Timer1B_ISR,                // 38   16/32-Bit Timer 1B
    Timer2A_ISR,                // 39   16/32-Bit Timer 2A
    Timer2B_ISR,                // 40   16/32-Bit Timer 2B
    AnalogComparator0_ISR,      // 41   Analog Comparator 0
    AnalogComparator1_ISR,      // 42   Analog Comparator 1
    AnalogComparator2_ISR,      // 43   Analog Comparator 2
    SystemCtrl_ISR,             // 44   System Control
    FlashCtrl_ISR,              // 45   Flash Memory Control
    GPIOPortF_ISR,              // 46   GPIO Port F
    GPIOPortG_ISR,              // 47   GPIO Port G
    GPIOPortH_ISR,              // 48   GPIO Port H
    UART2_ISR,                  // 49   UART2
    SPI1_ISR,                   // 50   SSI1
    Timer3A_ISR,                // 51   16/32-Bit Timer 3A
    Timer3B_ISR,                // 52   16/32-Bit Timer 3B
    I2C1_ISR,                   // 53   I2C1
    CAN0_ISR,                   // 54   CAN 0
    CAN1_ISR,                   // 55   CAN 1
    ENET_ISR,                   // 56   Ethernet MAC
    HIB_ISR,                    // 57   HIB
    USB_ISR,                    // 58   USB MAC
    PWMGenerator3_ISR,          // 59   PWM Generator 3
    UDMA0Software_ISR,          // 60   uDMA 0 Software
    UDMA0Error_ISR,             // 61   uDMA 0 Error
    ADC1Sequence0_ISR,          // 62   ADC1 Sequence 0
    ADC1Sequence1_ISR,          // 63   ADC1 Sequence 1
    ADC1Sequence2_ISR,          // 64   ADC1 Sequence 2
    ADC1Sequence3_ISR,          // 65   ADC1 Sequence 3
    EPI0_ISR,                   // 66   EPI 0
    GPIOPortJ_ISR,              // 67   GPIO Port J
    GPIOPortK_ISR,              // 68   GPIO Port K
    GPIOPortL_ISR,              // 69   GPIO Port L
    SSI2_ISR,                   // 70   SSI 2
    SSI3_ISR,                   // 71   SSI 3
    UART3_ISR,                  // 72   UART 3
    UART4_ISR,                  // 73   UART 4
    UART5_ISR,                  // 74   UART 5
    UART6_ISR,                  // 75   UART 6
    UART7_ISR,                  // 76   UART 7
    I2C2_ISR,                   // 77   I2C 2
    I2C3_ISR,                   // 78   I2C 3
    Timer4A_ISR,                // 79   Timer 4A
    Timer4B_ISR,                // 80   Timer 4B
    Timer5A_ISR,                // 81   Timer 5A
    Timer5B_ISR,                // 82   Timer 5B
    FPException_ISR,            // 83   Floating-Point Exception (imprecise)
    0,                          // 84   Reserved
    0,                          // 85   Reserved
    I2C4_ISR,                   // 86   I2C 4
    I2C5_ISR,                   // 87   I2C 5
    GPIOPortM_ISR,              // 88   GPIO Port M
    GPIOPortN_ISR,              // 89   GPIO Port N
    0,                          // 90   Reserved
    Tamper_ISR,                 // 91   Tamper
    GPIOPortP_ISR,              // 92   GPIO Port P (Summary or P0)
    GPIOPortP1_ISR,             // 93   GPIO Port P1
    GPIOPortP2_ISR,             // 94   GPIO Port P2
    GPIOPortP3_ISR,             // 95   GPIO Port P3
    GPIOPortP4_ISR,             // 96   GPIO Port P4
    GPIOPortP5_ISR,             // 97   GPIO Port P5
    GPIOPortP6_ISR,             // 98   GPIO Port P6
    GPIOPortP7_ISR,             // 99   GPIO Port P7
    GPIOPortQ_ISR,              // 100  GPIO Port Q (Summary or Q0)
    GPIOPortQ1_ISR,             // 101  GPIO Port Q1
    GPIOPortQ2_ISR,             // 102  GPIO Port Q2
    GPIOPortQ3_ISR,             // 103  GPIO Port Q3
    GPIOPortQ4_ISR,             // 104  GPIO Port Q4
    GPIOPortQ5_ISR,             // 105  GPIO Port Q5
    GPIOPortQ6_ISR,             // 106  GPIO Port Q6
    GPIOPortQ7_ISR,             // 107  GPIO Port Q7
    0,                          // 108  Reserved
    0,                          // 109  Reserved
    0,                          // 110  Reserved
    0,                          // 111  Reserved
    0,                          // 112  Reserved
    0,                          // 113  Reserved
    Timer6A_ISR,                // 114  16/32-Bit Timer 6A
    Timer6B_ISR,                // 115  16/32-Bit Timer 6B
    Timer7A_ISR,                // 116  16/32-Bit Timer 7A
    Timer7B_ISR,                // 117  16/32-Bit Timer 7B
    I2C6_ISR,                   // 118  I2C 6
    I2C7_ISR,                   // 119  I2C 7
    0,                          // 120  Reserved
    0,                          // 121  Reserved
    0,                          // 122  Reserved
    0,                          // 123  Reserved
    0,                          // 124  Reserved
    I2C8_ISR,                   // 125  I2C 8
    I2C9_ISR,                   // 126  I2C 9
    0,                          // 127  Reserved
    0,                          // 128  Reserved
    0                           // 129  Reserved
};

// +-----------------------------------------------------------------------------------+
// +                Implementations of Interrupt Service Routines                      +
// +-----------------------------------------------------------------------------------+
void Reset_Handler(void)
{

  int *src, *dest;

  /* copying of the .data values into RAM */

  src = &_etext;
  for (dest = &_data; dest < &_edata;)
  {
    *dest++ = *src++;
  }

  // /* initializing .bss values to zero*/
  // for (dest = &_bss; dest < &_ebss;)
  // {
  //   *dest++ = 0;
  // }
  // src = (int *) &_text;

  //
  // Zero fill the bss segment.
  //
  __asm("    ldr     r0, =_bss\n"
        "    ldr     r1, =_ebss\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");

  //
  // Call the user-supplied low level hardware initialization function
  // if provided.
  //
#ifdef BL_HW_INIT_FN_HOOK
    BL_HW_INIT_FN_HOOK
#endif

  //
  // See if an update should be performed.
  //
    CheckForceUpdate();

  //-------------Actual bootloader--------------
  //
  // Configure the microcontroller.
  //
  SystemInit();
  PortHInit();
#ifdef ENET_ENABLE_UPDATE
    ConfigureEnet();
#else
    ConfigureDevice();
#endif

  //
  // Call the user-supplied initialization function if provided.
  //
#ifdef BL_INIT_FN_HOOK
    BL_INIT_FN_HOOK
#endif

  //
  // Branch to the update handler.
  //
#ifdef ENET_ENABLE_UPDATE
    UpdateBOOTP();
#else
    Updater();
#endif

}

void SVC_Handler(void)
{
    int *src, *dest;

    /* copying of the .data values into RAM */

    src = &_etext;
    for (dest = &_data; dest < &_edata;)
    {
    *dest++ = *src++;
    }

    /* initializing .bss values to zero*/

    for (dest = &_bss; dest < &_ebss;)
    {
    *dest++ = 0;
    }
    src = (int *) &_text;

    //
    // Call the user-supplied low level hardware initialization function
    // if provided.
    //
#ifdef BL_HW_INIT_FN_HOOK
    BL_HW_INIT_FN_HOOK
#endif

    //
    // Call the user-supplied re-initialization function if provided.
    //
#ifdef BL_REINIT_FN_HOOK
    BL_REINIT_FN_HOOK
#endif

    //
    // Branch to the update handler.
    //
#ifdef ENET_ENABLE_UPDATE
    UpdateBOOTP();
#else
    Updater();
#endif
}

void Default_Handler(void)
{
  while (1)
  {
    //does literally nothing except infinitely loop
  }
}

void Delay(uint32_t ui32Count)
{
  while(--ui32Count);
}

/*****************************************END OF FILE*********************************************/

Linker script code:

/*
* Copyright (c) 2018, Shawn D'silva <shawn@shawndsilva.com>
* All rights reserved.
*
*  This file is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This file is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
*
* File:			TM4C123GH6PHM.ld
* Author:		Shawn D'silva <https://www.shawndsilva.com>.
* Version:		1.0.0.
* Description:	linker file for the TM4C Launchpad
*/

MEMORY
{
    FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16K /* FLASH size 16KB */
    RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K/* RAM size 256KB */

}

SECTIONS
{


    /* 
     * initial stack pointer pointing to top of stack,starts from higher addresses
     * i.e ORIGIN(RAM) + LENGTH(RAM)-1 in this case 0x20007FFF to lower addesses i.e
     * those lesser than 0x20007FFF to 0x2000000,which is the origina address of RAM,
     * until it comes in contact with .bss or .data in which case a buffer overflow occurs
    */
    PROVIDE( _stack_ptr = ORIGIN(RAM) + LENGTH(RAM));

    /* constants  and other code stored in FLASH */
    .text :
    {
        _text = .;               /* beginning of .text segment,also called code memory */
        KEEP(*(.vector_table)) /* vector table defined in startup.c to be included */
        *(.text*)                   /* other code */
        *(.rodata*)                /* constants go here */
        . = ALIGN (4);
        _etext = .;             /* end of .text segment */
    } > FLASH

    /* data, initialized variables, to be copied to RAM upon <RESET> by startup.c */
    .data : 
    {
        _data = .;          /* beginning of .data segment */
        *(.data*)           /* data goes here */
        . = ALIGN (4);
        _edata = .;         /* end of .data segment */
    } > RAM AT >FLASH   /* .data segment starts directly after the .text section in FLASH */

    /* uninitialized data which is initialized to 0 upon <RESET> by startup.c */
    .bss :
    {
        _bss = .;       /* beginning of .bss segment */
        *(.bss*)        /* .bss content goes here */
        *(COMMON)       
        . = ALIGN (4);
        _ebss = .;      /* end of .bss segment */
    } > RAM

}

Ethernet bootloader code:

//*****************************************************************************
//
// bl_emac.c - Functions to update via Ethernet.
//
// Copyright (c) 2013-2020 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.2.0.295 of the Tiva Firmware Development Package.
//
//*****************************************************************************

#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "bl_config.h"
#include "inc/hw_emac.h"
#include "inc/hw_flash.h"
#include "inc/hw_gpio.h"
#include "inc/hw_memmap.h"
#include "inc/hw_nvic.h"
#include "inc/hw_sysctl.h"
#include "inc/hw_types.h"
#include "inc/hw_ints.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/emac.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "bl_decrypt.h"
#include "bl_flash.h"
#include "bl_hooks.h"
#include "gpio.h"

#ifndef UIP_APPCALL
#define UIP_APPCALL UIP_UDP_APPCALL
#endif /* UIP_APPCALL */

//
// Define ROM_SysCtlClockFreqSet() for snowflake RA0. Even though this function
// is deprecated in RA0 ROM, the function operates correctly when
// SYSCTL_MOSCCTL register is configured correctly prior to calling this
// function.
//
#if defined(TARGET_IS_TM4C129_RA0)
#define ROM_SysCtlClockFreqSet         \
    ((uint32_t(*)(uint32_t ui32Config, \
                  uint32_t ui32SysClock))ROM_SYSCTLTABLE[48])
#endif

//
// Define ROM_GPIOPadConfigSet() for the Boot Loader for Snowflake.
// This function fails in Snowflake for higher drive strengths, it will work
// properly for the instances where it is used here in the boot loader.
//
#if defined(TARGET_IS_TM4C129_RA0) || \
    defined(TARGET_IS_TM4C129_RA1)
#define ROM_GPIOPadConfigSet          \
    ((void (*)(uint32_t ui32Port,     \
               uint8_t ui8Pins,       \
               uint32_t ui32Strength, \
               uint32_t ui32PadType))ROM_GPIOTABLE[5])
#endif

//
// Define ROM_EMACInit for the bootloader of Snowflake RA0.  This function is
// deprecated in RA0 ROM, as it does not disable some interrupts that are not
// cleared by ***_EMACIntClear().  But that is not a problem for the bootloader
// as we are not enabling any interrupts.
//
#if defined(TARGET_IS_TM4C129_RA0)
#define ROM_EMACInit                   \
    ((void (*)(uint32_t ui32Base,      \
               uint32_t ui32SysClk,    \
               uint32_t ui32BusConfig, \
               uint32_t ui32RxBurst,   \
               uint32_t ui32TxBurst,   \
               uint32_t ui32DescSkipSize))ROM_EMACTABLE[8])
#endif

//*****************************************************************************
//
//! \addtogroup bl_emac_api
//! @{
//
//*****************************************************************************

#if defined(ENET_ENABLE_UPDATE) || defined(DOXYGEN)
//*****************************************************************************
//
// Make sure that the crystal frequency is defined.
//
//*****************************************************************************
#if !defined(CRYSTAL_FREQ)
#error ERROR: CRYSTAL_FREQ must be defined for Ethernet update!
#endif

//*****************************************************************************
//
// Make sure that boot loader update is not enabled (it is not supported via
// BOOTP given that there is no way to distinguish between a normal firmware
// image and a boot loader update image).
//
//*****************************************************************************
#if defined(ENABLE_BL_UPDATE)
#error ERROR: Updating the boot loader is not supported over Ethernet!
#endif

//*****************************************************************************
//
// TFTP packets contain 512 bytes of data and a packet shorter than this
// indicates the end of the transfer.
//
//*****************************************************************************
#define TFTP_BLOCK_SIZE 512

//*****************************************************************************
//
// uIP uses memset, so a simple one is provided here.  This is not as efficient
// as the one in the C library (from an execution time perspective), but it is
// much smaller.
//
//*****************************************************************************
void *
my_memset(void *pvDest, int iChar, size_t i32Length)
{
    int8_t *pi8Buf = (int8_t *)pvDest;

    //
    // Fill the buffer with the given character.
    //
    while (i32Length--)
    {
        *pi8Buf++ = iChar;
    }

    //
    // Return a pointer to the beginning of the buffer.
    //
    return (pvDest);
}

//*****************************************************************************
//
// uIP uses memcpy, so a simple one is provided here.  This is not as efficient
// as the one in the C library (from an execution time perspective), but it is
// much smaller.
//
//*****************************************************************************
void *
my_memcpy(void *pvDest, const void *pvSrc, size_t i32Length)
{
    const int8_t *pi8Src = (const int8_t *)pvSrc;
    int8_t *pi8Dest = (int8_t *)pvDest;

    //
    // Copy bytes from the source buffer to the destination buffer.
    //
    while (i32Length--)
    {
        *pi8Dest++ = *pi8Src++;
    }

    //
    // Return a pointer to the beginning of the destination buffer.
    //
    return (pvDest);
}

//*****************************************************************************
//
// Directly include the uIP code if using Ethernet for the update.  This allows
// non-Ethernet boot loader builds to not have to supply the uip-conf.h file
// that would otherwise be required.
//
//*****************************************************************************
#define memcpy my_memcpy
#define memset my_memset
#undef htonl
#undef ntohl
#undef htons
#undef ntohs
#include "uip/pt.h"
#include "uip/uip_arp.c"
#undef BUF
#include "uip/uip.c"
#include "uip/uip_timer.c"

//*****************************************************************************
//
// A prototype for the function (in the startup code) for a predictable length
// delay.
//
//*****************************************************************************
extern void Delay(uint32_t ui32Count);

//*****************************************************************************
//
// Defines for setting up the system clock.
//
//*****************************************************************************
#define SYSTICKHZ 100
#define SYSTICKMS (1000 / SYSTICKHZ)

//*****************************************************************************
//
// UIP Timers (in ms)
//
//*****************************************************************************
#define UIP_PERIODIC_TIMER_MS 50
#define UIP_ARP_TIMER_MS 10000

//*****************************************************************************
//
// This structure defines the fields in a BOOTP request/reply packet.
//
//*****************************************************************************
typedef struct
{
    //
    // The operation; 1 is a request, 2 is a reply.
    //
    uint8_t ui8Op;

    //
    // The hardware type; 1 is Ethernet.
    //
    uint8_t ui8HType;

    //
    // The hardware address length; for Ethernet this will be 6, the length of
    // the MAC address.
    //
    uint8_t ui8HLen;

    //
    // Hop count, used by gateways for cross-gateway booting.
    //
    uint8_t ui8Hops;

    //
    // The transaction ID.
    //
    uint32_t ui32XID;

    //
    // The number of seconds elapsed since the client started trying to boot.
    //
    uint16_t ui16Secs;

    //
    // The BOOTP flags.
    //
    uint16_t ui16Flags;

    //
    // The client's IP address, if it knows it.
    //
    uint32_t ui32CIAddr;

    //
    // The client's IP address, as assigned by the BOOTP server.
    //
    uint32_t ui32YIAddr;

    //
    // The TFTP server's IP address.
    //
    uint32_t ui32SIAddr;

    //
    // The gateway IP address, if booting cross-gateway.
    //
    uint32_t ui32GIAddr;

    //
    // The hardware address; for Ethernet this is the MAC address.
    //
    uint8_t pui8CHAddr[16];

    //
    // The name, or nickname, of the server that should handle this BOOTP
    // request.
    //
    char pcSName[64];

    //
    // The name of the boot file to be loaded via TFTP.
    //
    char pcFile[128];

    //
    // Optional vendor-specific area; not used for BOOTP.
    //
    uint8_t pui8Vend[64];
} tBOOTPPacket;

//*****************************************************************************
//
// The BOOTP commands.
//
//*****************************************************************************
#define BOOTP_REQUEST 1
#define BOOTP_REPLY 2

//*****************************************************************************
//
// The TFTP commands.
//
//*****************************************************************************
#define TFTP_RRQ 1
#define TFTP_WRQ 2
#define TFTP_DATA 3
#define TFTP_ACK 4
#define TFTP_ERROR 5

//*****************************************************************************
//
// The UDP ports used by the BOOTP protocol.
//
//*****************************************************************************
#define BOOTP_SERVER_PORT 67
#define BOOTP_CLIENT_PORT 68

//*****************************************************************************
//
// The UDP port for the TFTP server.
//
//*****************************************************************************
#define TFTP_PORT 69

//*****************************************************************************
//
// The MAC address of the Ethernet interface.
//
//*****************************************************************************
#ifdef ENET_MAC_ADDR0
static struct uip_eth_addr g_sMACAddr =
    {
        {ENET_MAC_ADDR0,
         ENET_MAC_ADDR1,
         ENET_MAC_ADDR2,
         ENET_MAC_ADDR3,
         ENET_MAC_ADDR4,
         ENET_MAC_ADDR5}};
#else
static struct uip_eth_addr g_sMACAddr;
#endif

//*****************************************************************************
//
// The number of SysTick interrupts since the start of the boot loader.
//
//*****************************************************************************
static uint32_t g_ui32Ticks;

//*****************************************************************************
//
// The seed for the random number generator.
//
//*****************************************************************************
static uint32_t g_ui32RandomSeed;

//*****************************************************************************
//
// The number of milliseconds since the last call to uip_udp_periodic().
//
//*****************************************************************************
static volatile uint32_t g_ui32PeriodicTimer;

//*****************************************************************************
//
// The number of milliseconds since the last call to uip_arp_timer().
//
//*****************************************************************************
static volatile uint32_t g_ui32ARPTimer;

//*****************************************************************************
//
// The transaction ID of the most recently sent out BOOTP request.
//
//*****************************************************************************
static uint32_t g_ui32XID;

//*****************************************************************************
//
// The state for the proto-thread that handles the BOOTP process.
//
//*****************************************************************************
static struct pt g_sThread;

//*****************************************************************************
//
// The amount of time to wait for a BOOTP reply before sending out a new BOOTP
// request.
//
//*****************************************************************************
static uint32_t g_ui32Delay;

//*****************************************************************************
//
// The target time (relative to g_ui32Ticks) when the next timeout occurs.
//
//*****************************************************************************
static uint32_t g_ui32Target;

//*****************************************************************************
//
// The IP address of the TFTP server.
//
//*****************************************************************************
static uip_ipaddr_t g_sServerAddr;

//*****************************************************************************
//
// The name of the file to be read from the TFTP server.
//
//*****************************************************************************
static char g_pcFilename[128];

//*****************************************************************************
//
// The end of flash.  If there is not a reserved block at the end of flash,
// this is the real end of flash.  If there is a reserved block, this is the
// start of the reserved block (i.e. the virtual end of flash).
//
//*****************************************************************************
static uint32_t g_ui32FlashEnd;

//*****************************************************************************
//
// The current block being read from the TFTP server.
//
//*****************************************************************************
static uint32_t g_ui32TFTPBlock;

//*****************************************************************************
//
// The number of TFTP retries.
//
//*****************************************************************************
static uint32_t g_ui32TFTPRetries;

//*****************************************************************************
//
// The UDP socket used to communicate with the BOOTP and TFTP servers (in
// sequence).
//
//*****************************************************************************
struct uip_udp_conn *g_pConn;

//*****************************************************************************
//
// The current link status.
//
//*****************************************************************************
static uint32_t g_ui32Link;

//*****************************************************************************
//
// Ethernet DMA descriptors.
//
// Although uIP uses a single buffer, the MAC hardware needs a minimum of
// 3 receive descriptors to operate.
//
//*****************************************************************************
#define NUM_TX_DESCRIPTORS 3
#define NUM_RX_DESCRIPTORS 3
tEMACDMADescriptor g_psRxDescriptor[NUM_TX_DESCRIPTORS];
tEMACDMADescriptor g_psTxDescriptor[NUM_RX_DESCRIPTORS];
uint32_t g_ui32RxDescIndex;
uint32_t g_ui32TxDescIndex;

//*****************************************************************************
//
// Transmit and receive buffers.
//
//*****************************************************************************
#define RX_BUFFER_SIZE 1536 //1536
#define TX_BUFFER_SIZE 1536
uint8_t g_pui8RxBuffer[RX_BUFFER_SIZE];
uint8_t g_pui8TxBuffer[TX_BUFFER_SIZE];

//*****************************************************************************
//
//! Handles the SysTick interrupt.
//!
//! This function is called when the SysTick interrupt occurs.  It simply
//! keeps a running count of interrupts, used as a time basis for the BOOTP and
//! TFTP protocols.
//!
//! \return None.
//
//*****************************************************************************
void SysTickIntHandler(void)
{
    //
    // Increment the tick count.
    //
    g_ui32Ticks++;
    g_ui32PeriodicTimer += SYSTICKMS;
    g_ui32ARPTimer += SYSTICKMS;
}
clock_time_t
clock_time(void)
{
    return ((clock_time_t)g_ui32Ticks);
}
//*****************************************************************************
//
// The interrupt handler for the Ethernet interrupt.
//
//*****************************************************************************
void ENET_ISR(void)
{
    uint32_t ui32Temp;
    //
    // Read and Clear the interrupt.
    //
    ui32Temp = MAP_EMACIntStatus(EMAC0_BASE, true);
    MAP_EMACIntClear(EMAC0_BASE, ui32Temp);

    //
    // Check to see if an RX Interrupt has occurred.
    //
    if (ui32Temp & EMAC_INT_RECEIVE)
    {
        //
        // Indicate that a packet has been received.
        //
        //HWREGBITW(&g_ui32Flags, FLAG_RXPKT) = 1;
    }

    //
    // Has the DMA finished transferring a packet to the transmitter?
    //
    if (ui32Temp & EMAC_INT_TRANSMIT)
    {
        //
        // Indicate that a packet has been sent.
        //
        //HWREGBITW(&g_ui32Flags, FLAG_TXPKT) = 0;
    }
}
//*****************************************************************************
//
//! Computes a new random number.
//!
//! This function computes a new pseudo-random number, using a linear
//! congruence random number generator.  Note that if the entire 32-bits of the
//! produced random number are not being used, the upper N bits should be used
//! instead of the lower N bits as they are much more random (for example, use
//! ``RandomNumber() >> 28'' instead of ``RandomNumber() & 15'').
//!
//! \return Returns a 32-bit pseudo-random number.
//
//*****************************************************************************
static uint32_t
RandomNumber(void)
{
    //
    // Generate a new pseudo-random number with a linear congruence random
    // number generator.  This new random number becomes the seed for the next
    // random number.
    //
    g_ui32RandomSeed = (g_ui32RandomSeed * 1664525) + 1013904223;

    //
    // Return the new random number.
    //
    return (g_ui32RandomSeed);
}

//*****************************************************************************
//
// Read a packet from the DMA receive buffer into the uIP packet buffer.
//
//*****************************************************************************
static int32_t
PacketReceive(uint8_t *pui8Buf, int32_t i32BufLen)
{
    int_fast32_t i32FrameLen, i32Loop;

    //
    // By default, we assume we got a bad frame.
    //
    i32FrameLen = 0;

    //
    // See if the receive descriptor contains a valid frame.  Look for a
    // descriptor error, indicating that the incoming packet was truncated or,
    // if this is the last frame in a packet, the receive error bit.
    //
    if (!(g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus &
          DES0_RX_STAT_ERR))
    {
        //
        // We have a valid frame so copy the content to the supplied buffer.
        // First check that the "last descriptor" flag is set.  We sized the
        // receive buffer such that it can always hold a valid frame so this
        // flag should never be clear at this point but...
        //
        if (g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus &
            DES0_RX_STAT_LAST_DESC)
        {
            i32FrameLen =
                ((g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus &
                  DES0_RX_STAT_FRAME_LENGTH_M) >>
                 DES0_RX_STAT_FRAME_LENGTH_S);

            //
            // Sanity check.  This shouldn't be required since we sized the uIP
            // buffer such that it's the same size as the DMA receive buffer
            // but, just in case...
            //
            if (i32FrameLen > i32BufLen)
            {
                i32FrameLen = i32BufLen;
            }

            //
            // Copy the data from the DMA receive buffer into the provided
            // frame buffer.
            //
            for (i32Loop = 0; i32Loop < i32FrameLen; i32Loop++)
            {
                pui8Buf[i32Loop] = g_pui8RxBuffer[i32Loop];
            }
        }
    }

    //
    // Move on to the next descriptor in the chain.
    //
    g_ui32RxDescIndex++;
    if (g_ui32RxDescIndex == NUM_RX_DESCRIPTORS)
    {
        g_ui32RxDescIndex = 0;
    }

    //
    // Mark the next descriptor in the ring as available for the receiver to
    // write into.
    //
    g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus = DES0_RX_CTRL_OWN;

    //
    // Return the Frame Length
    //
    return (i32FrameLen);
}

//*****************************************************************************
//
// Transmit a packet from the supplied buffer.
//
//*****************************************************************************
static int32_t
PacketTransmit(uint8_t *pui8Buf, int32_t i32BufLen)
{
    int_fast32_t i32Loop;

    //
    // Wait for the previous packet to be transmitted.
    //
    while (g_psTxDescriptor[g_ui32TxDescIndex].ui32CtrlStatus &
           DES0_TX_CTRL_OWN)
    {
    }

    //
    // Check that we're not going to overflow the transmit buffer.  This
    // shouldn't be necessary since the uIP buffer is smaller than our DMA
    // transmit buffer but, just in case...
    //
    if (i32BufLen > TX_BUFFER_SIZE)
    {
        i32BufLen = TX_BUFFER_SIZE;
    }

    //
    // Copy the packet data into the transmit buffer.
    //
    for (i32Loop = 0; i32Loop < i32BufLen; i32Loop++)
    {
        g_pui8TxBuffer[i32Loop] = pui8Buf[i32Loop];
    }

    //
    // Move to the next descriptor.
    //
    g_ui32TxDescIndex++;
    if (g_ui32TxDescIndex == NUM_TX_DESCRIPTORS)
    {
        g_ui32TxDescIndex = 0;
    }

    //
    // Fill in the packet size and tell the transmitter to start work.
    //
    g_psTxDescriptor[g_ui32TxDescIndex].ui32Count = (uint32_t)i32BufLen;
    g_psTxDescriptor[g_ui32TxDescIndex].ui32CtrlStatus =
        (DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_FIRST_SEG |
         DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_IP_ALL_CKHSUMS |
         DES0_TX_CTRL_CHAINED | DES0_TX_CTRL_OWN);

    //
    // Tell the DMA to reacquire the descriptor now that we've filled it in.
    //
    ROM_EMACTxDMAPollDemand(EMAC0_BASE);

    //
    // Return the number of bytes sent.
    //
    return (i32BufLen);
}

//*****************************************************************************
//
//! Constructs and sends a BOOTP request packet.
//!
//! This function constructs a BOOTP request packet and sends it as a broadcast
//! message to the network.
//!
//! \return None.
//
//*****************************************************************************
static void
SendBOOTPRequest(void)
{
    uint8_t *pui8Packet = (uint8_t *)uip_appdata;
    tBOOTPPacket *psBOOTP = (tBOOTPPacket *)uip_appdata;
    uint32_t ui32Idx;

    //
    // Zero fill the BOOTP request packet.
    //
    for (ui32Idx = 0; ui32Idx < sizeof(tBOOTPPacket); ui32Idx++)
    {
        pui8Packet[ui32Idx] = 0;
    }

    //
    // Construct a BOOTP request.
    //
    psBOOTP->ui8Op = BOOTP_REQUEST;

    //
    // Set the hardware type to Ethernet.
    //
    psBOOTP->ui8HType = 0x01;

    //
    // Set the hardware address length to 6.
    //
    psBOOTP->ui8HLen = 0x06;

    //
    // Choose a random number for the transaction ID.
    //
    psBOOTP->ui32XID = g_ui32XID = RandomNumber();

    //
    // Set the number of seconds since we started.
    //
    psBOOTP->ui16Secs = HTONS(g_ui32Ticks / SYSTICKHZ);

    //
    // Fill in the Ethernet MAC address.
    //
    for (ui32Idx = 0; ui32Idx < 6; ui32Idx++)
    {
        psBOOTP->pui8CHAddr[ui32Idx] = g_sMACAddr.addr[ui32Idx];
    }

    //
    // Set the server name if defined.
    //
#ifdef ENET_BOOTP_SERVER
    for (ui32Idx = 0;
         (psBOOTP->pcSName[ui32Idx] = ENET_BOOTP_SERVER[ui32Idx]) != 0;
         ui32Idx++)
    {
    }
#endif

    //
    // Send the BOOTP request packet.
    //
    uip_udp_send(sizeof(tBOOTPPacket));
}

//*****************************************************************************
//
//! Parses a packet checking for a BOOTP reply message.
//!
//! This function parses a packet to determine if it is a BOOTP reply to our
//! currently outstanding BOOTP request.  If a valid reply is found, the
//! appropriate information from the packet is extracted and saved.
//!
//! \return Returns 1 if a valid BOOTP reply message was found and 0 otherwise.
//
//*****************************************************************************
static uint32_t
ParseBOOTPReply(void)
{
    tBOOTPPacket *psBOOTP = (tBOOTPPacket *)uip_appdata;
    uint32_t ui32Idx;

    //
    // See if this is a reply for our current BOOTP request.
    //
    if ((psBOOTP->ui8Op != BOOTP_REPLY) ||
        (psBOOTP->ui32XID != g_ui32XID) ||
        (*(uint32_t *)psBOOTP->pui8CHAddr != *(uint32_t *)g_sMACAddr.addr) ||
        (*(uint16_t *)(psBOOTP->pui8CHAddr + 4) !=
         *(uint16_t *)(g_sMACAddr.addr + 4)))
    {
        return (0);
    }

    //
    // Extract our IP address from the response.
    //
    *((uint32_t *)(void *)(&uip_hostaddr)) = psBOOTP->ui32YIAddr;

    //
    // Extract the server address from the response.
    //
    *((uint32_t *)(void *)(&g_sServerAddr)) = psBOOTP->ui32SIAddr;

    //
    // Save the boot file name.
    //
    for (ui32Idx = 0;
         ((g_pcFilename[ui32Idx] = psBOOTP->pcFile[ui32Idx]) != 0) &&
         (ui32Idx < (sizeof(g_pcFilename) - 1));
         ui32Idx++)
    {
    }
    g_pcFilename[ui32Idx] = 0;

    //
    // A valid BOOTP reply was found and decoded.
    //
    return (1);
}

//*****************************************************************************
//
//! Constructs and sends a TFTP error packet.
//!
//! This function constructs a TFTP read request packet (RRQ) and sends it to
//! the server.
//!
//! \return None.
//
//*****************************************************************************
static void
SendTFTPError(uint16_t ui16Error, char *pcString)
{
    uint8_t *pui8Packet = (uint8_t *)uip_appdata;
    int32_t i32Len;

    pui8Packet[0] = (TFTP_ERROR >> 8) & 0xff;
    pui8Packet[1] = TFTP_ERROR & 0xff;
    pui8Packet[2] = (ui16Error >> 8) & 0xFF;
    pui8Packet[3] = ui16Error & 0xFF;

    //
    // Get ready to copy the error string.
    //
    i32Len = 4;
    pui8Packet += 4;

    //
    // Copy as much of the string as we can fit.
    //
    while ((i32Len < (UIP_APPDATA_SIZE - 1)) && *pcString)
    {
        *pui8Packet++ = *pcString++;
        i32Len++;
    }

    //
    // Write the terminating 0.
    //
    *pui8Packet = (uint8_t)0;

    //
    // Send the error packet.
    //
    uip_udp_send(i32Len + 1);
}

//*****************************************************************************
//
//! Constructs and sends a TFTP read packet.
//!
//! This function constructs a TFTP read request packet (RRQ) and sends it to
//! the server.
//!
//! \return None.
//
//*****************************************************************************
static void
SendTFTPGet(void)
{
    uint8_t *pui8Packet = (uint8_t *)uip_appdata;
    uint32_t ui32Idx;
    char *pcFilename;

    //
    // The TFTP RRQ packet should be sent to the TFTP server port.
    //
    g_pConn->rport = HTONS(TFTP_PORT);

    //
    // Set the TFTP packet opcode to RRQ.
    //
    pui8Packet[0] = (TFTP_RRQ >> 8) & 0xff;
    pui8Packet[1] = TFTP_RRQ & 0xff;

    //
    // Copy the file name into the RRQ packet.
    //
    for (ui32Idx = 2, pcFilename = g_pcFilename;
         (pui8Packet[ui32Idx++] = *pcFilename++) != 0;)
    {
    }

    //
    // Set the transfer mode to binary.
    //
    for (pcFilename = "octet"; (pui8Packet[ui32Idx++] = *pcFilename++) != 0;)
    {
    }

    //
    // Send the TFTP read packet.
    //
    uip_udp_send(ui32Idx);
}

//*****************************************************************************
//
//! Parses a packet checking for a TFTP data packet.
//!
//! This function parses a packet to determine if it is a TFTP data packet for
//! a current TFTP transfer.  If a valid packet is found, the contents of the
//! packet are programmed into flash.
//!
//! \return Returns 1 if this packet was the last packet of the TFTP data
//! transfer and 0 otherwise.
//
//*****************************************************************************
static uint32_t
ParseTFTPData(void)
{
    uint8_t *pui8Packet = (uint8_t *)uip_appdata;
    uint32_t ui32FlashAddr;
    uint32_t ui32Idx;

    //
    // See if this is a TFTP data packet.
    //
    if ((pui8Packet[0] != ((TFTP_DATA >> 8) && 0xff)) ||
        (pui8Packet[1] != (TFTP_DATA & 0xff)))
    {
        return (0);
    }

    //
    // If the remote port on our connection is still the TFTP server port (i.e.
    // this is the first data packet), then copy the transaction ID for the
    // TFTP data connection into our connection.  This will ensure that our
    // response will be sent to the correct port.
    //
    if (g_pConn->rport == HTONS(TFTP_PORT))
    {
        g_pConn->rport =
            ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN])->srcport;
    }

    //
    // See if this is the correct data packet.
    //
    if ((pui8Packet[2] != ((g_ui32TFTPBlock >> 8) & 0xff)) ||
        (pui8Packet[3] != (g_ui32TFTPBlock & 0xff)))
    {
        //
        // Since the wrong data packet was sent, resend the ACK for it since
        // we've already processed it.
        //
        pui8Packet[0] = (TFTP_ACK >> 8) & 0xff;
        pui8Packet[1] = TFTP_ACK & 0xff;
        uip_udp_send(4);

        //
        // Ignore this packet.
        //
        return (0);
    }

    //
    // What address are we about to program to?
    //
    ui32FlashAddr =
        ((g_ui32TFTPBlock - 1) * TFTP_BLOCK_SIZE) + APP_START_ADDRESS;

    //
    // Do not program this data into flash if it is beyond the end of flash.
    //
    if (ui32FlashAddr < g_ui32FlashEnd)
    {
        //
        // If this is the first block and we have been provided with a start
        // hook function, call it here to indicate that we are about to begin
        // flashing a new image.
        //
#ifdef BL_START_FN_HOOK
        if (g_ui32TFTPBlock == 1)
        {
            BL_START_FN_HOOK();
        }
#endif

        //
        // Clear any flash error indicator.
        //
        BL_FLASH_CL_ERR_FN_HOOK();

        //
        // If this is the first data packet and code protection is enabled,
        // then erase the entire flash.
        //
#ifdef FLASH_CODE_PROTECTION
        if (g_ui32TFTPBlock == 1)
        {
            //
            // Loop through the pages in the flash, excluding the pages that
            // contain the boot loader and the optional reserved space.
            //
            for (ui32Idx = APP_START_ADDRESS; ui32Idx < g_ui32FlashEnd;
                 ui32Idx += FLASH_PAGE_SIZE)
            {
                //
                // Erase this block of the flash.
                //
                BL_FLASH_ERASE_FN_HOOK((ui32Idx);
            }
        }
#else
        //
        // Flash code protection is not enabled, so see if the data in this
        // packet will be programmed to the beginning of a flash block.  We
        // assume that the flash block size is always a multiple of 1KB so,
        // since each TFTP packet is 512 bytes and that the start must always
        // be on a flash page boundary, we can be sure that we will hit the
        // start of each page as we receive packets.
        //
        if (!(ui32FlashAddr & (FLASH_PAGE_SIZE - 1)))
        {
            //
            // Erase this block of the flash.
            //
            BL_FLASH_ERASE_FN_HOOK(ui32FlashAddr);
        }
#endif

        //
        // Decrypt the data if required.
        //
#ifdef BL_DECRYPT_FN_HOOK
        BL_DECRYPT_FN_HOOK(pui8Packet + 4, uip_len - 4);
#endif

        //
        // Program this block of data into flash.
        //
        BL_FLASH_PROGRAM_FN_HOOK(ui32FlashAddr, (pui8Packet + 4),
                                 (uip_len - 4));

        //
        // If a progress reporting hook function has been provided, call it
        // here.  The TFTP protocol doesn't let us know how large the image is
        // before it starts the transfer so we pass 0 as the ui32Total
        // parameter to indicate this.
        //
#ifdef BL_PROGRESS_FN_HOOK
        BL_PROGRESS_FN_HOOK(((ui32FlashAddr - APP_START_ADDRESS) +
                             (uip_len - 4)),
                            0);
#endif
    }

    //
    // Increment to the next block.
    //
    g_ui32TFTPBlock++;

    //
    // Save the packet length.
    //
    ui32Idx = uip_len;

    //
    // Did we see any error?
    //
    if (BL_FLASH_ERROR_FN_HOOK())
    {
        //
        // Yes - send back an error packet.
        //
        SendTFTPError(2, "Error programming flash.");
    }
    else
    {
        //
        // No errors reported so construct an ACK packet.  The block number
        // field is already correct, so it does not need to be set.
        //
        pui8Packet[0] = (TFTP_ACK >> 8) & 0xff;
        pui8Packet[1] = TFTP_ACK & 0xff;

        //
        // Send the ACK packet to the TFTP server.
        //
        uip_udp_send(4);
    }

    //
    // If the packet was shorter than TFTP_BLOCK_SIZE bytes then this was the
    // last packet in the file.
    //
    if (ui32Idx != (TFTP_BLOCK_SIZE + 4))
    {
        //
        // If an end signal hook function has been provided, call it here.
        //
#ifdef BL_END_FN_HOOK
        BL_END_FN_HOOK();
#endif
        return (1);
    }
    //
    // There is more data to be read.
    //
    return (0);
}

uint16_t
LOCAL_EMACPHYRead(uint32_t ui32Base, uint8_t ui8PhyAddr, uint8_t ui8RegAddr)
{

    //
    // Make sure the MII is idle.
    //
    while (HWREG(ui32Base + EMAC_O_MIIADDR) & EMAC_MIIADDR_MIIB)
    {
    }

    //
    // Tell the MAC to read the given PHY register.
    //
    HWREG(ui32Base + EMAC_O_MIIADDR) =
        ((HWREG(ui32Base + EMAC_O_MIIADDR) & EMAC_MIIADDR_CR_M) |
         (ui8RegAddr << EMAC_MIIADDR_MII_S) |
         (ui8PhyAddr << EMAC_MIIADDR_PLA_S) | EMAC_MIIADDR_MIIB);

    //
    // Wait for the read to complete.
    //
    while (HWREG(ui32Base + EMAC_O_MIIADDR) & EMAC_MIIADDR_MIIB)
    {
    }

    //
    // Return the result.
    //
    return (HWREG(ui32Base + EMAC_O_MIIDATA) & EMAC_MIIDATA_DATA_M);
}

//*****************************************************************************
//
//! Handles the BOOTP process.
//!
//! This function contains the proto-thread for handling the BOOTP process.  It
//! first communicates with the BOOTP server to get its boot parameters (IP
//! address, server address, and file name), then it communicates with the TFTP
//! server on the specified server to read the firmware image file.
//!
//! \return None.
//
//*****************************************************************************
#ifdef DOXYGEN
char BOOTPThread(void)
#else
PT_THREAD(BOOTPThread(void))
#endif
{
    //
    // Begin the proto-thread.
    //
    PT_BEGIN(&g_sThread);
wait_for_link:
    PT_WAIT_UNTIL(&g_sThread,
                  (LOCAL_EMACPHYRead(EMAC0_BASE, 0, EPHY_BMSR) &
                   EPHY_BMSR_LINKSTAT) != 0);

    //
    // Reset the host address.
    //
    *((uint32_t *)(void *)(&uip_hostaddr)) = 0;

    //
    // Re-bind the UDP socket for sending requests to the BOOTP server.
    //
    uip_udp_remove(g_pConn);
    *((uint32_t *)(void *)(&g_sServerAddr)) = 0xffffffff;
    uip_udp_new(&g_sServerAddr, HTONS(BOOTP_SERVER_PORT));
    uip_udp_bind(g_pConn, HTONS(BOOTP_CLIENT_PORT));
    //
    // Set the initial delay between BOOTP requests to 1 second.
    //
    g_ui32Delay = SYSTICKHZ;

    //
    // Loop forever.  This loop is explicitly exited when a valid BOOTP reply
    // is received.
    //
    while (1)
    {
        //
        // Send a BOOTP request.
        //
        SendBOOTPRequest();

        //
        // Set the amount of time to wait for the BOOTP reply message.
        //
        g_ui32Target = g_ui32Ticks + g_ui32Delay;

        //
        // Wait until a packet is received or the timeout has occurred.
        //
    wait_for_bootp_reply:
        PT_WAIT_UNTIL(&g_sThread,
                      ((g_ui32Link = (LOCAL_EMACPHYRead(EMAC0_BASE, 0, EPHY_BMSR) &
                                      EPHY_BMSR_LINKSTAT)) == 0) ||
                          uip_newdata() || (g_ui32Ticks > g_ui32Target));

        //
        // If the link has been lost, go back to waiting for a link.
        //
        if (g_ui32Link == 0)
        {
            goto wait_for_link;
        }
        //
        // See if a packet has been received.
        //
        if (uip_newdata())
        {
            //
            // Clear the new data flag so that this packet will only be
            // examined one time.
            //
            uip_flags &= ~(UIP_NEWDATA);
            //
            // See if this is a BOOTP reply.
            //
            if (ParseBOOTPReply() == 1)
            {
                break;
            }

            //
            // This was not a BOOTP reply packet, so go back to waiting.
            //
            goto wait_for_bootp_reply;
        }
        //
        // If the delay between BOOTP requests is less than 60 seconds, double
        // the delay time.  This avoids constantly slamming the network with
        // requests.
        //
        if (g_ui32Delay < (60 * SYSTICKHZ))
        {
            g_ui32Delay *= 2;
        }
    }

    //
    // Reconfigure the UDP socket to target the TFTP port on the server.
    //
    uip_ipaddr_copy(&g_pConn->ripaddr, g_sServerAddr);
    uip_udp_bind(g_pConn, HTONS(13633));

    //
    // Send a TFTP read request.
    //
    SendTFTPGet();

    //
    // Since the first TFTP read request will result in an ARP request, delay
    // for just a bit and then re-issue the TFTP read request.
    //
    PT_YIELD(&g_sThread);

    //
    // Resend the TFTP read request.  If the ARP request has already been
    // answered, this will go out as is and avoid the two second timeout below.
    //
    SendTFTPGet();

    //
    // Start the TFTP transfer from block one.
    //
    g_ui32TFTPBlock = 1;

    //
    // Set the number of TFTP retries to zero.
    //
    g_ui32TFTPRetries = 0;
    //
    // Loop forever.  This loop is explicitly exited when the TFTP transfer has
    // completed.
    //
    while (1)
    {
        //
        // Set the amount of time to wait for the TFTP data packet.
        //
        g_ui32Target = g_ui32Ticks + (SYSTICKHZ * 10);

        //
        // Wait until a packet is received or the timeout has occurred.
        //
        PT_WAIT_UNTIL(&g_sThread,
                      ((g_ui32Link = (LOCAL_EMACPHYRead(EMAC0_BASE, 0, EPHY_BMSR) &
                                      EPHY_BMSR_LINKSTAT)) == 0) ||
                          uip_newdata() || (g_ui32Ticks > g_ui32Target));

        //
        // If the link has been lost, go back to waiting for a link.
        //
        if (g_ui32Link == 0)
        {
            goto wait_for_link;
        }
        //
        // See if a packet has been received.
        //
        if (uip_newdata())
        {
            EnableLed();
            //
            // Clear the new data flag so that this packet will only be
            // examined one time.
            //
            uip_flags &= ~(UIP_NEWDATA);
            //
            // See if this is a TFTP data packet.
            //
            if (ParseTFTPData() == 1)
            {
                break;
            }
        }
        else if (g_ui32TFTPRetries < 3)
        {
            //
            // The transfer timed out, so send a new TFTP read request.
            //
            SendTFTPGet();

            //
            // Start the TFTP transfer from block one.
            //
            g_ui32TFTPBlock = 1;

            //
            // Increment the count of TFTP retries.
            //
            g_ui32TFTPRetries++;
        }
        else
        {
            //
            // The TFTP transfer failed after three retries, so start over.
            //
            goto wait_for_link;
        }
    }
    //
    // Wait for the last packet to be transmitted.
    //
    while (g_psTxDescriptor[g_ui32TxDescIndex].ui32CtrlStatus &
           DES0_TX_CTRL_OWN)
    {
    }

    //
    // Wait for a bit to make sure that the final ACK packet is transmitted.
    //
    g_ui32Target = g_ui32Ticks + (SYSTICKHZ / 4);
    while (g_ui32Ticks < g_ui32Target)
    {
        PT_YIELD(&g_sThread);
    }

    //
    // Perform a software reset request.  This will cause the microcontroller
    // to reset; no further code will be executed.
    //
    HWREG(NVIC_APINT) = NVIC_APINT_VECTKEY | NVIC_APINT_SYSRESETREQ;

    //
    // The microcontroller should have reset, so this should never be reached.
    // Just in case, loop forever.
    //
    while (1)
    {
    }

    //
    // End the proto-thread.
    //
    PT_END(&g_sThread);
}

static void
LOCAL_EMACPHYConfigSet(uint32_t ui32Base, uint32_t ui32Config)
{
    //
    // Write the Ethernet PHY configuration to the peripheral configuration
    // register.
    //
    HWREG(ui32Base + EMAC_O_PC) = ui32Config;

    //
    // If using the internal PHY, reset it to ensure that new configuration is
    // latched there.
    //
    if ((ui32Config & EMAC_PHY_TYPE_MASK) == EMAC_PHY_TYPE_INTERNAL)
    {
        ROM_SysCtlPeripheralReset(SYSCTL_PERIPH_EPHY0);
        while (!ROM_SysCtlPeripheralReady(SYSCTL_PERIPH_EPHY0))
        {
            //
            // Wait for the PHY reset to complete.
            //
        }

        //
        // Delay a bit longer to ensure that the PHY reset has completed.
        //
        ROM_SysCtlDelay(1000);
    }

    //
    // If using an external RMII PHY, we must set 2 bits in the Ethernet MAC
    // Clock Configuration Register.
    //
    if ((ui32Config & EMAC_PHY_TYPE_MASK) == EMAC_PHY_TYPE_EXTERNAL_RMII)
    {
        //
        // Select and enable the external clock from the RMII PHY.
        //
        HWREG(EMAC0_BASE + EMAC_O_CC) |= EMAC_CC_CLKEN;
    }
    else
    {
        //
        // Disable the external clock.
        //
        HWREG(EMAC0_BASE + EMAC_O_CC) &= ~EMAC_CC_CLKEN;
    }

    //
    // Reset the MAC regardless of whether the PHY connection changed or not.
    //
    ROM_EMACReset(EMAC0_BASE);

    ROM_SysCtlDelay(1000);
}

//*****************************************************************************
//
//! Reconfigures the Ethernet controller.
//!
//! \param ui32Clock is the system clock frequency.
//!
//! This function reconfigures the Ethernet controller, preparing it for use by
//! the boot loader.  This performs the steps common between the direct
//! invocation of the boot loader and the application invocation of the boot
//! loader.
//!
//! \return None.
//
//*****************************************************************************
void EnetReconfig()
{
    uip_ipaddr_t sAddr;
    uint32_t ui32Loop;
    uint32_t ui32User0, ui32User1;
    uint32_t ui32Clock = 120000000;
    //
    // Configure for use with the internal PHY.
    //
    LOCAL_EMACPHYConfigSet(EMAC0_BASE,
                           (EMAC_PHY_TYPE_INTERNAL | EMAC_PHY_INT_MDIX_EN |
                            EMAC_PHY_AN_100B_T_FULL_DUPLEX));

    //
    // Reset the MAC.
    //
    ROM_EMACReset(EMAC0_BASE);
    //
    // Initialize the MAC and set the DMA mode.
    //
    ROM_EMACInit(EMAC0_BASE, ui32Clock,
                 EMAC_BCONFIG_MIXED_BURST | EMAC_BCONFIG_PRIORITY_FIXED, 4, 4, 0);

    //
    // Get the MAC address from the flash user registers.  If it has not been
    // programmed, then use the boot loader default MAC address.
    //
    ROM_FlashUserGet(&ui32User0, &ui32User1);
    if ((ui32User0 == 0xffffffff) || (ui32User1 == 0xffffffff))
    {
        //
        // MAC address has not been programmed, use default.
        //
        g_sMACAddr.addr[0] = 0x00;
        g_sMACAddr.addr[1] = 0x1a;
        g_sMACAddr.addr[2] = 0xb6;
        g_sMACAddr.addr[3] = 0x00;
        g_sMACAddr.addr[4] = 0x64;
        g_sMACAddr.addr[5] = 0x00;
    }
    else
    {
        g_sMACAddr.addr[0] = ui32User0 & 0xff;
        g_sMACAddr.addr[1] = (ui32User0 >> 8) & 0xff;
        g_sMACAddr.addr[2] = (ui32User0 >> 16) & 0xff;
        g_sMACAddr.addr[3] = ui32User1 & 0xff;
        g_sMACAddr.addr[4] = (ui32User1 >> 8) & 0xff;
        g_sMACAddr.addr[5] = (ui32User1 >> 16) & 0xff;
    }

    //
    // Set MAC configuration options.
    //
    ROM_EMACConfigSet(EMAC0_BASE,
                      (EMAC_CONFIG_FULL_DUPLEX | EMAC_CONFIG_CHECKSUM_OFFLOAD |
                       EMAC_CONFIG_7BYTE_PREAMBLE | EMAC_CONFIG_IF_GAP_96BITS |
                       EMAC_CONFIG_USE_MACADDR0 | EMAC_CONFIG_SA_FROM_DESCRIPTOR |
                       EMAC_CONFIG_BO_LIMIT_1024),
                      (EMAC_MODE_RX_STORE_FORWARD | EMAC_MODE_TX_STORE_FORWARD |
                       EMAC_MODE_TX_THRESHOLD_64_BYTES |
                       EMAC_MODE_RX_THRESHOLD_64_BYTES),
                      0);

    //
    // Initialize each of the transmit descriptors.  Note that we leave the OWN
    // bit clear here since we have not set up any transmissions yet.
    //
    for (ui32Loop = 0; ui32Loop < NUM_TX_DESCRIPTORS; ui32Loop++)
    {
        g_psTxDescriptor[ui32Loop].ui32Count =
            (DES1_TX_CTRL_SADDR_INSERT |
             (TX_BUFFER_SIZE << DES1_TX_CTRL_BUFF1_SIZE_S));
        g_psTxDescriptor[ui32Loop].pvBuffer1 = g_pui8TxBuffer;
        g_psTxDescriptor[ui32Loop].DES3.pLink =
            (ui32Loop == (NUM_TX_DESCRIPTORS - 1)) ? g_psTxDescriptor : &g_psTxDescriptor[ui32Loop + 1];
        g_psTxDescriptor[ui32Loop].ui32CtrlStatus =
            (DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_FIRST_SEG |
             DES0_TX_CTRL_INTERRUPT | DES0_TX_CTRL_CHAINED |
             DES0_TX_CTRL_IP_ALL_CKHSUMS);
    }

    //
    // Initialize each of the receive descriptors.  We clear the OWN bit here
    // to make sure that the receiver doesn't start writing anything
    // immediately.
    //
    for (ui32Loop = 0; ui32Loop < NUM_RX_DESCRIPTORS; ui32Loop++)
    {
        g_psRxDescriptor[ui32Loop].ui32CtrlStatus = 0;
        g_psRxDescriptor[ui32Loop].ui32Count =
            (DES1_RX_CTRL_CHAINED |
             (RX_BUFFER_SIZE << DES1_RX_CTRL_BUFF1_SIZE_S));
        g_psRxDescriptor[ui32Loop].pvBuffer1 = g_pui8RxBuffer;
        g_psRxDescriptor[ui32Loop].DES3.pLink =
            (ui32Loop == (NUM_RX_DESCRIPTORS - 1)) ? g_psRxDescriptor : &g_psRxDescriptor[ui32Loop + 1];
    }

    //
    // Set the descriptor pointers in the hardware.
    //
    ROM_EMACRxDMADescriptorListSet(EMAC0_BASE, g_psRxDescriptor);
    ROM_EMACTxDMADescriptorListSet(EMAC0_BASE, g_psTxDescriptor);

    //
    // Start from the beginning of both descriptor chains.  We actually set
    // the transmit descriptor index to the last descriptor in the chain
    // since it will be incremented before use and this means the first
    // transmission we perform will use the correct descriptor.
    //
    g_ui32RxDescIndex = 0;
    g_ui32TxDescIndex = NUM_TX_DESCRIPTORS - 1;

    //
    // Program the MAC address.
    //
    ROM_EMACAddrSet(EMAC0_BASE, 0, g_sMACAddr.addr);

    //
    // Wait for the link to become active.
    //
    while ((ROM_EMACPHYRead(EMAC0_BASE, 0, EPHY_BMSR) &
            EPHY_BMSR_LINKSTAT) == 0)
    {
    }

    //
    // Set MAC filtering options.  We receive all broadcast and multicast
    // packets along with those addressed specifically for us.
    //
    ROM_EMACFrameFilterSet(EMAC0_BASE, (EMAC_FRMFILTER_SADDR |
                                        EMAC_FRMFILTER_PASS_MULTICAST |
                                        EMAC_FRMFILTER_PASS_NO_CTRL));

    //
    // Seed the random number generator from the MAC address.
    //
    g_ui32RandomSeed = *(uint32_t *)(g_sMACAddr.addr + 2);

    //
    // Initialize the uIP stack.
    //
    uip_init();
    uip_arp_init();

    //
    // Set the MAC address.
    //
    uip_setethaddr(g_sMACAddr);

    //
    // Initialize the proto-thread used by the BOOTP protocol handler.
    //
    PT_INIT(&g_sThread);

    //
    // Create a UDP socket for sending requests to the BOOTP server.  After the
    // BOOTP portion of the protocol has been handled, this socket will be
    // reused to communicate with the TFTP server.
    //
    *((uint32_t *)(void *)(&sAddr)) = 0xffffffff;
    g_pConn = uip_udp_new(&sAddr, HTONS(BOOTP_SERVER_PORT));
    uip_udp_bind(g_pConn, HTONS(BOOTP_CLIENT_PORT));

    //
    // Enable the Ethernet MAC transmitter and receiver.
    //
    ROM_EMACTxEnable(EMAC0_BASE);
    ROM_EMACRxEnable(EMAC0_BASE);

    //
    // Enable the Ethernet interrupt.
    //
    MAP_IntEnable(INT_EMAC0);

    //
    // Enable the Ethernet RX Packet interrupt source.
    //
    MAP_EMACIntEnable(EMAC0_BASE, EMAC_INT_RECEIVE);

    //
    // Mark the first receive descriptor as available to the DMA to start
    // the receive processing.
    //
    g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus |= DES0_RX_CTRL_OWN;

    //
    // Reset the counters that are incremented by SysTick.
    //
    g_ui32Ticks = 0;
    g_ui32PeriodicTimer = 0;
    g_ui32ARPTimer = 0;

    //
    // Setup SysTick.
    //
    HWREG(NVIC_ST_RELOAD) = (ui32Clock / SYSTICKHZ) - 1;
    HWREG(NVIC_ST_CTRL) = (NVIC_ST_CTRL_CLK_SRC | NVIC_ST_CTRL_INTEN |
                           NVIC_ST_CTRL_ENABLE);
}
//*****************************************************************************
//
//! Configures the Ethernet controller.
//!
//! This function configures the Ethernet controller, preparing it for use by
//! the boot loader.
//!
//! \return None.
//
//*****************************************************************************
void ConfigureEnet(void)
{
    //
    // Make sure the main oscillator is enabled because this is required by
    // the PHY.  The system must have a 25MHz crystal attached to the OSC
    // pins.  The SYSCTL_MOSC_HIGHFREQ parameter is used when the crystal
    // frequency is 10MHz or higher.
    //
    HWREG(SYSCTL_MOSCCTL) = SYSCTL_MOSC_HIGHFREQ;

    //
    // Delay while the main oscillator starts up.
    //
    Delay(5242880);

    ROM_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                            SYSCTL_OSC_MAIN |
                            SYSCTL_USE_PLL |
                            SYSCTL_CFG_VCO_480),
                           120000000);

#ifdef ENET_ENABLE_LEDS
    //
    // PF1/PK4/PK6 are used for Ethernet LEDs.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);
    ROM_GPIOPinConfigure(GPIO_PF1_EN0LED2);
    ROM_GPIOPinConfigure(GPIO_PK4_EN0LED0);
    ROM_GPIOPinConfigure(GPIO_PK6_EN0LED1);

    //
    // Make the pin(s) be peripheral controlled.
    //
    ROM_GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_DIR_MODE_HW);
    ROM_GPIODirModeSet(GPIO_PORTK_BASE, GPIO_PIN_4 | GPIO_PIN_6, GPIO_DIR_MODE_HW);

    //
    // Set the pad(s) for standard push-pull operation.
    //
    ROM_GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
    ROM_GPIOPadConfigSet(GPIO_PORTK_BASE, GPIO_PIN_4 | GPIO_PIN_6, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
#endif

    //
    // Enable and reset the Ethernet modules.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_EMAC0);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_EPHY0);
    ROM_SysCtlPeripheralReset(SYSCTL_PERIPH_EMAC0);
    ROM_SysCtlPeripheralReset(SYSCTL_PERIPH_EPHY0);

    while (!ROM_SysCtlPeripheralReady(SYSCTL_PERIPH_EMAC0))
    {
    }
}

//*****************************************************************************
//
//! Starts the update process via BOOTP.
//!
//! This function starts the Ethernet firmware update process.  The BOOTP
//! (as defined by RFC951 at http://tools.ietf.org/html/rfc951) and TFTP (as
//! defined by RFC1350 at http://tools.ietf.org/html/rfc1350) protocols are
//! used to transfer the firmware image over Ethernet.
//!
//! \return Never returns.
//
//*****************************************************************************
void UpdateBOOTP(void)
{
    //
    // Get the size of flash.
    //
    g_ui32FlashEnd = ROM_SysCtlFlashSizeGet();
#ifdef FLASH_RSVD_SPACE
    g_ui32FlashEnd -= FLASH_RSVD_SPACE;
#endif

    //
    // Perform the common Ethernet configuration. The frequency should
    // match whatever the application sets the system clock.
    //
    EnetReconfig();
    //
    // Main Application Loop.
    //
    while (1)
    {
        uint32_t ui32Temp;

        //
        // See if there is a packet waiting to be read.
        //
        if (!(g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus & DES0_RX_CTRL_OWN))
        {
            //
            // Read the packet from the Ethernet controller.
            //
            uip_len = PacketReceive(uip_buf, UIP_CONF_BUFFER_SIZE);

            //
            // See if this is an IP packet.
            //
            if ((uip_len != 0) && (((struct uip_eth_hdr *)&uip_buf[0])->type == HTONS(UIP_ETHTYPE_IP)))
            {

                //
                // Update the ARP tables based on this packet.
                //
                uip_arp_ipin();

                //
                // Process this packet.
                //
                uip_input();

                //
                // See if the processing of this packet resulted in a packet to be
                // sent.
                //
                if (uip_len > 0)
                {
                    //
                    // Update the ARP tables based on the packet to be sent.
                    //
                    uip_arp_out();

                    //
                    // Send the packet.
                    //
                    PacketTransmit(uip_buf, uip_len);
                    //
                    // Indicate that the packet has been sent.
                    //
                    uip_len = 0;
                }
            }

            //
            // See if this is an ARP packet.
            //
            else if ((uip_len != 0) &&
                     (((struct uip_eth_hdr *)&uip_buf[0])->type ==
                      HTONS(UIP_ETHTYPE_ARP)))
            {
                //
                // Process this packet.
                //
                uip_arp_arpin();

                //
                // See if the processing of this packet resulted in a packet to be
                // sent.
                //
                if (uip_len > 0)
                {
                    //
                    // Send the packet.
                    //
                    PacketTransmit(uip_buf, uip_len);

                    //
                    // Indicate that the packet has been sent.
                    //
                    uip_len = 0;
                }
            }
        }

        //
        // See if the periodic timer has expired.
        //
        if (g_ui32PeriodicTimer > UIP_PERIODIC_TIMER_MS)
        {
            //
            // Reset the periodic timer.
            //
            g_ui32PeriodicTimer = 0;
            //
            // Loop through the UDP connections.
            //
            for (ui32Temp = 0; ui32Temp < UIP_UDP_CONNS; ui32Temp++)
            {
                //
                // Perform the periodic processing on this UDP connection.
                //
                uip_udp_periodic(ui32Temp);

                //
                // See if the periodic processing of this connection resulted in a
                // packet to be sent.
                //
                if (uip_len > 0)
                {
                    //
                    // Update the ARP tables based on the packet to be sent.
                    //
                    uip_arp_out();

                    //
                    // Send the packet.
                    //
                    PacketTransmit(uip_buf, uip_len);

                    //
                    // Indicate that the packet has been sent.
                    //
                    uip_len = 0;
                }
            }
        }

        //
        // See if the ARP timer has expired.
        //
        if (g_ui32ARPTimer > UIP_ARP_TIMER_MS)
        {
            //
            // Reset the ARP timer.
            //
            g_ui32ARPTimer = 0;

            //
            // Perform periodic processing on the ARP table.
            //
            uip_arp_timer();
        }
    }
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************
#endif

  • Hi Mark,

      There is a readily available Ethernet bootloader example in TivaWare library. Please refer to C:\ti\TivaWare_C_Series-2.2.0.295\examples\boards\ek-tm4c1294xl\boot_emac_flash (this is a bootloader) and C:\ti\TivaWare_C_Series-2.2.0.295\examples\boards\ek-tm4c1294xl\boot_demo_emac_flash (this is an Ethernet application that is to be loaded by the bootloader). I wonder if you had a chance to run these examples. 

      Based on your Wireshark capture, all I can tell is that the client (the MCU) is not acknowledging the server after it receives the data. Below is what you would have seen if you try the TivaWare example as is. Can you please try the TivaWare bootloader as is without modification? This way you can compare your own bootloader with the TivaWare example. 

  • Hello,

    I have already tried to launch example projects, but it still requires quite a lot of modifications such as correct memory allocation, buffer size, system clock frequency and more. Therefore, I cannot use default code on my board. Also, I haven't changed functions themselves (only variables), so I tried to leave the code closer to original. Moreover, I am not sure that my code(and default one) has ACK response to the server, but here comes the problem with the flag as well. Even if descriptor contains full content of the 1st data block, it is not able to update the flag, so uip won't process it.

  • Hi,

    Moreover, I am not sure that my code(and default one) has ACK response to the server, but here comes the problem with the flag as well.

    I have some questions.

    Are you saying if you run the TivaWare bootloader example on the LaunchPad, it also doesn't work, meaning you are not seeing the ACK response packet sent by the client? I have just tried the TivaWare bootloader example on the LaunchPad and it is working as expected for me. 

    Are you using the LM Flash Programmer ( which is PC based tool) to download the code?  Or you have your own download tool?

    Does your server machine have multiple NIC? If yes, can you disable some of them so that only one NIC is active. 

    I suppose you are running your code on your custom board? Is this correct? Can you try your bootloader on the LaunchPad? Does it have the same problem?

    Can you try the TivaWare example on your custom board? What does it show?

    If you can get the as-is TivaWare bootloader example to run on the LaunchPad, then I hope you can compare the difference between your own bootloader on your custom board and the TivaWare example on the LaunchPad.

  • I cannot use LM Flash Programmer in order to write bootloader on the board since I am using Linux Mint. Therefore, I have used bl_emac.c from 'boot_loader' folder that is written on the board via openocd.
    I have a single NIC.
    I am using a custom board and don't have official LaunchPad.
    The output from my custom board was presented above.

  • Hi,

      As much as I'd like to help, your question is a bit open-ended. I understand there is an issue with your custom bootloader. But I cannot just identify your problem when I don't have your custom board and neither your software. This is why I suggested the first level of attack by running your code on a LaunchPad and run the TivaWare on your custom board as a process of elimination. We know that the TivaWare example and the LaunchPad are proven. Is it possible for you to get a LaunchPad? If you run your custom bootloader on the LaunchPad and if it works, then we at least know the software is working. Perhaps there is something wrong with the board or your network environment or maybe even machine related (linux box vs PC).  If the TivaWare example doesn't work on your custom board, then it further suggests there is something wrong with the board or your network or something else. It would really help eliminate many unknowns if you can get a LaunchPad and run the example as is. 

  • Hello.
    I have finally been able to test 'C:\ti\TivaWare_C_Series-2.2.0.295\examples\boards\ek-tm4c1294xl\boot_emac_flash' example. It works perfectly on my board. It seems that I have a problem somewhere in the code. Unfortunately, bootloader example does not provide source files (only configuration and executable files), where I might compare functions with mine version of ethernet bootloader. Is it possible to see the real code working behind the final .bin file?

    Update. I checked Makefile and saw that booloader is using the whole 'C:\ti\TivaWare_C_Series-2.2.0.295\boot_loader' directory.

  • Hi Mark,

      The entire source files for the bootloader are in C:\ti\TivaWare_C_Series-2.2.0.295\boot_loader.

      If you import the boot_emac_flash example into CCS, you should see the below directory structure. The bootloader will start from the bl_startup_css.s file. 

  • Yeah, I see.
    But now I have faced another problem that if I use already built boot_emac_flash.bin file(default one), which is 5.5 kB then I get correct work. However, after compiling the same project without any changes in it via Makefile it becomes 5.3 kB and does not run on my board at all.
    P.S. I am using GCC compiler, not CCS.

  • For experiment, what if you use TI Compiler to rebuild it again? Is it possible that your own bootloader may have also worked if it was built with the CCS TI compiler. It will be good to know. 

  • I am working via terminal commands. I have done the following things:
    1)Erased all data from microcontroller using openOCD
    2)Uploaded '.../examples/boards/ek-tm4c1294xl/boot_emac_flash/gcc/boot_emac_flash.bin' file via openOCD
    3)Check if program was uploaded via boot loader(simple program with blinking LED). In my case, it works fine.
    4)Rebuilding ''.../examples/boards/ek-tm4c1294xl/boot_emac_flash/' project using Makefile in this directory
    5)Repeating step 1-2
    6)Checking Wireshark for Ethernet communication. It doesn't do anything. Hence, rebuilt boot loader does not work anymore.
    I have rechecked flags in Makefile and do not see anything that should be fixed.

  • I have found the problem in rebuilding. The issue was in a vector table name that were differ in assembly startup file and in linker file. After fixing it, I got a perfectly working ethernet bootloader.