LPC1788 Ethernet DMA interrupt problem

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC1788 Ethernet DMA interrupt problem

2,415 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by FlySnake on Mon Jul 16 15:00:21 MST 2012
Hi everyone!
I'm trying to launch FreeRTOS + LwIP on LPC1788 + KS8721B
This work is seems to be done http://www.lpcware.com/content/project/lightweight-ip-lwip-networking-stack but this port is too weird and complex.
I've started with http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_IO/Demo_Applications/LPCXpresso_LPC1769/NXP_LPC1... FreeRTOS+IO featured demo #2
EMAC modules in LPC1768 and LPC1788 looks the same except that LPC1788 supports MII while LPC1768 only RMII.
EMAC initialization
Status EMAC_Init(EMAC_CFG_Type *EMAC_ConfigStruct)
{
/* Enable Ethernet Pins */
/*
 * Enable P1 Ethernet Pins:
 * P1.0 - ENET_TXD0
 * P1.1 - ENET_TXD1
 * P1.4 - ENET_TX_EN
 * P1.8 - ENET_CRS
 * P1.9 - ENET_RXD0
 * P1.10 - ENET_RXD1
 * P1.14 - ENET_RX_ER
 * P1.15 - ENET_REF_CLK
 * P1.16 - ENET_MDC
 * P1.17 - ENET_MDIO
 */
PINSEL_ConfigPin(1, 0, 1);
PINSEL_ConfigPin(1, 1, 1);
PINSEL_ConfigPin(1, 4, 1);
PINSEL_ConfigPin(1, 9, 1);
PINSEL_ConfigPin(1, 10, 1);
PINSEL_ConfigPin(1, 8, 1);
PINSEL_ConfigPin(1, 14, 1);
PINSEL_ConfigPin(1, 17, 1);
PINSEL_ConfigPin(1, 16, 1);
PINSEL_ConfigPin(1, 15, 1);

/* Initialize the EMAC Ethernet controller. */
int32_t regv,tout, tmp;

/* Set up clock and power for Ethernet module */
CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCENET, ENABLE);

/* Reset all EMAC internal modules */
LPC_EMAC->MAC1    = EMAC_MAC1_RES_TX | EMAC_MAC1_RES_MCS_TX | EMAC_MAC1_RES_RX |
EMAC_MAC1_RES_MCS_RX | EMAC_MAC1_SIM_RES | EMAC_MAC1_SOFT_RES;

LPC_EMAC->Command = EMAC_CR_REG_RES | EMAC_CR_TX_RES | EMAC_CR_RX_RES | EMAC_CR_PASS_RUNT_FRM;

/* A short delay after reset. */
vTaskDelay( 2 );

/* Enable Reduced MII interface. */
LPC_EMAC->Command = EMAC_CR_RMII | EMAC_CR_PASS_RUNT_FRM;

/* Reset Reduced MII Logic. */
LPC_EMAC->SUPP = EMAC_SUPP_RES_RMII;

/* Initialize MAC control registers. */
LPC_EMAC->MAC1 = EMAC_MAC1_PASS_ALL;
LPC_EMAC->MAC2 = EMAC_MAC2_CRC_EN | EMAC_MAC2_PAD_EN;
LPC_EMAC->MAXF = EMAC_ETH_MAX_FLEN;

/*
 * Find the clock that close to desired target clock
 */
tmp = SystemCoreClock / EMAC_MCFG_MII_MAXCLK;
for (tout = 0; tout < sizeof (EMAC_clkdiv); tout++){
if (EMAC_clkdiv[tout] >= tmp) break;
}
tout++;

// Write to MAC configuration register and reset
LPC_EMAC->MCFG = EMAC_MCFG_CLK_SEL(tout) | EMAC_MCFG_RES_MII;

// release reset
LPC_EMAC->MCFG &= ~(EMAC_MCFG_RES_MII);
LPC_EMAC->CLRT = EMAC_CLRT_DEF;
LPC_EMAC->IPGR = EMAC_IPGR_P2_DEF;

/* Enable Reduced MII interface. */
LPC_EMAC->Command = EMAC_CR_RMII | EMAC_CR_PASS_RUNT_FRM;

/* A short delay after reset. */
vTaskDelay( 2 );

//LPC_EMAC->SUPP = 0;

/* Put the DP83848C in reset mode */
write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_BMCR_RESET);

