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");