XAPP1026中记录一些lwIP的应用程序示例和性能测试情况,不过提供的示例工程都是在几个Xilinx的官方板子中跑的。可能很多学生没有机会碰到这些板子。。。另外这份应用笔记使用的SDK 2014.3版本也比较老,那个版本lwip还没有直接集成到SDK中。本文将这份笔记其中比较有用的代码编写思路和性能测试结果部分摘取出来。


1. 硬件系统


这个表是几个开发板上搭建的硬件系统。纯FPGA使用的处理器是软核MicroBlaze,以太网控制器也是软核axi_ethernet或axi_ethernetlite。现在软核的选择也越来越多了,除了Xilinx提供官方支持的PowerPC和MicroBlaze,ARM也提供了一些处理器软核。比如最近的“第三届集电赛”上ARM杯就是要求用ArmCortex-M3 DesignStart处理器搭建SoC。

Zynq用的是硬核Cortex-A9,以太网控制器是GigE。硬核的处理器速度比软核要高很多,在后面的测试结果中也可以看到lwIP的性能表现好很多。


2. Echo服务器

Echo将由网络传给程序的输入信息重复返回,作为一个简单的程序,可以让大家学习如何编写一个lwIP应用程序。Socket模式下echo服务器的结构如下:

1.主线程在制定的echo服务器端口上持续监听。
2.对于每个连接请求,生成一个单独的echo服务线程。
3.继续监听echo端口。

while (1) { new_sd = lwip_accept(sock, (struct sockaddr *)&remote, &size); sys_thread_new(process_echo_request, (void*)new_sd, DEFAULT_THREAD_PRIO);
}

echo服务线程接收一个新的socket描述符作为其输入,在该描述符上读取接收到的数据,并进行数据回传。

while (1) { /* read a max of RECV_BUF_SIZE bytes from socket */ n = lwip_read(sd, recv_buf, RECV_BUF_SIZE));/* handle request */ nwrote = lwip_write(sd, recv_buf, n));
}

Socket模式提供了一个简单的API,会阻塞socket的读和写,直到处理完为止。但是socket API需要很多部分来实现这个功能,主要是一个简单的多线程内核(MicroBlaze使用xilkernel、Zynq使用FreeRTOS)。由于socket API的操作开销较大,导致性能比较差。

RAW API提供了一个基于回调方式的接口。应用程序中使用RAW API注册回调函数,这些函数会在接收accept、读取read、写入write等重要事件发生时被调用。基于RAW API的echo服务是单线程的,所有的工作在回调函数中完成。主程序的循环结构如下:

while (1) {if (TcpFastTmrFlag) { tcp_fasttmr(); TcpFastTmrFlag = 0;}if (TcpSlowTmrFlag) { tcp_slowtmr();TcpSlowTmrFlag = 0;}xemacif_input(netif); transfer_data();
}

TCP发送处理需要用到TcpFastTmrFlag和TcpSlowTmrFlag,程序中两个定时器分别设置为250ms和500ms。程序循环的功能是不断地接收数据包(xemacif_input),将它们传递给lwIP。在进入主循环之前,应用程序要注册一些回调函数:

/* 创建新的TCP控制块结构 */
pcb = tcp_new();
/* 绑定到指定端口 */
err = tcp_bind(pcb, IP_ADDR_ANY, port);
/* 回调函数不需要参数 */
tcp_arg(pcb, NULL);
/* 监听连接 */
pcb = tcp_listen(pcb);
/* 指定用于传入连接的回调函数accept_callback*/
tcp_accept(pcb, accept_callback);

上面这串操作创建了一个TCP连接,并为在“接受accept”的连接设置了一个回调函数。当连接请求被接受时,函数accept_callback将被异步调用。echo只需要在接收到数据时相应,因此accept回调函数中还要设置一个“接收receive”的回调函数:

/* 为连接设置接收的回调函数 */
tcp_recv(newpcb, recv_callback);

当接收到数据包时,recv_callback函数被调用,该函数中将接收到的数据回传给发送者:

/* 数据包已被接收到 */
tcp_recved(tpcb, p->len);
/* 回传数据 */
err = tcp_write(tpcb, p->payload, p->len, 1);

尽管RAW API比Socket API要复杂,但因为没有很高的开销从而提供了更大的吞吐量。


3. Web服务器

