1、功能介绍

XVC—Xilinx Virtual Cable,是Xilinx推出的基于TCP/IP协议的远程调试方法,可用于Xilinx FPGA的远程下载。其作用是可替代目前常用的Xilinx官方下载器Platform Cable USB Ⅱ,通过网线即可实现FPGA程序的加载和调试,简要结构如下图所示:

ZYNQ通过网线实现与调试机之间的数据传输,ARM处理器通过AXI 转JTAG IP核,将文件通过V7的JTAG口与V7进行交互,实现V7的程序下载与调试,简要结构如下图所示。

2、硬件设计示意图

3、ZYNQ VIVADO功能设计

(1)、将axi转JTAG IP核添加在ZYNQ工程。具体操作:点击Tools,进入Project Settings ,进入IP,找到Repository Manager,将附件中的IP核添加到工程中;

(2)、在BD文件中,添加AXI-Lite to JTAG 核,另增加一个AXI GPIO核,该4T245的使能信号拉到软件顶层,方便在顶层APP进行XVC功能选用。

(3)、由于ZYNQ工作时会有一个默认的高电平,V7的JTAG信号直接拉到ZYNQ时,ZYNQ会将V7的JTAG拉死,造成Xilinx仿真器使用时无法扫到FPGA,因而ZYNQ的JTAG输出信号需要进行相应处理。

4、软件设计

  • 修改内核uio驱动文件“uio_pdrv_genirq.c”:

  • 修改内核配置文件打开“CONFIG_OF”并且编译配置文件,再使用“make menuconfig”命令,进入内核配置菜单,关闭“cpu idle PM support”选项,使能“userspace I/O platform driver with generic IRQ handling”;
  • 修改设备树文件,更改uio驱动:

  • 将xvcServer.c编译后生成的可执行文件传入板卡。注意:xvcServer.c中打开的是“/dev/uio0文件,如有冲突需修改设备树或者xvcServer.c文件。代码如下:
/* This work, "xvcServer.c", is a derivative of "xvcd.c" (https://github.com/tmbinc/xvcd) 
 * by tmbinc, used under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/). 
 * "xvcServer.c" is licensed under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/) 
 * by Avnet and is used by Xilinx for XAPP1251.
 *
 *  Description : XAPP1251 Xilinx Virtual Cable Server for Linux
 */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdint.h>

#include <sys/mman.h>
#include <fcntl.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netinet/in.h> 
#include <pthread.h>

#define MAP_SIZE      0x10000

typedef struct {
  uint32_t  length_offset;
  uint32_t  tms_offset;
  uint32_t  tdi_offset;
  uint32_t  tdo_offset;
  uint32_t  ctrl_offset;
} jtag_t;

static int verbose = 0;

static int sread(int fd, void *target, int len) {
   unsigned char *t = target;
   while (len) {
      int r = read(fd, t, len);
      if (r <= 0)
         return r;
      t += r;
      len -= r;
   }
   return 1;
}

