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