【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

第三十一章基于lwip的echo server实验

随着物联网的兴起,万物互联需要一个强大而又灵活的协议体系,TCP/IP协议得天独厚,而在嵌入式网络设备中,由于硬件资源的限制,需要特殊的实现方式。LWIP作为TCP/IP协议的一种轻量级实现方式,满足了这一要求。本章我们利用VITIS软件自带的lwIP Echo Server例程模板,初步了解lwip的使用。本章包括以下几个部分:
3131.1简介
31.2实验任务
31.3硬件设计
31.4软件设计
31.5下载验证

31.1简介

1)TCP/IP协议简介
TCP/IP协议中文名为传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议、Internet国际互联网络的基础,由网络层的IP协议和传输层的TCP协议组成。TCP/IP定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求。通俗而言:TCP负责发现传输的问题,一有问题就发出信号,要求重新传输,直到所有数据安全正确地传输到目的地。而IP是给因特网的每一台联网设备规定一个地址。
TCP/IP协议不是TCP和IP这两个协议的合称,而是指因特网整个TCP/IP协议族。从协议分层模型方面来讲,TCP/IP由四个层次组成:网络接口层、网络层、传输层、应用层。OSI(Open System Interconnection)是开放式系统互连参考模型,该模型将TCP/IP分为七层:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。TCP/IP模型与OSI模型对比如表32.1.1所示。
表32.1.1 OSI模型与TCP/IP模块
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第1张图片

2)LWIP简介
LWIP是瑞典计算机科学院(SICS)的Adam Dunkels等开发的一个小型开源的TCP/IP协议栈,是TCP/IP的一种实现方式。LWIP是轻量级IP协议,有无操作系统的支持都可以运行。LWIP实现的重点是在保持TCP协议主要功能的基础上减少对 RAM的占用,它只需十几KB的RAM和40K左右的ROM就可以运行,这使LWIP协议栈适合在低端的嵌入式系统中使用。关于LWIP的详细信息大家可以去http://savannah.nongnu.org/projects/lwip/这个网站去查阅。
LWIP 的主要特性如下:
•IGMP 协议,用于网络组管理,可以实现多播数据的接收
•Internet协议(IP),包括 IPv4 和 IPv6,支持 IP 分片与重装,包括通过多个网络接口的数据包转发
•用于网络维护和调试的Internet控制消息协议(ICMP)
•用户数据报协议(UDP)
•传输控制协议(TCP)拥塞控制,往返时间(RTT)估计,快速恢复和重传
•DNS,域名解析
•SNMP,简单网络管理协议
•动态主机配置协议(DHCP)
•以太网地址解析协议(ARP)
•AUTOIP,IP 地址自动配置
•PPP,点对点协议,支持 PPPoE
我们本次使用的lwip212_v1_1是一个基于开源lwIP库版本2.1.2构建的库(Vivado 2019.2版本)。lwip212_v1_1库为Ethernetlite(axi_ethernetlite)、TEMAC(axi_ethernet)以及千兆以太网控制器和MAC(GigE)内核提供适配器。该库可以在MicroBlaze、ARM Cortex-A9、ARM Cortex-A53和ARM Cortex-R5处理器上运行。Ethernetlite和TEMAC核心适用于MicroBlaze系统。千兆以太网控制器和MAC(GigE)内核仅适用于ARM Cortex-A9(MPSOC-7000处理器设备)、ARM Cortex-A53和ARM Cortex-R5系统(MPSOC UltraScale+ MPSoC)。
lwip212_v1_1提供二种用户编程接口方式:raw API和socket API。
Raw API:是为高性能和低内存开销而定制的。这种类型的API把网络协议栈和应用程序放在一个进程里,连接网络协议和应用程序的纽带是回调函数,回调函数实际上是一个普通的C函数。为了接收数据,应用程序会首先向协议栈注册一个回调函数,当关联的连接有一个信息到达时,该回调函数就被协议栈调用。这种实现方式即有优点也有缺点。优点是数据的接收和发送不会导致进程的切换,提供了最好的性能,执行速度快,而且消耗的内存资源少;缺点是应用程序无法进行连续运算,因为网络协议的处理和运算是在同一进程中完成的,二者无法并行发生。Raw API是资源较少的嵌入式系统的首选方法,也是在没有操作系统的情况下运行lwIP时唯一可用的API。
Socket API:提供了一个基于open-read-write-close 模块的BSD socket-style接口,需要操作系统。此接口在性能和内存要求方面不如Raw API高效,不适用于小型嵌入式系统,但移植性更好。
本章我们使用无需操作系统(standalone)的RAW API编程接口。
3)PS的千兆以太网控制器
在介绍PS的千兆以太网控制器之前,我们首先了解下MAC与PHY芯片及GMII与RGMII接口。
以太网卡工作在OSI模型的最后两层,物理层和数据链路层,物理层定义了数据传送与接收所需要的电与光信号、线路状态、时钟基准、数据编码和电路等,并向数据链路层设备提供标准接口。物理层的芯片称之为 PHY。此外PHY还提供了和对端设备连接的重要功能并通过LED灯显示出当前的连接的状态和工作状态。当我们给网卡接入网线的时候,PHY不断发出的脉冲信号检测到对端有设备,它们通过一套标准的语言交流,互相协商并确定连接速度、工作模式、是否采用流控等。通常情况下,协商的结果是两个设备中能同时支持的最大速度和最好的双工模式。这个技术被称为AutoNegotiation,即自协商。
数据链路层则提供寻址机构、数据帧的构建、数据差错检查、传送控制、向网络层提供标准的数据接口等功能。以太网卡中数据链路层的芯片称之为MAC控制器。
MAC控制器与PHY通过MII(Medium Independent Interface)接口进行连接。MII接口有很多类型,千兆以太网多使用GMII(Gigabit Medium Independent Interface)或RGMII(Reduced Gigabit Media Independent Interface)接口进行连接。
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第2张图片