int handle_data(int fd, volatile jtag_t* ptr) {

	const char xvcInfo[] = "xvcServer_v1.0:2048\n"; 

	do {
		char cmd[16];
		unsigned char buffer[2048], result[1024];
		memset(cmd, 0, 16);

		if (sread(fd, cmd, 2) != 1)
			return 1;

		if (memcmp(cmd, "ge", 2) == 0) {
			if (sread(fd, cmd, 6) != 1)
				return 1;
			memcpy(result, xvcInfo, strlen(xvcInfo));
			if (write(fd, result, strlen(xvcInfo)) != strlen(xvcInfo)) {
				perror("write");
				return 1;
			}
			if (verbose) {
				printf("%u : Received command: 'getinfo'\n", (int)time(NULL));
				printf("\t Replied with %s\n", xvcInfo);
			}
			break;
		} else if (memcmp(cmd, "se", 2) == 0) {
			if (sread(fd, cmd, 9) != 1)
				return 1;
			memcpy(result, cmd + 5, 4);
			if (write(fd, result, 4) != 4) {
				perror("write");
				return 1;
			}
			if (verbose) {
				printf("%u : Received command: 'settck'\n", (int)time(NULL));
				printf("\t Replied with '%.*s'\n\n", 4, cmd + 5);
			}
			break;
		} else if (memcmp(cmd, "sh", 2) == 0) {
			if (sread(fd, cmd, 4) != 1)
				return 1;
			if (verbose) {
				printf("%u : Received command: 'shift'\n", (int)time(NULL));
			}
		} else {

			fprintf(stderr, "invalid cmd '%s'\n", cmd);
			return 1;
		}

		int len;
		if (sread(fd, &len, 4) != 1) {
			fprintf(stderr, "reading length failed\n");
			return 1;
		}

		int nr_bytes = (len + 7) / 8;
		if (nr_bytes * 2 > sizeof(buffer)) {
			fprintf(stderr, "buffer size exceeded\n");
			return 1;
		}

		if (sread(fd, buffer, nr_bytes * 2) != 1) {
			fprintf(stderr, "reading data failed\n");
			return 1;
		}
		memset(result, 0, nr_bytes);

		if (verbose) {
			printf("\tNumber of Bits  : %d\n", len);
			printf("\tNumber of Bytes : %d \n", nr_bytes);
			printf("\n");
		}

			int bytesLeft = nr_bytes;
			int bitsLeft = len;
			int byteIndex = 0;
			int tdi, tms, tdo;

			while (bytesLeft > 0) {
				tms = 0;
				tdi = 0;
				tdo = 0;
				if (bytesLeft >= 4) {
					memcpy(&tms, &buffer[byteIndex], 4);
					memcpy(&tdi, &buffer[byteIndex + nr_bytes], 4);

					ptr->length_offset = 32;        
					ptr->tms_offset = tms;         
					ptr->tdi_offset = tdi;       
					ptr->ctrl_offset = 0x01;

					/* Switch this to interrupt in next revision */
					while (ptr->ctrl_offset)
						{
            }

					tdo = ptr->tdo_offset;
					memcpy(&result[byteIndex], &tdo, 4);

					bytesLeft -= 4;
					bitsLeft -= 32;         
					byteIndex += 4;

					if (verbose) {
						printf("LEN : 0x%08x\n", 32);
						printf("TMS : 0x%08x\n", tms);
						printf("TDI : 0x%08x\n", tdi);
						printf("TDO : 0x%08x\n", tdo);
					}

				} else {
					memcpy(&tms, &buffer[byteIndex], bytesLeft);
					memcpy(&tdi, &buffer[byteIndex + nr_bytes], bytesLeft);
          
					ptr->length_offset = bitsLeft;        
					ptr->tms_offset = tms;         
					ptr->tdi_offset = tdi;       
					ptr->ctrl_offset = 0x01;
					/* Switch this to interrupt in next revision */
					while (ptr->ctrl_offset)
						{
            }

					tdo = ptr->tdo_offset;
          
					memcpy(&result[byteIndex], &tdo, bytesLeft);

					if (verbose) {
						printf("LEN : 0x%08x\n", 32);
						printf("TMS : 0x%08x\n", tms);
						printf("TDI : 0x%08x\n", tdi);
						printf("TDO : 0x%08x\n", tdo);
					}
					break;
				}
			}
		if (write(fd, result, nr_bytes) != nr_bytes) {
			perror("write");
			return 1;
		}

	} while (1);
	/* Note: Need to fix JTAG state updates, until then no exit is allowed */
	return 0;
}

