// MLAB Xilinx Virtual Cable Network Server// ----------------------------------------//// (c) miho 2012, 2013 http://www.mlab.cz/PermaLink/XVC_SOFTWARE//// This program if free.////// History://// 1.00 2012_09 Proof of concept (no configuration, not for public release)// 1.01 2012_09 Added parameter for device selection// 1.02 2012_12 Error handling and debugged// 1.03 2012_12 Release version ready to publish// 1.04 2013_04 Socket Bind Error with explanation (multiple instance of XVC Server)// 1.05 2013_04 Test FTDI cable during wait for Accept (to stop the server immediately when cable is disconnected)// 1.06 2013_04 Added support for Linux (thanks to Martin Poviser)// 1.07 2013_04 Rewritten Host Address function for Linux (function gethostbyname returns 127.0.1.1 on Debian systems)// Solved compatibility problems on Linux (FT_SetLatncyTimer requires delay, udev problem with ftdi_sio driver)// 1.08 2013_06 Added linux x86_64 variant////// Purpose://// XILINX development software (ISE, WebPack) supports several types of JTAG programming// cables. Among them there is one particularly interesting. It is Xilinx Virtual Cable// which uses (documented) XVC network protocol to send JTAG commands across TCP/IP network.// So it is possible to realize own hardware/software and have it directly supported by// XILINX development software (both IMPACT and ChipScope).//// This program listens TCP data send by XILINX ISE IMAPACT (or ChipScope) and sends it// to the JTAG device (typically FPGA) connected to FTDI USB Chip. You can use ordinary// USB/RS232 translator based on FT232R chip or you can use our own module from// http://www.mlab.cz/PermaLink/XVC_FT220X//// Target device JTAG port is connected to pins on FTDI USB chip. Program writes to standard// output Which pins are used. Program writes what to set in ISE to enable XVC plugin.////// Environment://// This is Win32 Console Application and run in WinXP / Win7 / Win8 both 32 and 64 bit.//// Program needs to listen to the network so it is necessary to allow doing so. In Windows// firewall configuration enable networking for the exe file.// WinXP: run as Administrator c:\WINDOWS\System32\firewall.cpl and add the exe file// Win7: the system asks directly to do so////// Technology://// The program uses Windows WINSOCK2 library for network communication// and FTDI ftd2xx library for communication with FTDI USB chip.// It can be staticly linked to minimize dependencies on libraries.// Program requires FTDI drivers installed.// Because of the usage of standard libraries you don't need to solve how to sign drivers.//// The program was debug with FT232R and FT220X device.// It should work with any similar FTDI USB chip.//// XVC protocol is documented (you have to ask XILINX support to gain access).// The program is inspired by the work http://debugmo.de/2012/02/xvcd-the-xilinx-virtual-cable-daemon/// Ask Google about Xilinx Virtual Cable.////// Compilation for Windows://// MS Visual C++ 2010 Express (free, registration required)// Create new empty project for Win32 Console Application and name project mlab_xvcd (to build mlab_xvcd.exe)// Header Files / Add / Existing Items - all .h files// Source Files / Add / Existing Items - all .cpp files// Library Files / Add / Existing Items - all .lib .h files from lib_win32 directory// Select Release version (no debug info)// Set static linkage Project Properties / Configuration Release / Configuration Properties// / Code Generation / Runtime Library = Multithreaded (/MT)//// Compilation for Linux://// On Ubuntu 12.04LTS just run the .sh file//// Problems://// Programming of SPI FLASH configuration memory connected to FPGA does not work. No idea why.// It does not work for internal FLASH of Spartan XC3SxxAN either.////// Possible improvements://// External definition of JTAG pins.// Enable Socket Number (to be able to run multiple XVC Servers), now it is constant XVC_TCP_PORT (should be only a default)// Library Definitions// -------------------#undef UNICODE#define WIN32_LEAN_AND_MEAN#include "mlab_xvcd.h" // Program Configuration#include <stdlib.h> // Standard Library (exit, atoi, ...)#include <stdio.h> // Standard IO (printf, ...)#include <signal.h> // CTRL+C handling#ifdef WIN32#include <windows.h> // Windows Console Application#include <winsock2.h> // Windows WinSock2#include <ws2tcpip.h> // Windows WinSock2// Link with library#pragma comment (lib, "Ws2_32.lib")//#pragma comment (lib, "../lib_win32/ftd2xx.lib") // Add this file to Resources#else // not WIN32#include "lib_linux/WinTypes.h"#include <sys/types.h>#include <sys/socket.h>#include <fcntl.h>#include <errno.h>#include <unistd.h>#include <netdb.h>#include <net/if.h>#include <sys/ioctl.h>#include <arpa/inet.h>#endif#define XVC_RX_BUFLEN (XVC_JTAG_LEN/8*2+20) // Length of receive buffer in bytes (command+length+TMSbuffer+TDIbuffer)#define XVC_TX_BUFLEN (XVC_JTAG_LEN/8) // Length of transmit buffer in bytes (TDObuffer)#ifdef WIN32typedef int socklen_t;#else //not WIN32typedef int SOCKET;#define SOCKET_ERROR -1#define INVALID_SOCKET -1void closesocket(int socket){close(socket);}void WSACleanup(){}int WSAGetLastError(){return errno;}#endif// JTAG state machine// ------------------// JTAG Statesenum{test_logic_reset, run_test_idle, // Starts from 0select_dr_scan, capture_dr, shift_dr,exit1_dr, pause_dr, exit2_dr, update_dr,select_ir_scan, capture_ir, shift_ir,exit1_ir, pause_ir, exit2_ir, update_ir,num_states};// JTAG State Machine transfer Functionstatic int jtagStep(int state, int tms){static const int next_state[num_states][2] ={/* JTAG State -->> New State *//* -------------------------------------------------------------*//* | TMS=0 | TMS=1 *//* -------------------------------------------------------------*//* [test_logic_reset] -> */ { run_test_idle, test_logic_reset },/* [run_test_idle] -> */ { run_test_idle, select_dr_scan },/* [select_dr_scan] -> */ { capture_dr, select_ir_scan },/* [capture_dr] -> */ { shift_dr, exit1_dr },/* [shift_dr] -> */ { shift_dr, exit1_dr },/* [exit1_dr] -> */ { pause_dr, update_dr },/* [pause_dr] -> */ { pause_dr, exit2_dr },/* [exit2_dr] -> */ { shift_dr, update_dr },/* [update_dr] -> */ { run_test_idle, select_dr_scan },/* [select_ir_scan] -> */ { capture_ir, test_logic_reset },/* [capture_ir] -> */ { shift_ir, exit1_ir },/* [shift_ir] -> */ { shift_ir, exit1_ir },/* [exit1_ir] -> */ { pause_ir, update_ir },/* [pause_ir] -> */ { pause_ir, exit2_ir },/* [exit2_ir] -> */ { shift_ir, update_ir },/* [update_ir] -> */ { run_test_idle, select_dr_scan }};return next_state[state][tms];}int handleData(SOCKET ClientSocket){bool seen_tlr = false;bool jtagError = false;static int jtag_state;do{int iResult;// Read Commandchar command[16];unsigned int commandLen = 0;// Read String terminated by ':'do{iResult = recv(ClientSocket, command+commandLen, 1, 0);if (iResult==0){printf("\n Connection Closed\n\n");return -1;}else if (iResult==1){commandLen++;}else{fprintf(stderr, "Error Reading Command\n");return -2;}}while (command[commandLen-1]!=':' && commandLen<sizeof(command)-1 );command[commandLen] = char(0);if (0==strncmp(command, "shift:", sizeof(command))){}else{fprintf(stderr, "Invalid Command '%s'\n", command);return -2;}// Read Length (in bits, 32bit integer)int len;iResult = recv(ClientSocket, (char *)&len, 4, 0); // TODO: rewrite for multiplatform useif (iResult==0){printf("\n Connection Closed\n\n");return -1;}if (iResult != 4){fprintf(stderr, "Reading Length Failed\n");return -2;}char buffer[2048];// Read Data (data string for TMS and TDI)unsigned int nr_bytes = (len + 7) / 8;if (nr_bytes * 2 > sizeof(buffer)){fprintf(stderr, "Buffer Size Exceeded\n");return -2;}unsigned int iReceivedBytes=0;while (iReceivedBytes<nr_bytes * 2){iResult = recv(ClientSocket, buffer+iReceivedBytes, nr_bytes * 2 - iReceivedBytes, 0);if (iResult==0){printf("\n Connection Closed\n\n");return -1;}if (iResult<=0){fprintf(stderr, "Reading Data Failed %d %d\n", iResult, nr_bytes * 2);return -2;}iReceivedBytes += iResult;}char result[1024];memset(result, 0, nr_bytes);// Deal with JTAG// Only allow exiting if the state is rti and the IR// has the default value (IDCODE) by going through test_logic_reset.// As soon as going through capture_dr or capture_ir no exit is// allowed as this will change DR/IR.seen_tlr = (seen_tlr || jtag_state == test_logic_reset) && (jtag_state != capture_dr) && (jtag_state != capture_ir);// Due to a weird bug(??) xilinx impacts goes through another "capture_ir"/"capture_dr" cycle after// reading IR/DR which unfortunately sets IR to the read-out IR value.// Just ignore these transactions.if ((jtag_state == exit1_ir && len == 5 && buffer[0] == 0x17) || (jtag_state == exit1_dr && len == 4 && buffer[0] == 0x0b)){// printf("Ignoring Bogus jtag State movement at jtag_state %d\n", jtag_state);}else{for (int i = 0; i < len; ++i){//// Do the actual cycle.//int tms = !!(buffer[i/8] & (1<<(i&7)));//// Track the state.//jtag_state = jtagStep(jtag_state, tms);}if (jtagScan((unsigned char *) buffer, (unsigned char *) buffer + nr_bytes, (unsigned char *) result, len) < 0){//fprintf(stderr, "jtagScan failed\n");// Can't stop now, have to sent (any) answer not to hung the IMPACTjtagError = true;}}// Send the AnsveriResult = send(ClientSocket, result, nr_bytes, 0 );if (iResult == SOCKET_ERROR){printf("Send Failed with Error: %d\n", WSAGetLastError());closesocket(ClientSocket);WSACleanup();return -2;}// printf("Bytes Sent: %d\n", iSendResult);// printf("jtag state %d\n", jtag_state);}while (!(seen_tlr && jtag_state == run_test_idle));return jtagError ? -2 : 0;}// Stop Handler - switch JTAG port off and stop programvoid stopHandler(int){jtagClosePort();exit(1);}// Print help and stop program with errorvoid Help(char *progName){fprintf(stderr, "Bad Parameters\n");fprintf(stderr, "\n");fprintf(stderr, "Usage: %s [arg]\n", progName);fprintf(stderr, "\n");fprintf(stderr, " Where [arg] is one of: \n");fprintf(stderr, " -d Description Fing FTDI device by Description\n");fprintf(stderr, " -l Location Fing FTDI device by Loaction\n");fprintf(stderr, " -s Serial_number Fing FTDI device by it's SN\n");fprintf(stderr, " -n Number Use N-th FTDI device\n");fprintf(stderr, " The first FTDI device is used if no argument\n");exit(2);}int main(int argc, char *argv[]){// Variablesbool verbose = true;// Program Infoprintf("\n");printf("Xilinx Virtual Cable Network Server\n");printf("===================================\n");printf("(c) miho " YEAR " v " VERSION "\n\n");// Get program namechar *cp;char *progName;cp = argv[0];progName=cp;while (cp[0]!='\0'){if (cp[0]=='/' || cp[0]=='\\')progName=cp+1;cp++;}// Process command line paramschar *findDeviceByStr = 0; // String parameterint findDeviceBy = 0; // What does the string meansif (argc>1){if (argc==3){findDeviceByStr = argv[2];if (strcmp(argv[1], "-d")==0){findDeviceBy = OPEN_BY_DESCRIPTION;}else if (strcmp(argv[1], "-l")==0){findDeviceBy = OPEN_BY_LOCATION;}else if (strcmp(argv[1], "-s")==0){findDeviceBy = OPEN_BY_SERIAL_NUMBER;}else if (strcmp(argv[1], "-n")==0){findDeviceBy = 0;}else{Help(progName);}}else{Help(progName);}}else{// Empty String - find device by number and number is emptyfindDeviceBy = 0;findDeviceByStr = (char *)"";}// Find, Init and Open FTDI USB Chipif (jtagOpenPort(findDeviceBy, findDeviceByStr)<0) {// No Device Foundfprintf(stderr, "ERROR: No Device Found\n");return -1;}// Signal Handler (for CRTL+C)signal(SIGINT, &stopHandler);printf("Starting Network Server\n");int iResult;SOCKET ListenSocket = INVALID_SOCKET;SOCKET ClientSocket = INVALID_SOCKET;#ifdef WIN32// Initialize WinsockWSADATA wsaData;iResult = WSAStartup(MAKEWORD(2,2), &wsaData);if (iResult != 0){fprintf(stderr, "WSAStartup failed with error: %d\n", iResult);jtagClosePort();return -2;}#endif// Display HostNamechar sMyName[255];gethostname(sMyName, sizeof(sMyName));printf(" Host Name %s\n", sMyName);// Display Address#ifdef WIN32hostent * pHostInfo;pHostInfo = gethostbyname(sMyName);printf(" Network Name %s\n", pHostInfo->h_name);if (pHostInfo->h_length>0 && pHostInfo->h_length<=16){printf(" Host Address ");for (int i=0; i<pHostInfo->h_length-1; i++){printf("%d.", (unsigned char)pHostInfo->h_addr_list[0][i]);}printf("%d\n", (unsigned char)pHostInfo->h_addr_list[0][pHostInfo->h_length-1]);}#elseint TempSocket;struct ifreq ifreqs[20];struct ifconf ic;ic.ifc_len = sizeof ifreqs;ic.ifc_req = ifreqs;TempSocket = socket(AF_INET, SOCK_DGRAM, 0);if (TempSocket < 0) {perror("socket");return -2;}if (ioctl(TempSocket, SIOCGIFCONF, &ic) < 0) {perror("SIOCGIFCONF");return -2;}for (int i = 0; i < ic.ifc_len/sizeof(struct ifreq); ++i){if (ifreqs[i].ifr_name[0]!='l')// remove loprintf(" Host Address %s: %s\n",ifreqs[i].ifr_name,inet_ntoa(((struct sockaddr_in*)&ifreqs[i].ifr_addr)->sin_addr));}#endif// Create Protocol Structurestruct addrinfo hints;memset(&hints, 0, sizeof(hints));hints.ai_family = AF_INET; // IP6hints.ai_socktype = SOCK_STREAM; // Reliable two-way connectionhints.ai_protocol = IPPROTO_TCP; // Protocol TCPhints.ai_flags = AI_PASSIVE;// Resolve the server address and port (allocate structure "result")struct addrinfo *result = NULL;iResult = getaddrinfo(NULL, XVC_TCP_PORT, &hints, &result);if ( iResult != 0 ){fprintf(stderr, "getaddrinfo failed with error: %d\n", iResult);WSACleanup();jtagClosePort();return -2;}// Create a SOCKETListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);if (ListenSocket == INVALID_SOCKET){fprintf(stderr, "socket failed with error: %d\n", WSAGetLastError());freeaddrinfo(result);WSACleanup();jtagClosePort();return -2;}// Bind the SOCKED (assign the address)iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);if (iResult == SOCKET_ERROR){int LastError=WSAGetLastError();fprintf(stderr, "Bind failed with error: %d\n", LastError);#ifdef WIN32if (LastError==WSAEADDRINUSE)#elseif (LastError==EADDRINUSE)#endiffprintf(stderr, "Trying to start second instance of XVC Server?\n");freeaddrinfo(result);closesocket(ListenSocket);WSACleanup();jtagClosePort();return -2;}if (verbose){printf(" Bound Socket %s\n", XVC_TCP_PORT);}// Help for userprintf(" Set in IMPACT xilinx_xvc host=%s:%s disableversioncheck=true\n", sMyName, XVC_TCP_PORT);freeaddrinfo(result);// Listen SOCKETiResult = listen(ListenSocket, SOMAXCONN);if (iResult == SOCKET_ERROR){fprintf(stderr, "listen failed with error: %d\n", WSAGetLastError());closesocket(ListenSocket);WSACleanup();jtagClosePort();return -2;}printf("\n");do{printf(" Listen\n");jtagSetLED(true);// Set ListenSocket to non-blocking mode// We need during waiting for Accept to detect FTDI disconnect#ifdef WIN32u_long iMode = 1;iResult = ioctlsocket(ListenSocket, FIONBIO, &iMode);if (iResult != NO_ERROR){fprintf(stderr, "ioctlsocket failed with error: %ld\n", iResult);WSACleanup();jtagClosePort();return -2;}#elseiResult = fcntl(ListenSocket, F_GETFL, 0);if (iResult < 0 || fcntl(ListenSocket, F_SETFL, iResult | O_NONBLOCK) < 0){fprintf(stderr, "fcntl failed with error: %d\n", errno);jtagClosePort();return -2;}#endif// Accept a client SOCKET (wait for Accept)sockaddr ClientSocetAddr;socklen_t ClientSocetAddrLen = sizeof(sockaddr);do{// Try Accept (non-blocking)ClientSocket = accept(ListenSocket, &ClientSocetAddr, &ClientSocetAddrLen);if (ClientSocket == INVALID_SOCKET){// Accept Error#ifdef WIN32if (WSAGetLastError() != WSAEWOULDBLOCK)#elseif (WSAGetLastError() != EAGAIN && WSAGetLastError() != EWOULDBLOCK)#endif{fprintf(stderr, "accept failed with error: %d\n", WSAGetLastError());closesocket(ListenSocket);WSACleanup();jtagClosePort();return -2;}// Not yet Accepted{// Check FTDIif (!CheckCable()){fprintf(stderr, "XVC Cable unexpectedly disconnected\n");closesocket(ListenSocket);WSACleanup();jtagClosePort();return -2;}// Sleep some time (do not eat CPU time for nothong)#ifdef WIN32Sleep(100); //ms#elseusleep(100000); //us#endif}}}while (ClientSocket == INVALID_SOCKET);// Set (Accepted) Socket to blocking mode#ifdef WIN32iMode = 0;iResult = ioctlsocket(ClientSocket, FIONBIO, &iMode);if (iResult != NO_ERROR){fprintf(stderr, "ioctlsocket failed with error: %ld\n", iResult);WSACleanup();jtagClosePort();return -2;}#elseiResult = fcntl(ListenSocket, F_GETFL, 0);if (iResult < 0 || fcntl(ListenSocket, F_SETFL, iResult & ~O_NONBLOCK) < 0){fprintf(stderr, "fcntl failed with error: %d\n", errno);jtagClosePort();return -2;}#endif// Print Accepted + Addressprintf(" Accepted ");jtagSetLED(false);for (int i=2; i<2+4-1; i++){printf("%d.", (unsigned char)ClientSocetAddr.sa_data[i]);}printf("%d:%d\n", (unsigned char)ClientSocetAddr.sa_data[2+4-1], (unsigned char)ClientSocetAddr.sa_data[0]*256+(unsigned char)ClientSocetAddr.sa_data[1]);// Process Data until the peer shuts down the connectionint Cnt = 0;printf(" Handle Data ");do{iResult = handleData(ClientSocket);if (iResult>=0){printf(".");fflush(stdout);Cnt++;if (Cnt>40){Cnt = 0;printf("\n ");}}}while (iResult >= 0);// Connection Closed by peerif (iResult==-1){// JTAG portjtagSetIdle();}// Error - shutdown the connectionif (iResult==-2){fprintf(stderr, " Disconnect\n");#ifdef WIN32iResult = shutdown(ClientSocket, SD_SEND);#elseiResult = shutdown(ClientSocket, SHUT_WR);#endifif (iResult == SOCKET_ERROR){fprintf(stderr, "shutdown failed with error: %d\n", WSAGetLastError());}iResult=-2; // Error}// cleanupclosesocket(ClientSocket);}// If not Error Listen Againwhile (iResult!=-2);// cleanupclosesocket(ListenSocket);WSACleanup();jtagClosePort();return 1;}