当前位置: 首页 > news >正文

以太网基础⑥ ZYNQ PS端 基于LWIP的TCP例程测试

1.今日摸鱼任务

实现:基于 LWIP 模板的 TCP 回环测试

以官方模板中的 LwIP Echo Server 为例,学习使用 LwIP 模板。
小梅哥链接:【BX71】基于LWIP模板的TCP回环测试 - ACZ702开发板 - 芯路恒电子技术论坛 - Powered by Discuz!

2. LWIP简介

LwIP 是一个小型开源的TCP/IP协议栈。保持 TCP 协议主要功能的基础上减少RAM的占用。
 Vivado2018.3 中提供 LwIPv2.0.2 版本的 SDK 库,为 Ethernetliteaxi_Ethernetlite)、TEMACAxii_ethernet)以及千兆以太网控制器 MACGigE)核提供适配。能够在 MicroBlazeARM Cortex-A9、 ARM Cortex-A53ARM Cortex-R5 处理器上运行。
Ethernetlite TEMAC 核适用于 MicroBlaze 系统。
千兆以太网控制器MACGigE)核仅适用于 ARM Cortex-A9Zynq-7000 处理器设备)、ARM Cortex-A53 ARM Cortex-R5Zynq UltraScale + MPSoC)系统。
根据是否基于操作系统LwIP 提供了两套 API(术语为 A05PI),分别如下:
Raw API:事件驱动的 API,在没有操作系统的情况下运行 lwIP 时惟一可用的 API
Socket APIbsd 风格的套接字 API。线程安全,只能从非 tcpip 线程调用。
对于嵌入式而言,通常并不会使用到操作系统,因此大都是使用的 Raw API
LwIPv2.0.2:
① 支持多网络接口下的 IP 转发;
② 支持 ICMP 协议;
③ 支持 (Dynamic Host Configuration Protocol,DHCP )协议,动态分配 ip 地址
④ 支持 ARP 协议(以太网地址解析协议);
⑤ 支持 IGMP 协议(互联网组管理协议),可以实现多播数据的接收;
⑥ 支持 UDP 协议(用户数据报协议)
支持 TCP 协议(传输控制协议),包括阻塞控制、 RTT 估算、快速恢复和快速转发。
SDK 中官方模板默认使用的是 Realtek RTL8211E 芯片,开发板上是RTL8211F-CG
要注意 PHYSR 寄存器

RTL8211PHYSR 寄存器

RTL8211E-VB-CG -PDF数据手册-参考资料-立创商城

RTL8211F-CG  PHYSR 寄存器

RTL8211F-CG -PDF数据手册-参考资料-立创商城

        这两个寄存器无论是偏移地址还是位分布都有很大的差异。
        导致 PHY 芯片在查询 PHYSR 实时链路读取回错误值,导致自动协商一直处于失败状态。
        除此之外,由于Link Speed位分布的不同,无法获取到正确的速度配置。
        因此,在使用官方模板时,我们就需要修改这些相关位。

3. 硬件逻辑设计

3.1 IP核添加与配置

使用PL侧的以太网,在UG585CH16中,通过 EMIO 路由到 PL 时,其接口类型为 GMII

所以,除了 ZYNQ 核之外,设计还需要添加一个 GMII 转 RGMII 核,用来实现接口类型的转换。
IP 手册 pg160pg160-gmii-to-rgmii最新说明书手册.pdf-原创力文档

对于 ZYNQ- 7000 系列器件,在使用 GMII to RGMII 核时,需要为其提供 200MHz 输入时钟
设计还需要添加一个 Constant 核,用来输出常量,控制以太网复位信号。

3.1.1 ZYNQ核

3.1.2 GMII to RGMII

PHY Address:该项不是以太网 PHY 地址,而是用于标识 MDIO 事务中core 的虚拟地址

                        因此该项只需设置一个与板载 PHY 不同的地址即可。

Provide 2 ns Skew on RGMII TXC:选择为 RGMII TXC 添加 2ns 的偏斜;

                                                          可以选择由外部 PHY 添加,或由 IP 核通过 MMCM 添加。

