Zynq與PC間的以太網通信實驗(二)——PS側代碼分析


主函數:

 1 int main()
 2 {
 3     uint cycle = 0;
 4     ip_addr_t ipaddr, netmask, gw;
 5     RxBufNo = 0;
 6     RxBufferPtr[0] = (u16 *)RX_BUFFER0_BASE;
 7     RxBufferPtr[1] = (u16 *)RX_BUFFER1_BASE;
 8 
 9     /* the mac address of the board. this should be unique per board */
10     unsigned char mac_ethernet_address[] = { 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };
11 
12     XGpio_Initialize(&Gpio, XPAR_AXI_GPIO_0_DEVICE_ID);//初始化AXI-GPIO
13     XGpio_SetDataDirection(&Gpio, 1, 0);
14 
15 
16 /* Define this board specific macro in order perform PHY reset on ZCU102 */
17 
18     // dma configuration
19     int ret;
20     ret = dma_init(&AxiDma);
21     if(ret!=XST_SUCCESS)
22         xil_printf("dma init error!");
23     ret = Timer_init(&Timer, TIMER_LOAD_VALUE, 0);
24     if(ret!=1)
25         xil_printf("timer init failed!\n");
26     ret = dma_intr_init(&AxiDma);
27     if(ret!=XST_SUCCESS){
28         xil_printf("dma interrup init failed!\n");
29     }
30     Timer_Setup_Intr_System(&Intc,&Timer, XPAR_SCUTIMER_INTR);
31 
32     echo_netif = &server_netif;
33     /* initliaze IP addresses to be used */
34     IP4_ADDR(&ipaddr,  192, 168,   1, 10);
35     IP4_ADDR(&netmask, 255, 255, 255,  0);
36     IP4_ADDR(&gw,      192, 168,   1,  1);
37 
38     lwip_init();
39 
40       /* Add network interface to the netif_list, and set it as default */
41     if (!xemac_add(echo_netif, &ipaddr, &netmask,
42                         &gw, mac_ethernet_address,
43                         PLATFORM_EMAC_BASEADDR)) {
44         xil_printf("Error adding N/W interface\n\r");
45         return -1;
46     }
47     netif_set_default(echo_netif);
48 
49     /* specify that the network if is up */
50     netif_set_up(echo_netif);
51 
52     /* start the application (web server, rxtest, txtest, etc..) */
53     client_application();
54     // start data generation progress in PL side by AXI4-Lite bus
55     XGpio_DiscreteWrite(&Gpio, 1, 1);
56     Timer_start(&Timer);
57 
58     /* receive and process packets */
59     while (1) {
60         if (TcpFastTmrFlag) {
61             tcp_fasttmr();
62             TcpFastTmrFlag = 0;
63         }
64         if (TcpSlowTmrFlag) {
65             tcp_slowtmr();
66             TcpSlowTmrFlag = 0;
67         }
68         xemacif_input(echo_netif);
69         if(connected_pcb!=NULL){
70             send_data();
71         }
72     }
73     return 0;
74 }
main函數

首先是各種初始化,包括GPIO初始化,DMA初始化,定時器初始化;其次是DMA中斷和定時器中斷的初始化;然后是LWIP的初始化(IP地址、端口號、網關),添加PS側的MAC,設置回調函數;通過GPIO開啟PL側數據的產生。

