ZYNQ基於LWIP裸跑的千兆以太網TCP/UDP筆記

最近調試了ZYNQ用LWIP裸跑千兆以太網TCP/UDP協議,趁着這幾天有時間記錄一下,總結一下,同時也分享給大家,互相學習,共同進步。。。。。
對於我們初學者,都是站在巨人的肩膀上慢慢的學習,我的代碼借鑑的這位博主的源碼,在他的基礎上進行了修改:
FPGADesigner《學會Zynq》系列目錄與傳送門
這位博主好久都沒有更新博客了,不知道是不是工作太忙了還是改行了。。。。。。。。

這部分源碼支持TCP和UDP,同時也支持迴環比官方的echo測試例子好太多,我個人也是整理了半個月。

eth.h


#include "sleep.h"
#include "xparameters.h"
#include "xil_exception.h"
#include "xdebug.h"
#include "xscugic.h"
#include "lwip/err.h"
#include "lwip/udp.h"
#include "lwip/init.h"
#include "lwip/tcp.h"
#include "lwip/tcp_impl.h"
#include "lwipopts.h"
#include "netif/xadapter.h"
#include "xil_printf.h"
#include <stdio.h>
#include "xadcps.h"
#include "xil_types.h"
#include "Xscugic.h"
#include "Xil_exception.h"
#include "xscutimer.h"
#include <string.h>

#include "timer_intr.h"
#include "sys_intr.h"
#include "user_udp.h"
#include "user_udp_sendto.h"
#include "user_udp_echo.h"
#include "user_tcp_send_client.h"
#include "user_tcp_send_server.h"

extern int eth_test(u32 XPAR_XEMACPS_BASEADDR,u32 TEST_UDP_TCP,u32 tcp_echo);

eth.c


#include "eth.h"
#define TIMER_LOAD_VALUE_025    XPAR_CPU_CORTEXA9_0_CPU_CLK_FREQ_HZ / 8 //0.25S
#define TIMER_LOAD_VALUE_1    	XPAR_CPU_CORTEXA9_0_CPU_CLK_FREQ_HZ / 2 //1S
#define INTC_DEVICE_ID 	XPAR_SCUGIC_SINGLE_DEVICE_ID
#define TIMER_DEVICE_ID     XPAR_XSCUTIMER_0_DEVICE_ID
#define TIMER_IRPT_INTR     XPAR_SCUTIMER_INTR
// 1.ZYNQ作爲客戶端的時候,調試時,先打開上位機再跑ZYNQ
// 2.ZYNQ作爲服務端時,先運行ZYNQ,再打開上位機的client.
// 3.注意要正確設置好IP地址和端口號
//當使用usr_tcp_send_client模式時,遠程爲服務器端,需要先開啓服務器
//當使用usr_tcp_send_server模式時,開發板爲服務器,需要先下載好開發板的代碼,遠程的客戶端纔可以連接

volatile int send_printf = 0;//發送幀是否打印
volatile int receive_printf = 0;//接收幀是否打印
volatile int send_delay_time = 10;//us,發送一幀的間隔
volatile int tcp_server_echo_sel = 0;//1:tcp server傳輸迴環,0不迴環

