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,如下圖