图 32.1.1 GMII接口
GMII接口提供了8位数据通道,125MHz的时钟速率,从而具有1000Mbps的数据传输速率。除MDC和MDIO外,有24根接口信号线,如图 32.1.1所示。
GMII接口主要包括四个部分。一是从MAC层到物理层(PHY)的发送数据接口,二是从物理层到MAC层的接收数据接口,三是从物理层到MAC层的状态指示信号,四是MAC 层和物理层之间传送控制和状态信息的MDIO接口。各部分接口信号说明见下表:
表32.1.2 GMII接口信号
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第3张图片

RGMII接口即Reduced GMII,是GMII接口的简化版本。RGMII采用4位数据接口,工作时钟125MHz,并且在上升沿和下降沿同时传输数据,因此传输速率可达1000Mbps。采用 RGMII的目的是降低电路成本,使实现这种接口的器件的引脚数从24个减少到14个(不包括MDC和MDIO),接口信号如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第4张图片

图 32.1.2 RGMII接口
可以看到RGMII接口相对于GMII 接口,在TXD和RXD上总共减少8根数据线。TX_CTL信号线上传送TX_EN和TX_ER两种信息,在TX_CLK的上升沿发送TX_EN,下降沿发送TX_ER;同样的,RX_CTL信号线上传送RX_DV和RX_ER两种信息,在RX_CLK的上升沿发送RX_DV,下降沿发送RX_ER。进一步减少了2根数据线。其他信号同GMII接口。
现在我们来看下PS的千兆以太网控制器(GEM)。PS的千兆以太网控制器实现了与IEEE 802.3-2008标准兼容的10/100/1000 Mb/s以太网MAC,在10/100Mb/s速度下,能够在半双工或全双工模式下运行,在1000Mb/s速度下全双工运行。PS配备四个千兆以太网控制器。每个控制器都可以独立配置,其内部原理图如下:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第5张图片