//#define TEST_UDP_TCP 4  //1:"user_udp",2:"user_udp_sendto",3:"user_udp_echo",4:"user_tcp_send_client ",5:"user_tcp_send_server"
int eth_test(u32 XPAR_XEMACPS_BASEADDR,u32 TEST_UDP_TCP,u32 tcp_echo)
{
	static  XScuGic Intc; //GIC
	static  XScuTimer Timer;//timer
	struct netif *netif,server_netif;
	static unsigned local_port = 23;	//本地端口
	static unsigned remote_port = 45;	//遠程端口    //-----------------修改
	// 在 UDP 協議中測得一次發送1458個數據,最大爲119.58MB
	// 在TCP client協議中測得一次發送1024個數據,最大爲30.0MB左右,server協議中測得一次發送1024個數據,最大爲30.00MB左右,但是會隨着時間得增加,速率會降到5.0MB左右
	unsigned send_length = 1458;// UDP一次發送數據長度  <=1458, TCP大於等於108
	struct ip_addr local_ipaddr,local_netmask,local_gw;
	struct ip_addr remote_ipaddr;
	//1.板子的mac地址
	unsigned char mac_ethernet_address[]={0x00,0x0a,0x35,0x00,0x01,0x23};  //-----------------修改
	tcp_server_echo_sel = tcp_echo;
	//2.開啓中斷系統和定時器系統(跑TCP的時候才需要定時器系統)
	if((TEST_UDP_TCP==1)||(TEST_UDP_TCP==2)||(TEST_UDP_TCP==3))
		Timer_init(&Timer,TIMER_LOAD_VALUE_1,TIMER_DEVICE_ID);//定時1s的中斷
	if((TEST_UDP_TCP==4)||(TEST_UDP_TCP==5))
		Timer_init(&Timer,TIMER_LOAD_VALUE_025,TIMER_DEVICE_ID);//定時0.25s的中斷
	Init_Inter_System(&Intc);
	Setup_Inter_Exception(&Intc);
	Timer_Setup_Intr_System(&Intc,&Timer,TIMER_IRPT_INTR);
	Timer_start(&Timer);

	netif = &server_netif;
	//3.設置本地MAC地址
	IP4_ADDR(&local_ipaddr, 192, 168, 11,23); //本地IP地址
	IP4_ADDR(&local_netmask,255,255,255, 0); //網絡掩碼
	IP4_ADDR(&local_gw,	  192, 168, 11, 1); //網關
	IP4_ADDR(&remote_ipaddr, 192, 168, 11, 45);//遠程IP地址  //-----------------修改
	xil_printf("************eth config parameter************\r\n");
	xil_printf("    mac addr is is   : 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x. \r\n",mac_ethernet_address[0],mac_ethernet_address[1],
				mac_ethernet_address[2],mac_ethernet_address[3],mac_ethernet_address[4],mac_ethernet_address[5]);
	xil_printf("    local_ipaddr is  : %d.%d.%d.%d \r\n",(local_ipaddr.addr)&0xff,(local_ipaddr.addr>>8)&0xff,(local_ipaddr.addr>>16)&0xff,(local_ipaddr.addr>>24)&0xff);
	xil_printf("    local_netmask is : %d.%d.%d.%d \r\n",(local_netmask.addr)&0xff,(local_netmask.addr>>8)&0xff,(local_netmask.addr>>16)&0xff,(local_netmask.addr>>24)&0xff);
	xil_printf("    local_gw is      : %d.%d.%d.%d \r\n",(local_gw.addr)&0xff,(local_gw.addr>>8)&0xff,(local_gw.addr>>16)&0xff,(local_gw.addr>>24)&0xff);
	xil_printf("    remote_ipaddr is : %d.%d.%d.%d \r\n",(remote_ipaddr.addr)&0xff,(remote_ipaddr.addr>>8)&0xff,(remote_ipaddr.addr>>16)&0xff,(remote_ipaddr.addr>>24)&0xff);
	xil_printf("    local port       : %d \r\n",local_port);
	xil_printf("    remote port is   : %d \r\n",remote_port);
	xil_printf("    send data len is : %d \r\n",send_length);
	xil_printf("********************************************\r\n");
	//4.初始化lwip
	lwip_init();
	//5.添添加網絡接口
	if(!xemac_add(netif,&local_ipaddr,&local_netmask,&local_gw,mac_ethernet_address,XPAR_XEMACPS_BASEADDR)){
		xil_printf("Error adding N/W interface \r\n");
		return -1;
	}
	//6.將其設置爲默認接口
	netif_set_default(netif);
	//7.啓動網絡
	netif_set_up(netif);

	if(TEST_UDP_TCP==1) {
		xil_printf("--- test user_udp--- \r\n ");
		user_udp(netif,&remote_ipaddr,local_port,remote_port,send_length);	//測試結果該函數跑一段時間後會死掉
	}
	if(TEST_UDP_TCP==2){
		xil_printf("---test user_udp_sendto ---\r\n");
		user_udp_sendto(netif,&remote_ipaddr,local_port,remote_port,send_length);
	}
	if(TEST_UDP_TCP==3){
		xil_printf("---test user_udp_echo ---\r\n");
		user_udp_echo(netif,remote_ipaddr,local_port,remote_port);//測試結果該函數可以返回對端UDP送過來的數據,但是就是延時有將近半秒鐘
	}
	if(TEST_UDP_TCP==4){
		xil_printf("---test user_tcp_send_client ---\r\n");
		user_tcp_send_client(netif,remote_ipaddr,local_port,remote_port,send_length);//
	}
	if(TEST_UDP_TCP==5){
		xil_printf("---test user_tcp_send_server ---\r\n");
		user_tcp_send_server(netif,remote_ipaddr,local_port,remote_port,send_length);//
	}

	return 0;
}
//************************************************************************************
//但凡使用lwIP的程序,無論TCP還是UDP,在進入while(1)循環前,都會有這樣一個配置流程:
//1.設置開發板MAC地址
//2.開啓中斷系統
//3.設置本地IP地址
//4.初始化lwIP
//5.添加網絡接口
//6.設置默認網絡接口
//7.啓動網絡
//8.初始化TCP或UDP連接(自定義函數)
//9.在while(1)循環中,第一件事必然是使用xemacif_input函數將MAC隊列中的包傳輸到lwIP棧中,這是Xilinx適配器提供的函數。再之後纔是用戶代碼
//************************************************************************************
//8.使用UDP/TCP通信

