【ZYNQ】裸机 PS + PL 双网口实现之 LWIP 库文件修改

简介: 【ZYNQ】裸机 PS + PL 双网口实现之 LWIP 库文件修改

因项目需要,为实现 Zynq 裸机双网口通信功能,其中 ENET0 连接 PS 端网口,ENET1 通过 EMIO 扩展连接 PL 端网口,原 LWIP 库无板载 PHY 芯片支持及 EMIO 配置选项,故将 LWIP库文件进行修改以满足功能需求。


修改对象

Xilinx Vivado 2017.4 库文件 lwip141_v2_0

新增功能


  • 添加 PHY 芯片 KSZ9031 支持,10/100/1000 Mbps 速度自适应
  • 添加 SDK 中 LWIP 参数设置对话框 emio_options 选项
  • 添加通过 MDIO 配置 GMII to RGMII 寄存器相关的两个宏配置


测试环境

  • Windows 10,Vivado 2017.4
  • 黑金 Zynq 7035 板卡,芯片型号:XC7Z035-2FFG676I
  • 100M 以太网,TCP、UDP 通信


修改内容

1. 修改 lwip141_v2_0\src\contrib\ports\xilinx\netif\xaxiemacif_physpeed.c 文件

添加宏定义:

#define MICREL_PHY_IDENTIFIER   0x22
#define MICREL_PHY_KSZ9031_MODEL  0x220


添加 phy 芯片 ksz9031 速度获取函数:

ununsigned int get_phy_speed_ksz9031(XAxiEthernet *xaxiemacp, u32 phy_addr)
{
    u16 control;
    u16 status;
    u16 partner_capabilities;
    xil_printf("Start PHY autonegotiation \r\n");

    XAxiEthernet_PhyWrite(xaxiemacp,phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2);
    XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, &control);
    //control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK;
    control &= ~(0x10);
    XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, control);

    XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0);

    XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control);
    control |= IEEE_ASYMMETRIC_PAUSE_MASK;
    control |= IEEE_PAUSE_MASK;
    control |= ADVERTISE_100;
    control |= ADVERTISE_10;
    XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control);

    XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET,
                                                                    &control);
    control |= ADVERTISE_1000;
    XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET,
                                                                    control);

    XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0);
    XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG,
                                                                &control);
    control |= (7 << 12); /* max number of gigabit attempts */
    control |= (1 << 11); /* enable downshift */
    XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG,
                                                                control);
    XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
    control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE;
    control |= IEEE_STAT_AUTONEGOTIATE_RESTART;

    XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, control);

    XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
    control |= IEEE_CTRL_RESET_MASK;
    XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, control);

    while (1) {
        XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);
        if (control & IEEE_CTRL_RESET_MASK)
            continue;
        else
            break;
    }
    xil_printf("Waiting for PHY to complete autonegotiation.\r\n");

    XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);
    while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) {
        sleep(1);
        XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET,
                                                                &status);
        }

    xil_printf("autonegotiation complete \r\n");

    XAxiEthernet_PhyRead(xaxiemacp, phy_addr, 0x1f, &partner_capabilities);

    if ( (partner_capabilities & 0x40) == 0x40)/* 1000Mbps */
        return 1000;
    else if ( (partner_capabilities & 0x20) == 0x20)/* 100Mbps */
        return 100;
    else if ( (partner_capabilities & 0x10) == 0x10)/* 10Mbps */
        return 10;
    else
        return 0;
}

修改 get_IEEE_phy_speed 函数,添加对 ksz9031 的支持:

unsigned get_IEEE_phy_speed(XAxiEthernet *xaxiemacp)
{
    u16 phy_identifier;
    u16 phy_model;
    u8 phytype;

#ifdef XPAR_AXIETHERNET_0_BASEADDR
    u32 phy_addr = detect_phy(xaxiemacp);

    /* Get the PHY Identifier and Model number */
    XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_1_REG, &phy_identifier);
    XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_2_REG, &phy_model);

