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文件备份,后面再替换一次就可以了,参数就自动添加进去了。
在这里插入图片描述
在这里插入图片描述




声明:所有文章属于个人在工作中所记下和搜集的笔记,不得转载







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