///////////////////////////////////////////////////////////////----------------------------------------------------------------////////////////////////////////////////////////////////////////
sys_intr.h

#include "xparameters.h"
#include "xil_exception.h"
#include "xdebug.h"
#include "xscugic.h"

#define INTC_DEVICE_ID 	XPAR_SCUGIC_SINGLE_DEVICE_ID

int Init_Inter_System(XScuGic *IntcInstancePtr);
void Setup_Inter_Exception(XScuGic *IntcInstancePtr);

sys_intr.c


#include "sys_intr.h"

//初始化中斷系統
int Init_Inter_System(XScuGic *IntcInstancePtr)
{
	int Status;

	XScuGic_Config *IntcConfig;
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	if(NULL==IntcConfig) {
		return XST_FAILURE;
	}

	Status = XScuGic_CfgInitialize(IntcInstancePtr,IntcConfig,IntcConfig->CpuBaseAddress);
	if(Status!=XST_SUCCESS){
		return XST_FAILURE;
	}
	return XST_SUCCESS;
}

//設置中斷異常
void Setup_Inter_Exception(XScuGic *IntcInstancePtr)
{
	Xil_ExceptionInit();
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
		(Xil_ExceptionHandler)XScuGic_InterruptHandler,
		(void *)IntcInstancePtr);
	Xil_ExceptionEnable();
}

///////////////////////////////////////////////////////////////----------------------------------------------------------------////////////////////////////////////////////////////////////////

timer_intr.h

#ifndef SRC_TIMER_INTR_H_
#define SRC_TIMER_INTR_H_

//timer_intr.h文件代碼如下:
#include <stdio.h>
#include "xadcps.h"
#include "xil_types.h"
#include "Xscugic.h"
#include "Xil_exception.h"
#include "xscutimer.h"

extern volatile int TcpTmrFlag;

#define TIMER_DEVICE_ID     XPAR_XSCUTIMER_0_DEVICE_ID
#define TIMER_IRPT_INTR     XPAR_SCUTIMER_INTR

void Timer_start(XScuTimer *TimerPtr);
void Timer_Setup_Intr_System(XScuGic *GicInstancePtr,XScuTimer *TimerInstancePtr, u16 TimerIntrId);
int Timer_init(XScuTimer *TimerPtr,u32 Load_Value,u32 DeviceId);


#endif /* SRC_TIMER_INTR_H_ */

timer_intr.c

#include "timer_intr.h"

volatile int TcpTmrFlag;

// 定時器中斷處理函數
static void TimerIntrHandler(void *CallBackRef)
{

    XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
    XScuTimer_ClearInterruptStatus(TimerInstancePtr);
    TcpTmrFlag = 1;
}

