zynq 之lwip 之tcp調試

下位機發送間隔10s

1 測試平臺搭建
搭建過程請參考我的另一篇blog的過程:“https://blog.csdn.net/weixin_42066185/article/details/106421611

下位機測試發送的核心代碼

在這裏插入圖片描述
具體參考github:https://github.com/Scottars/TCP_UDPtranformonZYNQ

測試結果

在這裏插入圖片描述

測試結果原因

請參考另一篇blog:https://blog.csdn.net/weixin_42066185/article/details/106480029

結論

xemacif_input(echo_netif);

根據上面的這句話,其實際上的作用是將mac層的數據讀入到lwip
當中去,根據另外一篇blog 的內容:https://blog.csdn.net/FPGADesigner/article/details/88776615
也對這句話進行了說明了原因。

下位機發送間隔設定1s

下位機代碼片段

在這裏插入圖片描述

測試結果

在這裏插入圖片描述
第二次測試的結果,通過計數這個時候共有接收到了多少個數據包
在這裏插入圖片描述

測試結論

我們將延時降低,出現內存錯誤的地方顯然在已經傳輸額了很多數據之後纔出現了,這個時候也驗證了上面那一節blog 所提到的原因,此時能夠有快頻率的將mac中的隊列包讀入lwip堆棧中。

發送測試設定100us

這樣的測試結論,可仍然見我的另一篇blog:
https://blog.csdn.net/weixin_42066185/article/details/106421611

發送測試設定延時10us

上位機測試代碼

'''
子系統自身信息:
IP:192.168.127.11
slave:11
port:5001

子系統需要檢測的信息
電源電壓採樣 value1:05 03 07 data crc1 crc2----registerid=07   datatype=float
電源電流採樣 value1:05 03 08 data crc1 crc2----registerid=08   datatype=float

'''

import threading
import zmq
import time
import socket
import datetime
import struct
HWM_VAL = 100000*60*31*5

HWM_VAL = 100000


def zmq_recv(context, url):
    socket = context.socket(zmq.SUB)
    # socket = context.socket(zmq.REP)
    socket.connect(url)
    socket.setsockopt(zmq.SUBSCRIBE, ''.encode('utf-8'))  # 接收所有消息

    zhanbao = 0
    buzhanbao = 0
    start_time = time.clock()
    while True:
        b = socket.recv();
        # socket.send(b'1')
        # print(b)

        end_time = time.clock()
        if len(b) == 1:
            # print('總計耗時',end_time-start_time)
            break

        size = len(b)
        # print(size)

        # if end_time-start_time > 10:
        #     pass
        #     break
        if size > 10:
            zhanbao = zhanbao + 1

        else:
            buzhanbao = buzhanbao + 1

    print('接收不粘包', buzhanbao)
    print('接收粘包', zhanbao)

def set_keepalive_linux(sock, after_idle_sec=1, interval_sec=3, max_fails=5):
    """Set TCP keepalive on an open socket.

    It activates after 1 second (after_idle_sec) of idleness,
    then sends a keepalive ping once every 3 seconds (interval_sec),
    and closes the connection after 5 failed ping (max_fails), or 15 seconds
    """
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, after_idle_sec)
    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, interval_sec)
    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, max_fails)
    return sock


