LAN8720 PHY IC requires delay after reset? - Page 1
Forum section: electronics / general

author: syntax333
author_rank: Regular Contributor
timestamp: June 21, 2022, 04:50:29 pm
content: Hi I am currently working on a project where I have to implement an Ethernet Driver for STM32F429ZI Nucleo board with LAN8720 PHY IC. Below is my code.Code:#include "main.h"#define ETH_EXTPHY_ADDRESS 0#define ETH_EXTPHY_BCR0#define ETH_EXTPHY_BSR1#define ETH_EXTPHY_ANAR4#define ETH_EXTPHY_ANLPAR5#define ETH_EXTPHY_ANER6#define ETH_EXTPHY_PHYSCSR31#define ETH_RXBUFNUM 4#define ETH_TXBUFNUM 2#define ETH_RXBUFFSIZE 1524#define ETH_TXBUFFSIZE 1524uint8_t ETH_TxBuffer;//;uint8_t ETH_RxBuffer;typedef struct{uint32_t Status;uint32_t ControlBufferSize;uint32_t Buffer1Address;uint32_t NextDescriptorAddress;}ETH_Descriptor;ETH_Descriptor RxDescriptorTable;ETH_Descriptor TxDescriptorTable;void SystemClock_Config(void);uint16_t ETH_ReadPHYRegister(uint8_t PHYRegAddr){//read data from external PHY registeruint32_t tempReg = 0x0;while(ETH->MACMIIAR & ETH_MACMIIAR_MB); //Wait until all operations to finishtempReg |= (ETH_EXTPHY_ADDRESS<<ETH_MACMIIAR_PA_Pos) | (PHYRegAddr<<ETH_MACMIIAR_MR_Pos) | (0b100<<ETH_MACMIIAR_CR_Pos) | ETH_MACMIIAR_MB;ETH->MACMIIAR = tempReg;while(ETH->MACMIIAR & ETH_MACMIIAR_MB); //Wait until Data is receivedreturn (ETH->MACMIIDR & 0xFFFF);}void ETH_WritePHYRegister(uint8_t PHYRegAddr, uint16_t RegValue){//write data to external PHY registeruint32_t tempReg = 0x0;while(ETH->MACMIIAR & ETH_MACMIIAR_MB); //Wait until all operations to finishETH->MACMIIDR = RegValue; //Load Data to RegistertempReg |= (ETH_EXTPHY_ADDRESS<<ETH_MACMIIAR_PA_Pos) | (PHYRegAddr<<ETH_MACMIIAR_MR_Pos) | (0b100<<ETH_MACMIIAR_CR_Pos) | ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;ETH->MACMIIAR = tempReg;}void Initialization(){//----------------GPIO Configuration----------------//RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN; //Enable ClocksGPIOA->MODER |= (0b10<<GPIO_MODER_MODE1_Pos) | (0b10<<GPIO_MODER_MODE2_Pos) | (0b10<<GPIO_MODER_MODE7_Pos); //Set Pins as Alternate Function pinsGPIOA->OSPEEDR |= (0b11<<GPIO_OSPEEDR_OSPEED1_Pos) | (0b11<<GPIO_OSPEEDR_OSPEED2_Pos) | (0b11<<GPIO_OSPEEDR_OSPEED7_Pos); //Set Pins as Very High SpeedGPIOA->AFR |= (11<<GPIO_AFRL_AFSEL1_Pos) | (11<<GPIO_AFRL_AFSEL2_Pos) | (11<<GPIO_AFRL_AFSEL7_Pos); //Set pins Alternative FunctionsGPIOB->MODER |= (0b10<<GPIO_MODER_MODE11_Pos) | (0b10<<GPIO_MODER_MODE12_Pos) | (0b10<<GPIO_MODER_MODE13_Pos); //Set Pins as Alternate Function pinsGPIOB->OSPEEDR |= (0b11<<GPIO_OSPEEDR_OSPEED11_Pos) | (0b11<<GPIO_OSPEEDR_OSPEED12_Pos) | (0b11<<GPIO_OSPEEDR_OSPEED13_Pos); //Set Pins as Very High SpeedGPIOB->AFR |= (11<<GPIO_AFRH_AFSEL11_Pos) | (11<<GPIO_AFRH_AFSEL12_Pos) | (11<<GPIO_AFRH_AFSEL13_Pos); //Set pins Alternative FunctionsGPIOC->MODER |= (0b10<<GPIO_MODER_MODE1_Pos) | (0b10<<GPIO_MODER_MODE4_Pos) | (0b10<<GPIO_MODER_MODE5_Pos); //Set Pins as Alternate Function pinsGPIOC->OSPEEDR |= (0b11<<GPIO_OSPEEDR_OSPEED1_Pos) | (0b11<<GPIO_OSPEEDR_OSPEED4_Pos) | (0b11<<GPIO_OSPEEDR_OSPEED5_Pos); //Set Pins as Very High SpeedGPIOC->AFR |= (11<<GPIO_AFRL_AFSEL1_Pos) | (11<<GPIO_AFRL_AFSEL4_Pos) | (11<<GPIO_AFRL_AFSEL5_Pos); //Set pins Alternative Functions//----------------SYSCFG Configuration----------------//RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; //Enable ClocksSYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL; //Select RMII//----------------Ethernet Configuration----------------//RCC->AHB1ENR |= RCC_AHB1ENR_ETHMACEN | RCC_AHB1ENR_ETHMACTXEN | RCC_AHB1ENR_ETHMACRXEN; //Enable ClocksETH->DMABMR |= ETH_DMABMR_SR; //Resets Ethernet Corewhile(ETH->DMABMR & ETH_DMABMR_SR); //Wait until reset completes//--------PHY Configuration--------//uint16_t tempRegister;//Soft Reset PHYETH_WritePHYRegister(ETH_EXTPHY_BCR, 0x8000);while(ETH_ReadPHYRegister(ETH_EXTPHY_BCR) & 0x8000); //Wait until reset completes//Wait for Linked statuswhile(!(ETH_ReadPHYRegister(ETH_EXTPHY_BSR) & 0x4)); //Wait until link is up//Auto negotiation is enabled by hardware.tempRegister = ETH_ReadPHYRegister(ETH_EXTPHY_BCR);tempRegister |= (0b1<<9);ETH_WritePHYRegister(ETH_EXTPHY_BCR, tempRegister); //Restart auto-negotiation.while(!(ETH_ReadPHYRegister(ETH_EXTPHY_BSR) & (0b1<<5))); //Wait until auto-negotiation completes//Auto negotiation resulttempRegister = ETH_ReadPHYRegister(ETH_EXTPHY_PHYSCSR);//--------MAC Configuration--------//if(((tempRegister>>2) & 0b111) == 0b110) //100BASE-TX Full-duplex{ETH->MACCR |= ETH_MACCR_FES | ETH_MACCR_DM;}else if(((tempRegister>>2) & 0b111) == 0b010) //100BASE-TX Half-duplex{ETH->MACCR |= ETH_MACCR_FES;}else if(((tempRegister>>2) & 0b111) == 0b101) //10BASE-T Full-duplex{ETH->MACCR |= ETH_MACCR_DM;}else //10BASE-T Half-duplex{ETH->MACCR &= ~(ETH_MACCR_FES | ETH_MACCR_DM);}ETH->MACCR |= ETH_MACCR_IPCO; //Enable IPv4 Off-loadETH->MACIMR |= ETH_MACIMR_TSTIM | ETH_MACIMR_PMTIM; //Disable interrupt generation due to time stamp trigger and PMT.//MAC Address is set hereETH->MACA0HR &= ~(ETH_MACA0HR_MACA0H); //Clear MAC AddressETH->MACA0HR |= (0xMACADDRESS<<ETH_MACA0HR_MACA0H_Pos);ETH->MACA0LR &= ~(ETH_MACA0LR_MACA0L); //Clear MAC AddressETH->MACA0LR |= (0xMACADDRESS<<ETH_MACA0LR_MACA0L_Pos);//--------MMC Configuration--------////Mask Statistic InterruptsETH->MMCRIMR |= ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFAEM | ETH_MMCRIMR_RFCEM;ETH->MMCTIMR |= ETH_MMCTIMR_TGFM | ETH_MMCTIMR_TGFMSCM | ETH_MMCTIMR_TGFSCM;//--------DMA Configuration--------//ETH->DMARDLAR = (uint32_t) &RxDescriptorTable;ETH->DMATDLAR = (uint32_t) &TxDescriptorTable;ETH->DMAOMR |= ETH_DMAOMR_RSF | ETH_DMAOMR_TSF; //Forward ModeETH->DMAIER |= ETH_DMAIER_NISE | ETH_DMAIER_RIE; //Enable Rx interrupt//----------------NVIC Configuration----------------//NVIC_EnableIRQ(ETH_IRQn);//--------ETH Start--------//ETH->MACCR |= ETH_MACCR_TE;ETH->MACCR |= ETH_MACCR_RE;ETH->DMAOMR |= ETH_DMAOMR_FTF; // Transmit fifo flushwhile(ETH->DMAOMR & ETH_DMAOMR_FTF); // Wait until fifo flushesETH->DMAOMR |= ETH_DMAOMR_ST;ETH->DMAOMR |= ETH_DMAOMR_SR;}void ETH_IRQHandler(){}int main(void){HAL_Init();SystemClock_Config();Initialization();TxDescriptorTable.Status= ETH_DMATXDESC_TCH;TxDescriptorTable.ControlBufferSize= 0x0;TxDescriptorTable.Buffer1Address= (uint32_t)(&ETH_TxBuffer);TxDescriptorTable.NextDescriptorAddress= (uint32_t) &TxDescriptorTable;TxDescriptorTable.Status |= ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL;ETH_TxBuffer = 0xff;ETH_TxBuffer = 0xff;ETH_TxBuffer = 0xff;ETH_TxBuffer = 0xff;ETH_TxBuffer = 0xff;ETH_TxBuffer = 0xff;ETH_TxBuffer = 0xMAC1;ETH_TxBuffer = 0xMAC2;ETH_TxBuffer = 0xMAC3;ETH_TxBuffer = 0xMAC4;ETH_TxBuffer = 0xMAC5;ETH_TxBuffer = 0xMAC6;ETH_TxBuffer = 0x08;ETH_TxBuffer = 0x06;ETH_TxBuffer = 0x00;ETH_TxBuffer = 0x01;ETH_TxBuffer = 0x08;ETH_TxBuffer = 0x00;ETH_TxBuffer = 0x06;ETH_TxBuffer = 0x04;ETH_TxBuffer = 0x00;ETH_TxBuffer = 0x01;ETH_TxBuffer = 0xMAC1;ETH_TxBuffer = 0xMAC2;ETH_TxBuffer = 0xMAC3;ETH_TxBuffer = 0xMAC4;ETH_TxBuffer = 0xMAC5;ETH_TxBuffer = 0xMAC6;ETH_TxBuffer = 0xc0;ETH_TxBuffer = 0xa8;ETH_TxBuffer = 0x00;ETH_TxBuffer = 0x16;ETH_TxBuffer = 0x00;ETH_TxBuffer = 0x00;ETH_TxBuffer = 0x00;ETH_TxBuffer = 0x00;ETH_TxBuffer = 0x00;ETH_TxBuffer = 0x00;ETH_TxBuffer = 0xc0;ETH_TxBuffer = 0xa8;ETH_TxBuffer = 0x00;ETH_TxBuffer = 0x05;while(TxDescriptorTable.Status & ETH_DMATXDESC_OWN); //wait until DMA release DescriptorTxDescriptorTable.Status |=ETH_DMATXDESC_FS|ETH_DMATXDESC_LS|ETH_DMATXDESC_TER;TxDescriptorTable.ControlBufferSize = 42;TxDescriptorTable.Status |= ETH_DMATXDESC_OWN;if (ETH->DMASR & ETH_DMASR_TBUS){ETH->DMASR = ETH_DMASR_TBUS;ETH->DMATPDR = 0;}while (1){}}void SystemClock_Config(void){RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Configure the main internal regulator output voltage*/__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;RCC_OscInitStruct.PLL.PLLM = 8;RCC_OscInitStruct.PLL.PLLN = 180;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 4;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){}/** Activate the Over-Drive mode*/if (HAL_PWREx_EnableOverDrive() != HAL_OK){}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK){}SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk);}Code works fine if I am debugging but when I flash the code and reset the microcontroller it doesnt seem to send the ARP requests.If I add a delay after link status check for approximately 270ms the code works fine . So I believe I have to wait for the chip to wake up after reset or something? I counldn't find anything in datasheet. I was expecting a register bit that I can poll to make sure the IC is ready to handle the data. Normally I only check Link status and Auto negotiation result to continue the code.