Shared Logic:选择是否需要共享时钟资源。
Include Shared Logic in the Core: 只有一个 core,或者存在多个 core 但需要一个 core 共享时钟资源,以驱动其余核时,需要勾选

Include Shared Logic in the Example Design: 存在多个core,且已经有一个 core共享了时钟资源用来驱动其他核时,才需要勾选

3.1.3 Constant 核

用于控制以太网复位信号,只需给一个常量 1,确保以太网不会被复位即可

3.2 端口连接

3.3 .XDC

//这里使用的是ACZ702系列的PL端

set_property IOSTANDARD LVCMOS33 [get_ports MDIO_PHY_0_mdc]
set_property IOSTANDARD LVCMOS33 [get_ports MDIO_PHY_0_mdio_io]
set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_rd[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_rd[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_rd[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_rd[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_td[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_td[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_td[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_td[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports RGMII_0_rx_ctl]
set_property IOSTANDARD LVCMOS33 [get_ports RGMII_0_rxc]
set_property IOSTANDARD LVCMOS33 [get_ports RGMII_0_tx_ctl]
set_property IOSTANDARD LVCMOS33 [get_ports RGMII_0_txc]
set_property IOSTANDARD LVCMOS33 [get_ports {dout_0[0]}]
set_property PACKAGE_PIN P14 [get_ports {RGMII_0_rd[0]}]
set_property PACKAGE_PIN V15 [get_ports {RGMII_0_rd[1]}]
set_property PACKAGE_PIN Y16 [get_ports {RGMII_0_rd[2]}]
set_property PACKAGE_PIN P15 [get_ports {RGMII_0_rd[3]}]
set_property PACKAGE_PIN N18 [get_ports RGMII_0_rxc]
set_property PACKAGE_PIN Y14 [get_ports RGMII_0_rx_ctl]
set_property PACKAGE_PIN R18 [get_ports {RGMII_0_td[0]}]
set_property PACKAGE_PIN T19 [get_ports {RGMII_0_td[1]}]
set_property PACKAGE_PIN T20 [get_ports {RGMII_0_td[2]}]
set_property PACKAGE_PIN U20 [get_ports {RGMII_0_td[3]}]
set_property PACKAGE_PIN P16 [get_ports RGMII_0_txc]
set_property PACKAGE_PIN T17 [get_ports RGMII_0_tx_ctl]
set_property PACKAGE_PIN C20 [get_ports MDIO_PHY_0_mdc]
set_property PACKAGE_PIN B20 [get_ports MDIO_PHY_0_mdio_io]
set_property PACKAGE_PIN B19 [get_ports {dout_0[0]}]

.bit .hdf

4. 软件程序设计

SDK,选择模板:

该模板工程会将开发板的 IP 设置为 192.168.1.10,并在端口 7 监听数据,对接收到的数据进行回发。

修改以下参数:

xemacpsif_physpeed.c

#include "netif/xemacpsif.h"
#include "lwipopts.h"
#include "xparameters_ps.h"
#include "xparameters.h"
#include "xemac_ieee_reg.h"

#if defined (__aarch64__)
#include "bspconfig.h"
#include "xil_smc.h"
#endif
//此处为了兼容 RTL8211FDI 重定义寄存器地址和掩码
#undef IEEE_SPECIFIC_STATUS_REG
#undef IEEE_SPEED_MASK
#undef IEEE_SPEED_1000
#undef IEEE_SPEED_100
#define IEEE_SPECIFIC_STATUS_REG 0X1A
#define IEEE_SPEED_MASK 0x30
#define IEEE_SPEED_1000 0x20
#define IEEE_SPEED_100 0x10


********************************************************************

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");
    //开灯
        #if 1
        XEmacPs_PhyWrite(xemacpsp, phy_addr, 0x1F, 0x0D08);
        XEmacPs_PhyWrite(xemacpsp, phy_addr, 0x11, 0x0009);
        XEmacPs_PhyWrite(xemacpsp, phy_addr, 0x1F, 0x0000);
        #endif
        #if 1
        //参考 uboot 启动
        XEmacPs_PhyWrite(xemacpsp, phy_addr, 0x1F, 0x0D04);
        XEmacPs_PhyWrite(xemacpsp, phy_addr, 0x10, 0x617F);
        XEmacPs_PhyWrite(xemacpsp, phy_addr, 0x1F, 0x0000);
        #endif

    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 & 0x4) {
        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;
}

main.c

int main()
{
#if LWIP_IPV6==0
    ip_addr_t ipaddr, netmask, gw;

#endif
    /* the mac address of the board. this should be unique per board */
    unsigned char mac_ethernet_address[] =
    { 0x00, 0x0a, 0x35, 0x01, 0xfe, 0xc0 }; 

    echo_netif = &server_netif;
#if defined (__arm__) && !defined (ARMR5)
#if XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT == 1 || XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT == 1
    ProgramSi5324();
    ProgramSfpPhy();
#endif
#endif

/* Define this board specific macro in order perform PHY reset on ZCU102 */
#ifdef XPS_BOARD_ZCU102
    if(IicPhyReset()) {
        xil_printf("Error performing PHY reset \n\r");
        return -1;
    }
#endif

    init_platform();

#if LWIP_IPV6==0
#if LWIP_DHCP==1
    ipaddr.addr = 0;
    gw.addr = 0;
    netmask.addr = 0;
#else
    /* initliaze IP addresses to be used */
    IP4_ADDR(&ipaddr,  192, 168,   0, 2);
    IP4_ADDR(&netmask, 255, 255, 255,  0);
    IP4_ADDR(&gw,      192, 168,   0,  1);
#endif
#endif
    print_app_header();

    lwip_init();

#if (LWIP_IPV6 == 0)
    /* Add network interface to the netif_list, and set it as default */
    if (!xemac_add(echo_netif, &ipaddr, &netmask,
                        &gw, mac_ethernet_address,
                        PLATFORM_EMAC_BASEADDR)) {
        xil_printf("Error adding N/W interface\n\r");
        return -1;
    }
#else
    /* Add network interface to the netif_list, and set it as default */
    if (!xemac_add(echo_netif, NULL, NULL, NULL, mac_ethernet_address,
                        PLATFORM_EMAC_BASEADDR)) {
        xil_printf("Error adding N/W interface\n\r");
        return -1;
    }
    echo_netif->ip6_autoconfig_enabled = 1;

    netif_create_ip6_linklocal_address(echo_netif, 1);
    netif_ip6_addr_set_state(echo_netif, 0, IP6_ADDR_VALID);

    print_ip6("\n\rBoard IPv6 address ", &echo_netif->ip6_addr[0].u_addr.ip6);

#endif
    netif_set_default(echo_netif);

    /* now enable interrupts */
    platform_enable_interrupts();

    /* specify that the network if is up */
    netif_set_up(echo_netif);

#if (LWIP_IPV6 == 0)
#if (LWIP_DHCP==1)
    /* Create a new DHCP client for this interface.
     * Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at
     * the predefined regular intervals after starting the client.
     */
    dhcp_start(echo_netif);
    dhcp_timoutcntr = 24;

    while(((echo_netif->ip_addr.addr) == 0) && (dhcp_timoutcntr > 0))
        xemacif_input(echo_netif);

    if (dhcp_timoutcntr <= 0) {
        if ((echo_netif->ip_addr.addr) == 0) {
            xil_printf("DHCP Timeout\r\n");
            xil_printf("Configuring default IP of 192.168.0.2\r\n");
            IP4_ADDR(&(echo_netif->ip_addr),  192, 168,   0, 2);
            IP4_ADDR(&(echo_netif->netmask), 255, 255, 255,  0);
            IP4_ADDR(&(echo_netif->gw),      192, 168,   0,  1);
        }
    }

    ipaddr.addr = echo_netif->ip_addr.addr;
    gw.addr = echo_netif->gw.addr;
    netmask.addr = echo_netif->netmask.addr;
#endif

    print_ip_settings(&ipaddr, &netmask, &gw);

#endif
    /* start the application (web server, rxtest, txtest, etc..) */
    start_application();

    /* receive and process packets */
    while (1) {
        if (TcpFastTmrFlag) {
            tcp_fasttmr();
            TcpFastTmrFlag = 0;
        }
        if (TcpSlowTmrFlag) {
            tcp_slowtmr();
            TcpSlowTmrFlag = 0;
        }
        xemacif_input(echo_netif);
        transfer_data();
    }

    /* never reached */
    cleanup_platform();

    return 0;
}

echo.c

void print_app_header()
{
#if (LWIP_IPV6==0)
    xil_printf("\n\r\n\r-----lwIP TCP echo server ------\n\r");
#else
    xil_printf("\n\r\n\r-----lwIPv6 TCP echo server ------\n\r");
#endif
    xil_printf("TCP packets sent to port 5000 will be echoed back\n\r");
}

int start_application()
{
    struct tcp_pcb *pcb;
    err_t err;
    unsigned port = 5000;

    /* create new TCP PCB structure */
    pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
    if (!pcb) {
        xil_printf("Error creating PCB. Out of Memory\n\r");
        return -1;
    }

    /* bind to specified @port */
    err = tcp_bind(pcb, IP_ANY_TYPE, port);
    if (err != ERR_OK) {
        xil_printf("Unable to bind to port %d: err = %d\n\r", port, err);
        return -2;
    }

    /* we do not need any arguments to callback functions */
    tcp_arg(pcb, NULL);

    /* listen for connections */
    pcb = tcp_listen(pcb);
    if (!pcb) {
        xil_printf("Out of memory while tcp_listen\n\r");
        return -3;
    }

    /* specify callback to use for incoming connections */
    tcp_accept(pcb, accept_callback);

    xil_printf("TCP echo server started @ port %d\n\r", port);

    return 0;
}

开发板的 MAC 地址00_0a_35_01_fe_c0IP 地址 192.168.0.2,监听端口号 5000

5. 板级验证

//具体的验证步骤就不重复啦,链接的PDF比较详细

//主要是参数修改要看清楚位置,橙色是文件,红色要修改或者复制

http://www.dtcms.com/a/291701.html

相关文章:

  • uniapp “requestPayment:fail [payment支付宝:62009]未知错误“
  • 渗透第2次作业
  • 从零开始:Vue 3 + TypeScript 项目创建全记录
  • C++刷题常用方法
  • uniapp请求封装上传
  • DeepSPV:一种从2D超声图像中估算3D脾脏体积的深度学习流程|文献速递-医学影像算法文献分享
  • 从0到1:盲盒抽卡小程序开发全流程解析
  • 浙江大学PTA程序设计C语言基础编程练习题1-5
  • 【Python办公】Excel工作表拆分工具(按照sheet进行拆分-calamine-极速版)
  • Linux系统安装Bash自动补全(bash-completion)
  • 【React-Three-Fiber实践】放弃Shader!用顶点颜色实现高性能3D可视化
  • Python关于pandas的基础知识
  • 使用Minio后处理图片回显问题
  • Linux部署.net Core 环境
  • Claude 4 系列模型深度解析:引领 AI 编程与智能体应用新纪元
  • UE5 UI 控件切换器
  • Web3介绍(Web 3.0)(一种基于区块链技术的去中心化互联网范式,旨在通过技术手段实现用户对数据的自主权、隐私保护和价值共享)
  • 【Qt开发】Qt的背景介绍(四)
  • MatterPort3D 数据集 | 简介 | 多途径下载
  • Aspose.Cells 应用案例:法国能源企业实现能源数据报告Excel自动化
  • UE创作一个可以变更列数的万能表格
  • Excel file format cannot be determined, you must specify an engine manually.
  • 如何撤销Git提交误操作
  • 实用资源分享:50款出入库单据Excel模板集合
  • DFS习题篇【下】
  • 北京养老金计算公式网页实现案例:从需求分析到架构设计
  • 业务流逻辑如何搭建?为何橙武平台选用了 LogicFlow?
  • 【MyBatisPlus】一文讲清 MyBatisPlus 基本原理及基本使用方式
  • EMA《2025-2028年药品监管中的数据与AI 1.3版》信息分析
  • 深度分析:Kimi K2开源模型