Problem with comparison.
/Modules/CPLD_FPGA/XILINX_XVC/XVC_SOFTWARE/XVC_1x/mlab_xvcd.cpp |
---|
0,0 → 1,751 |
// 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 WIN32 |
typedef int socklen_t; |
#else //not WIN32 |
typedef int SOCKET; |
#define SOCKET_ERROR -1 |
#define INVALID_SOCKET -1 |
void closesocket(int socket) |
{ |
close(socket); |
} |
void WSACleanup() |
{ |
} |
int WSAGetLastError() |
{ |
return errno; |
} |
#endif |
// JTAG state machine |
// ------------------ |
// JTAG States |
enum |
{ |
test_logic_reset, run_test_idle, // Starts from 0 |
select_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 Function |
static 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 Command |
char 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); // pøepsat pøenositelnì |
if (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 IMPACT |
jtagError = true; |
} |
} |
// Send the Ansver |
iResult = 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 program |
void stopHandler(int) |
{ |
jtagClosePort(); |
exit(1); |
} |
// Print help and stop program with error |
void 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[]) |
{ |
// Variables |
bool verbose = true; |
// Program Info |
printf("\n"); |
printf("Xilinx Virtual Cable Network Server\n"); |
printf("===================================\n"); |
printf("(c) miho " YEAR " v " VERSION "\n\n"); |
// Get program name |
char *cp; |
char *progName; |
cp = argv[0]; |
progName=cp; |
while (cp[0]!='\0') |
{ |
if (cp[0]=='/' || cp[0]=='\\') |
progName=cp+1; |
cp++; |
} |
// Process command line params |
char *findDeviceByStr = 0; // String parameter |
int findDeviceBy = 0; // What does the string means |
if (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 empty |
findDeviceBy = 0; |
findDeviceByStr = (char *)""; |
} |
// Find, Init and Open FTDI USB Chip |
if (jtagOpenPort(findDeviceBy, findDeviceByStr)<0) { |
// No Device Found |
fprintf(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 Winsock |
WSADATA wsaData; |
iResult = WSAStartup(MAKEWORD(2,2), &wsaData); |
if (iResult != 0) |
{ |
fprintf(stderr, "WSAStartup failed with error: %d\n", iResult); |
jtagClosePort(); |
return -2; |
} |
#endif |
// Display HostName |
char sMyName[255]; |
gethostname(sMyName, sizeof(sMyName)); |
printf(" Host Name %s\n", sMyName); |
// Display Address |
#ifdef WIN32 |
hostent * 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]); |
} |
#else |
int 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 lo |
printf(" Host Address %s: %s\n", |
ifreqs[i].ifr_name, |
inet_ntoa(((struct sockaddr_in*)&ifreqs[i].ifr_addr)->sin_addr)); |
} |
#endif |
// Create Protocol Structure |
struct addrinfo hints; |
memset(&hints, 0, sizeof(hints)); |
hints.ai_family = AF_INET; // IP6 |
hints.ai_socktype = SOCK_STREAM; // Reliable two-way connection |
hints.ai_protocol = IPPROTO_TCP; // Protocol TCP |
hints.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 SOCKET |
ListenSocket = 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 WIN32 |
if (LastError==WSAEADDRINUSE) |
#else |
if (LastError==EADDRINUSE) |
#endif |
fprintf(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 user |
printf(" Set in IMPACT xilinx_xvc host=%s:%s disableversioncheck=true\n", sMyName, XVC_TCP_PORT); |
freeaddrinfo(result); |
// Listen SOCKET |
iResult = 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 WIN32 |
u_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; |
} |
#else |
iResult = 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 WIN32 |
if (WSAGetLastError() != WSAEWOULDBLOCK) |
#else |
if (WSAGetLastError() != EAGAIN && WSAGetLastError() != EWOULDBLOCK) |
#endif |
{ |
fprintf(stderr, "accept failed with error: %d\n", WSAGetLastError()); |
closesocket(ListenSocket); |
WSACleanup(); |
jtagClosePort(); |
return -2; |
} |
// Not yet Accepted |
{ |
// Check FTDI |
if (!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 WIN32 |
Sleep(100); //ms |
#else |
usleep(100000); //us |
#endif |
} |
} |
} |
while (ClientSocket == INVALID_SOCKET); |
// Set (Accepted) Socket to blocking mode |
#ifdef WIN32 |
iMode = 0; |
iResult = ioctlsocket(ClientSocket, FIONBIO, &iMode); |
if (iResult != NO_ERROR) |
{ |
fprintf(stderr, "ioctlsocket failed with error: %ld\n", iResult); |
WSACleanup(); |
jtagClosePort(); |
return -2; |
} |
#else |
iResult = 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 + Address |
printf(" 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 connection |
int 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 peer |
if (iResult==-1) |
{ |
// JTAG port |
jtagSetIdle(); |
} |
// Error - shutdown the connection |
if (iResult==-2) |
{ |
fprintf(stderr, " Disconnect\n"); |
#ifdef WIN32 |
iResult = shutdown(ClientSocket, SD_SEND); |
#else |
iResult = shutdown(ClientSocket, SHUT_WR); |
#endif |
if (iResult == SOCKET_ERROR) |
{ |
fprintf(stderr, "shutdown failed with error: %d\n", WSAGetLastError()); |
} |
iResult=-2; // Error |
} |
// cleanup |
closesocket(ClientSocket); |
} |
// If not Error Listen Again |
while (iResult!=-2); |
// cleanup |
closesocket(ListenSocket); |
WSACleanup(); |
jtagClosePort(); |
return 1; |
} |