这里是一个简单的Web服务器的实现,作为基于TCP的应用程序的参考。该Web服务器只实现了一个HTTP 1.1协议的子集。这样的Web服务器可用于通过浏览器控制或监听嵌入式平台。示例中的Web服务器具有如下特性:

  • 通过HTTP GET命令访问驻留在内存文件系统中的文件;
  • 使用HTTP POST命令控制开发板上的LED灯;
  • 使用HTTP POST命令获取开发板上的DIP开关的状态。

Xilinx内存文件系统(xilmfs)用于在开发板的内存中存储一组文件。将web浏览器的IP地址指向开发板,通过HTTP GET命令可以访问这些文件。

通过向一组映射到设备的URL发出POST命令,可以控制或监视开发板上组件的状态。当web服务器接收到它识别到的URL的POST命令时,会调用一个指定的函数来完成请求的工作。这个函数的输出以JavaScript对象表示法(JSON)格式发送回web浏览器。web浏览器根据接收到的数据更新显示。

web服务器的总体架构和echo服务器类似,有一个主线程在HTTP端口(80)上监听传入的连接。对于每个传入的连接,都会生成一个新线程来处理该连接上的请求。

HTTP线程首先读取请求,识别它是GET操作还是POST操作,据此执行对应的操作。对于GET请求,线程在内存文件系统中查找特定的文件。如果存在此文件,则将其返回到浏览器;如果该文件不可用,会返回HTTP 404的错误代码。

Socket模式下HTTP线程的结构如下:

/* 读取请求 */
if ((read_len = read(sd, recv_buf, RECV_BUF_SIZE)) < 0) return;/* 回应请求 */
generate_response(sd, recv_buf, read_len);

回应请求函数的伪代码结构如下:

/* 根据HTTP请求执行对应的响应 */
int generate_response(int sd, char *http_req, int http_req_len)
{enum http_req_type request_type = decode_http_request(http_req, http_req_len);switch(request_type) { case HTTP_GET: return do_http_get(sd, http_req, http_req_len);case HTTP_POST: return do_http_post(sd, http_req, http_req_len);default: return do_404(sd, http_req, http_req_len);}
}

RAW模式下web服务器主要使用回调函数来执行其任务。当接受(accept)新连接时,接受回调函数设置发送send和接收receive的回调函数。当发送的数据被确认或接收到数据时会执行注册的回调函数。

err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{/* 记住连接号,作为回调函数参数 */ tcp_arg(newpcb, (void*)palloc_arg());tcp_recv(newpcb, recv_callback); tcp_sent(newpcb, sent_callback);return ERR_OK;
}

当web浏览器发出请求时,recv_callback函数被调用。这个函数中解码请求并执行对应的响应。

/* 确认已读取了有效载荷(payload) */
tcp_recved(tpcb, p->len);
/* 读取并解译请求 */
generate_response(tpcb, p->payload, p->len);/*释放接收到的包cket */
pbuf_free(p);

数据传输过程是很复杂的。在socket模式下,应用程序使用lwip_write API发送数据。如果TCP发送缓冲区已满,会阻塞这个函数。然而在RAW模式下,应用程序决定可以发送多少数据、只发送多少数据。只有当发送缓冲区中有空间可用时,才能进一步发送数据。当接收方(客户端)对发送的数据产生应答时,空间就可以用了。此时lwIP会调用sent_callback函数,表示数据已经发送,并且send缓冲区中现在有空间存储更多数据。sent_callback的结构如下:

err_t sent_callback(void *arg, struct tcp_pcb *tpcb, u16_t len)
{int BUFSIZE = 1024, sndbuf, n; char buf[BUFSIZE];http_arg *a = (http_arg*)arg;/* 当连接关闭或没有数据发送时 */ if (tpcb->state > ESTABLISHED) { return ERR_OK;}/* 从文件中读取更多数据并发送 */ sndbuf = tcp_sndbuf(tpcb);if (sndbuf < BUFSIZE) return ERR_OK;n = mfs_file_read(a->fd, buf, BUFSIZE); tcp_write(tpcb, buf, n, 1);/* 计算还剩多少字节没有发送 */a->fsize -= n; if (a->fsize == 0) {mfs_file_close(a->fd); a->fd = 0;}return ERR_OK;
}

发送send和接收receive的回调函数都可以用tcp_arg设置参数来调用。对于web服务器,这个参数指向一个数据结构,记录了剩余要发送的字节数以及用来读取该文件的文件描述符。


4. TFTP服务器

TFTP是一个基于UDP的为文件发送和接收协议。因为UDP不能保证包的可靠传输,TFTP实现了另一个确保包在传输过程中不会丢失的协议。