/* Depending upon what manufacturer PHY is connected, a different mask is
 * needed to determine the specific model number of the PHY. */
    if (phy_identifier == MARVEL_PHY_IDENTIFIER) {
        phy_model = phy_model & MARVEL_PHY_MODEL_NUM_MASK;

        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);
        }
    } else if (phy_identifier == TI_PHY_IDENTIFIER) {
        phy_model = phy_model & TI_PHY_DP83867_MODEL;
        phytype = XAxiEthernet_GetPhysicalInterface(xaxiemacp);

        if (phy_model == TI_PHY_DP83867_MODEL && phytype == XAE_PHY_TYPE_SGMII) {
            return get_phy_speed_TI_DP83867_SGMII(xaxiemacp, phy_addr);
        }

        if (phy_model == TI_PHY_DP83867_MODEL) {
            return get_phy_speed_TI_DP83867(xaxiemacp, phy_addr);
        }
    }
    else if(phy_identifier == MICREL_PHY_IDENTIFIER)
    {
        xil_printf("Phy %d is KSZ9031\n\r", phy_addr);
        return get_phy_speed_ksz9031(xaxiemacp, phy_addr);
    }
    else {
        LWIP_DEBUGF(NETIF_DEBUG, ("XAxiEthernet get_IEEE_phy_speed: Detected PHY with unknown identifier/model.\r\n"));
    }
#endif
#ifdef PCM_PMA_CORE_PRESENT
    return get_phy_negotiated_speed(xaxiemacp, phy_addr);
#endif
}


2. 修改 lwip141_v2_0\src\contrib\ports\xilinx\netif\xemacpsif_physpeed.c 文件

添加宏定义:

#define MICREL_PHY_IDENTIFIER       0x22
#define MICREL_PHY_KSZ9031_MODEL      0x220

添加 phy 芯片 ksz9031 速度获取函数:

static u32_t get_phy_speed_ksz9031(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;
    u32_t phyregtemp;

    xil_printf("Start PHY autonegotiation \r\n");

    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;
        }
        XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);
    }
    xil_printf("autonegotiation complete \r\n");

    XEmacPs_PhyRead(xemacpsp, phy_addr,0x1f,
                    &status_speed);

    if ( (status_speed & 0x40) == 0x40)/* 1000Mbps */
        return 1000;
    else if ( (status_speed & 0x20) == 0x20)/* 100Mbps */
        return 100;
    else if ( (status_speed & 0x10) == 0x10)/* 10Mbps */
        return 10;
    else
        return 0;
    return XST_SUCCESS;
}

修改 get_IEEE_phy_speed 函数,添加对 ksz9031 的支持:

static u32_t get_IEEE_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr)
{
    u16_t phy_identity;
    u32_t RetStatus;

    XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_IDENTIFIER_1_REG,
                    &phy_identity);
    if(phy_identity == MICREL_PHY_IDENTIFIER)
    {
        RetStatus = get_phy_speed_ksz9031(xemacpsp, phy_addr);
    }
    else if (phy_identity == PHY_TI_IDENTIFIER) {
        RetStatus = get_TI_phy_speed(xemacpsp, phy_addr);
    } else {
        RetStatus = get_Marvell_phy_speed(xemacpsp, phy_addr);
    }

    return RetStatus;
}
修改 lwip141_v2_0\data\lwip141.mld 文件

添加如下字段:

    BEGIN CATEGORY emio_options
    PARAM name = emio_options, desc = "Settings for ETH using EMIO in PL";
    PARAM name = use_gmii2rgmii_core_on_eth0, desc = "Settings for ETH0 using GMII to RGMII ip core in PL", type = bool, default = false;
    PARAM name = use_gmii2rgmii_core_on_eth1, desc = "Settings for ETH1 using GMII to RGMII ip core in PL", type = bool, default = false;
    PARAM name = gmii2rgmii_core_address_on_eth0, desc = "Settings for ETH0's PHY address of GMII to RGMII ip core in PL", type = int, default = 0;
    PARAM name = gmii2rgmii_core_address_on_eth1, desc = "Settings for ETH1's PHY address of GMII to RGMII ip core in PL", type = int, default = 0;
    END CATEGORY

SDK Bsp‘s Settins 效果如图所示:

3. 修改 lwip141_v2_0\data\lwip141.tcl 文件

proc generate_lwip_opts {libhandle} 所在的大括号内添加如下字段:

    # EMIO options
    set use_gmii2rgmii_core_on_eth0 [common::get_property CONFIG.use_gmii2rgmii_core_on_eth0 $libhandle]
    set use_gmii2rgmii_core_on_eth1 [common::get_property CONFIG.use_gmii2rgmii_core_on_eth1 $libhandle]
    set gmii2rgmii_core_address_on_eth0 [common::get_property CONFIG.gmii2rgmii_core_address_on_eth0 $libhandle]
    set gmii2rgmii_core_address_on_eth1 [common::get_property CONFIG.gmii2rgmii_core_address_on_eth1 $libhandle]
    
    if { $use_gmii2rgmii_core_on_eth0 == true } {
       puts $lwipopts_fd "\#define XPAR_GMII2RGMIICON_0N_ETH1_ADDR $gmii2rgmii_core_address_on_eth0"
    }
    
    if { $use_gmii2rgmii_core_on_eth1 == true } {
       puts $lwipopts_fd "\#define XPAR_GMII2RGMIICON_0N_ETH1_ADDR $gmii2rgmii_core_address_on_eth1"
    }

板载验证

网卡启动时 log 信息输出:

GitHub 源链接 可直接替换原库文件。
相关文章
|
15天前
|
存储 安全 开发工具
【ZYNQ】基于 BRAM 的 PS 与 PL 数据交互
【ZYNQ】基于 BRAM 的 PS 与 PL 数据交互
|
15天前
|
网络协议 开发工具 Perl
【ZYNQ】裸机 PS + PL 双网口实现之 SDK 程序设计
【ZYNQ】裸机 PS + PL 双网口实现之 SDK 程序设计
|
15天前
|
开发工具 芯片 Perl
【ZYNQ】裸机 PS + PL 双网口实现之 ZYNQ 配置
【ZYNQ】裸机 PS + PL 双网口实现之 ZYNQ 配置
|
15天前
|
Linux
百度搜索:蓝易云【Linux系统ps命令:查看正在运行的进程】
通过这些简洁的ps命令用法,你可以方便地查看Linux系统中正在运行的进程信息。
40 1
|
15天前
|
存储 监控 Linux
【Shell 命令集合 系统管理 】⭐⭐⭐Linux 查看当前正在运行的进程信息 ps命令 使用指南
【Shell 命令集合 系统管理 】⭐⭐⭐Linux 查看当前正在运行的进程信息 ps命令 使用指南
49 0
|
15天前
|
安全 Linux 应用服务中间件
linux(三十一)系统信息命令ps查看系统进程
linux(三十一)系统信息命令ps查看系统进程
162 1
|
9月前
|
iOS开发
iOS 逆向编程(十三)PS命令获取进程PID与名称(Process Status)
iOS 逆向编程(十三)PS命令获取进程PID与名称(Process Status)
143 0
|
7月前
|
Linux Apache
百度搜索:蓝易云 ,Linux ps命令详解,Linux查看进程。
通过使用ps命令,您可以查看当前运行的进程,并获取有关进程的详细信息。根据您的需求,可以使用不同的选项来满足特定的进程查看和筛选要求。
765 0
|
15天前
|
Linux
Linux系统ps命令
这些是一些常见的 `ps`命令选项和用法,用于查看系统中运行的进程及其相关信息。您可以根据需要选择合适的选项以满足您的任务要求。
74 0
|
15天前
|
运维 监控 Linux
【专栏】Docker命令`docker ps`的使用,包括列出运行中的容器、筛选特定容器、组合使用与其他命令配合以及在故障排查中的应用
【4月更文挑战第28天】本文介绍了Docker命令`docker ps`的使用,包括列出运行中的容器、筛选特定容器、组合使用与其他命令配合以及在故障排查中的应用。通过基础和高级用法示例,如列出所有容器、搜索特定镜像、监控资源使用等,帮助读者理解和提升容器管理效率。对于Linux运维工程师,掌握`docker ps`是必备技能。