图 32.1.3 以太网控制器
DMA控制器通过AXI接口连接到存储器。MAC控制器与FIFO接口的连接为嵌入式处理系统中的分组数据存储提供scatter-gather类型的功能。另外从图 32.1.3中可以看到,如果通过MIO连接至PS端的以太网PHY芯片,则每个控制器使用RGMII接口以节省引脚。如果通过EMIO连接至PL端的以太网PHY芯片,则每个控制器使用GMII接口。
可以通过APB总线访问千兆以太网控制器的寄存器。寄存器用于配置MAC的功能、选择不同的操作模式、以及启用和监控网络管理统计信息。每个控制器为管理PHY芯片提供MDIO接口。

31.2实验任务

本章的实验任务是建立PS的以太网的硬件环境,使用VITIS软件自带的Lwip Echo Server模板了解LWIP的使用。

31.3硬件设计

MPSOC开发板上有一个PS端RJ45以太网接口,用于连接以太网线,其原理图如图 32.3.1所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第6张图片

图 32.3.1 RJ45接口原理图
以太网的数据传输离不开以太网PHY(物理层)芯片的支持。我们的MPSOC开发板上使用的PHY芯片为裕太车通公司的YT8521S,其部分原理图如图 32.3.2所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第7张图片

图 32.3.2 YT8521S原理图
YT8521S是高度集成的以太网收发器,符合10Base-T、100Base-TX和1000Base-T IEEE 802.3标准,提供了所有必要的物理层功能。YT8521S采用最先进的 DSP 技术和模拟前端(AFE),实现了交叉检测和自动校正、极性校正、自适应均衡、串音消除、回声消除、定时恢复和纠错等功能,以提供10Mbps、100Mbps或1000Mbps的强大传输和接收能力。
在图 32.3.2中,YT8521S右侧引脚连接到开发板的RJ45接口,左侧引脚通过RGMII接口与PS端相连接。MDC/MDIO接口用来配置YT8521S。其复位信号与PS的PS_POR_B相连接,在PS的上电复位时同时复位YT8521S,不需要通过MIO来控制。YT8521S与PS的MIO引脚的连接如下图所示,从中可以看到,YT8521S的RGMII接口通过MIO64-75引脚与PS相连接,MDC/MDIO连接到PS的MIO76-77引脚。
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第8张图片

图 32.3.3 PHY芯片同PS接口连接
根据实验任务我们可以画出本次实验的系统框图,如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第9张图片

图 32.3.4 系统框图
在图 32.3.4中,UART用于打印程序相关的信息,千兆以太网控制器GEM通过MIO与外部以太网进行连接。
step1:创建Vivado工程
本次实验的硬件设计可以在《第一章Hello World》实验的基础上进行。
1-1 我们先打开《第一章Hello World》实验的Vivado工程,打开后将工程另存为 “lwip_echo_server”工程,如下图所示,然后点击“OK”按钮。
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第10张图片

图 32.3.5 另存为工程为lwip_echo
step2:使用IP Integrator创建Processing System
2-1 在Flow Navigator中,点击IP INTEGRATOR下的Open Block Design,如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第11张图片

图 32.3.6 打开Block Design
2-2 在打开的下图Diagram窗口,双击打开Zynq UltraScale+ MPSOC重定义窗口。
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第12张图片

图 32.3.7 重定义Zynq UltraScale+ MPSOC
2-3 在打开的重定义窗口中,点击左侧的I/O Configuration,在右侧的界面中依次展开High Speed->GEM,勾选“GEM3”并选择“MIO64…75”,同时勾选“MDIO”并选择“MIO76…77”。这里选择“MIO64至MIO77”是为了和开发板硬件对应,如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第13张图片

图 32.3.8 PS以太网接口配置界面
2-4 同样是在刚才界面中,依次展开Low Speed->TTC,勾选TTC0至TTC3,完成后,点击右下角的“OK”,如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第14张图片

图 32.3.9配置三路定时器
2-5 由于不需要添加其它IP,点击Validate Design验证无误后,按Ctrl+S快捷键保存Diagram。此时我们的第二步完成,进入第三步。
step3:生成顶层HDL
在sources面板中,右键点击Block Design设计文件“design_1.bd”,然后执行“Generate Output Products”。因为我们在创建Hello World实验时创建顶层HDL Wrapper使用的是Let Vivado manage wrapper and auto-update选项,所以此处无需再创建顶层HDL Wrapper,Vivado会自动更新顶层HDL Wrapper
step4:生成Bitstream文件并导出到VITIS
由于本实验未用到PL部分,所以无需生成Bitstream文件,只需导出到VITIS即可。如果使用到PL,则需要添加引脚约束以及对该系统进行综合、实现并生成Bitstream文件。
4-1 导出硬件。
在菜单栏中选择 File > Export > Export hardware,并在弹出的对话框中,取消勾选“Include bitstream”,直接点击“OK”按钮。将导出的design_1_wrapper.xsa文件放到vitis文件夹中。
4-2 硬件导出完成后,选择菜单Tools->Launch Vitis,启动VITIS开发环境。
31.4软件设计
step5:在VITIS中创建应用工程
5-1 在菜单栏中选择File->New->Application Project, 新建一个VITIS应用工程。
5-2 在弹出的下图所示界面中,输入工程名“lwip_echo_server”。点击“Next”,在接下来的平台(platform)界面中,点击“Create a new platform from hardware(XSA)”标签页,添加硬件平台文件。
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第15张图片

图 32.4.1 创建工程
5-3 继续点击“Next”,在接下来的模板界面中(templates),选择“lwIP Echo Server”工程模版,然后点击“Finish”按钮,完成工程创建,如图 32.4.2所示。
lwIP Echo Server应用程序提供了如何使用轻量级IP堆栈(lwIP)的简单演示。此应用程序将MPSOC开发板MAC地址设置为00:0a:35:00:01:02,默认使用DHCP获取动态IP地址,如果DHCP失败,则使用默认设置的静态IPv4地址192.168.1.10或IPv6地址FE80:0:0:0:20​​A:35FF:FE00:102。服务器在端口7处侦听输入,并简单地回传发送到该端口的任何数据。
这里简单的介绍下DHCP。DHCP(Dynamic Host Configuration Protocol)即动态主机配置协议,通常应用在大型的局域网络环境中,主要作用是集中的管理、分配IP地址,使网络环境中的主机动态的获得IP地址、Gateway地址、DNS服务器地址等信息,并能够提升地址的使用率。
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第16张图片

图 32.4.2 选择“lwIP Echo Server”模版
5-4 展开lwip_echo_server应用工程目录下的src目录,可以看到很多源文件,如图 32.4.4所示,其中大多是平台相关的文件(platform开头的文件)。下面我们简单的介绍下各文件的作用。
echo.c:Echo服务的的主要实现代码。
i2c_access.c:IIC访问PHY的功能实现,本实验不用。
iic_phyreset.c:IIC复位PHY,本实验不用。
main.c:main函数所在文件。
platform_config.h:平台配置相关文件,主要是宏定义所使用的平台。
我们打开“platform_config.h”文件,内容如下:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第17张图片

图 32.4.3 platform_config.h文件内容
可以看到宏定义了 PLATFORM_ZYNQMP,也就是说本实验是与ZYNQMP平台相关的。所以本实验需要的是platform_zynqmp.c文件,而以下的与其它平台相关的platform_mb.c、platform_ppc.c、platform_zynq.c、platform.c都无需使用。
platform_config.h是基于硬件设计生成的。
sfp.c和si5324.c分别用于sfp PHY和si5324芯片,用于官方特定的开发板,与我们使用的MPSOC开发板不相关。
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第18张图片

图 32.4.4 src目录
5-5 为了方便分析,我们将src文件夹与本实验不相关的平台文件删除,删除后的src文件夹内容如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第19张图片

图 32.4.5 删除不相关文件后的src文件夹内容
5-6 现在我们打开main.c文件,为了方便分析源代码,在main.c文件中将带有下图箭头所指的预编译指令删除。
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第20张图片

图 32.4.6 删除不需要的预编译指令
删除不适用的预编译指令后的main.c代码如下:

1   #include <stdio.h>
2   
3   #include "xparameters.h"
4   
5   #include "netif/xadapter.h"
6   
7   #include "platform.h"
8   #include "platform_config.h"
9   #if defined (__arm__) || defined(__aarch64__)
10  #include "xil_printf.h"
11  #endif
12  
13  #include "lwip/tcp.h"
14  #include "xil_cache.h"
15  
16  #if LWIP_IPV6==1
17  #include "lwip/ip.h"
18  #else
19  #if LWIP_DHCP==1
20  #include "lwip/dhcp.h"
21  #endif
22  #endif
23  
24  /* defined by each RAW mode application */
25  void print_app_header();
26  int start_application();
27  int transfer_data();
28  void tcp_fasttmr(void);
29  void tcp_slowtmr(void);
30  
31  /* missing declaration in lwIP */
32  void lwip_init();
33  
34  #if LWIP_IPV6==0
35  #if LWIP_DHCP==1
36  extern volatile int dhcp_timoutcntr;
37  err_t dhcp_start(struct netif *netif);
38  #endif
39  #endif
40  
41  extern volatile int TcpFastTmrFlag;
42  extern volatile int TcpSlowTmrFlag;
43  static struct netif server_netif;
44  struct netif *echo_netif;
45  
46  #if LWIP_IPV6==1
47  void print_ip6(char *msg, ip_addr_t *ip)
48  {
49      print(msg);
50      xil_printf(" %x:%x:%x:%x:%x:%x:%x:%x\n\r",
51              IP6_ADDR_BLOCK1(&ip->u_addr.ip6),
52              IP6_ADDR_BLOCK2(&ip->u_addr.ip6),
53              IP6_ADDR_BLOCK3(&ip->u_addr.ip6),
54              IP6_ADDR_BLOCK4(&ip->u_addr.ip6),
55              IP6_ADDR_BLOCK5(&ip->u_addr.ip6),
56              IP6_ADDR_BLOCK6(&ip->u_addr.ip6),
57              IP6_ADDR_BLOCK7(&ip->u_addr.ip6),
58              IP6_ADDR_BLOCK8(&ip->u_addr.ip6));
59  
60  }
61  #else
62  void
63  print_ip(char *msg, ip_addr_t *ip)
64  {
65      print(msg);
66      xil_printf("%d.%d.%d.%d\n\r", ip4_addr1(ip), ip4_addr2(ip),
67              ip4_addr3(ip), ip4_addr4(ip));
68  }
69  
70  void
71  print_ip_settings(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw)
72  {
73  
74      print_ip("Board IP: ", ip);
75      print_ip("Netmask : ", mask);
76      print_ip("Gateway : ", gw);
77  }
78  #endif
79  
80  int main()
81  {
82  #if LWIP_IPV6==0
83      ip_addr_t ipaddr, netmask, gw;
84  
85  #endif
86      /* the mac address of the board. this should be unique per board */
87      unsigned char mac_ethernet_address[] =
88      { 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };
89  
90      echo_netif = &server_netif;
91  #if defined (__arm__) && !defined (ARMR5)
92  #if XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT == 1 || XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT == 1
93      ProgramSi5324();
94      ProgramSfpPhy();
95  #endif
96  #endif
97  
98  /* Define this board specific macro in order perform PHY reset on ZCU102 */
99  #ifdef XPS_BOARD_ZCU102
100     if(IicPhyReset()) {
101         xil_printf("Error performing PHY reset \n\r");
102         return -1;
103     }
104 #endif
105 
106     init_platform();
107 
108 #if LWIP_IPV6==0
109 #if LWIP_DHCP==1
110     ipaddr.addr = 0;
111     gw.addr = 0;
112     netmask.addr = 0;
113 #else
114     /* initialize IP addresses to be used */
115     IP4_ADDR(&ipaddr,  192, 168,   1, 10);
116     IP4_ADDR(&netmask, 255, 255, 255,  0);
117     IP4_ADDR(&gw,      192, 168,   1,  1);
118 #endif
119 #endif
120     print_app_header();
121 
122     lwip_init();
123 
124 #if (LWIP_IPV6 == 0)
125     /* Add network interface to the netif_list, and set it as default */
126     if (!xemac_add(echo_netif, &ipaddr, &netmask,
127                         &gw, mac_ethernet_address,
128                         PLATFORM_EMAC_BASEADDR)) {
129         xil_printf("Error adding N/W interface\n\r");
130         return -1;
131     }
132 #else
133     /* Add network interface to the netif_list, and set it as default */
134     if (!xemac_add(echo_netif, NULL, NULL, NULL, mac_ethernet_address,
135                         PLATFORM_EMAC_BASEADDR)) {
136         xil_printf("Error adding N/W interface\n\r");
137         return -1;
138     }
139     echo_netif->ip6_autoconfig_enabled = 1;
140 
141     netif_create_ip6_linklocal_address(echo_netif, 1);
142     netif_ip6_addr_set_state(echo_netif, 0, IP6_ADDR_VALID);
143 
144     print_ip6("\n\rBoard IPv6 address ", &echo_netif->ip6_addr[0].u_addr.ip6);
145 
146 #endif
147     netif_set_default(echo_netif);
148 
149     /* now enable interrupts */
150     platform_enable_interrupts();
151 
152     /* specify that the network if is up */
153     netif_set_up(echo_netif);
154 
155 #if (LWIP_IPV6 == 0)
156 #if (LWIP_DHCP==1)
157     /* Create a new DHCP client for this interface.
158      * Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at
159      * the predefined regular intervals after starting the client.
160      */
161     dhcp_start(echo_netif);
162     dhcp_timoutcntr = 24;
163 
164     while(((echo_netif->ip_addr.addr) == 0) && (dhcp_timoutcntr > 0))
165         xemacif_input(echo_netif);
166 
167     if (dhcp_timoutcntr <= 0) {
168         if ((echo_netif->ip_addr.addr) == 0) {
169             xil_printf("DHCP Timeout\r\n");
170             xil_printf("Configuring default IP of 192.168.1.10\r\n");
171             IP4_ADDR(&(echo_netif->ip_addr),  192, 168,   1, 10);
172             IP4_ADDR(&(echo_netif->netmask), 255, 255, 255,  0);
173             IP4_ADDR(&(echo_netif->gw),      192, 168,   1,  1);
174         }
175     }
176 
177     ipaddr.addr = echo_netif->ip_addr.addr;
178     gw.addr = echo_netif->gw.addr;
179     netmask.addr = echo_netif->netmask.addr;
180 #endif
181 
182     print_ip_settings(&ipaddr, &netmask, &gw);
183 
184 #endif
185     /* start the application (web server, rxtest, txtest, etc..) */
186     start_application();
187 
188     /* receive and process packets */
189     while (1) {
190         if (TcpFastTmrFlag) {
191             tcp_fasttmr();
192             TcpFastTmrFlag = 0;
193         }
194         if (TcpSlowTmrFlag) {
195             tcp_slowtmr();
196             TcpSlowTmrFlag = 0;
197         }
198         xemacif_input(echo_netif);
199         transfer_data();
200     }
201 
202     /* never reached */
203     cleanup_platform();
204 
205     return 0;
206 }