主循環中先判斷是否已經建立好連接,如果已經建立了連接,那么就啟動send_data函數:

 1 int send_data()
 2 {
 3     err_t err;
 4     int ret;
 5     struct tcp_pcb *tpcb = connected_pcb;
 6 
 7     if (!tpcb){
 8         xil_printf("tpcb created error!\r\n");
 9         return -1;
10     }
11     if(!first_dma_start){
12         ret = XAxiDma_SimpleTransfer(&AxiDma, (UINTPTR)RxBufferPtr[RxBufNo&1],MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
13         if(ret!=XST_SUCCESS)
14             xil_printf("first dma start failed!\n");
15         first_dma_start = 1;
16     }
17     if(dma_com_flag){
18         //判斷發送數據長度是否小於發送緩沖區剩余可用長度
19         if (TX_SIZE < tcp_sndbuf(tpcb)) {
20             //Write data for sending (but does not send it immediately).
21         //        err = tcp_write(tpcb, data, TX_SIZE, 1);
22             err = tcp_write(tpcb, RxBufferPtr[RxBufNo&1], TX_SIZE, 1);
23             if (err != ERR_OK) {
24                 xil_printf("txperf: Error on tcp_write: %d\r\n", err);
25                 connected_pcb = NULL;
26                 return -1;
27             }
28 
29             //Find out what we can send and send it
30             err = tcp_output(tpcb);
31             if (err != ERR_OK) {
32                 xil_printf("txperf: Error on tcp_output: %d\r\n",err);
33                 return -1;
34             }
35             dma_com_flag = 0;
36             RxBufNo++;
37             XAxiDma_SimpleTransfer(&AxiDma, (UINTPTR)RxBufferPtr[(RxBufNo)&1], MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
38         }
39     }
40     return 0;
41 }
send_data函數

首先判斷是否已經啟動了第一次DMA傳輸,如果還沒有,就先啟動一次DMA傳輸;如果已經啟動了,那么就判斷DMA傳輸是否已經完成,如果已經完成一次DAM傳輸,那么就啟動TCP傳輸對應緩沖的數據,同時再開啟另一塊緩沖的DMA傳輸。其中DMA傳輸完成的標志是在DMA傳輸中斷處理函數RxIntrHandler中設置的:

 1 static void RxIntrHandler(void *Callback)
 2 {
 3     u32 IrqStatus;
 4     int TimeOut;
 5     XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
 6 
 7     /* Read pending interrupts */
 8     IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);
 9 
10     /* Acknowledge pending interrupts */
11     XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);
12 
13     /*
14      * If no interrupt is asserted, we do not do anything
15      */
16     if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
17         return;
18     }
19 
20     /*
21      * If error interrupt is asserted, raise error flag, reset the
22      * hardware to recover from the error, and return with no further
23      * processing.
24      */
25     if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
26 
27         Error = 1;
28 
29         /* Reset could fail and hang
30          * NEED a way to handle this or do not call it??
31          */
32         XAxiDma_Reset(AxiDmaInst);
33 
34         TimeOut = RESET_TIMEOUT_COUNTER;
35 
36         while (TimeOut) {
37             if(XAxiDma_ResetIsDone(AxiDmaInst)) {
38                 break;
39             }
40 
41             TimeOut -= 1;
42         }
43 
44         return;
45     }
46 
47     /*
48      * If completion interrupt is asserted, then set RxDone flag
49      */
50     if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {
51         if(!dma_com_flag)
52             dma_com_flag = 1;
53     }
54 }
RxIntrHandler函數

send_data函數中的tcp_write函數和tcp_output函數是真正實現TCP數據傳輸功能的函數。若處於連接狀態,且發送緩沖區容量比帶發送數據量大,則調用tcp_write將待發送數據寫入發送緩沖區,之后調用tcp_output函數立即傳輸發送緩沖區內容。如果不調用tcp_output,LWIP會等待數據量達到一定值時一起發送來提高效率,是否調用tcp_output函數可根據具體需求而定。

client_application函數:

 1 int client_application()
 2 {
 3     struct tcp_pcb *pcb;
 4     ip_addr_t ipaddr;
 5     err_t err;
 6     unsigned port = 7;
 7 
 8     /* create new TCP PCB structure */
 9     pcb = tcp_new();
10     if (!pcb) {
11         xil_printf("Error creating PCB. Out of Memory\n\r");
12         return -1;
13     }
14 
15     /* connect to iperf tcp server */
16     IP4_ADDR(&ipaddr,  192, 168,   1, 209);//設置要連接的主機的地址
17 
18     //當連接到主機時,調用tcp_connected_callback
19     err = tcp_connect(pcb, &ipaddr, port, tcp_connected_callback);
20     if (err != ERR_OK) {
21         xil_printf("txperf: tcp_connect returned error: %d\r\n", err);
22         return err;
23     }
24 
25     return 0;
26 }
client_application函數

在本例程中, zynq 作為客戶端, PC 作為服務器。 由 zynq 向 PC 主動發起 TCP 連接請求,通過 tcp_connect 函數便可以完成這個過程。 該函數的參數包含了一個回調函數指針tcp_connected_fn, 該回調函數將在 TCP 連接請求三次握手完成后被自動調用。 該回調函數被調用時代表客戶端和服務器之間的 TCP 連接建立完成。 在本例程中, 該回調函數被定義為 tcp_connected_callback。

 1 static err_t tcp_connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err)
 2 {
 3     /* store state */
 4     connected_pcb = tpcb;
 5 
 6     /* set callback values & functions */
 7     tcp_arg(tpcb, NULL);
 8 
 9     //發送到遠程主機后調用tcp_sent_callback
10     tcp_sent(tpcb, tcp_sent_callback);
11 
12     client_connected = 1;
13 
14     /* initiate data transfer */
15     return ERR_OK;
16 }
tcp_connected_callback函數

在該函數中,通過 tcp_sent 函數配置另一個 TCP 發送完成的回調函數tcp_sent_callback。

1 static err_t tcp_sent_callback(void *arg, struct tcp_pcb *tpcb,u16_t len)
2 {
3     tcp_trans_done ++;
4     return ERR_OK;
5 }
tcp_sent_callback函數

該回調函數在每個 TCP 包發送完成后會被自動調用, 代表 TCP 包發送完成。 該回調函數在本例程中僅作發送完成數據包的計數。

 

參考:

1. 米聯客視頻教程及《Zynq SOC修煉秘籍》

2. https://www.cnblogs.com/moluoqishi/p/9865480.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM