zynq實現udp網絡的數據包的上傳(zynq7z035)

1、前面的文件的建立,參考另外的一篇blog

https://blog.csdn.net/weixin_42066185/article/details/106421611

2、直接進入udp建立的那個部分

對於udp的建立,在2018版本的vivado 上的,有相關對應的例程,然而,我是用的版本的是2017,當前並無相關udp的通信例程,因此,我主要參考的都是如下的參考的鏈接:

https://blog.csdn.net/FPGADesigner/article/details/88746532

https://blog.csdn.net/FPGADesigner/article/details/88748846(最主要參考)

https://blog.csdn.net/FPGADesigner/article/details/88751502

3、主函數建立過程

(1) 整體建立過程

	/*
	 * 設置開發板MAC地址
	 * 開啓中斷系統
	 * 設置本地IP地址
	 * 初始化lwIP
	 * 添加網絡接口
	 * 設置默認網絡接口
	 * 啓動網絡
	 * 初始化TCP或UDP連接(自定義函數)
	 * */

(2)設置開發板的mac地址

	struct netif *netif;
	struct ip_addr ipaddr, netmask, gw;

	/* the mac address of the board. this should be unique per board */ //設定開發板的MAC地址
	unsigned char mac_ethernet_address[] = {
		0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };

	netif = &server_netif;

熱555555555555555555555(3) 初始化平臺,開啓中斷

init_platform();  //開啓中斷系統,這個裏面設定的時定時中斷

(4)初始化網卡的ip

/* 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();

(5)  初始化lwip, 添加網卡的netlif , 並且將這個網卡設置成默認網卡


  	/* 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);

(6)初始化udp

	/* start the application (web server, rxtest, txtest, etc..) */
//	start_application();
	xil_printf("初始化udp網絡/r/n");
	user_udp_init();            //初始化UDP

(7) 循環初始化


	while (1) {
		xil_printf("we are sending \r\n");
		/*  將MAC隊列中的包傳輸的LwIP/IP棧中   */
		xemacif_input(netif);
//		if (udp_connected_flag) { //發送
		sleep(1);
//			xil_printf("we are in if nei\r\n");
		udp_printf();
//		}
	}

	/* never reached */
	cleanup_platform();

整個主函數文件內容:

/******************************************************************************
*
* Copyright (C) 2017 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"
#include "lwipopts.h"
#include "xil_printf.h"
#include "sleep.h"

#include "sleep.h"

#include "sys_intr.h"
#include "lwip/init.h"
#include "lwip/inet.h"
#include "xil_cache.h"
//#include "ip_addr.h"


#include "user_udp.h"


#if LWIP_DHCP==1
#include "lwip/dhcp.h"
extern volatile int dhcp_timoutcntr;

#endif
static  XScuGic Intc;   //GIC
extern unsigned udp_connected_flag;
extern volatile int TcpFastTmrFlag;
extern volatile int TcpSlowTmrFlag;
//
#define DEFAULT_IP_ADDRESS	"192.168.127.202"
#define DEFAULT_IP_MASK		"255.255.255.0"
#define DEFAULT_GW_ADDRESS	"192.168.127.254"

//#define DEFAULT_IP_ADDRESS	"115.156.163.175"
//#define DEFAULT_IP_MASK		"255.255.254.0"
//#define DEFAULT_GW_ADDRESS	"115.156.163.254"

void platform_enable_interrupts(void);
void start_application(void);
void print_app_header(void);

#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

struct netif server_netif;

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

static void print_ip_settings(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw)
{
	print_ip("Board IP:       ", ip);
	print_ip("Netmask :       ", mask);
	print_ip("Gateway :       ", gw);
}

static void assign_default_ip(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw)
{
	int err;

	xil_printf("Configuring default IP %s \r\n", DEFAULT_IP_ADDRESS);

	err = inet_aton(DEFAULT_IP_ADDRESS, ip);
	if (!err)
		xil_printf("Invalid default IP address: %d\r\n", err);

	err = inet_aton(DEFAULT_IP_MASK, mask);
	if (!err)
		xil_printf("Invalid default IP MASK: %d\r\n", err);

	err = inet_aton(DEFAULT_GW_ADDRESS, gw);
	if (!err)
		xil_printf("Invalid default gateway address: %d\r\n", err);
}

int main(void)
{

	/*
	 * 設置開發板MAC地址
	 * 開啓中斷系統
	 * 設置本地IP地址
	 * 初始化lwIP
	 * 添加網絡接口
	 * 設置默認網絡接口
	 * 啓動網絡
	 * 初始化TCP或UDP連接(自定義函數)
	 * */


	struct netif *netif;
	struct ip_addr ipaddr, netmask, gw;

	/* the mac address of the board. this should be unique per board */ //設定開發板的MAC地址
	unsigned char mac_ethernet_address[] = {
		0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };

	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
	xil_printf("we are in here\r\n");
	ProgramSi5324();
	ProgramSfpPhy();
#endif
#endif

//	Xil_ICacheEnable();
//	Xil_DCacheEnable();
	init_platform();  //開啓中斷系統,這個裏面設定的時定時中斷
//	Init_Intr_System(&Intc);
//	Setup_Intr_Exception(&Intc);
	//定義一個網卡
	netif = &server_netif;
	/*兩種方案實現對網卡ip、子網掩碼、以及網關*/
	//方案1
//	IP_ADDR(&ipaddr,  192, 168,   127, 202);
//	IP_ADDR(&netmask, 255, 255, 255, 0);
//	IP_ADDR(&gw,      192, 168,   127, 254);
	assign_default_ip(&(netif->ip_addr), &(netif->netmask), &(netif->gw));


	xil_printf("\r\n\r\n");
	xil_printf("-----lwIP RAW Mode UDP Server Application-----\r\n");


	/* initialize lwIP */
	lwip_init();
	xil_printf("we have finnished lwip init");

	/* Add network interface to the netif_list, and set it as default */
	if (!xemac_add(netif, NULL, NULL, NULL, mac_ethernet_address,
				PLATFORM_EMAC_BASEADDR)) {
		xil_printf("Error adding N/W interface\r\n");
		return -1;
	}
	xil_printf("before  the  the default set");
	netif_set_default(netif);  //將當前的網卡設定成默認的網卡
	xil_printf("after the default set");


	/* now enable interrupts */
	platform_enable_interrupts();  //開啓之前設定的中斷

	/* specify that the network if is up */
	netif_set_up(netif);     //啓動之前所設定的網絡



	print_ip_settings(&(netif->ip_addr), &(netif->netmask), &(netif->gw));

	xil_printf("\r\n");

	/* print app header */
	//print_app_header();

	/* start the application*/
	//start_application();   //初始化udp,啓動服務器
	user_udp_init();


	xil_printf("\r\n");

	while (1) {
		xil_printf("we are sending \r\n");
		/*  將MAC隊列中的包傳輸的LwIP/IP棧中   */
		xemacif_input(netif);
//		if (udp_connected_flag) { //發送
		sleep(1);
//			xil_printf("we are in if nei\r\n");
		udp_printf();
//		}
	}

	/* never reached */
	cleanup_platform();

	return 0;
}

 

 

3.1 user_udp.c 文件

#include "user_udp.h"

//---------------------------------------------------------
//                    變量定義
//---------------------------------------------------------
struct udp_pcb *connected_pcb = NULL;
static struct pbuf *pbuf_to_be_sent = NULL;
char *send_buff = "udploWorld";  //待發送字符
int *send_buffint = 100;
struct ip_addr ipaddr;

static unsigned local_port = 5002;      //本地端口
static unsigned remote_port = 8080;  //遠程端口