可以看到代码中有很多的#if LWIP_IPV60和#if LWIP_DHCP1这类预编译指令,这是因为lwip既支持IPv4也支持IPv6,IPv4和DHCP默认支持。如果想重新配置lwip如取消DHCP,可以通过以下方式重配置lwip。
双击硬件平台工程“platform.spr”,在右侧打开的界面中点击psu_cortexa_53_0裸机开发下的板级支持包(Board Support Package),然后在板级支持包界面中选择“Modify BSP Settings”,如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第21张图片

图 32.4.7 Board Support Package Settings
在弹出的界面中点击左侧standalone下的lwip211,右侧就是lwip的配置面板。如下图所示,箭头所指的两处分别是配置启用DHCP和使能IPv6,可以看到,dhcp默认为true,ipv6默认为false。
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第22张图片

图 32.4.8 lwip的配置面板
main函数可以说是使用lwip的一个标准函数,可以在不修改main函数的情况下就可实现其它的应用功能。main函数主要完成以下功能:
1)在代码的88行设置MPSOC开发板的MAC地址为00:0a:35:00:01:02。
2)通过调用init_platform函数配置定时器和建立中断以初始化平台,定时器产生周期性中断。
3)调用lwip_init函数完成对lwip协议栈的初始化,代码第122行。
4)初始化lwIP后,使用xemac_add函数添加以太网MAC到协议栈中。
5)使用platform_enable_interrupts函数使能中断和启动定时器。
6)从代码第156~174行,启动DHCP服务获取动态IP地址,如果超时未获取到动态IP地址,则使用默认的静态IP设置:
IP Address: 192.168.1.10
Netmask : 255.255.255.0
Gateway : 192.168.1.1
我们也可以根据需要修改代码第171~173行的值,从而使用不同的静态IP地址。
7)start_application函数是用户应用函数,当我们使用lwip实现不同的应用功能时,可以在保持main函数不变的情况下,修改start_application函数的实现即可。本实验的start_application函数在echo.c文件中定义,该函数创建了一个TCP服务并设定了对应该服务的回调函数。当一个TCP连接请求被接收时,回调函数对客户端发送来的数据原封不动的发送回去,从而实现echo服务器的功能。由于本实验的目的在于初步了解lwip的使用,对于涉及到的TCP协议的使用,我们将在下一章讲解,本实验我们不多做介绍。
8)程序进入while(1)循环执行数据包接收操作,以及它需要执行的任何其他特定于应用程序的操作。TcpFastTmrFlag和TcpSlowTmrFlag是TCP TX处理所必需的,定时器中断分别以250ms和500ms的周期来改变这两个标志位。数据包接收函数xemacif_input处理由中断处理程序接收的数据包,并将它们传递给lwIP,然后lwIP为每个接收到的数据包调用适当的回调处理程序。transfer_data函数无实际意义,可以删除。
5-7 程序修改完成后,按快捷键Ctrl+S保存main.c文件,右击应用工程名选择Build Project进行编译。编译完成后控制台(Console)中会出现提示信息“Build Finished”,同时在应用工程的Binaries目录下可以看到生成的elf文件。

