commit 82b4ecc0f15f602f432ac9c8610e6e151eb33889
Author: Brian Swetland <swetland@frotz.net>
Date: Sun, 21 Sep 2014 20:20:56 -0700
initial commit for yet another jtag tool
Based on my jtag-mpsse project, but seriously overhauled to
use a transactional model, better isolation between the driver
later and common core code, and general tidying up.
Diffstat:
A | .gitignore | | | 3 | +++ |
A | Makefile | | | 17 | +++++++++++++++++ |
A | jtag-core.c | | | 254 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | jtag-driver.h | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | jtag-mpsse-driver.c | | | 528 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | jtag.c | | | 34 | ++++++++++++++++++++++++++++++++++ |
A | jtag.h | | | 72 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
7 files changed, 964 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,3 @@
+.*.swp
+*.o
+jtag
diff --git a/Makefile b/Makefile
@@ -0,0 +1,17 @@
+
+CFLAGS := -Wall -O2 -g
+
+LIBS := -lusb-1.0
+
+
+all: jtag
+
+JTAG_OBJS := jtag-mpsse-driver.o jtag-core.o jtag.o
+
+$(JTAG_OBJS): jtag.h
+
+jtag: $(JTAG_OBJS)
+ $(CC) -o jtag $(JTAG_OBJS) $(LIBS)
+
+clean:
+ rm -f *.o jtag
diff --git a/jtag-core.c b/jtag-core.c
@@ -0,0 +1,254 @@
+// Copyright 2014 Brian Swetland <swetland@frotz.net>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "jtag-driver.h"
+
+#define TRACE_STATEMACHINE 1
+
+// configuration and state or IR or DR
+typedef struct JREG {
+ u8 *prebits;
+ u8 *postbits;
+ u32 precount;
+ u32 postcount;
+ u32 scanstate;
+ u32 idlestate;
+} JREG;
+
+// configuration and state of JTAG
+struct JTAG {
+ JDRV *drv;
+ JDVT *vt;
+
+ JREG ir;
+ JREG dr;
+
+ u32 state;
+};
+
+#define _setspeed(khz) \
+ jtag->vt->setspeed(jtag->drv, khz)
+#define _commit() \
+ jtag->vt->commit(jtag->drv)
+#define _scan_tms(obit, count, tbits, ioffset, ibits) \
+ jtag->vt->scan_tms(jtag->drv, obit, count, tbits, ioffset, ibits)
+#define _scan_io(count, obits, ibits) \
+ jtag->vt->scan_io(jtag->drv, count, obits, ibits)
+#define _close() \
+ jtag->vt->close(jtag->drv)
+
+static u8 ZEROS[32];
+static u8 ONES[32];
+
+int jtag_init(JTAG **_jtag, JDRV *drv, JDVT *vt) {
+ JTAG *jtag;
+ if ((jtag = malloc(sizeof(JTAG))) == 0) {
+ return -1;
+ }
+ memset(jtag, 0, sizeof(JTAG));
+ jtag->drv = drv;
+ jtag->vt = vt;
+ jtag->ir.idlestate = JTAG_IDLE;
+ jtag->ir.scanstate = JTAG_IRSHIFT;
+ jtag->dr.idlestate = JTAG_IDLE;
+ jtag->dr.scanstate = JTAG_DRSHIFT;
+ *_jtag = jtag;
+ memset(ZEROS, 0x00, 32);
+ memset(ONES, 0xFF, 32);
+ return 0;
+}
+
+void jtag_close(JTAG *jtag) {
+ _close();
+ free(jtag);
+}
+
+int jtag_setspeed(JTAG *jtag, int khz) {
+ return _setspeed(khz);
+}
+
+void jtag_set_ir_idle(JTAG *jtag, unsigned state) {
+ jtag->ir.idlestate = state;
+}
+
+void jtag_set_dr_idle(JTAG *jtag, unsigned state) {
+ jtag->dr.idlestate = state;
+}
+
+void jtag_set_ir_prefix(JTAG *jtag, unsigned count, const void *bits) {
+ jtag->ir.prebits = (u8*) bits;
+ jtag->ir.precount = count;
+}
+void jtag_set_ir_postfix(JTAG *jtag, unsigned count, const void *bits) {
+ jtag->ir.postbits = (u8*) bits;
+ jtag->ir.postcount = count;
+}
+void jtag_set_dr_prefix(JTAG *jtag, unsigned count, const void *bits) {
+ jtag->dr.prebits = (u8*) bits;
+ jtag->dr.precount = count;
+}
+void jtag_set_dr_postfix(JTAG *jtag, unsigned count, const void *bits) {
+ jtag->dr.postbits = (u8*) bits;
+ jtag->dr.postcount = count;
+}
+
+static u32 lastbit(const u8 *bits, u32 count) {
+ count -= 1;
+ return (bits[count >> 3] >> (count & 7)) & 1;
+}
+
+static const char *JSTATE[16] = {
+ "RESET", "IDLE", "DRSELECT", "DRCAPTURE",
+ "DRSHIFT", "DREXIT1", "DRPAUSE", "DREXIT2",
+ "DRUPDATE", "IRSELECT", "IRCAPTURE", "IRSHIFT",
+ "IREXIT1", "IRPAUSE", "IREXIT1", "IRUPDATE"
+};
+
+#define JPATH(x,c) { static u8 out = x; *bits = &out; return c; }
+
+static u32 jtag_plot(u32 from, u32 to, u8 **bits) {
+#if TRACE_STATEMACHINE
+ fprintf(stderr,"jtag_plot: move from %s to %s\n",
+ JSTATE[from], JSTATE[to]);
+#endif
+ switch (from) {
+ case JTAG_RESET:
+ if (to == JTAG_IDLE) JPATH(0x00, 1); // 0
+ if (to == JTAG_IRSHIFT) JPATH(0x06, 5); // 01100
+ if (to == JTAG_DRSHIFT) JPATH(0x02, 4); // 0100
+ break;
+ case JTAG_IDLE:
+ if (to == JTAG_IRSHIFT) JPATH(0x03, 4); // 1100
+ if (to == JTAG_DRSHIFT) JPATH(0x01, 3); // 100
+ break;
+ case JTAG_IRSHIFT:
+ if (to == JTAG_IDLE) JPATH(0x03, 3); // 110
+ if (to == JTAG_IRPAUSE) JPATH(0x01, 2); // 10
+ break;
+ case JTAG_DRSHIFT:
+ if (to == JTAG_IDLE) JPATH(0x03, 3); // 110
+ if (to == JTAG_DRPAUSE) JPATH(0x01, 2); // 10
+ break;
+ case JTAG_IRPAUSE:
+ if (to == JTAG_IDLE) JPATH(0x03, 3); // 110
+ if (to == JTAG_IRSHIFT) JPATH(0x01, 2); // 10
+ if (to == JTAG_DRSHIFT) JPATH(0x07, 5); // 11100
+ break;
+ case JTAG_DRPAUSE:
+ if (to == JTAG_IDLE) JPATH(0x03, 3); // 110
+ if (to == JTAG_DRSHIFT) JPATH(0x01, 2); // 10
+ if (to == JTAG_IRSHIFT) JPATH(0x0F, 6); // 111100
+ break;
+ }
+ if (to == JTAG_RESET) JPATH(0x3F, 6); // 111111
+
+ fprintf(stderr,"jtag_plot: cannot move from %s to %s\n",
+ JSTATE[from], JSTATE[to]);
+ return 0;
+};
+
+void jtag_goto(JTAG *jtag, unsigned state) {
+ u32 mcount;
+ u8 *mbits;
+ mcount = jtag_plot(jtag->state, state, &mbits);
+ if (mcount != 0) {
+ _scan_tms(0, mcount, mbits, 0, 0);
+ jtag->state = state;
+ }
+}
+
+static void jtag_xr_wr(JTAG *jtag, JREG *xr, u32 count, u8 *wbits) {
+ u32 mcount;
+ u8 *mbits;
+ jtag_goto(jtag, xr->scanstate);
+ mcount = jtag_plot(xr->scanstate, xr->idlestate, &mbits);
+ if (xr->prebits) {
+ _scan_io(xr->precount, xr->prebits, 0);
+ }
+ if (xr->postbits) {
+ _scan_io(count, wbits, 0);
+ _scan_io(xr->postcount - 1, xr->postbits, 0);
+ _scan_tms(lastbit(xr->postbits, xr->postcount), mcount, mbits, 0, 0);
+ } else {
+ _scan_io(count - 1, wbits, 0);
+ _scan_tms(lastbit(wbits, count), mcount, mbits, 0, 0);
+ }
+ jtag->state = xr->idlestate;
+}
+
+static void jtag_xr_rd(JTAG *jtag, JREG *xr, u32 count, u8 *rbits) {
+ u32 mcount;
+ u8 *mbits;
+ jtag_goto(jtag, xr->scanstate);
+ mcount = jtag_plot(xr->scanstate, xr->idlestate, &mbits);
+ if (xr->prebits) {
+ _scan_io(xr->precount, xr->prebits, 0);
+ }
+ if (xr->postbits) {
+ _scan_io(count, 0, rbits);
+ _scan_io(xr->postcount - 1, xr->postbits, 0);
+ _scan_tms(lastbit(xr->postbits, xr->postcount), mcount, mbits, 0, 0);
+ } else {
+ _scan_io(count - 1, 0, rbits);
+ _scan_tms(0, mcount, mbits, count - 1, rbits);
+ }
+ jtag->state = xr->idlestate;
+}
+
+static void jtag_xr_io(JTAG *jtag, JREG *xr, u32 count, u8 *wbits, u8 *rbits) {
+ u32 mcount;
+ u8 *mbits;
+ jtag_goto(jtag, xr->scanstate);
+ mcount = jtag_plot(xr->scanstate, xr->idlestate, &mbits);
+ if (xr->prebits) {
+ _scan_io(xr->precount, xr->prebits, 0);
+ }
+ if (xr->postbits) {
+ _scan_io(count, (void*) wbits, rbits);
+ _scan_io(xr->postcount - 1, xr->postbits, 0);
+ _scan_tms(lastbit(xr->postbits, xr->postcount), mcount, mbits, 0, 0);
+ } else {
+ _scan_io(count - 1, (void*) wbits, rbits);
+ _scan_tms(lastbit(wbits, count), mcount, mbits, count - 1, rbits);
+ }
+ jtag->state = xr->idlestate;
+}
+
+void jtag_ir_wr(JTAG *jtag, unsigned count, const void *wbits) {
+ jtag_xr_wr(jtag, &jtag->ir, count, (void*) wbits);
+}
+void jtag_ir_rd(JTAG *jtag, unsigned count, void *rbits) {
+ jtag_xr_rd(jtag, &jtag->ir, count, rbits);
+}
+void jtag_ir_io(JTAG *jtag, unsigned count, const void *wbits, void *rbits) {
+ jtag_xr_io(jtag, &jtag->ir, count, (void*) wbits, rbits);
+}
+
+void jtag_dr_wr(JTAG *jtag, unsigned count, const void *wbits) {
+ jtag_xr_wr(jtag, &jtag->dr, count, (void*) wbits);
+}
+void jtag_dr_rd(JTAG *jtag, unsigned count, void *rbits) {
+ jtag_xr_rd(jtag, &jtag->dr, count, rbits);
+}
+void jtag_dr_io(JTAG *jtag, unsigned count, const void *wbits, void *rbits) {
+ jtag_xr_io(jtag, &jtag->dr, count, (void*) wbits, rbits);
+}
+
+int jtag_commit(JTAG *jtag) {
+ return _commit();
+}
diff --git a/jtag-driver.h b/jtag-driver.h
@@ -0,0 +1,56 @@
+// Copyright 2014 Brian Swetland <swetland@frotz.net>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef _JTAG_DRIVER_
+#define _JTAG_DRIVER_
+
+#include "jtag.h"
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned long u32;
+
+typedef struct JDRV JDRV;
+
+typedef struct {
+ int (*init)(JDRV *d);
+
+// returns actual speed
+// if khz is negative, only query speed, do not set
+ int (*setspeed)(JDRV *drv, int khz);
+
+// Declare the end of a transaction, block until success or failure.
+// Once this returns, pointers passed via scan_*() may become invalid.
+ int (*commit)(JDRV *d);
+
+// Shift count TMS=tbits TDI=obit out.
+// If ibits is nonnull, capture the first TDO at offset ioffset in ibits.
+ int (*scan_tms)(JDRV *d, u32 obit, u32 count, u8 *tbits,
+ u32 ioffset, u8 *ibits);
+
+// Shift count bits.
+// If obits nonnull, shift out those bits to TDI.
+// If ibits nonnull, capture to those bits from TDO.
+// TMS does not change.
+ int (*scan_io)(JDRV *d, u32 count, u8 *obits, u8 *ibits);
+
+// Close and release driver.
+ int (*close)(JDRV *d);
+} JDVT;
+
+int jtag_init(JTAG **jtag, JDRV *drv, JDVT *vt);
+
+#endif
+
+
diff --git a/jtag-mpsse-driver.c b/jtag-mpsse-driver.c
@@ -0,0 +1,528 @@
+// Copyright 2014 Brian Swetland <swetland@frotz.net>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "jtag-driver.h"
+
+#define TRACE_IO 1
+#define TRACE_DISASM 1
+
+// FTDI MPSSE Device Info
+static struct {
+ u16 vid;
+ u16 pid;
+ u8 ep_in;
+ u8 ep_out;
+ const char *name;
+} devinfo[] = {
+ { 0x0403, 0x6010, 0x81, 0x02, "ftdi" },
+ { 0x0403, 0x6014, 0x81, 0x02, "ftdi" },
+ { 0x0000, 0x0000, 0},
+};
+
+static unsigned char mpsse_init[] = {
+ 0x85, // loopback off
+ 0x8a, // disable clock/5
+ 0x86, 0x01, 0x00, // set divisor
+ 0x80, 0xe8, 0xeb, // set low state and dir
+ 0x82, 0x00, 0x00, // set high state and dir
+};
+
+static void dump(char *prefix, void *data, int len) {
+ unsigned char *x = data;
+ fprintf(stderr,"%s: (%d)", prefix, len);
+ while (len > 0) {
+ fprintf(stderr," %02x", *x++);
+ len--;
+ }
+ fprintf(stderr,"\n");
+}
+
+#include <libusb-1.0/libusb.h>
+
+// how to process the reply buffer
+#define OP_END 0 // done
+#define OP_BITS 1 // copy top n (1-8) bits to ptr
+#define OP_BYTES 2 // copy n (1-65536) bytes to ptr
+#define OP_1BIT 3 // copy bitmask n to bitmask x of ptr
+
+typedef struct {
+ u8 *ptr;
+ u32 n;
+ u16 op;
+ u16 x;
+} JOP;
+
+#define CMD_MAX (16*1024)
+#define CMD_LEFT(n) (CMD_MAX - (n))
+
+struct JDRV {
+ struct libusb_device_handle *udev;
+ u8 ep_in;
+ u8 ep_out;
+ int speed;
+ u32 expected;
+ u32 status;
+ u8 *next;
+ JOP *nextop;
+ u32 read_count;
+ u32 read_size;
+ u8 *read_ptr;
+ u8 cmd[CMD_MAX];
+ JOP op[8192];
+ u8 read_buffer[512];
+};
+
+static int _jtag_setspeed(JDRV *d, int khz) {
+ return d->speed;
+}
+
+static void resetstate(JDRV *d) {
+ d->status = 0;
+ d->next = d->cmd;
+ d->nextop = d->op;
+ d->expected = 0;
+}
+
+#define FTDI_REQTYPE_OUT (LIBUSB_REQUEST_TYPE_VENDOR \
+ | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT)
+#define FTDI_CTL_RESET 0x00
+#define FTDI_CTL_SET_BITMODE 0x0B
+#define FTDI_CTL_SET_EVENT_CH 0x06
+#define FTDI_CTL_SET_ERROR_CH 0x07
+
+#define FTDI_IFC_A 1
+#define FTDI_IFC_B 2
+
+static int ftdi_reset(JDRV *d) {
+ struct libusb_device_handle *udev = d->udev;
+ if (libusb_control_transfer(udev, FTDI_REQTYPE_OUT, FTDI_CTL_RESET,
+ 0, FTDI_IFC_A, NULL, 0, 10000) < 0) {
+ fprintf(stderr, "ftdi: reset failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int ftdi_mpsse_enable(JDRV *d) {
+ struct libusb_device_handle *udev = d->udev;
+ if (libusb_control_transfer(udev, FTDI_REQTYPE_OUT, FTDI_CTL_SET_BITMODE,
+ 0x0000, FTDI_IFC_A, NULL, 0, 10000) < 0) {
+ fprintf(stderr, "ftdi: set bitmode failed\n");
+ return -1;
+ }
+ if (libusb_control_transfer(udev, FTDI_REQTYPE_OUT, FTDI_CTL_SET_BITMODE,
+ 0x020b, FTDI_IFC_A, NULL, 0, 10000) < 0) {
+ fprintf(stderr, "ftdi: set bitmode failed\n");
+ return -1;
+ }
+ if (libusb_control_transfer(udev, FTDI_REQTYPE_OUT, FTDI_CTL_SET_EVENT_CH,
+ 0, FTDI_IFC_A, NULL, 0, 10000) < 0) {
+ fprintf(stderr, "ftdi: disable event character failed\n");
+ return -1;
+ }
+ return 0;
+ if (libusb_control_transfer(udev, FTDI_REQTYPE_OUT, FTDI_CTL_SET_ERROR_CH,
+ 0, FTDI_IFC_A, NULL, 0, 10000) < 0) {
+ fprintf(stderr, "ftdi: disable error character failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int ftdi_open(JDRV *d) {
+ struct libusb_device_handle *udev;
+ int n;
+
+ if (libusb_init(NULL) < 0) {
+ fprintf(stderr, "jtag_init: failed to init libusb\n");
+ return -1;
+ }
+ for (n = 0; devinfo[n].name; n++) {
+ udev = libusb_open_device_with_vid_pid(NULL,
+ devinfo[n].vid, devinfo[n].pid);
+ if (udev == 0)
+ continue;
+ libusb_detach_kernel_driver(udev, 0);
+ if (libusb_claim_interface(udev, 0) < 0) {
+ //TODO: close
+ continue;
+ }
+ d->udev = udev;
+ d->read_ptr = d->read_buffer;
+ d->read_size = 512;
+ d->read_count = 0;
+ d->ep_in = devinfo[n].ep_in;
+ d->ep_out = devinfo[n].ep_out;
+ return 0;
+ }
+ fprintf(stderr, "jtag_init: failed to find usb device\n");
+ return -1;
+}
+
+static int usb_bulk(struct libusb_device_handle *udev,
+ unsigned char ep, void *data, int len, unsigned timeout) {
+ int r, xfer;
+ r = libusb_bulk_transfer(udev, ep, data, len, &xfer, timeout);
+ if (r < 0) {
+ fprintf(stderr,"bulk: error: %d\n", r);
+ return r;
+ }
+ return xfer;
+}
+
+/* TODO: handle smaller packet size for lowspeed version of the part */
+/* TODO: multi-packet reads */
+/* TODO: asynch/background reads */
+static int ftdi_read(JDRV *d, unsigned char *buffer, int count, int timeout) {
+ int xfer;
+ while (count > 0) {
+ if (d->read_count >= count) {
+ memcpy(buffer, d->read_ptr, count);
+ d->read_count -= count;
+ d->read_ptr += count;
+ return 0;
+ }
+ if (d->read_count > 0) {
+ memcpy(buffer, d->read_ptr, d->read_count);
+ count -= d->read_count;
+ d->read_count = 0;
+ }
+ xfer = usb_bulk(d->udev, d->ep_in, d->read_buffer, d->read_size, 1000);
+ if (xfer < 0)
+ return -1;
+ if (xfer < 2)
+ return -1;
+ /* discard header */
+ d->read_ptr = d->read_buffer + 2;
+ d->read_count = xfer - 2;
+ }
+ return 0;
+}
+
+static int _jtag_close(JDRV *d) {
+ if (d->udev) {
+ //TODO: close
+ }
+ free(d);
+ return 0;
+}
+
+static int _jtag_init(JDRV *d) {
+ d->speed = 15000;
+ resetstate(d);
+ if (ftdi_open(d))
+ goto fail;
+ if (ftdi_reset(d))
+ goto fail;
+ if (ftdi_mpsse_enable(d))
+ goto fail;
+ if (usb_bulk(d->udev, d->ep_out, mpsse_init, sizeof(mpsse_init), 1000) != sizeof(mpsse_init))
+ goto fail;
+ return 0;
+
+fail:
+ _jtag_close(d);
+ return -1;
+}
+
+#if TRACE_DISASM
+static void pbin(u32 val, u32 bits) {
+ u32 n;
+ for (n = 0; n < bits; n++) {
+ fprintf(stderr, "%c", (val & 1) ? '1' : '0');
+ val >>= 1;
+ }
+}
+
+// display mpsse command stream in a (sortof) human readable form
+static void dismpsse(u8 *data, u32 n) {
+ u32 x, i;
+ while (n > 0) {
+ fprintf(stderr,"%02x: ", data[0]);
+ switch(data[0]) {
+ case 0x6B: // tms rw
+ fprintf(stderr, "x1 <- TDO, ");
+ // fall through
+ case 0x4B: // tms wo
+ fprintf(stderr, "TMS <- ");
+ pbin(data[2],data[1]+1);
+ fprintf(stderr, ", TDI <- ");
+ pbin((data[2] & 0x80) ? 0xFF : 0, data[1] + 1);
+ fprintf(stderr, "\n");
+ data += 3;
+ n -= 3;
+ break;
+ case 0x2A: // ro bits
+ fprintf(stderr, "x%d <- TDO\n", data[1] + 1);
+ data += 2;
+ n -= 2;
+ break;
+ case 0x28: // ro bytes
+ x = ((data[2] << 8) | data[1]) + 1;
+ fprintf(stderr, "x%d <- TDO\n", (int) x * 8);
+ data += 3;
+ n -= 3;
+ break;
+ case 0x1B: // wo bits
+ case 0x3B: // rw bits
+ fprintf(stderr, "TDI <- ");
+ pbin(data[2], data[1] + 1);
+ if (data[0] == 0x3B) {
+ fprintf(stderr, ", x%d <- TDO\n", data[1] + 1);
+ } else {
+ fprintf(stderr, "\n");
+ }
+ data += 3;
+ n -= 3;
+ break;
+ case 0x19: // wo bytes
+ case 0x39: // rw bytes
+ x = ((data[2] << 8) | data[1]) + 1;
+ fprintf(stderr, "TDI <- ");
+ for (i = 0; i < x; i++) pbin(data[3+i], 8);
+ if (data[0] == 0x1B) {
+ fprintf(stderr, ", x%d <- TDO\n", (int) x);
+ } else {
+ fprintf(stderr,"\n");
+ }
+ data += (3 + x);
+ n -= (3 + x);
+ break;
+ case 0x87:
+ fprintf(stderr,"FLUSH\n");
+ data += 1;
+ n -= 1;
+ break;
+ default:
+ fprintf(stderr,"??? 0x%02x\n", data[0]);
+ n = 0;
+ }
+ }
+}
+#endif
+
+static u8 MASKBITS[9] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF
+};
+
+static int _jtag_commit(JDRV *d) {
+ unsigned n = d->next - d->cmd;
+ JOP *op;
+ u8 *x;
+
+ fprintf(stderr, "jtag_commit: tx(%d) rx(%d)\n", n, (int) d->expected);
+ if (d->status) {
+ // if we failed during prep, error out immediately
+ fprintf(stderr, "jtag_commit: pre-existing errors\n");
+ goto fail;
+ }
+ if (n == 0) {
+ goto done;
+ }
+
+ // always complete with an ioflush
+ *d->next = 0x87;
+ n++;
+ d->nextop->op = OP_END;
+
+#if TRACE_IO
+ dump("tx", d->cmd, n);
+#endif
+#if TRACE_DISASM
+ dismpsse(d->cmd, n);
+#endif
+
+ if (usb_bulk(d->udev, d->ep_out, d->cmd, n, 1000) != n) {
+ fprintf(stderr, "jtag_commit: write failed\n");
+ goto fail;
+ }
+ if (ftdi_read(d, d->cmd, d->expected, 1000)) {
+ fprintf(stderr, "jtag_commit: read failed\n");
+ goto fail;
+ }
+
+#if TRACE_IO
+ dump("rx", d->cmd, d->expected);
+#endif
+
+ for (op = d->op, x = d->cmd;;op++) {
+ switch(op->op) {
+ case OP_END:
+ goto done;
+ case OP_BITS:
+ *op->ptr = ((*x) >> (8 - op->n)) & MASKBITS[op->n];
+ x++;
+ break;
+ case OP_BYTES:
+ memcpy(op->ptr, x, op->n);
+ x += op->n;
+ break;
+ case OP_1BIT:
+ if (*x & op->n) {
+ *op->ptr |= op->x;
+ } else {
+ *op->ptr &= (~op->x);
+ }
+ x++;
+ break;
+ }
+ }
+done:
+ resetstate(d);
+ return 0;
+fail:
+ resetstate(d);
+ return -1;
+}
+
+static int _jtag_scan_tms(JDRV *d, u32 obit,
+ u32 count, u8 *tbits, u32 ioffset, u8 *ibits) {
+ if ((count > 6) || (count == 0)) {
+ fprintf(stderr, "jtag_scan_tms: invalid count %d\n", (int) count);
+ return (d->status = -1);
+ }
+ if ((d->next - d->cmd) > CMD_LEFT(8)) {
+ if (_jtag_commit(d))
+ return (d->status = -1);
+ }
+ *d->next++ = ibits ? 0x6B : 0x4B;
+ *d->next++ = count - 1;
+ *d->next++ = ((obit & 1) << 7) | (*tbits);
+ if (ibits) {
+ if (ioffset > 7) {
+ ibits += (ioffset >> 3);
+ ioffset &= 7;
+ }
+ d->nextop->op = OP_1BIT;
+ d->nextop->ptr = ibits;
+ d->nextop->n = 1 << (8 - count);
+ d->nextop->x = 1 << ioffset;
+ d->nextop++;
+ d->expected++;
+ }
+ return -1;
+}
+
+static int _jtag_scan_io(JDRV *d, u32 count, u8 *obits, u8 *ibits) {
+ u32 n;
+ u32 bcount = count >> 3;
+ u8 bytecmd;
+ u8 bitcmd;
+
+ // determine operating mode and ftdi mpsse command byte
+ if (obits) {
+ if (ibits) {
+ // read-write
+ bytecmd = 0x39;
+ bitcmd = 0x3B;
+ } else {
+ // write-only
+ bytecmd = 0x19;
+ bitcmd = 0x1B;
+ }
+ } else {
+ if (ibits) {
+ // read-only
+ bytecmd = 0x28;
+ bitcmd = 0x2A;
+ } else {
+ // do nothing?!
+ fprintf(stderr, "jtag_scan_io: no work?!\n");
+ return (d->status = -1);
+ }
+ }
+
+ // do as many bytemoves as possible first
+ // TODO: for exactly 1 byte, bitmove command is more efficient
+ while (bcount > 0) {
+ n = d->next - d->cmd;
+ if (n > CMD_LEFT(16)) {
+ if (_jtag_commit(d))
+ return (d->status = -1);
+ continue;
+ }
+ n -= 4; // leave room for header and io commit
+ if (n > bcount)
+ n = bcount;
+ *d->next++ = bytecmd;
+ *d->next++ = (n - 1);
+ *d->next++ = (n - 1) >> 8;
+ if (obits) {
+ memcpy(d->next, obits, n);
+ d->next += n;
+ obits += n;
+ }
+ if (ibits) {
+ d->nextop->op = OP_BYTES;
+ d->nextop->ptr = ibits;
+ d->nextop->n = n;
+ d->nextop++;
+ ibits += n;
+ d->expected += n;
+ }
+ bcount -= n;
+ }
+
+ // do a bitmove for any leftover bits
+ count = count & 7;
+ if (count == 0)
+ return 0;
+ if ((d->next - d->cmd) > CMD_LEFT(4)) {
+ if (_jtag_commit(d))
+ return (d->status = -1);
+ }
+ *d->next++ = bitcmd;
+ *d->next++ = count - 1;
+ if (obits) {
+ *d->next++ = *obits;
+ }
+ if (ibits) {
+ d->nextop->op = OP_BITS;
+ d->nextop->ptr = ibits;
+ d->nextop->n = count;
+ d->nextop++;
+ d->expected++;
+ }
+ return 0;
+}
+
+static JDVT vtable = {
+ .init = _jtag_init,
+ .close = _jtag_close,
+ .setspeed = _jtag_setspeed,
+ .commit = _jtag_commit,
+ .scan_tms = _jtag_scan_tms,
+ .scan_io = _jtag_scan_io,
+};
+
+int jtag_mpsse_open(JTAG **jtag) {
+ JDRV *d;
+
+ if ((d = malloc(sizeof(JDRV))) == 0) {
+ return -1;
+ }
+ memset(d, 0, sizeof(JDRV));
+
+ if (jtag_init(jtag, d, &vtable)) {
+ _jtag_close(d);
+ return -1;
+ }
+
+ return _jtag_init(d);
+}
+
diff --git a/jtag.c b/jtag.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "jtag.h"
+
+int main(int argc, char **argv) {
+ unsigned ir, x;
+ unsigned p0 = 0x80808080;
+ unsigned p1 = 0x12345678;
+ unsigned p2 = 0xaabbccdd;
+ unsigned x0, x1, x2;
+ JTAG *jtag;
+
+ if (jtag_mpsse_open(&jtag)) {
+ fprintf(stderr, "error opening jtag\n");
+ return -1;
+ }
+
+ ir = 9;
+ jtag_set_dr_idle(jtag, JTAG_DRPAUSE);
+ jtag_goto(jtag, JTAG_RESET);
+ jtag_ir_wr(jtag, 6, &ir);
+ jtag_dr_io(jtag, 32, &p0, &x);
+ jtag_dr_io(jtag, 32, &p1, &x0);
+ jtag_dr_io(jtag, 32, &p1, &x1);
+ jtag_dr_wr(jtag, 32, &p2);
+ jtag_dr_rd(jtag, 32, &x2);
+ jtag_commit(jtag);
+
+ fprintf(stderr,"%x %x %x %x\n", x, x0, x1, x2);
+
+ return 0;
+}
diff --git a/jtag.h b/jtag.h
@@ -0,0 +1,72 @@
+// Copyright 2014 Brian Swetland <swetland@frotz.net>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef _JTAG_
+#define _JTAG_
+
+#define JTAG_RESET 0
+#define JTAG_IDLE 1
+#define JTAG_DRSELECT 2
+#define JTAG_DRCAPTURE 3
+#define JTAG_DRSHIFT 4
+#define JTAG_DREXIT1 5
+#define JTAG_DRPAUSE 6
+#define JTAG_DREXIT2 7
+#define JTAG_DRUPDATE 8
+#define JTAG_IRSELECT 9
+#define JTAG_IRCAPTURE 10
+#define JTAG_IRSHIFT 11
+#define JTAG_IREXIT1 12
+#define JTAG_IRPAUSE 13
+#define JTAG_IREXIT2 14
+#define JTAG_IRUPDATE 15
+
+typedef struct JTAG JTAG;
+
+int jtag_mpsse_open(JTAG **jtag);
+
+void jtag_close(JTAG *jtag);
+
+int jtag_setspeed(JTAG *jtag, int khz);
+
+// which state to arrive at upon completion of jtag_ir_*()
+void jtag_set_ir_idle(JTAG *jtag, unsigned state);
+
+// which state to arrive at upon completion of jtag_dr_*()
+void jtag_set_dr_idle(JTAG *jtag, unsigned state);
+
+// bits to shift in ahead/behind the args for jtag_xr_*()
+void jtag_set_ir_prefix(JTAG *jtag, unsigned count, const void *bits);
+void jtag_set_ir_postfix(JTAG *jtag, unsigned count, const void *bits);
+void jtag_set_dr_prefix(JTAG *jtag, unsigned count, const void *bits);
+void jtag_set_dr_postfix(JTAG *jtag, unsigned count, const void *bits);
+
+// Move jtag state machine from current state to new state.
+// Moving to JTAG_RESET will work even if current state
+// is out of sync.
+void jtag_goto(JTAG *jtag, unsigned state);
+
+// Move to IRSHIFT, shift count bits, then move to after_ir state.
+void jtag_ir_wr(JTAG *jtag, unsigned count, const void *wbits);
+void jtag_ir_rd(JTAG *jtag, unsigned count, void *rbits);
+void jtag_ir_io(JTAG *jtag, unsigned count, const void *wbits, void *rbits);
+
+// Move to DRSHIFT, shift count bits, then move to after_dr state;
+void jtag_dr_wr(JTAG *jtag, unsigned count, const void *wbits);
+void jtag_dr_rd(JTAG *jtag, unsigned count, void *rbits);
+void jtag_dr_io(JTAG *jtag, unsigned count, const void *wbits, void *rbits);
+
+int jtag_commit(JTAG *jtag);
+
+#endif