//---------------------------------------------------------
//                  UDP連接初始化函數
//---------------------------------------------------------
int user_udp_init(void)
{
	struct udp_pcb *pcb;
	err_t err;

	/*  創建UDP控制塊   */
	pcb = udp_new();
	if (!pcb) {
		xil_printf("Error Creating PCB.\r\n");
		return -1;
	}
	/*  綁定本地端口   */
	err = udp_bind(pcb, IP_ADDR_ANY, local_port);
	if (err != ERR_OK) {
		xil_printf("Unable to bind to port %d\r\n", local_port);
		return -2;
	}
	/*  設置遠程地址   */
	IP4_ADDR(&ipaddr, 192, 168, 127, 200);
//	IP4_ADDR(&ipaddr, 192, 168, 1, 101);
//	IP4_ADDR(&ipaddr, 115, 156, 162, 123);
//	IP4_ADDR(&ipaddr, 115, 156, 162, 76);
	connected_pcb = pcb;

	/*  申請pbuf資源  */
	pbuf_to_be_sent = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_ROM);
	memset(pbuf_to_be_sent->payload, 0, 10);
//	memcpy(pbuf_to_be_sent->payload, (u8 *)send_buff, 4);

	return 0;
}

//---------------------------------------------------------
//                   UDP發送數據函數
//---------------------------------------------------------
void udp_printf(void)
{
	err_t err;
	struct udp_pcb *tpcb = connected_pcb;
	if (!tpcb) {
		xil_printf("error connect.\r\n");
	}
//
//	send_buff[0] ='0';
//	send_buff[1] = '1';
//	send_buff[2] = '2';
//	send_buff[3] = '3';
//	send_buff[4] = '4';
//	send_buff[5] = '5';
//	send_buff[6] = '6';
//	send_buff[7] = '7';
//	send_buff[8] = '8';
//	send_buff[9] = '9';
	*send_buff = "0123456789";

//	memset(pbuf_to_be_sent->payload, 0, 10);
	memcpy(pbuf_to_be_sent->payload, (u8 *)send_buff, 10);

	/*  發送字符串  */
	err = udp_sendto(tpcb, pbuf_to_be_sent, &ipaddr, remote_port);
	if (err != ERR_OK) {
//		xil_printf("Error on udp send : %d\r\n", err);
		return;
	}
}
void udp_senddata(int data)
{
	err_t err;
	struct udp_pcb *tpcb = connected_pcb;
	if (!tpcb) {
		xil_printf("error connect.\r\n");
	}
//	data =100;

	*send_buffint = data;
	memcpy(pbuf_to_be_sent->payload, send_buffint, 2);

	xil_printf("we are sending here:%d\r\n",*send_buffint);

	/*  發送字符串  */
	err = udp_sendto(tpcb, pbuf_to_be_sent, &ipaddr, remote_port);
	if (err != ERR_OK) {
//		xil_printf("Error on udp send : %d\r\n", err);
		return;
	}
}

3.2 user_udp.h

/*
 * usr_udp.h
 *
 *  Created on: 2020年1月16日
 *      Author: Scottar
 */


#ifndef SRC_USER_UDP_H_
#define SRC_USER_UDP_H_

#include "lwip/err.h"
#include "lwip/udp.h"
#include "lwip/init.h"
#include "lwipopts.h"
#include "lwip/err.h"
#include "lwipopts.h"
#include "netif/xadapter.h"
#include "xil_printf.h"

int user_udp_init(void);
void udp_printf(void);
void udp_senddata(int data);

#endif /* SRC_USER_UDP_H_ */

4. 過程測試

4.1 上位機接收程序

  reveiver_url = "ipc://11_Router"

    socketzmq = context.socket(zmq.ROUTER)
    socketzmq.set_hwm(HWM_VAL)
    sendinglist=[]

    client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    client_socket.bind(("192.168.127.200", 8080))
    print('we have connected to the tcp data send server!---port is :', port)
    packagenum = 0
    start_time_perf = time.perf_counter()
    start_time_process = time.process_time()
    count = 0
    # 實際上應當啓用的市多線程來做這些事情的
    # 每一個線程要做的事情就是接收對應的內容
    # 我想epics裏面做的也是基本想同樣的事情  ---最後寫一個自動化的腳本多線程
    start_flag = True
    while True:
        b, addr = client_socket.recvfrom(10)
        print('I',count)
        if count==1000000:
            break
        count = count + 1
        # timestample = str(datetime.datetime.now()).encode()
        # b = b + timestample
        #

            # sendinglist.append(b)


    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)  #

    socketzmq.close()