/* Wait for hardware reset to end. */
for (tout = EMAC_PHY_RESP_TOUT; tout; tout--) {
regv = read_PHY (EMAC_PHY_REG_BMCR);
if (!(regv & (EMAC_PHY_BMCR_RESET | EMAC_PHY_BMCR_POWERDOWN))) {
/* Reset complete, device not Power Down. */
break;
}
if (tout == 0){
// Time out, return ERROR
return (ERROR);
}
vTaskDelay( 2 );
}

// Set PHY mode
if (EMAC_SetPHYMode(EMAC_ConfigStruct->Mode) < 0){
return (ERROR);
}

// Set EMAC address
setEmacAddr(EMAC_ConfigStruct->pbEMAC_Addr);

/* Initialize Tx and Rx DMA Descriptors */
rx_descr_init ();
tx_descr_init ();

// Set Receive Filter register: enable broadcast and multicast
LPC_EMAC->RxFilterCtrl = EMAC_RFC_MCAST_EN | EMAC_RFC_BCAST_EN | EMAC_RFC_PERFECT_EN;

/* Enable Rx Done and Tx Done interrupt for EMAC */
LPC_EMAC->IntEnable = EMAC_INT_RX_DONE | EMAC_INT_TX_DONE;

/* Reset all interrupts */
LPC_EMAC->IntClear  = 0xFFFF;

/* Enable receive and transmit mode of MAC Ethernet core */
LPC_EMAC->Command  |= (EMAC_CR_RX_EN | EMAC_CR_TX_EN);
LPC_EMAC->MAC1     |= EMAC_MAC1_REC_EN;

        NVIC_SetPriority( ENET_IRQn, configEMAC_INTERRUPT_PRIORITY );
        NVIC_EnableIRQ( ENET_IRQn );

return SUCCESS;
}

Set PHY mode
int32_t EMAC_SetPHYMode(uint32_t ulPHYMode)
{
int32_t id1, id2, tout, regv;

/* Check if this is a DP83848C PHY. */
id1 = read_PHY (EMAC_PHY_REG_IDR1);
id2 = read_PHY (EMAC_PHY_REG_IDR2);

if (((id1 << 16) | (id2 & 0xFFF0)) != 0 /*0x0007C0F0UL*/ ) { // stupid workaround for KS8721
switch(ulPHYMode){
case EMAC_MODE_AUTO:
write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_AUTO_NEG);
/* Wait to complete Auto_Negotiation */
for (tout = EMAC_PHY_RESP_TOUT; tout; tout--) {
regv = read_PHY (EMAC_PHY_REG_BMSR);
if (regv & EMAC_PHY_BMSR_AUTO_DONE) {
/* Auto-negotiation Complete. */
break;
}
if (tout == 0){
// Time out, return error
return (-1);
}

vTaskDelay( 1000 );
}
break;
case EMAC_MODE_10M_FULL:
/* Connect at 10MBit full-duplex */
write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_FULLD_10M);
break;
case EMAC_MODE_10M_HALF:
/* Connect at 10MBit half-duplex */
write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_HALFD_10M);
break;
case EMAC_MODE_100M_FULL:
/* Connect at 100MBit full-duplex */
write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_FULLD_100M);
break;
case EMAC_MODE_100M_HALF:
/* Connect at 100MBit half-duplex */
write_PHY (EMAC_PHY_REG_BMCR, EMAC_PHY_HALFD_100M);
break;
default:
// un-supported
return (-1);
}
}
// It's not correct module ID
else {
return (-1);
}

// Update EMAC configuration with current PHY status
if (EMAC_UpdatePHYStatus() < 0){
return (-1);
}

// Complete
return (0);
}

Until now all is OK. PHY initializes as expected (btw, this code works with LPC1768 + KS8721B in another project)

Initialize RX descriptor
/** Rx Descriptor data array */
static volatile RX_Desc Rx_Desc[EMAC_NUM_RX_FRAG];

/** Rx Status data array - Must be 8-Byte aligned */
static volatile __attribute__ ((aligned (8))) RX_Stat Rx_Stat[EMAC_NUM_RX_FRAG];

/** Tx Descriptor data array */
static volatile TX_Desc Tx_Desc[EMAC_NUM_TX_FRAG];
/** Tx Status data array */
static volatile TX_Stat Tx_Stat[EMAC_NUM_TX_FRAG];