def tcp_recv_zmq_send(context, sub_server_addr, syncaddr, down_computer_addr, port):
    # socketzmq = context.socket(zmq.PUB)
    # socketzmq.bind("tcp://115.156.162.76:6000")
    reveiver_url = "ipc://11_Router"

    socketzmq = context.socket(zmq.ROUTER)
    socketzmq.set_hwm(HWM_VAL)

    # socketzmq.connect(reveiver_url)

    sendinglist=[]

    # client = context.socket(zmq.ROUTER)


    # 爲了定義一個對象線程
    # 創建一個socket:
    # tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#創建套接字
    # tcp_server_socket.bind((down_computer_addr,port))#綁定本機地址和接收端口
    # tcp_server_socket.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,True)
    # tcp_server_socket.listen(1)#監聽()內爲最大監聽值
    # # tcp_server_socket = set_keepalive_linux(tcp_server_socket)
    #
    # s,s_addr= tcp_server_socket.accept()#建立連接(accept(無參數)
    # s = set_keepalive_linuZZ8888888888888888888888;Bx(s)

    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 建立連接,這個建立的是tcp的鏈接
    # client_socket.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,True)
    IP_Server='192.168.127.202'
    Port=5011
    client_socket.connect((IP_Server,Port))





    # s.connect(('192.168.127.5', 5001))
    print('we have connected to the tcp data send server!---port is :', port)

    packagenum = 0

    zhanbao = 0
    buzhanbao = 0
    start_time_perf = time.perf_counter()
    start_time_process = time.process_time()
    count = 0
    # 實際上應當啓用的市多線程來做這些事情的
    # 每一個線程要做的事情就是接收對應的內容
    # 我想epics裏面做的也是基本想同樣的事情  ---最後寫一個自動化的腳本多線程
    start_flag = True
    while True:

        # try:  #許久沒有接收到下位機來的消息,首先會有一個keep  alive 的數據包的出現,如果太久沒有了就直接關閉當前socket
        b = client_socket.recv(10)
        print('I',count)

        if count==1000000:
            break
        # size = len(b)
        count = count + 1
        # timestample = str(datetime.datetime.now()).encode()
        # b = b + timestample
        #

            # sendinglist.append(b)

    # print('Sending list size ',len(sendinglist))

    print(packagenum)
    end_time_perf = time.perf_counter()
    end_time_process = time.process_time()
    print('Receiving port is: ', port)
    print('Package num:', count)
    print('receing time cost:', end_time_perf - start_time_perf)  #
    # print('tcp接收不粘包', buzhanbao)
    # print('tcp接收粘包', zhanbao)

    # socketzmq.send(b)


    socketzmq.close()

    s.close()
    tcp_server_socket.close()



if __name__ == '__main__':
    print('Kaishile ')
    context = zmq.Context()  # 這個上下文是真的迷,到底什麼情況下要用共同的上下文,什麼時候用單獨的上下文,找時間測試清楚
    sub_server_addr = "tcp://115.156.162.123:6000"
    syncaddr = "tcp://115.156.162.76:5555"
    down_computer_addr = '115.156.163.107'
    # down_computer_addr = '127.0.0.1'
    down_computer_addr = '192.168.127.11'
    down_computer_addr = '192.168.127.100'

    down_computer_addr = '127.0.0.1'

    # sub_server_addr = "tcp://127.0.0.1:6001"
    sub_server_addr = "tcp://192.168.127.100:6002"


    # syncaddr = "tcp://127.0.0.1:5555"
    syncaddr = "tcp://192.168.127.100:5556"



    port = [5001, 5002, 5003, 5004, 5005, 5006, 5007, 5008, 5009, 5010]

    tcp_recv_zmq_send(context,sub_server_addr,syncaddr,down_computer_addr,5011)
    # for i in port:
    #     t2 = threading.Thread(target=tcp_recv_zmq_send,
    #                           args=(context, sub_server_addr, syncaddr, down_computer_addr, port))
    #     t2.start()


測試結果

在這裏插入圖片描述

測試原因

由於測試中包含大量的xil_print信息,導致實際數據上傳的速度並未達到每隔10us上傳的速度。

修改去掉printf 後延時10us測試結果

上位機僅僅接收到199個數據包的時候就出現了tcp_write報錯,即內存溢出的錯誤:
在這裏插入圖片描述
第二次測試的結果:
在這裏插入圖片描述
在這裏插入圖片描述

原因剖析

–update- on 2020-06-01

根本原因溯源

根本原因溯源

我們看到我們串口打印的數據的實際上出現在tcp_write 這句話的函數上面。如下:
​​​​​​
​​在這裏插入圖片描述
​​
進一步查看tcpwrite 中報錯具體是哪一句話:
在這裏插入圖片描述

進一步查看tcp_write_check 函數的報錯
在這裏插入圖片描述

通過上面圖片中的代碼,我們可以得出的兩點原因如下:

原因1:由於發送的數據太多。

原因2:在未發送或者未確認的隊列中的pubfs 的總數,超過了所配置的大小;

解決方案

我們看一下上面代碼中的 TCP_SND_QUEUELEN的定義:

pcb->snd_queuelen >= TCP_SND_QUEUELEN

查看定義:

在這裏插入圖片描述

