基于ZYNQ的千兆网项目(2)

    技术2022-07-10  103

    基于ZYNQ的TCP Server实现

    项目描述PL端设计PS端设计下板测试总结

    项目描述

    上一篇文章我们讲解了ZYNQ做Client来进行与PC机进行通信,那么相应的ZYNQ就可以做Server来进行与PC机得通信,此时PC机就是Client。这种通信方式在板卡与板卡之间得通信中还是比较常见,所以就用这篇文章来进行相应得讲解。

    工程描述:讲ZYNQ当作Server来进行与上位机通信,实现千兆网的循环测试。

    本次实验所用到的软硬件环境如下: 1、VIVADO 2019.1 2、米联客MZ7015FA开发板 3、NetAssist网络调试助手

    PL端设计

    这里与上一篇文章相同,我们也没有在PL端进行相应的设计。所以我们PL端的设计没有任何代码只是例化了一个ZYNQ的IP,如下:

    PS端设计

    与前面ZYNQ做从机的文章相同,这篇文章也用到了开源的系统框架,所以我们也需要设置BSP文件。 首先右击相应工程的bsp文件,选择Board Support Package Setting 然后点击相应的lwip 然后重新生成相应的bsp文件即可。

    本例程使用 RAW API,即函数调用不依赖操作系统。传输效率也比 SOCKET API 高, (具体可参考 xapp1026)。 将 use_axieth_on_zynq 和 use_emaclite_on_zynq 设为 0。如下图所示。 修改 lwip_memory_options 设置,将 mem_size, memp_n_pbuf, mem_n_tcp_pcb, memp_n_tcp_seg 这 4 个参数 值设大,这样会提高 TCP 传输效率。如下图所示。 修改 pbuf_options 设置,将 pbuf_pool_size 设大,增加可用的 pbuf 数量,这样同样会提高 TCP 传输效率。如下 图所示。 修改 tcp_options 设置,将 tcp_snd_buf, tcp_wnd 参数设大,这样同样会提高 TCP 传输效率。如下图所示。 修改 temac_adapter_options 设置,将 n_rx_descriptors 和 n_tx_descriptors 参数设大。这样可以提高 zynq 内部 emac dma 的数据迁移效率,同样能提高 TCP 传输效率。如下图所示。 所以需要手动修改 LWIP 库让网口芯片工作于 1000Mbps。 其余选项的参数默认即可,不用修改。点击 OK,重建 bsp。 一般情况下,修改完会自动更新,如果没有更新,手动更新一下,选中 bsp—>右键—> Re-generate BSP Sources。重新生成一下 BSP 包。上面进行这样设置的原因是为了增加lwip的缓存,进而提高千兆网的通信速度。 main.c函数

    #include <stdio.h> #include "xscugic.h" #include "xparameters.h" #include "sleep.h" #include "xscutimer.h" #include "lwip/err.h" #include "lwip/tcp.h" #include "lwip/init.h" #include "lwipopts.h" #include "netif/xadapter.h" #include "lwipopts.h" #include "lwip/priv/tcp_priv.h" #include "tcp_transmission.h" #define GIC_ID XPAR_PS7_SCUGIC_0_DEVICE_ID #define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR #define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID #define TIMER_LOAD_VALUE 0x13D92D3F/8 //1S #define TCP_RXBUFFER_BASE_ADDR 0x10000000 #define PC_TCP_SERVER_PORT 5001 void TimerIntrHandler(void *CallBackRef); int initimer(); int initSwIntr(); int inittcp(struct netif *netif); int tcp_recv_init(); err_t tcp_connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err); err_t tcp_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); static err_t tcp_sent_callback(void *arg, struct tcp_pcb *tpcb, u16_t len); void send_received_data(); static XScuGic ScuGic; static XScuGic_Config * ScuGicCfgPtr; XScuTimer Timer; XScuTimer_Config *Config; volatile int TcpTmrFlag; int flag; int rec_cnt; int main() { int status; err_t err; struct netif *netif, server_netif; netif = &server_netif; status = initSwIntr(); status = initimer(); status = inittcp(netif); if(status != XST_SUCCESS){ return status; } tcp_recv_init(); while(1){ if(TcpTmrFlag){ if(request_pcb->state == CLOSED || (request_pcb->state == SYN_SENT && request_pcb->nrtx == TCP_SYNMAXRTX)){ request_pcb = tcp_new(); if (!request_pcb) { xil_printf("txperf: Error creating PCB. Out of Memory\r\n"); return -1; } //ip_set_option(request_pcb, SOF_REUSEADDR); err = tcp_connect(request_pcb, &ipaddress, port, tcp_connected_callback); if (err != ERR_OK) { xil_printf("txperf: tcp_connect returned error: %d\r\n", err); return err; } } tcp_tmr(); TcpTmrFlag = 0; } /*receive input packet and control command from emac*/ xemacif_input(netif);//将MAC队列里的packets传输到你的LwIP/IP stack里 /* if connected to the server and received start command, * start receiving data from PL through AXI DMA, * then transmit the data to the PC using TCP * */ if(tcp_client_connected && flag == 1) send_received_data(); } return 0; } int initSwIntr(){ int status; Xil_ExceptionInit(); ScuGicCfgPtr = XScuGic_LookupConfig(GIC_ID); status = XScuGic_CfgInitialize(&ScuGic,ScuGicCfgPtr,ScuGicCfgPtr->CpuBaseAddress); if(status != XST_SUCCESS){ return status; } Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&ScuGic); status = XScuGic_Connect(&ScuGic,TIMER_IRPT_INTR,(Xil_ExceptionHandler)TimerIntrHandler,&Timer); if(status != XST_SUCCESS){ return status; } XScuGic_Enable(&ScuGic,TIMER_IRPT_INTR); Xil_ExceptionEnable(); return XST_SUCCESS; } int initimer(){ int status; Config = XScuTimer_LookupConfig(TIMER_DEVICE_ID); status = XScuTimer_CfgInitialize(&Timer, Config, Config->BaseAddr); XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE); //自动装载 XScuTimer_EnableAutoReload(&Timer); XScuTimer_Start(&Timer); XScuTimer_EnableInterrupt(&Timer);//一定等定时器初始化好了之后再开始使能定时器中断 return status; } void TimerIntrHandler(void *CallBackRef){ XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef; XScuTimer_ClearInterruptStatus(TimerInstancePtr); TcpTmrFlag = 1; } int inittcp(struct netif *netif){ struct ip4_addr ipaddr, netmask, gw; /* the mac address of the board. this should be unique per board */ unsigned char mac_ethernet_address[] = { 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 }; /*local ip address*/ IP4_ADDR(&ipaddr, 192, 168, 2, 10); IP4_ADDR(&netmask, 255, 255, 255, 0); IP4_ADDR(&gw, 192, 168, 2, 1); /*lwip library init*/ lwip_init(); /* Add network interface to the netif_list, and set it as default */ if (!xemac_add(netif, &ipaddr, &netmask, &gw, mac_ethernet_address, XPAR_XEMACPS_0_BASEADDR)) { xil_printf("Error adding N/W interface\r\n"); return -1; } netif_set_default(netif); /* specify that the network if is up */ netif_set_up(netif); return XST_SUCCESS; } int tcp_recv_init() { struct tcp_pcb *pcb; err_t err; /* create new TCP PCB structure */ tcp_rx_buffer = (u32 *)TCP_RXBUFFER_BASE_ADDR; pcb = tcp_new(); if (!pcb) { xil_printf("tcp_server: Error creating PCB. Out of Memory\r\n"); return -1; } /* bind to local port */ err = tcp_bind(pcb, IP_ADDR_ANY, local_port); if (err != ERR_OK) { xil_printf("tcp_server: Unable to bind to port %d: err = %d\r\n", local_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("tcp_server: Out of memory while tcp_listen\r\n"); return -3; } /* specify callback to use for incoming connections */ tcp_accept(pcb, tcp_connected_callback); return 0; } err_t tcp_connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err) { xil_printf("txperf: Connected to iperf server\r\n"); /* store state */ connected_pcb = tpcb; /* set callback values & functions */ //tcp_arg(tpcb, NULL); tcp_sent(connected_pcb, tcp_sent_callback); tcp_recv(connected_pcb, tcp_recv_callback); /* disable nagle algorithm to ensure * the last small segment of a ADC packet will be sent out immediately * with no delay * */ //tcp_nagle_disable(tpcb); //if(!tcp_nagle_disabled(tpcb)) // xil_printf("tcp nagle disable failed!\r\n"); tcp_client_connected = 1; /* initiate data transfer */ return ERR_OK; } err_t tcp_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { //err_t error; struct pbuf *q; u32 remain_length; q = p; flag = 1; rec_cnt = q->tot_len; /* close socket if the peer has sent the FIN packet */ if (p == NULL) { tcp_close(tpcb); xil_printf("tcp connection closed\r\n"); return ERR_OK; } /*if received ip fragment packets*/ if(q->tot_len > q->len) { remain_length = q->tot_len; while(remain_length > 0) { memcpy(tcp_rx_buffer + file_length, q->payload, q->len); file_length += q->len; remain_length -= q->len; /*go to next pbuf pointer*/ q = q->next; } } /*if received no ip fragment packets*/ else { memcpy(tcp_rx_buffer, q->payload, q->len); } /*change the endian of received command*/ *tcp_rx_buffer = ntohl(*tcp_rx_buffer); //xil_printf("tcp data come in!%d, %d, x\r\n", p->tot_len, p->len, *file); /* tell lwip we've received the tcp packet */ tcp_recved(tpcb, p->tot_len); pbuf_free(p); return ERR_OK; } static err_t tcp_sent_callback(void *arg, struct tcp_pcb *tpcb, u16_t len) { err_t err; tcp_trans_done = 1; err = tcp_output(tpcb); if (err != ERR_OK) { xil_printf("txperf: Error on tcp_output: %d\r\n",err); return -1; } return ERR_OK; } void send_received_data() { err_t err; struct tcp_pcb *tpcb = connected_pcb; flag = 0; if (!connected_pcb) return; /* if tcp send buffer has enough space to hold the data we want to transmit from PL, then start tcp transmission*/ *tcp_rx_buffer = ntohl(*tcp_rx_buffer); err = tcp_write(tpcb,tcp_rx_buffer , rec_cnt, TCP_WRITE_FLAG_COPY & (~TCP_WRITE_FLAG_MORE)); if (err != ERR_OK) { xil_printf("txperf: Error on tcp_write: %d\r\n", err); connected_pcb = NULL; return; } err = tcp_output(tpcb); if (err != ERR_OK) { xil_printf("txperf: Error on tcp_output: %d\r\n",err); return; } packet_index++; }

    tcp_transmission.h头文件

    /* * tcp_transmission.h * * Created on: 2017年3月13日 * Author: 201607062058 */ #ifndef TCP_TRANSMISSION_H_ #define TCP_TRANSMISSION_H_ #include <stdio.h> #include "xadcps.h" #include "xil_types.h" #include "Xscugic.h" #include "Xil_exception.h" #define TCP_START_CMD 0xAA55FFA0 #define TCP_STOP_CMD 0xAA55FFB1 #define TCP_RESET_CMD 0xAA55FFC1 #define PC_TCP_SERVER_PORT 5001 static unsigned local_port = 5010; #define HEADER_ID0 0xAA55AA55 #define HEADER_ID1 0xAA55AA55 #define HEADER_SIZE (16) #define ADC_PACKET_LENGTH (16 * 1023) #define TCP_PACKET_SIZE (ADC_PACKET_LENGTH + HEADER_SIZE) #define TCP_RXBUFFER_BASE_ADDR 0x10000000 volatile int tcp_trans_start; volatile int tcp_trans_reset; unsigned first_trans_start; volatile u32 packet_index; volatile unsigned tcp_client_connected; struct tcp_pcb *connected_pcb; struct tcp_pcb *request_pcb; volatile int tcp_trans_done; volatile u32 file_length; struct ip4_addr ipaddress; u16_t port; u32 *tcp_rx_buffer; typedef struct packet_header { u32 ID0; u32 ID1; u32 frame_cnt; u32 length; }packet_header; packet_header *header_p; #endif /* TCP_TRANSMISSION_H_ */

    因为这里ZYNQ是所为Server来使用的,所以这边接收来自Client的请求信号。相应的代码及中断函数的设置代码如下: 上面的TCP协议使用到了我们前面讲解的定时器中断,因为TCP协议需要保证稳定的连接,每隔一定时间检测连接的稳定性。代码如下: TCP协议初始化代码: TCP协议中断服务函数如下: ZYNQ接收中断响应函数,可以看出,这部分的处理与上一篇ZYNQ做Client的文章非常相似,其实只要是ZYNQ接收中断几乎都是下面的代码。

    ZYNQ作为Server发射数据的函数: ZYNQ发射中断服务函数: 因为这篇文章与上一篇ZYNQ做Client的文章非常相似,包括我们验证的方法都是循环测试,所以可以先尝试调通上面文章中的程序再来调试这篇博客。

    下板测试

    我们利用NetAssist网络调试助手对其进行TCP循环测试,结果如下:

    这里特别注意一定要让ZYNQ跑起来,否则无法正常连接,因为主机没工作。从上面接过可以看出PC机发送数据与接收数据相一致,且测试了7328个数据,进而证明了我们实验的正确性。

    总结

    创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。为行业贡献及其微小的一部分。对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群:

    Processed: 0.009, SQL: 9