31.5下载验证

首先将下载器与开发板上的JTAG接口连接,下载器另外一端与电脑连接。再使用USB连接线将USB_UART(开发板PS PORT)接口与电脑连接,用于串口通信。使用网线一端连接开发板的PS以太网接口(PS_ETH),另一端与电脑或路由器连接。最后连接开发板的电源,给开发板上电,如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第23张图片

图 32.5.1 MPSOC开发板实物图
现在进入最后一步。
step6:板级验证
6-1打开Vitis Terminal终端,设置并连接串口。
6-2 下载程序。
6-3 可以看到串口打印的结果如下:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第24张图片

图 32.5.2 显示打印结果
在打印出“link speed for phy address 7: 1000”后要等一段时间进行DHCP服务。因为我们将开发板的以太网接口与电脑进行连接,所以出现了DHCP Timeout,开发板的IP地址为默认设置的静态IP地址192.168.1.10。如果和路由器进行连接,开发板的IP则是由DHCP获取的动态IP设置。TCP应答服务的端口号为7。另外需要说明的是串口打印的“TCP packets sent to port 6001 will be echoed back”可能是该模板最初版本使用的是6001端口,后面更新的时候使用了端口7,而该语句没有修改或删除,所以这是无意义的语句,可以找到print_app_header函数从而删除该语句。
在打印信息的第三行出现一个警告,这是因为lwip模板默认phy芯片是Marvell、TI或Realtek Ethernet这三家的,而我们开发板上使用的是其它厂家的,不影响正常工作,所以这个地方的警告忽略就可以了。
在使用静态IP地址时,需要确保同网段内没有主机使用192.168.1.10 的IP地址,否则会造成IP冲突。可以在开发板未上电前或未下载程序前在CMD里输入“ping 192.168.1.10”命令查看是否能ping通,如果能ping通,说明网络中有此IP地址,此时需要更改静态IP地址为其他值,如192.168.1.123等。
6-4 使用网络调试助手发送数据。在使用网络助手调试之前需要按照本章实验的步骤6-7设置网络适配器。设置完后在电脑端打开网络调试助手,设置协议类型为:TCP Client,服务器IP地址为串口打印的地址,此处为:192.168.1.10,服务器端口号为:7,然后点击连接,即可连上开发板的TCP Sever,如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第25张图片