我們再進而查看:lwip的配置界面:
在這裏插入圖片描述
通是修改memp_n_pbuf的大小,將從原來默認的16,變成1600,再次進行tcp的數據的測試。

情況並沒有出現改觀。

上網進行查詢了一下如下是幾個參考鏈接:
沒有結論:https://bbs.csdn.net/topics/390939619?page=1
這篇轉載的的文章提到提高發緩衝區的大小:https://blog.csdn.net/mazihong/article/details/52463895

這篇blog:https://blog.csdn.net/weixin_33888907/article/details/91637939
給出了一個比較明確的答案,也跟我們上面的結論是一樣的。

修改 define TCP_SND_QUEUELEN 大小

修改代碼位置:

測試結修改前:16 * TCP_SND_BUF)/TCP_MSS
測試修改後:在這裏插入圖片描述

代碼編譯報錯:
在這裏插入圖片描述

調整如下大小:
在這裏插入圖片描述

測試結果:
在這裏插入圖片描述
在這裏插入圖片描述

修改tcp_send_buf 的大小同樣不能解決問題

-------------未完待續---------------------

—update on 2020 - 06 -02

再一次溯源

查詢參考資料

https://www.xilinx.com/support/answers/61298.html
這篇blog 中提到了,參數回調的方式。
由於博主之前參考zynq 示例的時候有zynq2018 版本的sdk中,有一個tcp_perf的例程,而其在這個過程中採用的就是函數回調的方式。

例程

/******************************************************************************
*
* Copyright (C) 2018 Xilinx, Inc.  All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/

/* Connection handle for a TCP Client session */

#include "tcp_perf_client.h"
//pcb = protocol control block

static struct tcp_pcb *c_pcb;
static char send_buf[TCP_SEND_BUFSIZE];
static struct perf_stats client;

void print_app_header()
{
#if LWIP_IPV6==1
	xil_printf("TCP client connecting to %s on port %d\r\n",
			TCP_SERVER_IPV6_ADDRESS, TCP_CONN_PORT);
	xil_printf("On Host: Run $iperf -V -s -i %d -w 2M\r\n",
			INTERIM_REPORT_INTERVAL);
#else
	xil_printf("TCP client connecting to %s on port %d\r\n",
			TCP_SERVER_IP_ADDRESS, TCP_CONN_PORT);
	xil_printf("On Host: Run $iperf -s -i %d -w 2M\r\n",
			INTERIM_REPORT_INTERVAL);
#endif /* LWIP_IPV6 */
}

static void print_tcp_conn_stats()
{
#if LWIP_IPv6==1
	xil_printf("[%3d] local %s port %d connected with ",
			client.client_id, inet6_ntoa(c_pcb->local_ip),
			c_pcb->local_port);
	xil_printf("%s port %d\r\n",inet6_ntoa(c_pcb->remote_ip),
			c_pcb->remote_port);
#else
	xil_printf("[%3d] local %s port %d connected with ",
			client.client_id, inet_ntoa(c_pcb->local_ip),
			c_pcb->local_port);
	xil_printf("%s port %d\r\n",inet_ntoa(c_pcb->remote_ip),
			c_pcb->remote_port);
#endif /* LWIP_IPV6 */

	xil_printf("[ ID] Interval\t\tTransfer   Bandwidth\n\r");
}

static void stats_buffer(char* outString,
		double data, enum measure_t type)
{
	int conv = KCONV_UNIT;
	const char *format;
	double unit = 1024.0;

	if (type == SPEED)
		unit = 1000.0;

	while (data >= unit && conv <= KCONV_GIGA) {
		data /= unit;
		conv++;
	}

	/* Fit data in 4 places */
	if (data < 9.995) { /* 9.995 rounded to 10.0 */
		format = "%4.2f %c"; /* #.## */
	} else if (data < 99.95) { /* 99.95 rounded to 100 */
		format = "%4.1f %c"; /* ##.# */
	} else {
		format = "%4.0f %c"; /* #### */
	}
	sprintf(outString, format, data, kLabel[conv]);
}


/** The report function of a TCP client session */
static void tcp_conn_report(u64_t diff,
		enum report_type report_type)
{
	u64_t total_len;
	double duration, bandwidth = 0;
	char data[16], perf[16], time[64];

