目录
问题现象
最近把Vivado更新到了2023,SDK工具也更新到了Vitis 2023.2,更新之前的工程后也随之而来的诸多bug,这此遇到的是Vitis例程lwip_echo_server无法配置RTL8211F的问题。
生成DSP以及platform后,直接生成lwip_echo_server模板,测试出现以下问题:
类似的问题在Vivado 2018版本也遇到过,不过那个问题是由于设置了自动协商速度,由于lwip本身的bug所导致,当时设置成固定速度就解决了bug,于是尝试这个方法。
先设置固定连接速度100Mbps:
测试发现phy芯片在不停的重连:
查找原因
首先这个硬件平台在之前的Vivado 2018版本时是可以正常使用lwip_echo_server,因此可以确定不是平台硬件问题,之前通过不使用自动协商临时解决了问题,现在看来只能找到问题的原因了,可以看到出现问题时自动协商速度已经完成了,然后是在phy setup时出现的问题,于是打开获取协商速度的代码:xemacpsif_physpeed.c 准备从这里开始排查
在函数get_Realtek_phy_speed看到了获取速度的代码
static u32_t get_Realtek_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr)
{
u16_t control;
u16_t status;
u16_t status_speed;
u32_t timeout_counter = 0;
u32_t temp_speed;
xil_printf("Start PHY autonegotiation \r\n");
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control);
control |= IEEE_ASYMMETRIC_PAUSE_MASK;
control |= IEEE_PAUSE_MASK;
control |= ADVERTISE_100;
control |= ADVERTISE_10;
XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control);
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET,
&control);
control |= ADVERTISE_1000;
XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET,
control);
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE;
control |= IEEE_STAT_AUTONEGOTIATE_RESTART;
XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control);
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
control |= IEEE_CTRL_RESET_MASK;
XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control);
while (1) {
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
if (control & IEEE_CTRL_RESET_MASK)
continue;
else
break;
}
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);
xil_printf("Waiting for PHY to complete autonegotiation.\r\n");
while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) {
sleep(1);
timeout_counter++;
if (timeout_counter == 30) {
xil_printf("Auto negotiation error \r\n");
return XST_FAILURE;
}
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);
}
xil_printf("autonegotiation complete \r\n");
XEmacPs_PhyRead(xemacpsp, phy_addr,IEEE_SPECIFIC_STATUS_REG,
&status_speed);
if (status_speed & 0x400) {
temp_speed = status_speed & IEEE_SPEED_MASK;
if (temp_speed == IEEE_SPEED_1000)
return 1000;
else if(temp_speed == IEEE_SPEED_100)
return 100;
else
return 10;
}
return XST_FAILURE;
}
上面串口已经给出了 autonegotiation complete 打印,先给出代码里用到的宏的值:
#define IEEE_STATUS_REG_OFFSET 1
#define IEEE_SPECIFIC_STATUS_REG 17
#define IEEE_STAT_AUTONEGOTIATE_COMPLETE 0x0020
#define IEEE_SPEED_MASK 0xC000
#define IEEE_SPEED_1000 0x8000
#define IEEE_SPEED_100 0x4000
检查一下数据手册,至少在读取自动协商状态的代码
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);
//读取寄存PHY芯片器寄存器1数据
//轮询读取此寄存器第5位,此位是速度协商状态位,与数据手册一致
while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) {
sleep(1);
timeout_counter++;
if (timeout_counter == 5) {
xil_printf("Auto negotiation error \r\n");
return XST_FAILURE;
}
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);
}
xil_printf("autonegotiation complete \r\n");
以上没有问题,然后,获取速度的代码,就不对了;
XEmacPs_PhyRead(xemacpsp, phy_addr,IEEE_SPECIFIC_STATUS_REG,
&status_speed);
if (status_speed & 0x400) {
temp_speed = status_speed & IEEE_SPEED_MASK;
if (temp_speed == IEEE_SPEED_1000)
return 1000;
else if(temp_speed == IEEE_SPEED_100)
return 100;
else
return 10;
}
这里是读取了17号寄存器,也就是0X11寄存器的数据,然后通过且上0x400来获取第10位的数据,再通过且上0xC000来获取14、15位的数据,但是RTL8211F的数据手册里
此寄存器时设置网口LED的,这错了十万八千里,这个寄存器的地址应该在0x1A
而且获取连接状态的位在第2位,获取速度的位在第4位和第5位,因此猜测问题就处在这里。
猜想验证
按照这个临时改了一下
XEmacPs_PhyRead(xemacpsp, phy_addr,0x1a,
&status_speed);
if (status_speed & 0x4) {
temp_speed = status_speed & 0x30;
if (temp_speed == 0x20)
return 1000;
else if(temp_speed == 0x10)
return 100;
else
return 10;
}
于是问题就被解决了
正当我疑惑为啥会出现get_Realtek_phy_speed不支持Realtek芯片时,偶然查到了RTL8211竟然分为商业级和工业级,商业级可以看RTL8211E,找到数据手册直接看0x11寄存器,如我所料
正是程序里默认的配置,所以lwip213支持了商业级RTL8211,但是还是不支持RTL8211F,这两种芯片可以通过查询PHY_IDENTIFIER_2_REG(0X03)寄存器的Revision Number来判别
RTL8211F的Revision Number是0110 ,PHY_IDENTIFIER_2_REG是0xC916
RTL8211E的Revision Number是0101 ,PHY_IDENTIFIER_2_REG是0xC915
解决方案
为了兼容这两种芯片,于是把get_Realtek_phy_speed做以下更改:
static u32_t get_Realtek_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr)
{
u16_t control;
u16_t status;
u16_t status_speed;
u32_t timeout_counter = 0;
u32_t temp_speed;
u16_t phy_identity;
u32_t RetStatus;
u32_t RTL_SPECIFIC_STATUS_REG;
u32_t RTL_LINK_STATUS_MASK;
u32_t RTL_SPEED_MASK;
u32_t RTL_SPEED_1000;
u32_t RTL_SPEED_100;
//此处判断是RTL8211F或RTL8211E,根据型号重定义寄存器地址和掩码
XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_IDENTIFIER_2_REG,
&phy_identity);
if (phy_identity == 0XC916) {
xil_printf("Start PHY:RTL8211F autonegotiation\r\n");
RTL_SPECIFIC_STATUS_REG = 0X1A;
RTL_LINK_STATUS_MASK = 0x04;
RTL_SPEED_MASK = 0x30;
RTL_SPEED_1000 = 0x20;
RTL_SPEED_100 = 0x10;
}
else if (phy_identity == 0XC915) {
xil_printf("Start PHY:RTL8211E autonegotiation\r\n");
RTL_SPECIFIC_STATUS_REG = 0X0011;
RTL_LINK_STATUS_MASK = 0x0400;
RTL_SPEED_MASK = 0xC000;
RTL_SPEED_1000 = 0x8000;
RTL_SPEED_100 = 0x4000;
}
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control);
control |= IEEE_ASYMMETRIC_PAUSE_MASK;
control |= IEEE_PAUSE_MASK;
control |= ADVERTISE_100;
control |= ADVERTISE_10;
XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control);
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET,
&control);
control |= ADVERTISE_1000;
XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET,
control);
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE;
control |= IEEE_STAT_AUTONEGOTIATE_RESTART;
XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control);
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
control |= IEEE_CTRL_RESET_MASK;
XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control);
while (1) {
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
if (control & IEEE_CTRL_RESET_MASK)
continue;
else
break;
}
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);
xil_printf("Waiting for PHY to complete autonegotiation.\r\n");
while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) {
sleep(1);
timeout_counter++;
if (timeout_counter == 5) {
xil_printf("Auto negotiation error \r\n");
return XST_FAILURE;
}
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);
}
xil_printf("autonegotiation complete \r\n");
//此处使用重定义的寄存器地址和掩码
XEmacPs_PhyRead(xemacpsp, phy_addr,RTL_SPECIFIC_STATUS_REG,
&status_speed);
if (status_speed & RTL_LINK_STATUS_MASK) {
temp_speed = status_speed & RTL_SPEED_MASK;
if (temp_speed == RTL_SPEED_1000)
return 1000;
else if(temp_speed == RTL_SPEED_100)
return 100;
else
return 10;
}
return XST_FAILURE;
}
即可