author: spostma
author_rank: Regular Contributor
timestamp: June 21, 2022, 06:07:59 pm
content: I have seen this (or similar) issue before on the WESP32 project:https://hackaday.io/project/85389-wesp32-wired-esp32-with-ethernet-and-poe/log/160416-the-phy-reset-saga

author: syntax333
author_rank: Regular Contributor
timestamp: June 21, 2022, 06:50:55 pm
content: Thanks for reply but I understand that this guys problem is, not being able to use clock pin and boot pin for MCU at same time. Since boot pin has to low and clock pin has to high for initial startup for both MCU and PHY. So he uses custom "reset" supervisor IC.In article also in datasheet it is stated that reset must be released after stable clock is applied to the PHY .On my board IC has its own crystal with 25MHz always so it shouldn't have a problem about reset being released when no clock is available.

author: ajb
author_rank: Super Contributor
timestamp: June 21, 2022, 07:48:10 pm
content: From the datasheet (emphasis mine)QuoteSo the reset bit is itself the bit that will tell you when the reset is complete, and 270ms is well within the quoted time for the reset to be completed. It's possible that the link management section of the PHY complete the reset process before other parts do, and attempting to interact with the PHY before those other parts are fully reset causes some sort of invalid internal state. Regardless, it's generally a risky idea to interact with a complicated part like this during a reset as behavior can be difficult to predict.