// 啓動定時器函數
void Timer_start(XScuTimer *TimerPtr)
{
XScuTimer_Start(TimerPtr);
}

//定時器中斷設置函數
void Timer_Setup_Intr_System(XScuGic *GicInstancePtr,XScuTimer *TimerInstancePtr, u16 TimerIntrId)
{
XScuGic_Connect(GicInstancePtr, TimerIntrId,
         (Xil_ExceptionHandler)TimerIntrHandler, (void *)TimerInstancePtr);

XScuGic_Enable(GicInstancePtr, TimerIntrId);
XScuTimer_EnableInterrupt(TimerInstancePtr);
}

// 定時器初始化函數
int Timer_init(XScuTimer *TimerPtr,u32 Load_Value,u32 DeviceId)
{
     XScuTimer_Config *TMRConfigPtr;
     TMRConfigPtr = XScuTimer_LookupConfig(DeviceId);
     XScuTimer_CfgInitialize(TimerPtr,TMRConfigPtr,TMRConfigPtr->BaseAddr);
     XScuTimer_LoadTimer(TimerPtr, Load_Value);
     XScuTimer_EnableAutoReload(TimerPtr);

     return 1;
}

///////////////////////////////////////////////////////////////----------------------------------------------------------------////////////////////////////////////////////////////////////////
user_tcp_send_client.h
在這裏插入圖片描述

user_tcp_send_client.c
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述在這裏插入圖片描述
在這裏插入圖片描述

///////////////////////////////////////////////////////////////----------------------------------------------------------------////////////////////////////////////////////////////////////////
user_tcp_send_server.h

在這裏插入圖片描述
user_tcp_send_server.c

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

///////////////////////////////////////////////////////////////----------------------------------------------------------------////////////////////////////////////////////////////////////////
user_udp.h
在這裏插入圖片描述

user_udp.c
在這裏插入圖片描述
在這裏插入圖片描述

///////////////////////////////////////////////////////////////----------------------------------------------------------------////////////////////////////////////////////////////////////////

user_udp_echo.h

在這裏插入圖片描述

user_udp_echo.c

在這裏插入圖片描述
在這裏插入圖片描述

///////////////////////////////////////////////////////////////----------------------------------------------------------------////////////////////////////////////////////////////////////////
user_udp_sendto.h
在這裏插入圖片描述

user_udp_sendto.c

在這裏插入圖片描述

需要注意的是,代碼中主動發送數據是使用最大的速率發送,經過測試UDP協議最大速率達到了114MBytes左右、TCP協議的最大速率達到了100MBytes左右,滿足千兆速率。但是如果使用LWIP的默認設置則跑一會就會報告出錯,主要原因的LWIP的緩存資源不夠,因此需要將LWIP的資源設置增大。
以下圖片來自 米聯客官方的 S02-CH27 利用LWIP實現以太網數據傳輸 ,這個設置跑最大速率的時候很有必要,我也懶得去SDK中截圖了,直接截圖了他的文章,米聯客官方的很多文檔都寫得不錯,值得拿來學習。
在這裏插入圖片描述
在這裏插入圖片描述

ZYNQ得PS上有兩個網口可以設置,ETH0和ETH1,在網上找了很多資料都沒有找到說使用LWIP裸跑同時跑兩個網口,講的都是使用操作系統同時跑兩個網口,這個還值得再研究研究。。。。。。。

另外Vivado2017.4的SDK中調用LWIP庫的時候,有時候修改了bit文件,SDK重新加載的時候會出現LWIP庫找不到的情況,不知道你們有沒有遇到,我是遇到了很多次,然後我的解決方法就是再bsp中先去掉LWIP庫編譯一次,然後再加上LWIP庫編譯就好了,但是這時LWIP庫的參數又變成默認的了,因爲LWIP庫的參數都是保存在system.mss文件中,所以我會提前將system.mss文件備份,後面再替換一次就可以了,參數就自動添加進去了。
在這裏插入圖片描述
在這裏插入圖片描述




聲明:所有文章屬於個人在工作中所記下和蒐集的筆記,不得轉載







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