Socket模式下TFTP服务器的应用程序主架构和web服务器很类似。主线程监TFTP端口,并为每个传入的连接请求生成一个新的TFTP线程。新线程中为实现了TFTP协议的一个子集,支持读或写请求。最多只运行一个TFTP数据包或应答包,极大地简化了TFTP协议的实现。RAW模式下的TFTP服务器也非常简单,没有对超时做处理,因此只能用于点对点的以太网连接(0数据包丢失)。


5. TCP RX与TX吞吐量测试

TCP传输和接收吞吐量测试是一个很简单的应用程序,检测使用lwIP和Xilinx Ethernet MAC适配器可以实现的最大TCP传输和接收的吞吐量。

发送测试测量从运行lwIP的开发板到主机的传输吞吐量。lwIP应用程序连接到运行在主机上的Iperf服务器(一个开源软件https://sourceforge.net/projects/iperf/),然后不断向主机发送数据。Iperf会检测数据传输的速率并在终端显示(这份应用笔记比较老,现在win10的任务管理器->性能->以太网也可以测速)。

接收测试测量板上的最大接收传输吞吐量。lwIP应用程序作为服务器,接受来自任一主机的某个端口的连接。程序一边接收一边删除,主机上的Iperf(客户端模式)连接到此服务器,并根据需要向其传输数据。Iperf会将计算的吞吐量显示在控制台中。


6. 创建lwIP应用程序

lwIP的Socket API与Barkeley/BSD Socket很类似,唯一的区别的是初始化过程需要耦合到lwip 1.4.1库和xilkernel(或FreeTROS)中。所有socket模式的应用程序都要执行下面这些步骤

  1. MicroBlaze使用Xilkernel,为其配置一个静态线程(示例工程中都叫main_thread);Zynq使用FreeRTOS,启动调度器前要创建一个任务。
  2. 主线程调用lwip_init函数初始化lwIP,然后使用sys_thread_new函数启动网络线程。所有使用lwIP socket API的线程都必须由sys_thread_new函数启动。
  3. 主线程使用xemac_add函数添加一个网络接口,这个函数要为接口设置IP地址和以太网MAC地址,并对其初始化。
  4. 由网络线程启动xemacif_input_thread线程。这个线程将从中断处理程序接收到的数据移动到tcpip_thread(lwIP通过它来处理TCP/IP)。
  5. 这样便已经初始化了lwIP库,然后根据应用程序的需求启动其它线程。
    lwIP RAW模式的API更为复杂,因为需要编程者了解lwIP的内部结构。

RAW模式下程序的典型结构如下:

  1. 首先使用lwip_init初始化所有的lwIP结构。
  2. lwIP初始化后,使用xemac_add函数添加以太网MAC。
  3. 由于Xilinx lwIP适配器是基于中断的,所以要启用处理器中的中断和中断控制器。
  4. 设置一个定时器,以固定间隔产生中断,通常取250ms。在定时器中断中更新标志信号(flag),主程序循环中根据flag来调用lwip TCP的API函数tcp_fasttmmrtcp_slowtmr
  5. 应用程序初始化后,主程序进入一个无限循环,执行包接收操作,以起其它应用程序需要完成的操作。
  6. 包接收操作(xemacif_input)会把中断处理程序接收到的包传递给lwIP,然后lwIP为每个接收到的包调用相应的回调函数。

7. lwIP性能测试

下表是不同配置下得到TCP最大吞吐量测试结果。可以看到cache大小、lwIP模式、处理器选择都会影响到lwIP的性能表现。


8. DHCP的支持

XAPP1026中的所有应用程序都使用了动态主机配置协议(DHCP)。应用程序会假设网络中有一个DHCP服务器,会为连接的板子分配一个IP地址。如果板子连接的网络中没有DHCP服务器,在一定时间后会发生DHCP超时,此时会为开发板分配一个静态地址(SDK自带的lwip echo例程用的IP地址是192.168.1.10)。在BSP设置中可以选择是否启用DHCP。

学会Zynq(13)lwIP官方应用程序示例相关推荐

  1. 通过调试微信小程序示例代码解析flex布局参数功能(一)

    通过调试微信小程序示例代码解析flex布局参数功能 官方示例小程序源代码下载地址:https://github.com/wechat-miniprogram/miniprogram-demo 通过调试 ...

  2. 第一个Mybatis程序示例 Mybatis简介(一)

    在JDBC小结中(可以参阅本人JDBC系列文章),介绍到了ORM,其中Mybatis就是一个不错的ORM框架 MyBatis由iBatis演化而来 iBATIS一词来源于"internet& ...

  3. 小程序-demo:小程序示例-page/component

    ylbtech-小程序-demo:小程序示例-page/component 以下将展示小程序官方组件能力,组件样式仅供参考,开发者可根据自身需求自定义组件样式,具体属性参数详见小程序开发文档. 1. ...

  4. 《C Primer Plus》第二章——C语言概述(程序示例与解释,提高程序可读性,函数的定义与使用,调试,关键字,复习题与编程练习)

    文章目录 第二章-C语言概述 本章内容 简单的C程序示例 示例解释 快速概要 程序细节 简单程序的结构 提高程序可读性的技巧 进一步使用C 多个函数 调试程序 语法错误 语义错误 程序状态 关键字和保 ...

  5. 学会Zynq(11)RAW API的TCP和UDP编程

    RAW API RAW API(有时称作native API)是一种事件驱动型的API,在没有操作系统的情况下使用.核心栈通过这个API完成不同协议间的交互. 使用lwIP栈的应用程序通过一组回调函数 ...

  6. Centos 异步 IO framework io_uring,基本原理,程序示例与性能压测

    尊重原创版权: https://www.conghengx.com/hot/37285.html 更多内容参考: https://www.conghengx.com/ Linux 异步 I/O 框架 ...

  7. MapGuide应用程序示例——你好,MapGuide!

    图 3‑4显示了基于MapGuide的Web应用程序的开发流程,整个开发流程可以分为五个阶段.图中,矩形代表任务,椭圆形被任务使用的或被任务创建的实体,箭头代表数据流. 1) 加载文件类型的数据,配置 ...

  8. 微信小程序无法访问豆瓣API, 豆瓣图书小程序示例

    一.访问豆瓣API 官方API地址:图书 Api V2 按照API的示例,图书检索的URL为:https://api.douban.com/v2/book/search?q=检索信息&star ...

  9. 示例程序 示例一、由 CLSID 得到 ProgID......

    五.示例程序 示例一.由 CLSID 得到 ProgID.(程序以 word 为例子.如果运行不正确,嘿嘿,你没有安装 word 吧?) ::CoInitialize( NULL );HRESULT ...