author: syntax333
author_rank: Regular Contributor
timestamp: June 22, 2022, 06:29:23 am
content: Basic Control register has soft reset bit when you trigger it will issue a soft reset then you can check the bit if the process is finish by polling this bit. However, I need a bit that can tell me if IC is ready afterHardware reset. It seems like there is no such bit. I have to put dummy delays at the beginning of the code.Also I tried to Soft reset after startup and polled the bit but behavior didn't changed.

author: syntax333
author_rank: Regular Contributor
timestamp: June 22, 2022, 07:57:45 am
content: I scoped RST pin and MDC pin. I also removed the delay. It seems like it is waiting for approximately 1.5s for the link go up. I don't understand why this configuration doesn't work. Blue is RST pin, yellow is MDC. MDIO and MDC constantly checks for link status thats why its seems like oscillating.

author: ajb
author_rank: Super Contributor
timestamp: June 22, 2022, 07:32:39 pm
content: Your code doesn't show the MCU issuing a hard reset, but it DOES show it issuing a soft reset.Is that the reset line on the PHY you're probing, or the MCU? How is the PHY reset line connected? Either way it looks like you are waiting for the reset status bit to clear after the reset, so that part should be fine. What happens when it doesn't transmit? Are you seeing any data on the RMII lines? Is the Eth DMAC releasing the descriptor?