4.2 以100us 發送的速率

(1)下位機的測試程序

(2)測試結果如下:

 

4.3 以10us 的速度進行發送。

(1)下位機的測試的程序

(2) 測試結果

上位機的測試結果:

(3) 上位機去掉print 打印進行結果測試

第一次測試結果

第二次測試結果:

第三次測試結果:

 

 

4.4  設定下位機的上傳延時爲5us

(1)上位機數據接收程序以及下位機發程序

(2)測試結果:

第一次測試結果:

 

第二次測試結果:

第三次測試結果:

 

4.5  設定在7us下的的測試結果

 

測試結論:

對於udp來說,以10us的速度進行上傳,上位機的接收速度沒有辦法完全達到10us 的接受速度, 這樣的原因,可能是由與下位機在發送的時候設定的程序的延時就是10us,但是,由於其調用udp的發送程序,其也會出現相應的時間的消耗,所以整體出現了接收1M個數據包會出現較多的耗時,經過不斷的測試,可以發現將下位機的發送程序設定的延時在7us的情況下,上位機接收到1M個數據包,所消耗的時間十分接近10s,也就是100k/s的上傳的速度。

 

4.6   關於udp數據上傳的丟包情況驗證

4.6.1 下位機的上傳的的速度設定爲100us

(1)上位機的測試代碼

(2)以下位機以100us的速度 ,通過udp進行上傳。上位機接收到數據後,打上時間標籤。

(3) 測試結果如下:

可見,對於這樣的速度的udp來說,也基本不會出現丟包的問題。

 

4.6.2下位機上傳的速度設定爲10us

(1) 下位機數據上傳代碼:

下面的主要代碼是提前設定好了,數據包發送的速度是10us,然後每個數字每10個一輪迴

(2)上位機測試代碼:

(3) 測試結果

通過上面的測試結果,我們可以得出,數據包的傳輸會出現相對較多的丟包情況。

4.6.3 設定下位機的上傳速度是7us

測試打時間標籤的耗時影響。

(1)下位機測試程序代碼:

(2)上位機測試程序

(3) 測試結果

第一次測試結果

第二次測試結果

第三次測試結果

第四次測試結果:

 

(4) 判斷丟包情況

通過上面的測試結果,可以看到會丟包情況。

5 整體接收與轉發的測試:

 

5.1 在另一臺linux 電腦上進行測試數據的接收與打時標(接收1M數據的測試)

(1) 測試接收代碼:

(2) 測試接收耗時:

第一次測試:

第二次測試:

 

5.2 測試接收和轉發1M個數據包的耗時

(1)上位機測試代碼

 

(2) 測試結果:

          

 

5.3  中間的數據監測接收模塊(累積10個數據後發送)(這一節的測試是沒有考慮pub和sub之間的連接的耗時所導致的數據接收,其他地方都進行了考慮)

(1) 上位機接收代碼:

 

(2)中間測試結果

   

(3) 測試結論

    中間累積10個數據包再進行發送,可以大量減少中間數據中轉的情況,但是可以看到實際的數據丟包的情況,其接近1%的丟包率。這個丟包率應該是建立在pub與sub之間的數據包丟包,應當,出現這樣的原因顯然是由於pub-sub的定義的客戶端和服務器不合適,我們通過現在將pub定義成客戶端,連接好sub服務器之後,再進行數據的發送。

 

5.4 中間累積10個數據包後進行數據發送(並且修改成pub 是客戶端)

(1)第一次測試結果:

(2)第二次測試結果

(3) 第三次測試結果:

(4) 第四次測試結果:

 

(5) 第五次測試結果:

 