最新文章

  1. C#进阶系列——动态Lamada
  2. java开发app教程,进阶加薪全靠它!
  3. HDU 4628 Pieces(DP + 状态压缩)
  4. java监控任务进度_Java Swing组件实现进度监视功能示例
  5. 安洵杯——game(混淆控制流平坦化)
  6. printf 地址_C程序显示主机名和IP地址
  7. 求字典key的和python_python怎么将字典key相同的value值, 合并
  8. 高位字节 低位字节_所有字节从何而来?
  9. JAVA本地文本读取---解决中文乱码
  10. Linux之find xargs
  11. 用window.showModalDialog()实现DIV模式弹出窗口
  12. C++ 重制植物大战僵尸(Cocos2dx开源项目)
  13. 学学就能进步之LM358介绍
  14. JavaWeb开发技术笔记(配置篇)
  15. 渗透tools之POC-bomber
  16. postman使用方法
  17. 数码宝贝Butter-fly(罗马音+中文谐音)
  18. 美观大气商城小程序源码/单商户商城/拼团、抢购商城/积分商城/商城小程序/优惠券
  19. linux好用便携电脑,ThinkPad x230i简单评测,便携且良好兼容Linux的12寸笔记本
  20. 数字IC测试系统波形格式简介

热门文章

  1. 关于Kernel的一点解释
  2. 如何营造性能至上的团队文化
  3. 使用JDK自带工具keytool生成ssl证书
  4. 软考系统集成项目管理工程师视频教程(下)-乔俊峰-专题视频课程
  5. 天刀手游测试服服务器维护中,谈谈天涯明月刀手游吧。 因为开测四天,今天我想上线维护中。...
  6. 山东大学软件学院2019web数据管理
  7. Vue——05-02组件的数据、为什么data要使用函数、父组件给子组件传递数据的三种写法、父传子以及传两种以上的值、默认值以及父传子的引用类型
  8. python 量化分析 入门_量化入门-小白到菜鸟的学习路线
  9. 华硕服务器组件,华硕最新推出一系列服务器及主板解决方案
  10. 利用二维码进行市场推广的十大新玩法