基於ZYNQ的千兆網項目(2)

項目描述

上一篇文章我們講解了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, %08x\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個數據,進而證明了我們實驗的正確性。

總結

創作不易,認爲文章有幫助的同學們可以關注、點贊、轉發支持。爲行業貢獻及其微小的一部分。對文章有什麼看法或者需要更近一步交流的同學,可以加入下面的羣:
在這裏插入圖片描述

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