Integração do Driver para o Transceptor PHY 88E1510 em Sistemas Zynq

Este artigo aborda a integração do transceptor PHY Marvell 88E1510 em sistemas baseados em Zynq, utilizando a SDK V2014.4. O foco está na configuração para comunicação Ethrenet no lado do processador programável (PL) com autonegociação ativada em uma aplicação standalone.

Problema Identificado

Após a geração de um arquivo HDF a partir do design do PL e a criação de um projeto baseado no exemplo oficial do lwIP echo server, o resultado inicial exibe informações incorretas sobre a velocidade de link negociada:


-----lwIP TCP echo server ------
TCP packets sent to port 6001 will be echoed back
auto-negotiated link speed: 69073
   

O valor "69073" é claramente um indicativo de falha na detecção da velocidade.

Aálise e Solução

A depuração revelou que a função get_IEEE_phy_speed(), localizada no arquivo xaxiemacif_physpeed.c, não identificava corretamente o modelo do PHY. O valor retornado para phy_model era 464 (0x1D0), que não correspondia aos modelos pré-definidos no código de exemplo:


if (phy_identifier == MARVEL_PHY_IDENTIFIER) {
   if (phy_model == MARVEL_PHY_88E1116R_MODEL) {
       return get_phy_speed_88E1116R(xaxiemacp, phy_addr);
   } else if (phy_model == MARVEL_PHY_88111_MODEL) {
       return get_phy_speed_88E1111(xaxiemacp, phy_addr);
   }
}
   

1. Implementação do Driver Específico

Para solucionar isso, foi necessário adicionar suporte para o modelo 0x1D0, que corresponde ao 88E1510. Isso envolveu a criação de uma nova função de driver, get_phy_speed_88E1510():


#define MARVEL_PHY_88E1510_MODEL  				0x1D0

unsigned get_phy_speed_88E1510(XAxiEthernet *xaxiemacp, u32 phy_addr)
{
   u16 temp_reg_val;
   u16 phy_id;
   u16 phy_model_val;
   u16 control_reg;
   u16 status_reg;
   u16 partner_capabilities;

   xil_printf("Entering get_phy_speed_88E1510 driver.\r\n"); // Debug message

   // Read PHY identifier and model
   XAxiEthernet_PhyRead(xaxiemacp, phy_addr, 2, &phy_id);
   XAxiEthernet_PhyRead(xaxiemacp, phy_addr, 3, &phy_model_val);
   phy_model_val = phy_model_val & PHY_MODEL_NUM_MASK; // Mask to get only the model number

   // Configure RGMII delay if necessary (example shows disabling)
   XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2);
   XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, &control_reg);
   control_reg &= ~(0x10); // Example: Disable delayed clock
   XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, control_reg);

   // Configure Autonegotiation Advertisements
   XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0);

   // Enable Pause, Asymmetric Pause
   XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control_reg);
   control_reg |= IEEE_ASYMMETRIC_PAUSE_MASK;
   control_reg |= IEEE_PAUSE_MASK;
   control_reg |= ADVERTISE_100; // Advertise 100 Mbps
   control_reg |= ADVERTISE_10;  // Advertise 10 Mbps
   XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control_reg);

   // Advertise 1000 Mbps
   XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, &control_reg);
   control_reg |= ADVERTISE_1000;
   XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, control_reg);

   // Configure Gigabit settings
   XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0);
   XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, &control_reg);
   control_reg |= (7 << 12); // max number of gigabit attempts
   control_reg |= (1 << 11); // enable downshift
   XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, control_reg);

   // Enable Autonegotiation and Restart
   XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control_reg);
   control_reg |= IEEE_CTRL_AUTONEGOTIATE_ENABLE;
   control_reg |= IEEE_STAT_AUTONEGOTIATE_RESTART;
   XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, control_reg);

   // Perform PHY Reset
   XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control_reg);
   control_reg |= IEEE_CTRL_RESET_MASK;
   XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, control_reg);

   // Wait for reset to complete
   while (true) {
       XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control_reg);
       if (!(control_reg & IEEE_CTRL_RESET_MASK))
           break;
   }
   xil_printf("Waiting for PHY autonegotiation to complete.\r\n");

   // Wait for autonegotiation to finish
   XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status_reg);
   while (!(status_reg & IEEE_STAT_AUTONEGOTIATE_COMPLETE)) {
       sleep(1); // Wait for 1 second
       XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_COPPER_SPECIFIC_STATUS_REG_2, &temp_reg_val);
       if (temp_reg_val & IEEE_AUTONEG_ERROR_MASK) {
           xil_printf("Autonegotiation error detected.\r\n");
           // Handle error, maybe restart autonegotiation or return an error code
           break;
       }
       XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status_reg);
   }
   xil_printf("Autonegotiation finished.\r\n");

   // Determine negotiated speed
   XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_SPECIFIC_STATUS_REG, &partner_capabilities);

   if (((partner_capabilities >> 14) & 3) == 2) return 1000; // 1000Mbps
   else if (((partner_capabilities >> 14) & 3) == 1) return 100;  // 100Mbps
   else return 10; // 10Mbps
}
   

2. Modificação da Função Principal

Em seguida, a função get_IEEE_phy_speed() foi atualizada para chamar a nova função quando o modelo MARVEL_PHY_88E1510_MODEL for detectado:


if (phy_identifier == MARVEL_PHY_IDENTIFIER) {
   if (phy_model == MARVEL_PHY_88E1116R_MODEL) {
       return get_phy_speed_88E1116R(xaxiemacp, phy_addr);
   } else if (phy_model == MARVEL_PHY_88E1111_MODEL) {
       return get_phy_speed_88E1111(xaxiemacp, phy_addr);
   }
   // Added for 88E1510
   else if (phy_model == MARVEL_PHY_88E1510_MODEL) {
       return get_phy_speed_88E1510(xaxiemacp, phy_addr);
   }
}
   

Resultado Esperado

Após a compilação e execução com as modificações, a saída do sistema agora exibe a velocidade de link corretamente:


-----lwIP TCP echo server ------
TCP packets sent to port 7 will be echoed back
Entering get_phy_speed_88E1510 driver.
Waiting for PHY autonegotiation to complete.
Autonegotiation finished.
auto-negotiated link speed: 1000
Board IP: 192.168.1.10
Netmask : 255.255.255.0
Gateway : 192.168.1.1
TCP echo server started @ port 7
   

Extensão da Funcionalidade (Echo em UART)

Para que os dados recebidos pela interface Ethernet também sejam exibidos no console UART, a função recv_callback() pode ser modificada. Adicionar a seguinte linha dentro do bloco if (tcp_sndbuf(tpcb) > p->len) permite o echo dos dados recebidos via rede também no terminal serial:


if (tcp_sndbuf(tpcb) > p->len) {
   err = tcp_write(tpcb, p->payload, p->len, 1);
   xil_printf("%s\n\r",(char*)( p->payload)); // This line enables UART echo
} else
   xil_printf("no space in tcp_sndbuf\n\r");

Tags: Zynq Marvell 88E1510 PHY Driver lwIP Autonegociação

Publicado em 6-8 00:01 por Thomas