/* * Lib(X)SVF - A library for implementing SVF and XSVF JTAG players * * Copyright (C) 2009 RIEGL Research ForschungsGmbH * Copyright (C) 2009 Clifford Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * * A JTAG SVF/XSVF Player based on libxsvf for the FTDI FT232H, FT2232H and * FT4232H High Speed USB to Multipurpose UART/FIFO ICs. * * This also serves as an example program for using libxvsf with asynchonous * hardware interfaces. Have a look at 'xsvftool-gpio.c' for a simple libxsvf * example for synchonous interfaces (such as register mapped GPIOs). * * IMPORTANT NOTE: You need libftdi [1] (version 0.16 or newer) installed * to build this program. * * To run it at full speed you need a version of libftdi that has been * compiled with '--with-async-mode', and must enable all four defines * BLOCK_WRITE, ASYNC_WRITE, BACKGROUND_READ and INTERLACED_READ_WRITE below. * * [1] http://www.intra2net.com/en/developer/libftdi/ */ #include "libxsvf.h" #define BUFFER_SIZE (1024*16) #define BLOCK_WRITE // #define ASYNC_WRITE // #define BACKGROUND_READ // #define INTERLACED_READ_WRITE #include #include #include #include #include #include #include #include #include #ifdef BACKGROUND_READ # include #endif struct read_job_s; struct udata_s; struct buffer_s; typedef void job_handler_t(struct udata_s *u, struct read_job_s *job, unsigned char *data); struct read_job_s { struct read_job_s *next; int data_len, bits_len; struct buffer_s *buffer; job_handler_t *handler; unsigned int command_id; }; struct buffer_s { unsigned int tms:1; unsigned int tdi:1; unsigned int tdi_enable:1; unsigned int tdo:1; unsigned int tdo_enable:1; unsigned int rmask:1; }; struct udata_s { FILE *f; struct ftdi_context ftdic; uint16_t device_vendor; uint16_t device_product; int device_channel; int eeprom_size; int buffer_size; struct buffer_s buffer[BUFFER_SIZE]; struct read_job_s *job_fifo_out, *job_fifo_in; int last_tms; int last_tdo; int buffer_i; int retval_i; int retval[256]; int error_rc; int verbose; int syncmode; int forcemode; int frequency; #ifdef BACKGROUND_READ # ifdef INTERLACED_READ_WRITE int total_job_bits; int writer_wait_flag; pthread_mutex_t writer_wait_flag_mutex; # endif int reader_terminate; pthread_mutex_t read_write_mutex; pthread_cond_t read_more_cond; pthread_cond_t read_done_cond; pthread_t read_thread; #endif #ifdef BLOCK_WRITE int ftdibuf_len; unsigned char ftdibuf[4096]; #endif }; static FILE *dumpfile = NULL; static void write_dumpfile(int wr, unsigned char *buf, int size, unsigned int command_id) { int i; if (!dumpfile) return; fprintf(dumpfile, "%s[%u] %04x:", wr ? "SEND" : "RECV", command_id, size); for (i = 0; i < size; i++) fprintf(dumpfile, " %02x", buf[i]); fprintf(dumpfile, "\n"); } static int my_ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size, unsigned int command_id) { int pos = 0; int poll_count = 0; while (pos < size) { int rc = ftdi_read_data(ftdi, buf+pos, size-pos); if (rc < 0) { fprintf(stderr, "[***] ftdi_read_data returned error `%s' (rc=%d).\n", ftdi_get_error_string(ftdi), rc); break; } // this check should only be needed for very low JTAG clock frequencies if (rc == 0) { if (++poll_count > 8) { fprintf(stderr, "[***] my_ftdi_read_data gives up polling .\n", command_id, pos, size); break; } // fprintf(stderr, "[%d/8] my_ftdi_read_data with len=%d polling at %d..\n", poll_count, size, pos); usleep(4096 << poll_count); } pos += rc; } write_dumpfile(0, buf, pos, command_id); return pos; } static int my_ftdi_write_data(struct udata_s *u, unsigned char *buf, int size, int sync) { #ifdef BLOCK_WRITE int rc, total_queued = 0; sync = 1; while (size > 0) { if (u->ftdibuf_len == 4096) { if (dumpfile) fprintf(dumpfile, "WRITE %d BYTES (buffer full)\n", u->ftdibuf_len); #ifdef ASYNC_WRITE rc = ftdi_write_data_async(&u->ftdic, u->ftdibuf, u->ftdibuf_len); #else rc = ftdi_write_data(&u->ftdic, u->ftdibuf, u->ftdibuf_len); #endif if (rc != u->ftdibuf_len) return -1; u->ftdibuf_len = 0; } int chunksize = 4096 - u->ftdibuf_len; if (chunksize > size) chunksize = size; memcpy(u->ftdibuf + u->ftdibuf_len, buf, chunksize); u->ftdibuf_len += chunksize; total_queued += chunksize; size -= chunksize; buf += chunksize; } if (sync && u->ftdibuf_len > 0) { if (dumpfile) fprintf(dumpfile, "WRITE %d BYTES (sync)\n", u->ftdibuf_len); #ifdef ASYNC_WRITE rc = ftdi_write_data_async(&u->ftdic, u->ftdibuf, u->ftdibuf_len); #else rc = ftdi_write_data(&u->ftdic, u->ftdibuf, u->ftdibuf_len); #endif if (rc != u->ftdibuf_len) return -1; u->ftdibuf_len = 0; } return total_queued; #else # ifdef ASYNC_WRITE return ftdi_write_data_async(&u->ftdic, buf, size); # else return ftdi_write_data(&u->ftdic, buf, size); # endif #endif } static struct read_job_s *new_read_job(struct udata_s *u, int data_len, int bits_len, struct buffer_s *buffer, job_handler_t *handler) { struct read_job_s *job = calloc(1, sizeof(struct read_job_s)); static unsigned int command_count = 0; job->data_len = data_len; job->bits_len = bits_len; job->buffer = calloc(bits_len, sizeof(struct buffer_s)); memcpy(job->buffer, buffer, bits_len*sizeof(struct buffer_s)); job->handler = handler; job->command_id = command_count++; if (u->job_fifo_in) u->job_fifo_in->next = job; if (!u->job_fifo_out) u->job_fifo_out = job; u->job_fifo_in = job; #ifdef BACKGROUND_READ # ifdef INTERLACED_READ_WRITE u->total_job_bits += bits_len; # endif #endif return job; } static void transfer_tms_job_handler(struct udata_s *u, struct read_job_s *job, unsigned char *data) { int i; for (i=0; ibits_len; i++) { // seams like output is align to the MSB in the byte and is LSB first int bitpos = i + (8 - job->bits_len); int line_tdo = (*data & (1 << bitpos)) != 0 ? 1 : 0; if (job->buffer[i].tdo_enable && job->buffer[i].tdo != line_tdo) u->error_rc = -1; if (job->buffer[i].rmask && u->retval_i < 256) u->retval[u->retval_i++] = line_tdo; u->last_tdo = line_tdo; } } static void transfer_tms(struct udata_s *u, struct buffer_s *d, int tdi, int len) { int i, rc; unsigned char data_command[] = { 0x6e, len-1, tdi << 7, 0x87 }; for (i=0; ilast_tms = d[len-1].tms; struct read_job_s *rj = new_read_job(u, 1, len, d, &transfer_tms_job_handler); write_dumpfile(1, data_command, sizeof(data_command), rj->command_id); rc = my_ftdi_write_data(u, data_command, sizeof(data_command), 0); if (rc != sizeof(data_command)) { fprintf(stderr, "IO Error: Transfer tms write failed: %s (rc=%d/%d)\n", ftdi_get_error_string(&u->ftdic), rc, (int)sizeof(data_command)); u->error_rc = -1; } } static void transfer_tdi_job_handler(struct udata_s *u, struct read_job_s *job, unsigned char *data) { int i, j, k; int bytes = job->bits_len / 8; int bits = job->bits_len % 8; for (i=0, j=0; jbuffer[i].tdo_enable && job->buffer[i].tdo != line_tdo) if (!u->forcemode) u->error_rc = -1; if (job->buffer[j*8+k].rmask && u->retval_i < 256) u->retval[u->retval_i++] = line_tdo; } } for (j=0; jbuffer[i].tdo_enable && job->buffer[i].tdo != line_tdo) if (!u->forcemode) u->error_rc = -1; if (job->buffer[i].rmask && u->retval_i < 256) u->retval[u->retval_i++] = line_tdo; u->last_tdo = line_tdo; } } static void transfer_tdi(struct udata_s *u, struct buffer_s *d, int len) { int bytes = len / 8; int bits = len % 8; int command_len = 1; int data_len = 0; if (bytes) { command_len += 3 + bytes; data_len += bytes; } if (bits) { command_len += 3; data_len++; } int i, j, k, rc; unsigned char command[command_len]; i = 0; if (bytes) { command[i++] = 0x39; command[i++] = (bytes-1) & 0xff; command[i++] = (bytes-1) >> 8; for (j=0; jcommand_id); rc = my_ftdi_write_data(u, command, command_len, 0); if (rc != command_len) { fprintf(stderr, "IO Error: Transfer tdi write failed: %s (rc=%d/%d)\n", ftdi_get_error_string(&u->ftdic), rc, command_len); u->error_rc = -1; } } static void process_next_read_job(struct udata_s *u) { if (!u->job_fifo_out) return; #ifdef ASYNC_WRITE ftdi_async_complete(&u->ftdic,1); #endif struct read_job_s *job = u->job_fifo_out; u->job_fifo_out = job->next; if (!u->job_fifo_out) u->job_fifo_in = NULL; unsigned char data[job->data_len]; if (my_ftdi_read_data(&u->ftdic, data, job->data_len, job->command_id) != job->data_len) { fprintf(stderr, "IO Error: FTDI/USB read failed!\n"); u->error_rc = -1; } else { job->handler(u, job, data); } #ifdef BACKGROUND_READ # ifdef INTERLACED_READ_WRITE u->total_job_bits -= job->bits_len; # endif #endif free(job->buffer); free(job); } #ifdef BACKGROUND_READ static void *reader_main(void *arg) { struct udata_s *u = arg; pthread_mutex_lock(&u->read_write_mutex); while (!u->reader_terminate) { while (u->job_fifo_out) { process_next_read_job(u); #ifdef INTERLACED_READ_WRITE if (u->total_job_bits <= u->buffer_size/2) { pthread_mutex_lock(&u->writer_wait_flag_mutex); int writer_is_waiting = u->writer_wait_flag; pthread_mutex_unlock(&u->writer_wait_flag_mutex); if (writer_is_waiting) break; } #endif } pthread_cond_signal(&u->read_done_cond); pthread_cond_wait(&u->read_more_cond, &u->read_write_mutex); } pthread_mutex_unlock(&u->read_write_mutex); return NULL; } #endif static void buffer_flush(struct udata_s *u) { #ifdef BACKGROUND_READ # ifdef INTERLACED_READ_WRITE pthread_mutex_lock(&u->writer_wait_flag_mutex); u->writer_wait_flag = 1; pthread_mutex_unlock(&u->writer_wait_flag_mutex); pthread_mutex_lock(&u->read_write_mutex); while (u->total_job_bits > u->buffer_size/2) { pthread_cond_wait(&u->read_done_cond, &u->read_write_mutex); } # else pthread_mutex_lock(&u->read_write_mutex); while (u->job_fifo_out) { pthread_cond_wait(&u->read_done_cond, &u->read_write_mutex); } # endif #endif int pos = 0; while (pos < u->buffer_i) { struct buffer_s b = u->buffer[pos]; if (u->last_tms != b.tms) { int len = u->buffer_i - pos; len = len > 6 ? 6 : len; int tdi=-1, i; for (i=0; ibuffer[pos+i].tdi_enable) continue; if (tdi < 0) tdi = u->buffer[pos+i].tdi; if (tdi != u->buffer[pos+i].tdi) len = i; } // printf("transfer_tms \n", len, tdi < 0 ? 1 : tdi); transfer_tms(u, u->buffer+pos, (tdi & 1), len); pos += len; continue; } int len = u->buffer_i - pos; int i; for (i=0; ibuffer[pos+i].tms != u->last_tms) len = i; } // printf("transfer_tdi \n", len, u->last_tms); transfer_tdi(u, u->buffer+pos, len); pos += len; } u->buffer_i = 0; #ifdef BLOCK_WRITE int rc = my_ftdi_write_data(u, NULL, 0, 1); if (rc != 0) { fprintf(stderr, "IO Error: Ftdi write failed: %s\n", ftdi_get_error_string(&u->ftdic)); u->error_rc = -1; } #endif #ifdef BACKGROUND_READ # ifdef INTERLACED_READ_WRITE pthread_mutex_lock(&u->writer_wait_flag_mutex); u->writer_wait_flag = 0; pthread_mutex_unlock(&u->writer_wait_flag_mutex); # endif pthread_mutex_unlock(&u->read_write_mutex); pthread_cond_signal(&u->read_more_cond); #else while (u->job_fifo_out) process_next_read_job(u); #endif } static void buffer_sync(struct udata_s *u) { buffer_flush(u); #ifdef BACKGROUND_READ pthread_mutex_lock(&u->read_write_mutex); while (u->job_fifo_out) { pthread_cond_wait(&u->read_done_cond, &u->read_write_mutex); } pthread_mutex_unlock(&u->read_write_mutex); #endif } static void buffer_add(struct udata_s *u, int tms, int tdi, int tdo, int rmask) { u->buffer[u->buffer_i].tms = tms; u->buffer[u->buffer_i].tdi = tdi; u->buffer[u->buffer_i].tdi_enable = tdi >= 0; u->buffer[u->buffer_i].tdo = tdo; u->buffer[u->buffer_i].tdo_enable = tdo >= 0; u->buffer[u->buffer_i].rmask = rmask; u->buffer_i++; if (u->buffer_i >= u->buffer_size) buffer_flush(u); } static int h_setup(struct libxsvf_host *h) { int device_is_amontec_jtagkey_2p = 0; struct udata_s *u = h->user_data; u->buffer_size = BUFFER_SIZE; #ifdef BLOCK_WRITE u->ftdibuf_len = 0; #endif if (ftdi_init(&u->ftdic) < 0) return -1; if (u->eeprom_size > 0) u->ftdic.eeprom_size = u->eeprom_size; if (u->device_channel > 0) { enum ftdi_interface interface = u->device_channel == 1 ? INTERFACE_A : u->device_channel == 2 ? INTERFACE_B : u->device_channel == 3 ? INTERFACE_C : u->device_channel == 4 ? INTERFACE_D : INTERFACE_ANY; if (ftdi_set_interface(&u->ftdic, interface) < 0) { fprintf(stderr, "IO Error: Interface setup failed (set port).\n"); ftdi_deinit(&u->ftdic); return -1; } } if (u->device_vendor > 0 || u->device_product > 0) { if (ftdi_usb_open(&u->ftdic, u->device_vendor, u->device_product) == 0) goto found_device; goto failed_device; } // 0x0403:0xcff8 = Amontec JTAGkey2P if (ftdi_usb_open(&u->ftdic, 0x0403, 0xcff8) == 0) { device_is_amontec_jtagkey_2p = 1; goto found_device; } // 0x0403:0x6010 = Plain FTDI 2232H if (ftdi_usb_open(&u->ftdic, 0x0403, 0x6010) == 0) { goto found_device; } // 0x0403:0x6011 = Plain FTDI 4232H if (ftdi_usb_open(&u->ftdic, 0x0403, 0x6011) == 0) { goto found_device; } // 0x0403:0x6014 = Plain FTDI 232H if (ftdi_usb_open(&u->ftdic, 0x0403, 0x6014) == 0) { u->buffer_size = 64; goto found_device; } failed_device: fprintf(stderr, "IO Error: Interface setup failed (can't find or can't open device).\n"); ftdi_deinit(&u->ftdic); return -1; found_device:; #if 0 // Older versions of libftdi don't have the TYPE_232H enum value. // So we simply skip this check and let BITMODE_MPSSE below fail for non-H type chips. if (u->ftdic.type != TYPE_232H && u->ftdic.type != TYPE_2232H && u->ftdic.type != TYPE_4232H) { fprintf(stderr, "IO Error: Interface setup failed (wrong chip type).\n"); ftdi_usb_close(&u->ftdic); ftdi_deinit(&u->ftdic); return -1; } #endif #if 1 if (ftdi_usb_reset(&u->ftdic) < 0) { fprintf(stderr, "IO Error: Interface setup failed (usb reset).\n"); ftdi_usb_close(&u->ftdic); ftdi_deinit(&u->ftdic); return -1; } if (ftdi_usb_purge_buffers(&u->ftdic) < 0) { fprintf(stderr, "IO Error: Interface setup failed (purge buffers).\n"); ftdi_usb_close(&u->ftdic); ftdi_deinit(&u->ftdic); return -1; } #endif if (ftdi_set_bitmode(&u->ftdic, 0xff, BITMODE_MPSSE) < 0) { fprintf(stderr, "IO Error: Interface setup failed (MPSSE mode).\n"); ftdi_usb_close(&u->ftdic); ftdi_deinit(&u->ftdic); return -1; } unsigned char plain_init_commands[] = { // 0x86, 0x6f, 0x17, // initial clk freq (1 kHz) // 0x86, 0x05, 0x00, // initial clk freq (1 MHz) 0x86, 0x02, 0x00, // initial clk freq (2 MHz) 0x80, 0x08, 0x0b, // initial line states // 0x84, // enable loopback 0x85, // disable loopback }; unsigned char amontec_init_commands[] = { 0x86, 0x02, 0x00, // initial clk freq (2 MHz) 0x80, 0x08, 0x1b, // initial line states 0x85, // disable loopback }; unsigned char *init_commands_p = plain_init_commands; int init_commands_sz = sizeof(plain_init_commands); if (device_is_amontec_jtagkey_2p) { init_commands_p = amontec_init_commands; init_commands_sz = sizeof(amontec_init_commands); } write_dumpfile(1, init_commands_p, init_commands_sz, 0); if (ftdi_write_data(&u->ftdic, init_commands_p, init_commands_sz) != init_commands_sz) { fprintf(stderr, "IO Error: Interface setup failed (init commands): %s\n", ftdi_get_error_string(&u->ftdic)); ftdi_disable_bitbang(&u->ftdic); ftdi_usb_close(&u->ftdic); ftdi_deinit(&u->ftdic); return -1; } if (u->frequency > 0) h->set_frequency(h, u->frequency); u->job_fifo_out = NULL; u->job_fifo_in = NULL; u->last_tms = -1; u->last_tdo = -1; u->buffer_i = 0; u->error_rc = 0; #ifdef BACKGROUND_READ # ifdef INTERLACED_READ_WRITE u->total_job_bits = 0; u->writer_wait_flag = 0; pthread_mutex_init(&u->writer_wait_flag_mutex, NULL); # endif u->reader_terminate = 0; pthread_mutex_init(&u->read_write_mutex, NULL); pthread_cond_init(&u->read_more_cond, NULL); pthread_cond_init(&u->read_done_cond, NULL); pthread_create(&u->read_thread, NULL, &reader_main, u); #endif return 0; } static int h_shutdown(struct libxsvf_host *h) { struct udata_s *u = h->user_data; buffer_sync(u); #ifdef BACKGROUND_READ pthread_mutex_lock(&u->read_write_mutex); u->reader_terminate = 1; pthread_cond_signal(&u->read_more_cond); pthread_mutex_unlock(&u->read_write_mutex); pthread_join(u->read_thread, NULL); pthread_cond_destroy(&u->read_done_cond); pthread_cond_destroy(&u->read_more_cond); pthread_mutex_destroy(&u->read_write_mutex); # ifdef INTERLACED_READ_WRITE pthread_mutex_destroy(&u->writer_wait_flag_mutex); # endif #endif ftdi_disable_bitbang(&u->ftdic); ftdi_usb_close(&u->ftdic); ftdi_deinit(&u->ftdic); return u->error_rc; } static void h_udelay(struct libxsvf_host *h, long usecs, int tms, long num_tck) { struct udata_s *u = h->user_data; buffer_sync(u); if (num_tck > 0) { struct timeval tv1, tv2; gettimeofday(&tv1, NULL); while (num_tck > 0) { buffer_add(u, tms, -1, -1, 0); num_tck--; } buffer_sync(u); gettimeofday(&tv2, NULL); if (tv2.tv_sec > tv1.tv_sec) { usecs -= (1000000 - tv1.tv_usec) + (tv2.tv_sec - tv1.tv_sec - 1) * 1000000; tv1.tv_usec = 0; } usecs -= tv2.tv_usec - tv1.tv_usec; } if (usecs > 0) { usleep(usecs); } } static int h_getbyte(struct libxsvf_host *h) { struct udata_s *u = h->user_data; return fgetc(u->f); } static int h_sync(struct libxsvf_host *h) { struct udata_s *u = h->user_data; buffer_sync(u); int rc = u->error_rc; u->error_rc = 0; return rc; } static int h_pulse_tck(struct libxsvf_host *h, int tms, int tdi, int tdo, int rmask, int sync) { struct udata_s *u = h->user_data; if (u->syncmode) sync = 1; buffer_add(u, tms, tdi, tdo, rmask); if (sync) { buffer_sync(u); int rc = u->error_rc < 0 ? u->error_rc : u->last_tdo; u->error_rc = 0; return rc; } return u->error_rc < 0 ? u->error_rc : 1; } static int h_set_frequency(struct libxsvf_host *h, int v) { struct udata_s *u = h->user_data; if (u->syncmode && v > 10000) v = 10000; unsigned char setfreq_command[] = { 0x86, 0x02, 0x00 }; int div = fmax(ceil(12e6 / (2*v) - 1), 2); setfreq_command[1] = div >> 0; setfreq_command[2] = div >> 8; write_dumpfile(1, setfreq_command, sizeof(setfreq_command), 0); int rc = my_ftdi_write_data(u, setfreq_command, sizeof(setfreq_command), 1); if (rc != sizeof(setfreq_command)) { fprintf(stderr, "IO Error: Set frequency write failed: %s (rc=%d/%d)\n", ftdi_get_error_string(&u->ftdic), rc, (int)sizeof(setfreq_command)); u->error_rc = -1; } return 0; } static void h_report_tapstate(struct libxsvf_host *h) { struct udata_s *u = h->user_data; if (u->verbose >= 2) printf("[%s]\n", libxsvf_state2str(h->tap_state)); } static void h_report_device(struct libxsvf_host *h, unsigned long idcode) { printf("idcode=0x%08lx, revision=0x%01lx, part=0x%04lx, manufactor=0x%03lx\n", idcode, (idcode >> 28) & 0xf, (idcode >> 12) & 0xffff, (idcode >> 1) & 0x7ff); } static void h_report_status(struct libxsvf_host *h, const char *message) { struct udata_s *u = h->user_data; if (u->verbose >= 1) printf("[STATUS] %s\n", message); } static void h_report_error(struct libxsvf_host *h, const char *file, int line, const char *message) { fprintf(stderr, "[%s:%d] %s\n", file, line, message); } static void *h_realloc(struct libxsvf_host *h, void *ptr, int size, enum libxsvf_mem which) { return realloc(ptr, size); } static struct udata_s u = { }; static struct libxsvf_host h = { .udelay = h_udelay, .setup = h_setup, .shutdown = h_shutdown, .getbyte = h_getbyte, .sync = h_sync, .pulse_tck = h_pulse_tck, .set_frequency = h_set_frequency, .report_tapstate = h_report_tapstate, .report_device = h_report_device, .report_status = h_report_status, .report_error = h_report_error, .realloc = h_realloc, .user_data = &u }; static uint16_t eeprom_checksum(unsigned char *data, int len) { uint16_t checksum = 0xAAAA; int i; for (i = 0; i < len; i+=2) { uint16_t value = (data[i+1] << 8) | data[i]; checksum = value ^ checksum; checksum = (checksum << 1) | (checksum >> 15); } return checksum; } const char *progname; static void help() { fprintf(stderr, "\n"); fprintf(stderr, "A JTAG SVF/XSVF Player based on libxsvf for the FTDI FT232H, FT2232H and\n"); fprintf(stderr, "FT4232H High Speed USB to Multipurpose UART/FIFO ICs.\n"); fprintf(stderr, "\n"); fprintf(stderr, "xsvftool-ft2232h, part of Lib(X)SVF (http://www.clifford.at/libxsvf/).\n"); fprintf(stderr, "Copyright (C) 2009 RIEGL Research ForschungsGmbH\n"); fprintf(stderr, "Copyright (C) 2009 Clifford Wolf \n"); fprintf(stderr, "Lib(X)SVF is free software licensed under the ISC license.\n"); fprintf(stderr, "\n"); fprintf(stderr, "Usage: %s [ -v[v..] ] [ -d dumpfile ] [ -L | -B ] [ -S ] [ -F ] \\\n", progname); fprintf(stderr, " %*s [ -D vendor:product ] [ -C channel ] [ -f freq[k|M] ] \\\n", (int)(strlen(progname)+1), ""); fprintf(stderr, " %*s [ -Z eeprom-size] [ [-G] -W eeprom-filename ] [ -R eeprom-filename ] \\\n", (int)(strlen(progname)+1), ""); fprintf(stderr, " %*s { -s svf-file | -x xsvf-file | -c } ...\n", (int)(strlen(progname)+1), ""); fprintf(stderr, "\n"); fprintf(stderr, " -v\n"); fprintf(stderr, " Enable verbose output (repeat for incrased verbosity)\n"); fprintf(stderr, "\n"); fprintf(stderr, " -d dumpfile\n"); fprintf(stderr, " Write a logfile of all MPSSE comunication\n"); fprintf(stderr, "\n"); fprintf(stderr, " -L, -B\n"); fprintf(stderr, " Print RMASK bits as hex value (little or big endian)\n"); fprintf(stderr, "\n"); fprintf(stderr, " -S\n"); fprintf(stderr, " Run in synchronous mode (slow but report errors right away)\n"); fprintf(stderr, "\n"); fprintf(stderr, " -F\n"); fprintf(stderr, " Force mode (ignore all TDO mismatches)\n"); fprintf(stderr, "\n"); fprintf(stderr, " -f freq[k|M]\n"); fprintf(stderr, " Set maximum frequency in Hz, kHz or MHz\n"); fprintf(stderr, "\n"); fprintf(stderr, " -D vendor:product\n"); fprintf(stderr, " Select device using USB vendor and product id\n"); fprintf(stderr, "\n"); fprintf(stderr, " -C channel\n"); fprintf(stderr, " Select channel on target device (A, B, C or D)\n"); fprintf(stderr, "\n"); fprintf(stderr, " -Z eeprom-size\n"); fprintf(stderr, " Set size of the FTDI EEPROM\n"); fprintf(stderr, "\n"); fprintf(stderr, " -G\n"); fprintf(stderr, " Generate checksum before writing EEPROM data\n"); fprintf(stderr, "\n"); fprintf(stderr, " -W eeprom-filename\n"); fprintf(stderr, " Write content of the given file to the FTDI EEPROM\n"); fprintf(stderr, "\n"); fprintf(stderr, " -R eeprom-filename\n"); fprintf(stderr, " Write content of the FTDI EEPROM to the given file\n"); fprintf(stderr, "\n"); fprintf(stderr, " -s svf-file\n"); fprintf(stderr, " Play the specified SVF file\n"); fprintf(stderr, "\n"); fprintf(stderr, " -x xsvf-file\n"); fprintf(stderr, " Play the specified XSVF file\n"); fprintf(stderr, "\n"); fprintf(stderr, " -c\n"); fprintf(stderr, " List devices in JTAG chain\n"); fprintf(stderr, "\n"); exit(1); } int main(int argc, char **argv) { int rc = 0; int gotaction = 0; int genchecksum = 0; int hex_mode = 0; int opt, i, j; progname = argc >= 1 ? argv[0] : "xsvftool-ft232h"; while ((opt = getopt(argc, argv, "vd:LBSFD:C:Z:GW:R:f:x:s:c")) != -1) { switch (opt) { case 'v': u.verbose++; break; case 'd': if (!strcmp(optarg, "-")) dumpfile = stdout; else dumpfile = fopen(optarg, "w"); if (!dumpfile) { fprintf(stderr, "Can't open dumpfile `%s': %s\n", optarg, strerror(errno)); rc = 1; } break; case 'f': u.frequency = strtol(optarg, &optarg, 10); while (*optarg != 0) { if (*optarg == 'k') { u.frequency *= 1000; optarg++; continue; } if (*optarg == 'M') { u.frequency *= 1000000; optarg++; continue; } if (optarg[0] == 'H' && optarg[1] == 'z') { optarg += 2; continue; } help(); } break; case 'D': { char *endptr = NULL; u.device_vendor = strtol(optarg, &endptr, 16); if (!endptr || *endptr != ':') help(); u.device_product = strtol(endptr, &endptr, 16); if (!endptr || *endptr != 0) help(); } break; case 'C': if (!strcmp(optarg, "A")) u.device_channel = 1; else if (!strcmp(optarg, "B")) u.device_channel = 2; else if (!strcmp(optarg, "C")) u.device_channel = 3; else if (!strcmp(optarg, "D")) u.device_channel = 4; else help(); break; case 'Z': { char *endptr = NULL; u.eeprom_size = strtol(optarg, &endptr, 0); if (!endptr || *endptr != 0) help(); } break; case 'G': genchecksum = 1; break; case 'W': { gotaction = 1; if (h_setup(&h) < 0) return 1; unsigned char eeprom_data[u.ftdic.eeprom_size]; FILE *f = fopen(optarg, "r"); if (f == NULL) { fprintf(stderr, "Can't open EEPROM file `%s' for reading: %s\n", optarg, strerror(errno)); h_shutdown(&h); return 1; } if (fread(eeprom_data, u.ftdic.eeprom_size, 1, f) != 1) { fprintf(stderr, "Can't read EEPROM file `%s': %s\n", optarg, strerror(errno)); h_shutdown(&h); return 1; } fclose(f); uint16_t checksum = eeprom_checksum(eeprom_data, u.ftdic.eeprom_size-2); if (genchecksum) { eeprom_data[u.ftdic.eeprom_size-1] = checksum >> 8; eeprom_data[u.ftdic.eeprom_size-2] = checksum; } uint16_t checksum_chip = (eeprom_data[u.ftdic.eeprom_size-1] << 8) | eeprom_data[u.ftdic.eeprom_size-2]; if (checksum != checksum_chip) { fprintf(stderr, "ERROR: Checksum from EEPROM data is invalid! (is 0x%04x instead of 0x%04x)\n", checksum_chip, checksum); h_shutdown(&h); return 1; } if (ftdi_write_eeprom(&u.ftdic, eeprom_data) < 0) { fprintf(stderr, "Writing EEPROM data failed! (size=%d)\n", u.ftdic.eeprom_size); h_shutdown(&h); return 1; } if (h_shutdown(&h) < 0) return 1; } break; case 'R': { gotaction = 1; if (h_setup(&h) < 0) return 1; int eeprom_size = u.ftdic.eeprom_size; unsigned char eeprom_data[eeprom_size]; if (ftdi_read_eeprom(&u.ftdic, eeprom_data) < 0) { fprintf(stderr, "Reading EEPROM data failed! (size=%d)\n", u.ftdic.eeprom_size); h_shutdown(&h); return 1; } if (h_shutdown(&h) < 0) return 1; FILE *f = fopen(optarg, "w"); if (f == NULL) { fprintf(stderr, "Can't open EEPROM file `%s' for writing: %s\n", optarg, strerror(errno)); return 1; } if (fwrite(eeprom_data, eeprom_size, 1, f) != 1) { fprintf(stderr, "Can't write EEPROM file `%s': %s\n", optarg, strerror(errno)); return 1; } fclose(f); uint16_t checksum = eeprom_checksum(eeprom_data, eeprom_size-2); uint16_t checksum_chip = (eeprom_data[eeprom_size-1] << 8) | eeprom_data[eeprom_size-2]; if (checksum != checksum_chip) fprintf(stderr, "WARNING: Checksum from EEPROM data is invalid! (is 0x%04x instead of 0x%04x)\n", checksum_chip, checksum); } break; case 'x': case 's': gotaction = 1; if (!strcmp(optarg, "-")) u.f = stdin; else u.f = fopen(optarg, "rb"); if (u.f == NULL) { fprintf(stderr, "Can't open %s file `%s': %s\n", opt == 's' ? "SVF" : "XSVF", optarg, strerror(errno)); rc = 1; break; } if (libxsvf_play(&h, opt == 's' ? LIBXSVF_MODE_SVF : LIBXSVF_MODE_XSVF) < 0) { fprintf(stderr, "Error while playing %s file `%s'.\n", opt == 's' ? "SVF" : "XSVF", optarg); rc = 1; } if (strcmp(optarg, "-")) fclose(u.f); break; case 'c': gotaction = 1; int old_frequency = u.frequency; if (u.frequency == 0) u.frequency = 10000; if (libxsvf_play(&h, LIBXSVF_MODE_SCAN) < 0) { fprintf(stderr, "Error while scanning JTAG chain.\n"); rc = 1; } u.frequency = old_frequency; break; case 'L': hex_mode = 1; break; case 'B': hex_mode = 2; break; case 'S': if (u.frequency == 0) u.frequency = 10000; u.syncmode = 1; break; case 'F': u.forcemode = 1; break; default: help(); break; } } if (!gotaction) help(); if (u.retval_i) { if (hex_mode) { printf("0x"); for (i=0; i < u.retval_i; i+=4) { int val = 0; for (j=i; j 1 ? j : u.retval_i - j - 1]; printf("%x", val); } } else { printf("%d rmask bits:", u.retval_i); for (i=0; i < u.retval_i; i++) printf(" %d", u.retval[i]); } printf("\n"); } return rc; }