ps:以上所有的測試結果中,第三層次的測試耗時,都與第二層次的耗時基本一樣,如下:

(6) 第六次測試結果

5.5 測試結論

    通過上面的測試過程與結果,我們可以得到zmq 的發送函數zmq.send 函數頻繁的調用,會出現比較消耗系統的資源,佔用一定的時間。因此,當我們累積10個之後,再進行數據的發送,在這樣的情況下,理論上,可以將由於數據發送的耗時降低10倍

 

6  驗證數據包之間打上時間標籤的時間間隔。

6.1 驗證原理

應當首先默認下位機的上傳的速度是能夠大概在每隔10us 上傳一次數據,我們需要驗證上位機的打時標的前後兩個數據包的時間,保證其前後兩個數據包的時間個間隔在可接受的範圍內,通過上述的測試的結果,我們可以計算每個數據包之間平均間隔。

對於10.68s,對應1M個數據包,即每個數據包的時間爲如下:10.68us,即約10us。

 10.68/1000000
1.068e-05

但是實際上,兩個數據包之間的數據的間隔是總是變化的,因此,我們將這樣的數據包通過單上時間標籤,然後通過通過對包的數據格式的解析之後,將其存儲到數據庫當中,然後對之後存儲進去的數據進行實際的驗證。


6.2 驗證實際兩個數據包之間的間隔

---------------updata----on 2020-0601-------------

6.2.1第三層的數據接收中加入實際的數據的存儲之後,系統耗時加長

(1)測試結果

中間數據監測層耗時:

第三層的數據解析與存儲層次的耗時:

(2) 第二次的測試結果

中間監測層的耗時:

 

第三層的數據的解析與存儲層次的耗時:

(3) 第三次測試結果

中間數據接收和監測層的耗時:

第三層數據解析與存儲層的耗時:

 

6.2.2 查看系統中的數據存儲的數據

 

 

 

 

 

6.2.3 結論:

  通過上面存入到數據庫當中,的時間標籤間隔,可以發現很多數據之間的監測大概都在13us左右,由於我們接收的數據的時間爲13s,因此,每個數據包的時間的間隔約爲13s/1M=13us。

  根據上一小節展示的結果,我們可以看到結果是存在一定成都上的數據包的丟失,但是丟失很少。這也是udp通訊協議不可避免的地方。

 

 

 

6.3 系統資源的監測與分析

6.3.1 系統資源監測

(1) 在數據傳輸過程中的系統監測圖:

(2)監測結論

>>> 100000*(64)*8/1024/1024
48.828125

當下位機設備通過100k/s 的速度進行上傳數據包的時候,我們可以通過將udp的頭部,ip頭部,數據長度加在一起,可以大致計算出實際上傳的速度的大小。

對於tcp,udp ip mac 的幀格式,請參考:https://www.cnblogs.com/OctoptusLian/p/8580052.html

udp+ip的頭部整體的字節數爲:8個字節(udp頭部) +  20字節(ip)+ mac(14) =42字節,我們再額外加上10個字節的數據,我們可以得到52個字節。由於mac會幫助我們進行數據的填充,所以實際還是應當按照64個字節進行計算,結果如上所示,並且也可驗證出我們的結果的正確性。

 

6.3.2  耗時解決方案

    佔用了更多的系統的資源,此時對於udp的數據的接收來說,我們可以通過降低udp數據的發送延時,來提高其發送的速度,從而使得實際的這邊的數據的接收的時間的間隔更加短一些。

 

 

 

 

 

 

 

 

 

 

 

 

 


 

6.0 debug 說明(中間偶爾出現的bug的)

中間的某些時刻有可能會出現,上位機接收不到的情況,很有可能是由於phy芯片自動協商的問題,這個時候,由於筆記本的電腦採用一般都是千兆的網卡, 但是有時候由於介質的原因可能協商到百兆,就會出現意向不到問題,總之,要確定自己的網卡協調後的網速應當是1Gb,如下圖

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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