static void rx_descr_init (void)
{
/* Initialize Receive Descriptor and Status array. */
uint32_t i;
extern void *pvApplicationGetEMACRxBufferAddress( void );
xEMACRxBuffer_t *rx_buf;

rx_buf = ( xEMACRxBuffer_t * ) pvApplicationGetEMACRxBufferAddress();
for (i = 0; i < EMAC_NUM_RX_FRAG; i++) {
Rx_Desc.Packet  = (uint32_t)&(*rx_buf);
Rx_Desc.Ctrl    = EMAC_RCTRL_INT | (EMAC_ETH_MAX_FLEN - 1);
Rx_Stat.Info    = 0xFFFFFFFF;
Rx_Stat.HashCRC = 0xFFFFFFFF;
}

/* Set EMAC Receive Descriptor Registers. */
LPC_EMAC->RxDescriptor       = (uint32_t)&Rx_Desc[0];
LPC_EMAC->RxStatus           = (uint32_t)&Rx_Stat[0];
LPC_EMAC->RxDescriptorNumber = EMAC_NUM_RX_FRAG - 1;

/* Rx Descriptors Point to 0 */
LPC_EMAC->RxConsumeIndex  = 0;
}

void *pvApplicationGetEMACRxBufferAddress(void)
{

static volatile xEMACRxBuffer_t rxb __attribute__ ((aligned (4)));
return (void *)rxb;
}

typedef uint32_t xEMACRxBuffer_t[ EMAC_NUM_RX_FRAG ][ EMAC_ETH_MAX_FLEN >> 2 ];


The green led in RJ45 female connector blinks whenever PHY receives some broadcast traffic, but it does not assert interrupt for either receive or transmit done. I don't know what can be wrong
0 Kudos
Reply
3 Replies

1,546 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by FlySnake on Wed Jul 18 04:32:24 MST 2012
Almost solved
I placed descriptors and buffers in RamPeriph32 (starting @ ox2000000) and it begun to work. But why it doesn't work when descriptors are located in RamLoc64?
Where is this written?

Answer
In UM page 13 "Detailed block diagram" EMAC module has no connection to System RAM
http://www.lpcware.com/content/project/lightweight-ip-lwip-networking-stack/lwip-buffer-management

Quote:
Note: For the LPC17xx, the descriptors and buffers must be located only in peripheral RAM or external memory. For the LPC18xx/43xx, descriptors and buffers can be located anywhere.


Except LPC1768 in which EMAC can use both RAM regions.
0 Kudos
Reply

1,546 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by FlySnake on Tue Jul 17 16:11:15 MST 2012
User manual says:

Quote:
After receiving a fragment, the Rx DMA manager writes status information back to the StatusInfo and StatusHashCRC words of the status. The Ethernet block writes the size in bytes of a descriptor’s fragment buffer in the RxSize field of the Status word. [B]The value of the RxProduceIndex is only updated after the fragment data and the fragment status information has been committed to memory, which is checked by an internal tag protocol in the memory interface. [/B]


But in my case StatusInfo and StatusHashCRC always = 0 (or whatever they are initialized to), fragment data NOT written to defined address and RxProduceIndex increments! How can this be possible?
0 Kudos
Reply

1,546 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by FlySnake on Tue Jul 17 14:10:57 MST 2012
Something wrong with DMA descriptors. Especially with RX.
RX buffer is located somewhere in memory and I can watch it via debugger. This address appears in RxDesc and I also can check it via debugger. EMAC module successfully receives the data and increments RxProduceIndex, but no data written to the buffer! And no RxDoneInt asserted. RxProduceIndex goes up to 3 (maximum number of rx descriptors - 1) and stops here i.e. buffer is full, but RxConsumeIndex stays at 0, no interrupts and no data actually written to the buffer. When RxProduceIndex == 3 interrupt RxFinishedInt asserted and that's it.
I put this statement in ENET_IRQHandler
printf("Int 0x%x RxCon %d RxProd %d TxCon %d TxProd %d\n",LPC_EMAC->IntStatus, LPC_EMAC->RxConsumeIndex, LPC_EMAC->RxProduceIndex, LPC_EMAC->TxConsumeIndex, LPC_EMAC->TxProduceIndex);

Abd this is the output:

Quote:
Int 0x2 RxCon 0 RxProd 1 TxCon 0 TxProd 1
Int 0x2 RxCon 0 RxProd 2 TxCon 0 TxProd 1
Int 0x6 RxCon 0 RxProd 3 TxCon 0 TxProd 1


0x2 is RxErrorInt. I see the error in LPC1768 running this code, and LPC1788 running EasyWeb example from CMSIS driver library. It seems like nobody cares about this error
0x6 - RxErrorInt | RxFinishedInt - last interrupt occurred.
0 Kudos
Reply