板卡采用了 LMK04828 芯片为 FPGA 的 RF-ADC/DAC 提供时钟,其使用方法可以参考:
应用案例: 理解IW-RFSOC-2T2R时钟树结构并实现寄存器配置
板卡采用了高通 AR8035-AL1A 10/100/1000M 三态以太网 PHY 芯片,然而这个芯片无法被 Vitis 自带的 LwIP 样例驱动,需要对其进行一些修改以驱动该PHY芯片。
通常来说,有两种方法对其进行修改,一是在建立工程时修改 BSP 代码,二是直接修改 Vitis 的 LwIP 库代码。这里介绍使用第二种方式,因为第一种方式在频繁修改 BSP 设置以及重新生成时会比较麻烦。况且经过目前的测试,我们提供的修补方式并没有带来更多的副作用。
首先在安装文件夹中找到需要修改的文件,此处以 Vitis 2022.2 版本为例:
/tools/Xilinx/Vitis/2022.2/data/embeddedsw/ThirdParty/sw_services/lwip211_v1_8/src/contrib/ports/xilinx/netif/xemacpsif_physpeed.c
在大约 123-128 行的位置,加入 IDENTIFIER 宏定义:
#define PHY_MARVELL_IDENTIFIER 0x0141
#define PHY_TI_IDENTIFIER 0x2000
#define PHY_REALTEK_IDENTIFIER 0x001c
#define PHY_ATHEROS_IDENTIFIER 0x004D //<==++++++
#define PHY_XILINX_PCS_PMA_ID1 0x0174
#define PHY_XILINX_PCS_PMA_ID2 0x0C00
在大约 307-311 行的位置,修改 if 语句:
if ((phy_reg != PHY_MARVELL_IDENTIFIER) &&
(phy_reg != PHY_TI_IDENTIFIER) &&
(phy_reg != PHY_REALTEK_IDENTIFIER) &&
(phy_reg != PHY_ATHEROS_IDENTIFIER)) {
xil_printf("WARNING: Not a Marvell or TI or Realtek or Atheros Ethernet PHY. Please verify the initialization sequence\r\n");
}
在大约 678 行的位置加入寄存器配置函数 get_Atheros_phy_speed :
static u32_t get_Atheros_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr)
{
u16_t temp;
u16_t control;
u16_t status;
u16_t status_speed;
u32_t timeout_counter = 0;
u32_t temp_speed;
xil_printf("Start Atheros PHY autonegotiation \r\n");
XEmacPs_PhyRead(xemacpsp, phy_addr, 0xe, &control);
control |= 0x0018;
XEmacPs_PhyWrite(xemacpsp, phy_addr, 0xe, control);
XEmacPs_PhyWrite(xemacpsp,phy_addr, 0x1d, 0x05);
XEmacPs_PhyRead(xemacpsp, phy_addr, 0x1e, &control);
control |= 0x0100;
XEmacPs_PhyWrite(xemacpsp, phy_addr, 0x1e, control);
XEmacPs_PhyWrite(xemacpsp, phy_addr, 0x1d, 0x0);
XEmacPs_PhyWrite(xemacpsp, phy_addr, 0x1e, 0x8000);
XEmacPs_PhyWrite(xemacpsp,phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2);
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, &control);
control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK;
XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, control);
XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0);
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_PhyWrite(xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0);
XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG,
&control);
control |= (7 << 12); /* max number of gigabit attempts */
control |= (1 << 11); /* enable downshift */
XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG,
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);
XEmacPs_PhyRead(xemacpsp, phy_addr,
IEEE_COPPER_SPECIFIC_STATUS_REG_2, &temp);
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_SUCCESS;
}
在大约 686-692 行的位置修改 if 语句:
if (phy_identity == PHY_ATHEROS_IDENTIFIER) {
RetStatus = get_Atheros_phy_speed(xemacpsp, phy_addr);
} else if (phy_identity == PHY_TI_IDENTIFIER) {
RetStatus = get_TI_phy_speed(xemacpsp, phy_addr);
} else if (phy_identity == PHY_REALTEK_IDENTIFIER) {
RetStatus = get_Realtek_phy_speed(xemacpsp, phy_addr);
} else {
RetStatus = get_Marvell_phy_speed(xemacpsp, phy_addr);
}
至此所有的修补工作就完成了,可以对板卡直接使用自带 LwIP 例程了。
在一些情况下,我们观察到即使 PHY 芯片已经正确配置,在控制台仍然会输出 “unable to determine type of EMAC with baseaddress” 这样的错误消息,根据调试我们发现其是由于 Vitis 提供的 LwIP 库文件有缺陷导致的,可以对其进行修补。
首先找到需要修改的文件,此处以 Vitis 2022.2 版本为例:
/tools/Xilinx/Vitis/2022.2/data/embeddedsw/ThirdParty/sw_services/lwip211_v1_8/src/contrib/ports/xilinx/netif/xadapter.c
改正其 136-184 行之间的 switch-case 语句,为其每个 case 后添加 break;
/* initialize based on MAC type */
switch (find_mac_type(mac_baseaddr)) {
case xemac_type_xps_emaclite:
#ifdef XLWIP_CONFIG_INCLUDE_EMACLITE
nif = netif_add(netif, ipaddr, netmask, gw,
(void*)(UINTPTR)mac_baseaddr,
xemacliteif_init,
#if NO_SYS
ethernet_input
#else
tcpip_input
#endif
);
#else
nif = NULL;
#endif
break; //<==++++++
case xemac_type_axi_ethernet:
#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET
nif = netif_add(netif, ipaddr, netmask, gw,
(void*)(UINTPTR)mac_baseaddr,
xaxiemacif_init,
#if NO_SYS
ethernet_input
#else
tcpip_input
#endif
);
#else
nif = NULL;
#endif
break; //<==++++++
#if defined (__arm__) || defined (__aarch64__)
case xemac_type_emacps:
#ifdef XLWIP_CONFIG_INCLUDE_GEM
nif = netif_add(netif, ipaddr, netmask, gw,
(void*)(UINTPTR)mac_baseaddr,
xemacpsif_init,
#if NO_SYS
ethernet_input
#else
tcpip_input
#endif
);
#endif
break; //<==++++++
#endif
default:
xil_printf("unable to determine type of EMAC with baseaddress 0x%08x\r\n",
mac_baseaddr);
}