Part Number: TM4C129ENCPDT
Hi, I am trying to test the maximum data rate that can be achieved with ethernet link on tm4c129encpdt microcontroller.
I have created a minimal project that basically just send ethernet frame as fast as possible to my Windows PC and the
maximum date rate that I observe in the task manager is 29 Mbps. I don't have a lot of experience with this type of
communication, so I am assuming I did something wrong, can someone kindly take a look at the code below? Also, I
have tried to enable jumbo frame, but without success (the packets are never received by my windows machine when
I try to send packet greater than 1514 bytes).
Thank you
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_emac.h"
#include "inc/hw_ints.h"
#include "driverlib/rom_map.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/flash.h"
#include "driverlib/emac.h"
#define NUM_TX_DESCRIPTORS 24
#define NUM_RX_DESCRIPTORS 3
tEMACDMADescriptor g_psRxDescriptor[NUM_RX_DESCRIPTORS];
tEMACDMADescriptor g_psTxDescriptor[NUM_TX_DESCRIPTORS];
uint32_t g_ui32RxDescIndex;
uint32_t g_ui32TxDescIndex;
#define RX_BUFFER_SIZE 1536
uint8_t g_ppui8RxBuffer[NUM_RX_DESCRIPTORS][RX_BUFFER_SIZE];
#define ETH_DMAC_OFFSET 0
#define ETH_SMAC_OFFSET 6
#define ETH_LEN_OFFSET 12
#define ETH_DATA_OFFSET 14
#define ETH_DATA_LEN 1500
#define ETH_BUFFER_SIZE \
6 /*destination MAC*/ \
+ 6 /*source MAC*/ \
+ 2 /*length*/ \
+ ETH_DATA_LEN /*maximum length*/ \
const uint8_t destMacAddr[6] = {0x00, 0x2B, 0x67, 0x65, 0x14, 0x73};
uint8_t srcMacAddr[6];
uint8_t ethernetBuffers[NUM_TX_DESCRIPTORS][ETH_BUFFER_SIZE];
uint32_t ethernetBuffersCursor = 0;
void InitDescriptors(uint32_t ui32Base);
void EthernetIntHandler(void);
int32_t PacketTransmit(uint8_t *pui8Buf, int32_t i32BufLen);
int32_t ProcessReceivedPacket(void);
void DelayMs(uint32_t systemClock, uint32_t ms);
void DelayUs(uint32_t systemClock, uint32_t us);
uint32_t BufferWriteBeUint16At(uint8_t* buffer, uint32_t cursor, uint16_t value);
int main(void)
{
uint32_t ui32User0, ui32User1, ui32Loop, ui32SysClock;
uint8_t ui8PHYAddr;
//
// 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.
//
MAP_SysCtlMOSCConfigSet(SYSCTL_MOSC_HIGHFREQ);
//
// Run from the PLL at 120 MHz.
//
ui32SysClock = SysCtlClockFreqSet(
(SYSCTL_XTAL_25MHZ |
SYSCTL_OSC_MAIN |
SYSCTL_USE_PLL |
SYSCTL_CFG_VCO_480),
120000000);
//
// "Debug" GPIO.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOP);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOP)){}
GPIOPinTypeGPIOOutput(GPIO_PORTP_BASE, GPIO_PIN_2);
//
// Read the MAC address from the user registers.
//
FlashUserGet(&ui32User0, &ui32User1);
if((ui32User0 == 0xffffffff) || (ui32User1 == 0xffffffff))
{
//
// We should never get here. This is an error if the MAC address has
// not been programmed into the device. Exit the program.
//
while(1) {}
}
//
// Convert the 24/24 split MAC address from NV ram into a 32/16 split MAC
// address needed to program the hardware registers, then program the MAC
// address into the Ethernet Controller registers.
//
srcMacAddr[0] = ((ui32User0 >> 0) & 0xff);
srcMacAddr[1] = ((ui32User0 >> 8) & 0xff);
srcMacAddr[2] = ((ui32User0 >> 16) & 0xff);
srcMacAddr[3] = ((ui32User1 >> 0) & 0xff);
srcMacAddr[4] = ((ui32User1 >> 8) & 0xff);
srcMacAddr[5] = ((ui32User1 >> 16) & 0xff);
//
// Enable and reset the Ethernet modules.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_EMAC0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_EPHY0);
SysCtlPeripheralReset(SYSCTL_PERIPH_EMAC0);
SysCtlPeripheralReset(SYSCTL_PERIPH_EPHY0);
//
// Wait for the MAC to be ready.
//
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_EMAC0)) {}
//
// Configure for use with the internal PHY.
//
ui8PHYAddr = 0;
EMACPHYConfigSet(EMAC0_BASE,
(EMAC_PHY_TYPE_INTERNAL |
EMAC_PHY_INT_MDIX_EN |
EMAC_PHY_AN_100B_T_FULL_DUPLEX));
//
// Reset the MAC to latch the PHY configuration.
//
EMACReset(EMAC0_BASE);
//
// Initialize the MAC and set the DMA mode.
//
EMACInit(
EMAC0_BASE,
ui32SysClock,
(EMAC_BCONFIG_MIXED_BURST | EMAC_BCONFIG_TX_PRIORITY),
32,
32,
0);
//
// Set MAC configuration options.
//
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_JABBER_DISABLE |
EMAC_CONFIG_JUMBO_ENABLE |
EMAC_CONFIG_100MBPS |
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);
//
// Program the hardware with its MAC address (for filtering).
//
EMACAddrSet(EMAC0_BASE, 0, srcMacAddr);
//
// Initialize the Ethernet DMA descriptors.
//
InitDescriptors(EMAC0_BASE);
//
// Wait for the link to become active.
//
while((EMACPHYRead(EMAC0_BASE, ui8PHYAddr, EPHY_BMSR) & EPHY_BMSR_LINKSTAT) == 0)
{
}
//
// Set MAC filtering options. We receive all broadcast and multicast
// packets along with those addressed specifically for us.
//
EMACFrameFilterSet(EMAC0_BASE, (EMAC_FRMFILTER_SADDR |
EMAC_FRMFILTER_PASS_MULTICAST |
EMAC_FRMFILTER_PASS_NO_CTRL));
//
// Clear any pending interrupts.
//
EMACIntClear(EMAC0_BASE, EMACIntStatus(EMAC0_BASE, false));
//
// Mark the receive descriptors as available to the DMA to start
// the receive processing.
//
for(ui32Loop = 0; ui32Loop < NUM_RX_DESCRIPTORS; ui32Loop++)
{
g_psRxDescriptor[ui32Loop].ui32CtrlStatus |= DES0_RX_CTRL_OWN;
}
//
// Enable the Ethernet MAC transmitter and receiver.
//
EMACTxEnable(EMAC0_BASE);
EMACRxEnable(EMAC0_BASE);
//
// Enable the Ethernet interrupt.
//
IntEnable(INT_EMAC0);
//
// Enable the Ethernet RX Packet interrupt source.
//
EMACIntEnable(EMAC0_BASE, EMAC_INT_RECEIVE);
// EMACIntEnable(
// EMAC0_BASE,
// (EMAC_INT_BUS_ERROR
// | EMAC_INT_RECEIVE
// | EMAC_INT_TX_UNDERFLOW
// | EMAC_INT_TX_NO_BUFFER
// | EMAC_INT_TX_STOPPED
// | EMAC_INT_TRANSMIT)
// );
while(1)
{
uint32_t serCursor = 0;
uint32_t i = 0;
for(serCursor = ETH_DMAC_OFFSET, i = 0; serCursor< (ETH_DMAC_OFFSET + 6); serCursor++, i++)
{
ethernetBuffers[ethernetBuffersCursor][serCursor] = destMacAddr[i];
}
for(serCursor = ETH_SMAC_OFFSET, i = 0; serCursor< (ETH_SMAC_OFFSET + 6); serCursor++, i++)
{
ethernetBuffers[ethernetBuffersCursor][serCursor] = srcMacAddr[i];
}
serCursor = BufferWriteBeUint16At(ethernetBuffers[ethernetBuffersCursor], serCursor, ETH_DATA_LEN);
for(; serCursor < ETH_BUFFER_SIZE; serCursor++)
{
ethernetBuffers[ethernetBuffersCursor][serCursor] = 0;
}
PacketTransmit(ethernetBuffers[ethernetBuffersCursor], serCursor);
ethernetBuffersCursor++;
if(ethernetBuffersCursor >= NUM_TX_DESCRIPTORS)
{
ethernetBuffersCursor = 0;
}
MAP_GPIOPinWrite(GPIO_PORTP_BASE, GPIO_PIN_2, MAP_GPIOPinRead(GPIO_PORTP_BASE, GPIO_PIN_2) ^ GPIO_PIN_2);
}
}
void InitDescriptors(uint32_t ui32Base)
{
uint32_t ui32Loop;
//
// Initialize each of the transmit descriptors. Note that we leave the
// buffer pointer and size empty and 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;
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_ppui8RxBuffer[ui32Loop];
g_psRxDescriptor[ui32Loop].DES3.pLink =
(ui32Loop == (NUM_RX_DESCRIPTORS - 1)) ?
g_psRxDescriptor : &g_psRxDescriptor[ui32Loop + 1];
}
//
// Set the descriptor pointers in the hardware.
//
EMACRxDMADescriptorListSet(ui32Base, g_psRxDescriptor);
EMACTxDMADescriptorListSet(ui32Base, 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 = 0;
}
void EthernetIntHandler(void)
{
uint32_t ui32Temp;
//
// Read and Clear the interrupt.
//
ui32Temp = EMACIntStatus(EMAC0_BASE, true);
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.
//
ProcessReceivedPacket();
}
}
int32_t ProcessReceivedPacket(void)
{
int_fast32_t i32FrameLen;
//
// By default, we assume we got a bad frame.
//
i32FrameLen = 0;
//
// Make sure that we own the receive descriptor.
//
if(!(g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus & DES0_RX_CTRL_OWN))
{
//
// We own the receive descriptor so check to see if it contains a valid
// frame.
//
if(!(g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus & DES0_RX_STAT_ERR))
{
//
// We have a valid frame. 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)
{
//
// What size is the received frame?
//
i32FrameLen =
((g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus &
DES0_RX_STAT_FRAME_LENGTH_M) >>
DES0_RX_STAT_FRAME_LENGTH_S);
//
// Pass the received buffer up to the application to handle.
//
// ApplicationProcessFrame(i32FrameLen, g_psRxDescriptor[g_ui32RxDescIndex].pvBuffer1);
}
}
//
// Now that we are finished dealing with this descriptor, hand
// it back to the hardware. Note that we assume
// ApplicationProcessFrame() is finished with the buffer at this point
// so it is safe to reuse.
//
g_psRxDescriptor[g_ui32RxDescIndex].ui32CtrlStatus = DES0_RX_CTRL_OWN;
//
// Move on to the next descriptor in the chain.
//
g_ui32RxDescIndex++;
if(g_ui32RxDescIndex == NUM_RX_DESCRIPTORS)
{
g_ui32RxDescIndex = 0;
}
}
//
// Return the Frame Length
//
return(i32FrameLen);
}
int32_t PacketTransmit(uint8_t *pui8Buf, int32_t i32BufLen)
{
//
// Wait for the transmit descriptor to free up.
//
while(g_psTxDescriptor[g_ui32TxDescIndex].ui32CtrlStatus &
DES0_TX_CTRL_OWN)
{
//
// Spin and waste time.
//
}
//
// Fill in the packet size and pointer, and tell the transmitter to start
// work.
//
g_psTxDescriptor[g_ui32TxDescIndex].ui32Count = (uint32_t)i32BufLen;
g_psTxDescriptor[g_ui32TxDescIndex].pvBuffer1 = pui8Buf;
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.
// This call is benign if the transmitter hasn’t stalled and checking
// the state takes longer than just issuing a poll demand so we do this
// for all packets.
//
EMACTxDMAPollDemand(EMAC0_BASE);
//
// Move to the next descriptor.
//
g_ui32TxDescIndex++;
if(g_ui32TxDescIndex == NUM_TX_DESCRIPTORS)
{
g_ui32TxDescIndex = 0;
}
//
// Return the number of bytes sent.
//
return(i32BufLen);
}
void DelayMs(uint32_t systemClock, uint32_t ms)
{
uint32_t oneMsDelay = (systemClock / 3u) / 1000u;
SysCtlDelay(oneMsDelay * ms);
}
void DelayUs(uint32_t systemClock, uint32_t us)
{
uint32_t oneUsDelay = (systemClock / 3u) / 1000000u;
SysCtlDelay(oneUsDelay * us);
}
void revmemcpy(void *dest, const void *src, uint32_t len)
{
char* d = (char*)dest + len - 1;
const char* s = src;
while (len--)
{
*d-- = *s++;
}
}
uint32_t BufferWriteBeUint16At(uint8_t* buffer, uint32_t cursor, uint16_t value)
{
revmemcpy((buffer + cursor), &value, 2);
return cursor + 2;
}