author: syntax333
author_rank: Regular Contributor
timestamp: June 23, 2022, 04:46:05 am
content: I am pressing reset button on nucleo board to reset both MCU and ETH PHY. Reset line connected both MCU and ETH PHY in nucleo board. The screen shot I sent shows MDC so after reset I am constantly checking Link status. I didn't checked other RMII lines. I am waiting for DMA to release the descriptor ((" while(TxDescriptorTable.Status & ETH_DMATXDESC_OWN); //wait until DMA release Descriptor" )).Also I realised that If I flash the code it runs fine. However, If I issue a reset or plug out/in the power after flashing code it won't run. That is the main problem I think.

author: syntax333
author_rank: Regular Contributor
timestamp: June 23, 2022, 07:52:54 am
content: I realised my laptop sends arp requests to line after 4-5seconds after link is up. I am using wireshark. If I add 4 second delay to my code it works fine. So is this something about wireshark not being able to see things?

author: syntax333
author_rank: Regular Contributor
timestamp: June 23, 2022, 02:10:32 pm
content: So I found the IC requires 4-5 seconds to power up. While powering up it is unresponsive. After 4-5 seconds it operates as it should. However, It is interesting to me that nowhere in datasheet says anything about powering up -> being able to accept data timing. Has anyone worked with this IC before? Encountered same problem?