	if (report_type == INTER_REPORT) {
		total_len = client.i_report.total_bytes;
	} else {
		client.i_report.last_report_time = 0;
		total_len = client.total_bytes;
	}

	/* Converting duration from milliseconds to secs,
	 * and bandwidth to bits/sec .
	 */
	duration = diff / 1000.0; /* secs */
	if (duration)
		bandwidth = (total_len / duration) * 8.0;

	stats_buffer(data, total_len, BYTES);
	stats_buffer(perf, bandwidth, SPEED);
	/* On 32-bit platforms, xil_printf is not able to print
	 * u64_t values, so converting these values in strings and
	 * displaying results
	 */
	sprintf(time, "%4.1f-%4.1f sec",
			(double)client.i_report.last_report_time,
			(double)(client.i_report.last_report_time + duration));
	xil_printf("[%3d] %s  %sBytes  %sbits/sec\n\r", client.client_id,
			time, data, perf);

	if (report_type == INTER_REPORT)
		client.i_report.last_report_time += duration;
}

/** Close a tcp session */
static void tcp_client_close(struct tcp_pcb *pcb)
{
	err_t err;

	if (pcb != NULL) {
		tcp_sent(pcb, NULL);
		tcp_err(pcb, NULL);
		err = tcp_close(pcb);
		if (err != ERR_OK) {
			/* Free memory with abort */
			tcp_abort(pcb);
		}
	}
}

/** Error callback, tcp session aborted */
static void tcp_client_err(void *arg, err_t err)
{
	LWIP_UNUSED_ARG(err);
	u64_t now = get_time_ms();
	u64_t diff_ms = now - client.start_time;


	tcp_client_close(c_pcb);
	c_pcb = NULL;
	tcp_conn_report(diff_ms, TCP_ABORTED_REMOTE);
	xil_printf("TCP connection aborted %d\n\r",err);

}

static err_t tcp_send_perf_traffic(void)
{
	err_t err;
	u8_t apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;

	if (c_pcb == NULL) {
		return ERR_CONN;
	}

#ifdef __MICROBLAZE__
	/* Zero-copy pbufs is used to get maximum performance for Microblaze.
	 * For Zynq A9, ZynqMP A53 and R5 zero-copy pbufs does not give
	 * significant improvement hense not used. */
	apiflags = 0;
#endif

	while (tcp_sndbuf(c_pcb) > TCP_SEND_BUFSIZE) {

		send_buf[0]='i';
		send_buf[1]='a';
		send_buf[2]='m';
		send_buf[3]='a';
		send_buf[4]='g';
		send_buf[5]='o';
		send_buf[6]='o';
		send_buf[7]='d';
		send_buf[8]='m';
		send_buf[9]='a';
		send_buf[10]='n';
		err = tcp_write(c_pcb, send_buf, TCP_SEND_BUFSIZE, apiflags);
		if (err != ERR_OK) {
			xil_printf("TCP client: Error on tcp_write: %d\r\n",
					err);
			return err;
		}

		err = tcp_output(c_pcb);
		if (err != ERR_OK) {
			xil_printf("TCP client: Error on tcp_output: %d\r\n",
					err);
			return err;
		}
		client.total_bytes += TCP_SEND_BUFSIZE;
		client.i_report.total_bytes += TCP_SEND_BUFSIZE;
	}

	if (client.end_time || client.i_report.report_interval_time) {
		u64_t now = get_time_ms();
		if (client.i_report.report_interval_time) {
			if (client.i_report.start_time) {
				u64_t diff_ms = now - client.i_report.start_time;
				u64_t rtime_ms = client.i_report.report_interval_time;
				if (diff_ms >= rtime_ms) {
					tcp_conn_report(diff_ms, INTER_REPORT);
					client.i_report.start_time = 0;
					client.i_report.total_bytes = 0;
				}
			} else {
				client.i_report.start_time = now;
			}
		}

		if (client.end_time) {
			/* this session is time-limited */
			u64_t diff_ms = now - client.start_time;
			if (diff_ms >= client.end_time) {
				/* time specified is over,
				 * close the connection */
				tcp_conn_report(diff_ms, TCP_DONE_CLIENT);
				xil_printf("TCP test passed Successfully\n\r");
				tcp_client_close(c_pcb);
				c_pcb = NULL;
				return ERR_OK;
			}
		}
	}
	return ERR_OK;
}






