/* XSVF Parser for Project VGA, see . * v0.2 - Early beta (works for XC95144XL) * (C) Copyright 2008, Michael Meeuwisse * All Rights Reserved. * Credits to Reinder de Haan * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * How to compile in Cygwin: * Copy ftd2xx.h and ftd2xx.lib to the same folder this file is in. * gcc -Wall -o xsvfparser xsvfparser.c ftd2xx.lib */ #include #include #include #include #include #include #include #include "ftd2xx.h" /* Give debug data, 0: None, 1: Commands, 2: MPSSE Output */ #define DEBUG 0 /* Report error and force quit */ void error(const char *msg, ...) { va_list arg; fprintf(stderr, "\nError: "); va_start(arg, msg); vfprintf(stderr, msg, arg); va_end(arg); fprintf(stderr, "\n"); exit(1); } /* wrapper-malloc */ void *wmalloc(size_t size) { void *ret = calloc(1, size); if(!ret) error("No memory?"); return ret; } FT_HANDLE openDevice() { char *devID = "projectvga mod1 rev0 A"; FT_HANDLE devHandle = NULL; DWORD len = 0; // ------- DEBUG; 0x04 instead of 0x00 gives a 1.2MHz clock to make sure we're not having any speed issues char jtagSettings[7] = { 0x80, 0x08, 0x0B, /* Set lower bits behavior */ 0x85, /* Disconnect TDI/DO - TDO/DI */ 0x86, 0x04, 0x00 }; /* Set TCK/SK divisor */ /* Open device, set some defaults, enable MPSSE, configure JTAG */ if(FT_OpenEx(devID, FT_OPEN_BY_DESCRIPTION, &devHandle) != FT_OK || FT_ResetDevice(devHandle) != FT_OK || FT_SetBitMode(devHandle, 0x00, 0) != FT_OK || FT_SetLatencyTimer(devHandle, 2) != FT_OK || FT_SetTimeouts(devHandle, 5000, 5000) != FT_OK || FT_SetBitMode(devHandle, 0x0B, 2) != FT_OK || FT_Write(devHandle, jtagSettings, 7, &len) != FT_OK) return NULL; return devHandle; } /* Input file buffer & handles */ typedef struct _inbuf { int fileHandle; char *data; unsigned int cur; unsigned int len; #if DEBUG == 0 unsigned int infoCur; unsigned int infoMax; #endif } inbuf; #define INBUFSIZE 65536 /* Get a byte of data from inbuf */ unsigned char get(inbuf *input) { if(input->cur == input->len) { if(!input->data) input->data = wmalloc(INBUFSIZE); #if DEBUG == 0 if(!input->infoMax) { input->infoMax = lseek(input->fileHandle, 0, SEEK_END); lseek(input->fileHandle, 0, SEEK_SET); } #endif if((input->len = read(input->fileHandle, input->data, INBUFSIZE)) == -1) error("Failed to read File"); if(input->len == 0) error("Premature End of File"); input->cur = 0; } #if DEBUG == 0 if(!(input->infoCur++ % 100)) fprintf(stderr, "\rProgress: %.2f%% done.", ((float) input->infoCur / (float) input->infoMax) * 100); #endif return input->data[input->cur++]; } /* Output device buffer */ typedef struct _outbuf { FT_HANDLE devHandle; char *data; unsigned char *bytes; unsigned int bytesPending; unsigned int bytesSize; unsigned char bits; unsigned int bitsPending; unsigned int cur; unsigned char curState; enum INSTR_OPT { INSTR_W = 0, INSTR_RW = 0x20 } enableRW; } outbuf; /* FTDI recommends this as it equals the USB packetsize */ #define OUTBUFSIZE 3968 /* Max number of output bytes for MPSSE command */ #define MAXTCKCMD (1 << 16) /* Round up and give back number of bytes */ #define bits2bytes(x) ((x + 7) / 8) /* Send off the data buffer to the device */ void flush(outbuf *output) { int len = output->cur, retries = 5; DWORD tmp; #if DEBUG == 2 fprintf(stderr, "Flush: "); for(len = 0; len < output->cur; len++) fprintf(stderr, "%02x", 0xFF & output->data[len]); fprintf(stderr, "\n"); len = output->cur; #endif /* Retry if not the entire buffer was send, fail after 5 attempts */ while(len && retries) { if(FT_Write(output->devHandle, output->data + output->cur - len, len, &tmp) != FT_OK || !retries--) error("Couldn't write to Device"); len -= tmp; } output->cur = 0; } /* Put a byte in the output buffer */ void put(outbuf *output, unsigned char data) { if(output->cur >= OUTBUFSIZE - 1) flush(output); output->data[output->cur++] = data; } /* Put instruction to write bits on TDI in output buffer */ void putBitsInstr(outbuf *output, unsigned int numBits, unsigned char bits) { if(!numBits) return; put(output, 0x1B | output->enableRW); put(output, 0x07 & (numBits - 1)); put(output, bits); } /* Put instructions to write bytes on TDI in output buffer */ void putBytesInstr(outbuf *output, unsigned int bufSize, unsigned char *data) { while(bufSize > 1) { unsigned int i = bufSize; if(bufSize > MAXTCKCMD) bufSize -= MAXTCKCMD; else bufSize = 0; put(output, 0x19 | output->enableRW); put(output, 0xFF & ((i - bufSize) - 1)); put(output, 0xFF & (((i - bufSize) - 1) >> 8)); while(i-- > bufSize) if(data) put(output, data[i]); else put(output, 0x00); } if(bufSize) { if(data) putBitsInstr(output, 0x08, data[0]); else putBitsInstr(output, 0x08, 0x00); bufSize = 0; } } /* Create instructions for pending buffer */ void buildTCKInstr(outbuf *output) { putBytesInstr(output, output->bytesPending, output->bytes); output->bytesPending = 0; if(output->bitsPending) { putBitsInstr(output, output->bitsPending, output->bits); output->bitsPending = 0; } } /* Put x bits from inbuf in pending buffer for outbuf */ void putBits(outbuf *output, inbuf *input, unsigned int numBits) { unsigned char bits = 0x00, numBytes = numBits / 8; if(!numBits) error("Trying to write 0 bits"); numBits %= 8; if(numBits) bits = get(input); buildTCKInstr(output); while(numBytes--) { /* Also used for IR, which can be accessed before XSDRSIZE */ if(output->bytesPending > output->bytesSize) buildTCKInstr(output); output->bytes[output->bytesPending++] = get(input); } output->bits = bits; output->bitsPending = numBits; } /* Put x bits from cache in pending buffer for outbuf */ void putBitsFromCache(outbuf *output, unsigned char *cache, unsigned int numBits) { unsigned int numBytes = numBits / 8; if(!numBits) error("Trying to read from null-sized cache"); numBits %= 8; buildTCKInstr(output); output->bits = cache[numBytes + (numBits? 1: 0)]; output->bitsPending = numBits; while(numBytes--) output->bytes[output->bytesPending++] = cache[numBytes]; } /* Set pending buffer size */ void setBuffersSize(outbuf *output, unsigned int numBits) { if(!numBits || bits2bytes(numBits) < output->bytesSize) return; buildTCKInstr(output); if(output->bytes) free(output->bytes); output->bytesSize = bits2bytes(numBits); output->bytes = wmalloc(bits2bytes(numBits)); } /* Set or Clear the Readback option for TDO */ void setEnableRW(outbuf *output, enum INSTR_OPT direction) { /* Clear buffer if it contains data not matching the new option */ if(output->enableRW != direction) buildTCKInstr(output); output->enableRW = direction; } /* Get x bytes from TDO */ unsigned int getTDO(outbuf *output, unsigned char *buffer, unsigned int numBytes) { DWORD ret; /* Make sure nothing is pending */ buildTCKInstr(output); flush(output); if(FT_Read(output->devHandle, buffer, numBytes, &ret) != FT_OK || numBytes != ret) error("Failed to read from Device %d %d", numBytes, ret); return ret; } /* Shift between stable moves and from these to Shift-DR or Shift-IR */ static unsigned char moveState[6 /* From */][6 /* To */][2 /* TMS sequence, TCK times */] = { /* To: RTI Shift-DR Pause-DR Shift-IR Pause-IR From: */ {{0x00, 0x01}, {0x02, 0x04}, {0x0A, 0x05}, {0x06, 0x05}, {0x16, 0x06}}, /* TLR */ {{0x00, 0x00}, {0x01, 0x03}, {0x05, 0x04}, {0x03, 0x04}, {0x0B, 0x05}}, /* RTI */ {{0x03, 0x03}, {0x00, 0x00}, {0x01, 0x02}, {0x0F, 0x06}, {0x2F, 0x07}}, /* Shift-DR */ {{0x03, 0x03}, {0x01, 0x02}, {0x00, 0x00}, {0x0F, 0x06}, {0x2F, 0x07}}, /* Pause-DR */ {{0x03, 0x03}, {0x07, 0x05}, {0x17, 0x06}, {0x00, 0x00}, {0x01, 0x02}}, /* Shift-IR */ {{0x03, 0x03}, {0x07, 0x05}, {0x17, 0x06}, {0x01, 0x02}, {0x00, 0x00}} /* Pause-IR */ }; /* Translate value from xsvf to state value which, if < 0x06, can be looked up in moveState */ static unsigned char xsvfState[16] = { 0x00, 0x01, 0x06, 0x07, 0x02, 0x08, 0x03, 0x09, 0x0A, 0x0B, 0x0C, 0x04, 0x0D, 0x05, 0x0E, 0x0F }; /* If TMS = 0, what state would you end up in next */ static unsigned char nextState[16] = { 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x02, 0x03, 0x02, 0x01, 0x0C, 0x04, 0x05, 0x04, 0x01 }; /* Move from current state to newState (as XSVF value) */ void gotoState(outbuf *output, unsigned char newState) { unsigned char tck = 0x00; newState = xsvfState[newState]; /* Are we there yet? - unless TLR */ if(output->curState == newState && newState != 0x00) return; /* Steal bit from pending buffer, overlap TDI output with TMS output */ if(output->bytesPending || output->bitsPending) { if(!output->bitsPending) { output->bits = output->bytes[output->bytesPending--]; output->bitsPending = 8; } tck = 0x80 & (output->bits << (8 - output->bitsPending--)); } buildTCKInstr(output); /* Reset */ if(newState == 0x00) { put(output, 0x4B); put(output, 0x05); put(output, tck | 0x1F); output->curState = newState; return; } /* Single state */ if(output->curState > 0x05 || newState > 0x05) { put(output, 0x4B); put(output, 0x00); put(output, tck | (nextState[output->curState] == newState? 0x00: 0x01)); output->curState = newState; return; } if(output->curState > 0x05) error("Invalid state move from 0x%02x to 0x%02x", output->curState, newState); /* Stable states */ put(output, 0x4B); put(output, moveState[output->curState][newState - 1][1] - 1); put(output, tck | moveState[output->curState][newState - 1][0]); output->curState = newState; } /* XSVF state values */ #define RTI 0x01 #define SHIFTDR 0x04 #define EXIT1DR 0x05 #define PAUSEDR 0x06 #define UPDATDR 0x08 #define SHIFTIR 0x0B #define PAUSEIR 0x0D /* Go to RTI, output x clocks, wait x microseconds */ void putRunTest(outbuf *output, unsigned int numBits) { enum INSTR_OPT cacheRW = output->enableRW; output->enableRW = INSTR_W; gotoState(output, RTI); if(!numBits) return; putBytesInstr(output, bits2bytes(numBits), NULL); flush(output); usleep(numBits); output->enableRW = cacheRW; } /* Get string of x bits from inbuf and put it in buf */ void getString(inbuf *input, char *buf, unsigned int numBits) { unsigned int len = bits2bytes(numBits); if(!buf) error("Trying to write to null-sized buffer"); while(len--) buf[len] = get(input); } /* Get integer from inbuf */ unsigned int getInt(inbuf *input) { unsigned int ret = get(input) | get(input) << 8 | get(input) << 16 | get(input) << 24; return ntohl(ret); } /* Get short from inbuf */ unsigned short getShort(inbuf *input) { unsigned short ret = get(input) | get(input) << 8; return ntohs(ret); } /* Compare tdoCache with tdoMatch after applying (optional) tdoMask for len bits */ unsigned int compareTDO(outbuf *output, unsigned char *tdoMask, unsigned char *tdoCache, unsigned char *tdoMatch, unsigned int len) { unsigned int failed = 0, bits = len % 8, tmp; getTDO(output, tdoCache, bits2bytes(len)); len = tmp = bits2bytes(len) - 1; if(bits) tdoCache[len] = tdoCache[len] >> (8 - bits); do { if(!tdoMask) { if(tdoCache[len] != tdoMatch[len]) failed = 1; } else if((tdoCache[len] & tdoMask[len]) != (tdoMatch[len] & tdoMask[len])) failed = 1; } while(len-- && !failed); #if DEBUG == 1 if(failed) { len = tmp; fprintf(stderr, "tdoMask:\t0x"); do fprintf(stderr, "%02x", 0xFF & tdoMask[len]); while(len--); len = tmp; fprintf(stderr, "\ntdoCache:\t0x"); do fprintf(stderr, "%02x", 0xFF & tdoCache[len]); while(len--); len = tmp; fprintf(stderr, "\ntdoMatch:\t0x"); do fprintf(stderr, "%02x", 0xFF & tdoMatch[len]); while(len--); fprintf(stderr, "\n"); } #endif return failed; } /* Parse XSVF commands from inbuf and output commands to outbuf */ int parseFile(outbuf *output, inbuf *input) { unsigned int len = 0, xruntest = 0; unsigned char xendirState = RTI, xenddrState = RTI, xrepeat = 0x20; unsigned char *tdoMask = NULL, *tdoCache = NULL, *tdoMatch = NULL, *tdiCache = NULL; while(1) { unsigned char cmd = get(input); #if DEBUG == 1 fprintf(stderr, "cmd: 0x%02x\n", cmd); #endif switch(cmd) { case 0x00: /* XCOMPLETE */ // if(output->curState != 0x00) // gotoState(output, 0x00); buildTCKInstr(output); flush(output); #if DEBUG == 0 fprintf(stdout, "\rProgress: 100%% done. \n"); #endif return 0; case 0x01: /* XTDOMASK */ getString(input, tdoMask, len); break; case 0x04: /* XRUNTEST */ xruntest = getInt(input); #if DEBUG == 1 fprintf(stderr, "RUN: %d\n", xruntest); #endif break; case 0x07: /* XREPEAT */ xrepeat = get(input); #if DEBUG == 1 fprintf(stderr, "REP: %d\n", xrepeat); #endif break; case 0x08: /* XSDRSIZE */ if(len) { free(tdoMask); free(tdoCache); free(tdoMatch); free(tdiCache); } len = getInt(input); #if DEBUG == 1 fprintf(stderr, "LEN: %d\n", len); #endif tdoMask = wmalloc(bits2bytes(len)); tdoCache = wmalloc(bits2bytes(len)); tdoMatch = wmalloc(bits2bytes(len)); tdiCache = wmalloc(bits2bytes(len)); setBuffersSize(output, len); break; case 0x03: /* XSDR */ case 0x09: /* XSDRTDO */ { unsigned int tries = 0, tdoResult = 0; /* Use cache, we might need to output these commands xrepeat times */ getString(input, tdiCache, len); if(cmd == 0x09) getString(input, tdoMatch, len); setEnableRW(output, INSTR_RW); do { gotoState(output, SHIFTDR); putBitsFromCache(output, tdiCache, len); gotoState(output, EXIT1DR); if((tdoResult = compareTDO(output, tdoMask, tdoCache, tdoMatch, len))) { /* Error handling routine for XC9500 */ gotoState(output, PAUSEDR); gotoState(output, SHIFTDR); xruntest *= 1.25; /* Don't report the first few times - that gets annoying */ if(tries < xrepeat && tries > 0x03) fprintf(stderr, "\nWARNING: compareTDO failed, retrying %d/%d", tries + 1, xrepeat); } else gotoState(output, UPDATDR); /* Now wait for a moment */ putRunTest(output, xruntest); } while(tdoResult && tries++ < xrepeat); if(tdoResult) error("Failed TDO check instruction 0x%02x", cmd); } break; case 0x0C: /* XSDRB */ gotoState(output, SHIFTDR); case 0x0D: /* XSDRC */ case 0x0E: /* XSDRE */ setEnableRW(output, INSTR_W); putBits(output, input, len); if(cmd == 0x0E) gotoState(output, xenddrState); break; case 0x0F: /* XSDRTDOB */ gotoState(output, SHIFTDR); case 0x10: /* XSDRTDOC */ case 0x11: /* XSDRTDOE */ setEnableRW(output, INSTR_RW); putBits(output, input, len); getString(input, tdoMatch, len); if(compareTDO(output, NULL, tdoCache, tdoMatch, len)) error("Failed TDO check instruction 0x%02x", cmd); if(cmd == 0x11) gotoState(output, xenddrState); break; case 0x12: /* XSTATE */ gotoState(output, get(input)); break; case 0x13: /* XENDIR */ { unsigned char new = get(input); if(new > 0x01) error("Invalid XENDIR State 0x%02x", new); xendirState = new? PAUSEIR: RTI; } break; case 0x14: /* XENDDR */ { unsigned char new = get(input); if(new > 0x01) error("Invalid XENDDR State 0x%02x", new); xenddrState = new? PAUSEDR: RTI; } break; case 0x02: /* XSIR */ case 0x15: /* XSIR2 */ { unsigned int tdiLen = (int) (cmd == 0x02? get(input): getShort(input)); #if DEBUG == 1 fprintf(stderr, "SIR %d\n", tdiLen); #endif gotoState(output, SHIFTIR); setEnableRW(output, INSTR_W); putBits(output, input, tdiLen); if(xruntest) putRunTest(output, xruntest); else gotoState(output, xendirState); } break; case 0x16: /* XCOMMENT */ while(get(input)); break; case 0x17: /* XWAIT */ { unsigned char end = 0x00; gotoState(output, get(input)); end = get(input); usleep(getInt(input)); gotoState(output, end); } break; default: error("Unknown command 0x%02x", cmd); } } return 0; } int main(int argc, char *argv[]) { inbuf *input = wmalloc(sizeof(inbuf)); outbuf *output = wmalloc(sizeof(outbuf)); output->data = wmalloc(OUTBUFSIZE); /* Unknown size yet, but it's also used for XSIR so set it to something */ output->bytes = wmalloc(128); output->bytesSize = 128; if(argc < 2) { fprintf(stderr, "XSVF Parser for FT2232 based JTAG Programmers"); error("Usage: %s File_to_parse", argv[0]); } if(!(output->devHandle = openDevice())) error("Unable to open Device"); if((input->fileHandle = open(argv[1], O_RDONLY)) == -1) error("Unable to open File"); if(parseFile(output, input)) error("Couldn't parse File"); close(input->fileHandle); free(input); FT_Close(output->devHandle); free(output->data); free(output->bytes); free(output); return 0; }