author: syntax333
author_rank: Regular Contributor
timestamp: June 23, 2022, 03:44:10 pm
content: I added schematic of PHY IC on board.

author: bson
author_rank: Unknown
timestamp: June 25, 2022, 07:53:48 pm
content: With the boot pins = 111 you have auto-negotiation enabled. Maybe this just takes a few seconds in your particular setup. (Patch cable directly to your laptop?)I think I would watch the basic status register, or similar, and timestamp and decode/log it to the console whenever it changes. This would give a clue as to what's going on, watching what it's doing, and at which stage it's working again. My wild guess is when you hit reset it also up ends up seeing a spurious link fault - or something. (Or the other end does.) It's probably not the best to reset the PHY while the cable is plugged in anyway.

author: syntax333
author_rank: Regular Contributor
timestamp: June 25, 2022, 09:17:43 pm
content: Ethernet cable was directly connected to my PC. I found a work around by replacingCode:while(!(ETH_ReadPHYRegister(ETH_EXTPHY_BSR) & 0x4));withCode:uint16_t tempReg;do{DummyDelay(250); //250ms delaytempReg = ETH_ReadPHYRegister(ETH_EXTPHY_BSR);}while(!(tempReg & 0x4));Which basically checks the link status every 250ms instead continuously. This resulted in connection time of approximately 2 seconds instead of 4-5. I still don't know what I am doing wrong and inner workings of the IC.I tried logging Basic status register, but everything seems fine before and after the link status check and autonegotiation complete check.

author: ajb
author_rank: Super Contributor
timestamp: June 28, 2022, 05:37:32 pm
content: Quote from: syntax333 on June 23, 2022, 04:46:05 amThat makes sense if your toolchain and programming interface are configured to soft reset the MCU after programming, since it won't cause a hardware reset to the PHY. So the PHY will still be up and running from the last reset unless/until you issue a soft reset.Speaking which, it's hard to offer advice when you aren't clear and consistent in your description of the problem. You started off asking about hard reset, but then the code you posted shows the MCU issuing a soft reset, which confuses what's going on and makes it harder to help you. If you're making changes to the code as you troubleshoot that's fine, but please be clear about which changes to the code or your programming/reset process have what effects on the system behavior.QuoteSure, your code is waiting for the descriptor to be released, but is that actually happening? Without information like this it's harder to tell where the problem is.All that aside, if the link comes up faster when you reduce the polling rate, then my guess is that servicing an SMI transaction is keeping the PHY's internal logic from getting the link worked out or performing whatever other reset housekeeping it needs to do. I don't know how PHYs are designed internally but given how much work they do I could believe there's something like a little general purpose processor core in there that handles a lot of the configuration and housekeeping necessary to manage the link hardware, and it probably wasn't designed to service SMI continuously at the rate that something like an STM32F4 can poll it during otherwise normal operation.