int main(int argc, char **argv) {
   int i;
   int s;
   int c; 
   int fd_uio;
   
   struct sockaddr_in address;
   


   opterr = 0;

   while ((c = getopt(argc, argv, "v")) != -1)
      switch (c) {
      case 'v':
         verbose = 1;
         break;
      case '?':
         fprintf(stderr, "usage: %s [-v]\n", *argv);
         return 1;
      }

   fd_uio = open("/dev/uio0", O_RDWR );
   if (fd_uio < 1) {
      fprintf(stderr,"Failed to Open UIO Device\n");
      return -1;
   }

   s = socket(AF_INET, SOCK_STREAM, 0);
               
   if (s < 0) {
      perror("socket");
      return 1;
   }

   volatile jtag_t* ptr = (volatile jtag_t*) mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
					fd_uio, 0);
   if (ptr == MAP_FAILED)
				fprintf(stderr, "MMAP Failed\n");   
        
   close(fd_uio);
   
   i = 1;
   setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof i);

   address.sin_addr.s_addr = INADDR_ANY;
   address.sin_port = htons(2542);
   address.sin_family = AF_INET;

   if (bind(s, (struct sockaddr*) &address, sizeof(address)) < 0) {
      perror("bind");
      return 1;
   }

   if (listen(s, 5) < 0) {
      perror("listen");
      return 1;
   }

   fd_set conn;
   int maxfd = 0;

   FD_ZERO(&conn);
   FD_SET(s, &conn);

   maxfd = s;

   while (1) {
      fd_set read = conn, except = conn;
      int fd;

      if (select(maxfd + 1, &read, 0, &except, 0) < 0) {
         perror("select");
         break;
      }

      for (fd = 0; fd <= maxfd; ++fd) {
         if (FD_ISSET(fd, &read)) {
            if (fd == s) {
               int newfd;
               socklen_t nsize = sizeof(address);

               newfd = accept(s, (struct sockaddr*) &address, &nsize);

//               if (verbose)
                  printf("connection accepted - fd %d\n", newfd);
               if (newfd < 0) {
                  perror("accept");
               } else {
            	   printf("setting TCP_NODELAY to 1\n");
            	  int flag = 1;
            	  int optResult = setsockopt(newfd,
            			  	  	  	  	  	 IPPROTO_TCP,
            			  	  	  	  	  	 TCP_NODELAY,
            			  	  	  	  	  	 (char *)&flag,
            			  	  	  	  	  	 sizeof(int));
            	  if (optResult < 0)
            		  perror("TCP_NODELAY error");
                  if (newfd > maxfd) {
                     maxfd = newfd;
                  }
                  FD_SET(newfd, &conn);
               }
            }
            else if (handle_data(fd,ptr)) {

               if (verbose)
                  printf("connection closed - fd %d\n", fd);
               close(fd);
               FD_CLR(fd, &conn);
            }
         }
         else if (FD_ISSET(fd, &except)) {
            if (verbose)
               printf("connection aborted - fd %d\n", fd);
            close(fd);
            FD_CLR(fd, &conn);
            if (fd == s)
               break;
         }
      }
   }
   munmap((void *) ptr, MAP_SIZE);
   return 0;
}

5、使用方法

如图所示准备环境:

模块上电、系统启动完成后,设置模块及调试机网络地址(同网段,不同地址),后台运行xvcServer程序(ZYNQ APP)(./xvcServer.elf &);

如果模块设计兼容两种调试方式(JTAG/XVC),需要使能XVC,一般为GPIO拉高或者拉低;

在调试机打开vivado2018.3软件(验证时低版本可能会有问题),点击“Open Hardware Manager”; 

在tcl console 命令窗口中输入 “connect_hw_server”,或者点击open target->auto connect;

右键localhost,选择Add Xilinx Virtual Cable(xvc),填写板子上的ZYNQ IP,即可。

可以看到通过将ZYNQ作为JTAG使用,通过网络就连上了模块上面的V7。

6、不足

XVC的性能受处理器影响,在处理器的资源使用过多时,可能会影响XVC的性能。

更多参考xilinx官方材料:

xapp1251.zip

xapp1251-xvc-zynq-petalinux.pdf

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:1 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