static err_t tcp_send_hello_traffic(void)
{
	err_t err;
	u8_t apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;

	if (c_pcb == NULL) {
		return ERR_CONN;
	}

#ifdef __MICROBLAZE__
	/* Zero-copy pbufs is used to get maximum performance for Microblaze.
	 * For Zynq A9, ZynqMP A53 and R5 zero-copy pbufs does not give
	 * significant improvement hense not used. */
	apiflags = 0;
#endif



		send_buf[0]='h';
		send_buf[1]='e';
		send_buf[2]='l';
		send_buf[3]='l';
		send_buf[4]='o';
		send_buf[5]='w';
		send_buf[6]='o';
		send_buf[7]='r';
		send_buf[8]='l';
		send_buf[9]='d';
		send_buf[10]='!';
		err = tcp_write(c_pcb, send_buf, TCP_SEND_BUFSIZE, apiflags);
		if (err != ERR_OK) {
			xil_printf("TCP client: Error on tcp_write: %d\r\n",
					err);
			return err;
		}

		err = tcp_output(c_pcb);
		if (err != ERR_OK) {
			xil_printf("TCP client: Error on tcp_output: %d\r\n",
					err);
			return err;
		}
		client.total_bytes += TCP_SEND_BUFSIZE;
		client.i_report.total_bytes += TCP_SEND_BUFSIZE;

	xil_printf("we are sending in hello traffic!");
//
//	if (client.end_time || client.i_report.report_interval_time) {
//		u64_t now = get_time_ms();
//		if (client.i_report.report_interval_time) {
//			if (client.i_report.start_time) {
//				u64_t diff_ms = now - client.i_report.start_time;
//				u64_t rtime_ms = client.i_report.report_interval_time;
//				if (diff_ms >= rtime_ms) {
//					tcp_conn_report(diff_ms, INTER_REPORT);
//					client.i_report.start_time = 0;
//					client.i_report.total_bytes = 0;
//				}
//			} else {
//				client.i_report.start_time = now;
//			}
//		}
//
//		if (client.end_time) {
//			/* this session is time-limited */
//			u64_t diff_ms = now - client.start_time;
//			if (diff_ms >= client.end_time) {
//				/* time specified is over,
//				 * close the connection */
//				tcp_conn_report(diff_ms, TCP_DONE_CLIENT);
//				xil_printf("TCP test passed Successfully\n\r");
//				tcp_client_close(c_pcb);
//				c_pcb = NULL;
//				return ERR_OK;
//			}
//		}
//	}
	return ERR_OK;
}


/** TCP sent callback, try to send more data */
static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
	xil_printf("we have sent the package \r\n");

//	return tcp_send_perf_traffic();
	return ERR_OK;
}

/** TCP connected callback (active connection), send data now */
static err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
{
	xil_printf("we are 回調!\r\n");
	if (err != ERR_OK) {
		tcp_client_close(tpcb);
		xil_printf("Connection error\n\r");
		return err;
	}
	xil_printf("we have connected to the server!\r\n");
	/* store state */
	c_pcb = tpcb;
	tcp_nagle_disable(c_pcb);

	client.start_time = get_time_ms();
	client.end_time = TCP_TIME_INTERVAL * 1000; /* ms */
	client.client_id++;
	client.total_bytes = 0;

	/* report interval time in ms */
	client.i_report.report_interval_time = INTERIM_REPORT_INTERVAL * 1000;
	client.i_report.last_report_time = 0;
	client.i_report.start_time = 0;
	client.i_report.total_bytes = 0;

	print_tcp_conn_stats();

	/* set callback values & functions */
	tcp_arg(c_pcb, NULL);
	tcp_sent(c_pcb, tcp_client_sent);
	tcp_err(c_pcb, tcp_client_err);

	/* initiate data transfer */
	return ERR_OK;
}

void transfer_data(void)
{
	if (client.client_id)
		//tcp_send_perf_traffic();
		tcp_send_hello_traffic();
}

void start_application(void)
{
	err_t err;
	struct tcp_pcb* pcb;
	ip_addr_t remote_addr;
	u32_t i;

	xil_printf("WE ARE AT start appklication!!!\r\n");
#if LWIP_IPV6==1
	remote_addr.type= IPADDR_TYPE_V6;
	err = inet6_aton(TCP_SERVER_IPV6_ADDRESS, &remote_addr);
#else
	err = inet_aton(TCP_SERVER_IP_ADDRESS, &remote_addr);
#endif /* LWIP_IPV6 */

	if (!err) {
		xil_printf("Invalid Server IP address: %d\r\n", err);
		return;
	}

	/* Create Client PCB */
	pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
	if (!pcb) {
		xil_printf("Error in PCB creation. out of memory\r\n");
		return;
	}

	err = tcp_connect(pcb, &remote_addr, TCP_CONN_PORT,
			tcp_client_connected);
	if (err) {
		xil_printf("Error on tcp_connect: %d\r\n", err);
		tcp_client_close(pcb);
		return;
	}
	client.client_id = 0;
	xil_printf("err status , %d\r\n",err);
	xil_printf("WE have connected to the server and ready to send\r\n");
	/* initialize data buffer being sent with same as used in iperf */
//	for (i = 0; i < TCP_SEND_BUFSIZE; i++)
//		send_buf[i] = (i % 10) + 'a';//這個就相當於ascill數據碼,發出去的時候,再解析。是不是我每次更新這個sendbuf 就會從新更新
//	當我們將TCP_send_buffsize 設定5  就表示每次發送前面三個字節,它是怎麼指導我每次發送多少個呢??????哪個機制導致了我發送的呢???
	//實際上,我們可以認爲這個地方只算是把數據防盜我們的發送緩衝區的部分, 實際上,是由其他的某個地方實現的數據得發送
		//send_buf[i] = (i % 10) + '0';

	return;
}

上面是zynq開發板作爲perf 客戶端

設定回調

在這裏插入圖片描述

撰寫回調函數

在這裏插入圖片描述

代碼遷移

遷移後的echo.c 文件:

/******************************************************************************
*
* Copyright (C) 2009 - 2014 Xilinx, Inc.  All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/

#include <stdio.h>
#include <string.h>
#include "sleep.h"

#include "lwip/err.h"
#include "lwip/tcp.h"
#if defined (__arm__) || defined (__aarch64__)
#include "xil_printf.h"
#endif
static struct tcp_pcb *connected_pcb = NULL;
char sendBuffer[10]="HELLOWORLD";
volatile unsigned tcp_client_connected = 0;

err_t transfer_data() {
	err_t err;
	struct tcp_pcb *tpcb = connected_pcb;
	//xil_printf("we ccan in transfer data\r\n", err);
	if (!connected_pcb)
			return ERR_OK;

	err = tcp_write(tpcb, sendBuffer, 10, 3);
	if (err != ERR_OK) {
		xil_printf("txperf: Error on tcp_write: %d\r\n", err);
		connected_pcb = NULL;
		return err;
	}
	err = tcp_output(tpcb);
	if (err != ERR_OK) {
		xil_printf("txperf: Error on tcp_output: %d\r\n",err);
		return err;
	}


	return ERR_OK;
}
/** Error callback, tcp session aborted */
static void tcp_client_err(void *arg, err_t err)
{
	xil_printf("TCP connection aborted %d\n\r",err);

}
void print_app_header()
{
	xil_printf("\n\r\n\r-----lwIP TCP echo server ------\n\r");
	xil_printf("TCP packets sent to port 6001 will be echoed back\n\r");
}

err_t recv_callback(void *arg, struct tcp_pcb *tpcb,
                               struct pbuf *p, err_t err)
{
	/* do not read the packet if we are not in ESTABLISHED state */
	if (!p) {
		tcp_close(tpcb);
		tcp_recv(tpcb, NULL);
		return ERR_OK;
	}

	/* indicate that the packet has been received */
	tcp_recved(tpcb, p->len);

	/* echo back the payload */
	/* in this case, we assume that the payload is < TCP_SND_BUF */
	if (tcp_sndbuf(tpcb) > p->len) {
		err = tcp_write(tpcb, p->payload, p->len, 1);
	} else
		xil_printf("no space in tcp_sndbuf\n\r");

	/* free the received pbuf */
	pbuf_free(p);

	return ERR_OK;
}

/** TCP sent callback, try to send more data */
static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
	xil_printf("we have sent the package \r\n");
	usleep(100);

	return transfer_data();
//	return ERR_OK;
}
err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{
	static int connection = 1;

	/* set the receive callback for this connection */
	tcp_recv(newpcb, recv_callback);

	/* just use an integer number indicating the connection id as the
	   callback argument */
	tcp_arg(newpcb, (void*)(UINTPTR)connection);

	/* increment for subsequent accepted connections */
	connection++;

	//將新這個接收的這個accept 賦值給全局變量,以便我們進行其他的處理,例如主動發送數據
	connected_pcb = newpcb;
	//禁用nagle 算法
	tcp_nagle_disable(connected_pcb);
	// zhiwei
	tcp_client_connected=1;

	tcp_arg(connected_pcb, NULL);
	tcp_sent(connected_pcb, tcp_client_sent);
	tcp_err(connected_pcb, tcp_client_err);


	transfer_data();


	return ERR_OK;
}


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

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

	/* bind to specified @port */
	err = tcp_bind(pcb, IP_ADDR_ANY, 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;
}

main.c 文件修改後的內容:

/******************************************************************************
*
* Copyright (C) 2009 - 2014 Xilinx, Inc.  All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/

#include <stdio.h>

#include "xparameters.h"

#include "netif/xadapter.h"

#include "platform.h"
#include "platform_config.h"
#if defined (__arm__) || defined(__aarch64__)
#include "xil_printf.h"
#endif
#include "sleep.h"

#include "lwip/tcp.h"
#include "xil_cache.h"

#if LWIP_DHCP==1
#include "lwip/dhcp.h"
#endif

/* defined by each RAW mode application */
void print_app_header();
int start_application();
err_t transfer_data();
void tcp_fasttmr(void);
void tcp_slowtmr(void);

/* missing declaration in lwIP */
void lwip_init();

#if LWIP_DHCP==1
extern volatile int dhcp_timoutcntr;
err_t dhcp_start(struct netif *netif);
#endif

extern volatile int TcpFastTmrFlag;
extern volatile int TcpSlowTmrFlag;
extern volatile unsigned tcp_client_connected;
static struct netif server_netif;
struct netif *echo_netif;

void
print_ip(char *msg, struct ip_addr *ip) 
{
	print(msg);
	xil_printf("%d.%d.%d.%d\n\r", ip4_addr1(ip), ip4_addr2(ip), 
			ip4_addr3(ip), ip4_addr4(ip));
}

void
print_ip_settings(struct ip_addr *ip, struct ip_addr *mask, struct ip_addr *gw)
{

	print_ip("Board IP: ", ip);
	print_ip("Netmask : ", mask);
	print_ip("Gateway : ", gw);
}

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

#ifdef XPS_BOARD_ZCU102
#ifdef XPAR_XIICPS_0_DEVICE_ID
int IicPhyReset(void);
#endif
#endif

int main()
{
	struct ip_addr ipaddr, netmask, gw;
	err_t tmperr;
	/* the mac address of the board. this should be unique per board */
	unsigned char mac_ethernet_address[] =
	{ 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };

	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
	IicPhyReset();
#endif

	//TO enable platform interupt
	init_platform();

	/* initliaze IP addresses to be used */
	IP4_ADDR(&ipaddr,  192, 168,   127, 202);
	IP4_ADDR(&netmask, 255, 255, 255,  0);
	IP4_ADDR(&gw,      192, 168,   127,  254);
	print_app_header();
	lwip_init();
  	/* 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;
	}
	netif_set_default(echo_netif);

	/* now enable interrupts */
	platform_enable_interrupts();

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

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

	/* 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;
		}
		//xil_printf("we are sending in while loop\r\n");
		xemacif_input(echo_netif);
//		xemacif_input(echo_netif);
//		if (tcp_client_connected) {  //連接成功則發送數據
//			xil_printf("we are sending in connect\r\n");


//			transfer_data();

//		}

	}
  
	/* never reached */
	cleanup_platform();

	return 0;
}

上述的代碼:

  • 加入回調函數的方式
  • 在連接成回調函數中調用數據傳輸函數
  • 在數據傳輸成功的回調函數中調用數據傳輸函數
  • 從而形成循環

結果測試

採用這種類似回調函數的形式,能夠正常的工作,但是實際看起zynq的發送的速度很慢。如下是arm平臺沒有涉惡頂延時的情況得到的結果。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章