图 32.5.3 电脑端网络调试助手TCP Client测试界面
6-5 本实验除了可以使用网络调试助手软件外,还可以使用telnet。
我们打开电脑的CMD(按win+r键后输入cmd),输入“telnet 192.168.1.10 7”,如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第26张图片

图 32.5.4 进行telnet连接
回车后,进入下图所示界面:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第27张图片

图 32.5.5 连接成功后的界面
如果回车后出现像下图所示界面所示“telnet不是内部或外部命令,也不是可运行的程序或批处理文件”,则表明未开启Windows的telnet客户端功能,开启方式见6-6。
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第28张图片

图 32.5.6 未启用telnet客户端时的界面
此时我们按键盘上的字母和数字键,如123abc,控制台显示如下:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第29张图片

图 32.5.7 控制台显示
可以看到除了1只有一个外,其它字符都显示两个,一个是我们按下的,另一个是开发板的lwip echo server应答的。可见功能基本正确,不过从使用上看还是有问题的,一是第一次按下的字符没有回显;二是不能输入字符串,这些问题主要是windows自带的telnet的问题。如果使用第三方提供的telnet工具,例如使用Win10系统的读者可以开启WSL,从而使用linux系统提供的telnet功能进行连接,如下图(通过WSL使用Ubuntu系统):
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第30张图片

图 32.5.8 功能完全正常
可以看到一点问题也没有。按“Ctrl+]”可以退出输入界面,然后输入“quit”即可退出telnet连接。
注:Telnet协议是TCP/IP协议族中的一员,是Internet远程登录服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序,用它连接到服务器。终端使用者可以在telnet程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。
6-6 下面我们介绍一下如何开启Windows的telnet客户端功能。在Win10或Win7系统中,按“Win+r”快捷键后,在下图所示界面中输入“control”。
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第31张图片

图 32.5.9 打开控制面板界面
进入下图所示控制面板界面,将查看方式设置为“类别”,单击“程序”下的“卸载程序”,如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第32张图片

图 32.5.10 点击进入“程序和功能”界面
在弹出的界面中,单击“启用或关闭Windows功能”,如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第33张图片

图 32.5.11 点击“启用或关闭Windows功能”
在弹出的“Windows功能”界面中,找到“Telnet Client”,并勾选,如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第34张图片

图 32.5.12 勾选telnet client
单击确定后,如果出现“Windows需要重启电脑才能完成安装所请求的更改”字样,重新启动电脑即可,至此,Windows的telnet客户端服务已启用。
此时我们进行telnet连接会连接成功,但也有部分电脑会出现下图所示现象:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第35张图片

图 32.5.13 无法连接
出现这种情况是因为本地连接(Win10为以太网)设置有问题,需要重新设置。
6-7 更改适配器设置。在控制面板界面,单击“网络和Internet”下的“查看网络状态和任务”,如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第36张图片

图 32.5.14 打开“查看网络和状态”
进入下图所示界面:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第37张图片

图 32.5.15 更改适配器设置
点击箭头所指的“更改适配器设置”,进入下图所示界面:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第38张图片

图 32.5.16 打开属性界面
点击“以太网”(或者本地连接,如果有)后出现“更改此连接的设置”,点击“更改此连接的设置”,进入“属性”界面,点击“Internet协议版本4(TCP/IPv4)”,如下图所示:
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第39张图片

图 32.5.17 进入IPv4设置
进入下图所示界面后,按下图的设置进行修改。
【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南_第40张图片

图 32.5.18 设置IPv4
经过这些设置后,基本上就可以使用telnet了。
至此,本实验完成。

你可能感兴趣的:(正点原子,fpga开发,网络,tcp/ip)