commit 30dc81288d66ebb99639b0f0dc3f9f79d6c64d19
Author: Brian Swetland <swetland@frotz.net>
Date: Mon, 20 Feb 2012 22:13:58 -0800
initial commit
Diffstat:
A | Makefile | | | 21 | +++++++++++++++++++++ |
A | README | | | 20 | ++++++++++++++++++++ |
A | jinfo.c | | | 23 | +++++++++++++++++++++++ |
A | jload.c | | | 46 | ++++++++++++++++++++++++++++++++++++++++++++++ |
A | jtag-virtual.c | | | 112 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | jtag.c | | | 214 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | jtag.h | | | 34 | ++++++++++++++++++++++++++++++++++ |
A | usb-blaster-protocol.txt | | | 60 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
8 files changed, 530 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,21 @@
+
+CFLAGS := -g -Wall
+LIBS := -lusb-1.0
+
+all: jload jinfo
+
+jinfo.c: jtag.h
+jload.c: jtag.h
+jtag.c: jtag.h
+jtag-virtual.c: jtag.h
+
+JLOAD_OBJS := jload.o jtag-virtual.o jtag.o
+jload: $(JLOAD_OBJS)
+ $(CC) -o jload $(JLOAD_OBJS) $(LIBS)
+
+JINFO_OBJS := jinfo.o jtag-virtual.o jtag.o
+jinfo: $(JINFO_OBJS)
+ $(CC) -o jinfo $(JINFO_OBJS) $(LIBS)
+
+clean::
+ rm -f jload jinfo *.o
diff --git a/README b/README
@@ -0,0 +1,20 @@
+
+Quick hack commandline tools to interact with Altera FPGA Virtual JTAG interfaces,
+using the USB Blaster device (as integrated on Terasic dev boards, etc).
+
+Not terribly fancy or optimized but only depends on libusb-1.0
+
+Currently does not support multiple devices on the chain.
+
+jtag.c - provides simple jtag interface
+jtag-virtual.c - provides simple virtual jtag interface
+
+jload.c - example of using the virtual jtag interface for a downloader interface
+ with a CTRL/ADDR/DATA register set. CTRL[0] asserts reset, writes to
+ DATA store to [ADDR] and auto-increment ADRR.
+
+jinfo.c - dumps idcode and virtual jtag hub and device info table
+
+
+Why? Scripting the Altera quartus_stp tool in TCL was driving me nuts.A
+
diff --git a/jinfo.c b/jinfo.c
@@ -0,0 +1,23 @@
+// Copyright 2012, Brian Swetland
+
+#include <stdio.h>
+#include "jtag.h"
+
+int main(int argc, char **argv) {
+ unsigned bits;
+
+ if (jtag_open() < 0)
+ return -1;
+
+ if (jtag_reset() < 0)
+ return -1;
+ if (jtag_dr(32, 0, &bits) < 0)
+ return -1;
+ fprintf(stderr,"IDCODE: %08x\n", bits);
+
+ if (jtag_open_virtual_device(0xffffffff))
+ return -1;
+
+ jtag_close();
+ return 0;
+}
diff --git a/jload.c b/jload.c
@@ -0,0 +1,46 @@
+// Copyright 2012, Brian Swetland
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "jtag.h"
+
+#define VIR_CTRL 0x0
+#define VIR_ADDR 0x1
+#define VIR_DATA 0x2
+
+int main(int argc, char **argv) {
+ unsigned bits;
+ char buf[1024];
+ FILE *fp;
+
+ if (argc != 2) {
+ fprintf(stderr,"usage: download hexfile\n");
+ return -1;
+ }
+
+ if (jtag_open_virtual_device(0x00))
+ return -1;
+
+ fp = fopen(argv[1],"r");
+ if (!fp) return -1;
+
+ jtag_vir(VIR_CTRL);
+ jtag_vdr(32, 1, 0);
+
+ jtag_vir(VIR_ADDR);
+ jtag_vdr(32, 0, 0);
+
+ jtag_vir(VIR_DATA);
+ while (fgets(buf, 1024, fp)) {
+ if(buf[0] == '/') continue;
+ if(isspace(buf[0])) continue;
+ bits = strtoul(buf, 0, 16);
+ jtag_vdr(32, bits, 0);
+ }
+
+ jtag_vir(VIR_CTRL);
+ jtag_vdr(32, 0, 0);
+ return 0;
+}
+
diff --git a/jtag-virtual.c b/jtag-virtual.c
@@ -0,0 +1,112 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "jtag.h"
+
+int jtag_dr_8x4(unsigned *out) {
+ unsigned bits = 0;
+ unsigned tmp;
+ int n, r;
+
+ for (n = 0; n < 8; n++) {
+ if ((r = jtag_dr(4, 0, &tmp)) < 0) return r;
+ bits |= (tmp <<= (n * 4));
+ }
+ *out = bits;
+ return 0;
+}
+
+/* number of bits needed given a max value 1-255 */
+unsigned needbits(unsigned max) {
+ if (max > 127) return 8;
+ if (max > 63) return 7;
+ if (max > 31) return 6;
+ if (max > 15) return 5;
+ if (max > 7) return 4;
+ if (max > 3) return 3;
+ if (max > 1) return 2;
+ return 1;
+}
+
+static unsigned ir_width = 10;
+
+static unsigned hub_version = 0;
+static unsigned hub_nodecount = 0;
+static unsigned hub_mfg = 0;
+
+static unsigned vir_width = 0;
+static unsigned vir_width_addr = 0;
+static unsigned vir_width_ir = 0;
+static unsigned vir_addr = 0;
+
+
+int jtag_vir(unsigned vir) {
+ int r;
+ if ((r = jtag_ir(ir_width, 14)) < 0) return r;
+ if ((r = jtag_dr(vir_width, vir_addr | vir, 0)) < 0) return r;
+ return 0;
+}
+
+int jtag_vdr(unsigned sz, unsigned bits, unsigned *out) {
+ int r;
+ if ((r = jtag_ir(ir_width, 12)) < 0) return r;
+ if ((r = jtag_dr(sz, bits, out)) < 0) return r;
+ return 0;
+}
+
+int jtag_open_virtual_device(unsigned iid) {
+ unsigned n, bits;
+ int r;
+
+ if ((r = jtag_open()) < 0) return r;
+
+ if ((r = jtag_reset()) < 0) return r;
+
+ /* select empty node_addr + node_vir -- all zeros */
+ if ((r = jtag_ir(ir_width, 14)) < 0) return r;
+ if ((r = jtag_dr(32, 0, 0)) < 0) return r;
+
+ /* select vdr - this will be the hub info (addr=0,vir=0) */
+ if ((r = jtag_ir(ir_width, 12)) < 0) return r;
+
+ /* read hub info */
+ if ((r = jtag_dr_8x4(&bits)) < 0) return r;
+ hub_version = (bits >> 27) & 0x1F;
+ hub_nodecount = (bits >> 19) & 0xFF;
+ hub_mfg = (bits >> 8) & 0x7FF;
+
+ /* altera docs claim this field is the sum of M bits (VIR field) and
+ * N bits (ADDR field), but empirical evidence suggests it is actually
+ * just the width of the ADDR field and the docs are wrong...
+ */
+ vir_width_ir = bits & 0xFF;
+ vir_width_addr = needbits(hub_nodecount);
+ vir_width = vir_width_ir + vir_width_addr;
+
+ fprintf(stderr,"HUB: Mfg=0x%03x, Ver=0x%02x, Nodes=%d, VIR=%d+%d bits\n",
+ hub_mfg, hub_version, hub_nodecount, vir_width_addr, vir_width_ir);
+
+ for (n = 0; n < hub_nodecount; n++) {
+ unsigned node_ver, node_id, node_mfg, node_iid;
+ if ((r = jtag_dr_8x4(&bits)) < 0) return r;
+ node_ver = (bits >> 27) & 0x1F;
+ node_id = (bits >> 19) & 0xFF;
+ node_mfg = (bits >> 8) & 0x7FF;
+ node_iid = bits & 0xFF;
+
+ fprintf(stderr,"NODE: Mfg=0x%03x, Ver=0x%02x, ID=0x%02x, IID=0x%02x\n",
+ node_mfg, node_ver, node_id, node_iid);
+
+ if ((node_id == 0x08) && (node_iid) == iid) {
+ vir_addr = (n + 1) << vir_width_ir;
+ }
+ }
+
+ if ((vir_addr == 0) && (iid < 256)) {
+ fprintf(stderr,"ERROR: IID 0x%02x not found\n", iid);
+ return -1;
+ }
+ return 0;
+}
+
+
diff --git a/jtag.c b/jtag.c
@@ -0,0 +1,214 @@
+// Copyright 2012, Brian Swetland
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <libusb-1.0/libusb.h>
+
+#define TRACE_USB 0
+#define TRACE_JTAG 0
+
+static struct libusb_device_handle *udev;
+static int usb_open(unsigned vid, unsigned pid) {
+ if (libusb_init(NULL) < 0)
+ return -1;
+
+ if (!(udev = libusb_open_device_with_vid_pid(NULL, vid, pid))) {
+ fprintf(stderr,"cannot find device\n");
+ return -1;
+ }
+
+ if (libusb_claim_interface(udev, 0) < 0) {
+ fprintf(stderr,"cannot claim interface\n");
+ return -1;
+ }
+ return 0;
+}
+static void usb_close(void) {
+ libusb_exit(NULL);
+}
+#if TRACE_USB
+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");
+}
+#endif
+static int usb_bulk(unsigned char ep, void *data, int len, unsigned timeout) {
+ int r, xfer;
+#if TRACE_USB
+ if (!(ep & 0x80))
+ dump("xmit", data, len);
+#endif
+ r = libusb_bulk_transfer(udev, ep, data, len, &xfer, timeout);
+ if (r < 0) {
+ fprintf(stderr,"bulk: error: %d\n", r);
+ return r;
+ }
+#if TRACE_USB
+ if (ep & 0x80)
+ dump("recv", data, xfer);
+#endif
+ return xfer;
+}
+
+#define EP1_IN 0x81
+#define EP2_OUT 0x02
+
+#define UB_BYTEMODE 0x80
+#define UB_BITMODE 0x00
+#define UB_READBACK 0x40
+
+/* bits in bit mode */
+#define UB_OE 0x20
+#define UB_TDI 0x10
+#define UB_nCS 0x08
+#define UB_nCE 0x04
+#define UB_TMS 0x02
+#define UB_TCK 0x01
+
+/* bytecount for data bytes that follow in byte mode */
+#define UB_COUNT(n) ((n) & 0x3F)
+
+int jtag_move(int count, unsigned bits){
+ unsigned char buf[64];
+ int n = 0;
+#if TRACE_JTAG
+ fprintf(stderr,"move: %08x (%d)\n", bits, count);
+#endif
+ while (count-- > 0) {
+ if (bits & 1) {
+ buf[n++] = UB_TMS;
+ buf[n++] = UB_TMS | UB_TCK;
+ } else {
+ buf[n++] = 0;
+ buf[n++] = UB_TCK;
+ }
+ bits >>= 1;
+ }
+ return usb_bulk(EP2_OUT, buf, n, 1000);
+}
+
+int jtag_shift(int count, unsigned bits, unsigned *out) {
+ unsigned char buf[64];
+ unsigned RB = out ? UB_READBACK : 0;
+ int n = 0;
+ int readcount = count;
+ int r,bit;
+#if TRACE_JTAG
+ fprintf(stderr,"xfer: %08x (%d)\n", bits, count);
+#endif
+ while (count-- > 0) {
+ if (bits & 1) {
+ buf[n++] = UB_TDI;
+ buf[n++] = UB_TDI | UB_TCK | RB;
+ } else {
+ buf[n++] = 0;
+ buf[n++] = UB_TCK | RB;
+ }
+ bits >>= 1;
+ }
+ buf[n-1] |= UB_TMS;
+ buf[n-2] |= UB_TMS;
+ r = usb_bulk(EP2_OUT, buf, n, 1000);
+ if (r < 0)
+ return r;
+ if (!out)
+ return 0;
+ bits = 0;
+ bit = 1;
+ while (readcount > 0) {
+ r = usb_bulk(EP1_IN, buf, 64, 1000);
+ if (r < 0)
+ return r;
+ if (r < 3)
+ continue;
+ for (n = 2; n < r; n++) {
+ if (buf[n] & 1)
+ bits |= bit;
+ bit <<= 1;
+ readcount--;
+ if (readcount == 0) {
+#if TRACE_JTAG
+ fprintf(stderr," : %08x\n", bits);
+#endif
+ *out = bits;
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
+/* JTAG notes
+ *
+ * TMS is sampled on +TCK
+ * Capture-XR state loads shift register on +TCK as state is exited
+ * Shift-XR state TDO goes active (containing shiftr[0]) on the first -TCK
+ * after entry, shifts occur on each +TCK, *including* the +TCK
+ * that will exist Shift-XR when TMS=1 again
+ * Update-XR update occurs on the -TCK after entry to state
+ *
+ * Any -> Reset: 11111
+ * Any -> Reset -> RTI: 111110
+ * RTI -> ShiftDR: 100
+ * ShiftDR shifting: 0 x N
+ * ShiftDR -> UpdateDR -> RTI: 110
+ * ShiftDR -> UpdateDR -> ShiftDR: 11100
+ * RTI -> ShiftIR: 1100
+ * ShiftIR shifting: 0 x N
+ * ShiftIR -> UpdateIR -> RTI: 110
+ */
+
+#define RESET 8,0b01111111
+#define SHIFTDR 3,0b001
+#define SHIFTIR 4,0b0011
+#define DONE 2,0b01
+#define AGAIN 4,0b0011
+
+int jtag_ir(unsigned sz, unsigned bits) {
+ int r;
+ if ((r = jtag_move(SHIFTIR)) < 0) return r;
+ if ((r = jtag_shift(sz, bits, 0)) < 0) return r;
+ if ((r = jtag_move(DONE)) < 0) return r;
+ return 0;
+}
+
+int jtag_dr(unsigned sz, unsigned bits, unsigned *out) {
+ int r;
+ if ((r = jtag_move(SHIFTDR)) < 0) return r;
+ if ((r = jtag_shift(sz, bits, out)) < 0) return r;
+ if ((r = jtag_move(DONE)) < 0) return r;
+ return 0;
+}
+
+static int jtag_is_open = 0;
+
+int jtag_open(void) {
+ int r;
+ if (!jtag_is_open) {
+ r = usb_open(0x09fb, 0x6001);
+ if (r < 0)
+ return r;
+ jtag_is_open = 1;
+ }
+ return 0;
+}
+int jtag_close(void) {
+ if (jtag_is_open) {
+ usb_close();
+ jtag_is_open = 0;
+ }
+ return 0;
+}
+int jtag_reset(void) {
+ return jtag_move(RESET);
+}
+
diff --git a/jtag.h b/jtag.h
@@ -0,0 +1,34 @@
+// Copyright 2012, Brian Swetland
+
+#ifndef _JTAG_H_
+#define _JTAG_H_
+
+int jtag_open(void);
+int jtag_close(void);
+
+/* move into RESET state */
+int jtag_reset(void);
+
+/* clock count times, TDI=0, TMS=bits[0], bits >>= 1 */
+int jtag_move(int count, unsigned bits);
+
+/* clock count-1 times, TMS=0, TDI=bits[0], bits >>= 1
+ * clock 1 time, TMS=1, TDI=bits[0]
+ * if out, capture TDO into out
+ */
+int jtag_shift(int count, unsigned bits, unsigned *out);
+
+
+/* load sz bits into IR */
+int jtag_ir(unsigned sz, unsigned bits);
+
+/* load sz bits into DR, capture sz bits into out if non-null */
+int jtag_dr(unsigned sz, unsigned bits, unsigned *out);
+
+
+/* altera virtual jtag support */
+int jtag_open_virtual_device(unsigned iid);
+int jtag_vir(unsigned vir);
+int jtag_vdr(unsigned sz, unsigned bits, unsigned *out);
+
+#endif
diff --git a/usb-blaster-protocol.txt b/usb-blaster-protocol.txt
@@ -0,0 +1,60 @@
+http://sf.net/apps/mediawiki/urjtag/index.php?title=Cable_Altera_USB-Blaster
+----------------------------------------------------------------------------
+
+Altera USB-Blaster
+------------------
+
+General
+-------
+ _________
+ | |
+ | AT93C46 |
+ |_________|
+ __|__________ _________
+ | | | |
+ USB__| FTDI 245BM |__| EPM7064 |__JTAG (B_TDO,B_TDI,B_TMS,B_TCK)
+ |_____________| |_________|
+ __|__________ _|___________
+ | | | |
+ | 6 MHz XTAL | | 24 MHz Osc. |
+ |_____________| |_____________|
+
+
+Quoting from ixo.de (http://www.ixo.de/info/usb_jtag/)
+usb_jtag/device/c51/usbjtag.c comments:
+
+usb_jtag firmware now happens to behave just like the combination of
+FT245BM and Altera-programmed EPM7064 CPLD in Altera's USB-Blaster.
+The CPLD knows two major modes: Bit banging mode and Byte shift mode.
+It starts in Bit banging mode. While bytes are received from the host
+on EP2OUT, each byte B of them is processed as follows:
+
+Bit banging mode
+----------------
+1. Remember bit 6 (0x40) in B as the "Read bit".
+2. If bit 7 (0x80) is set, switch to Byte shift mode for the coming X
+ bytes ( X := B & 0x3F ), and don't do anything else now.
+3. Otherwise, set the JTAG signals as follows:
+ - TCK/DCLK high if bit 0 was set (0x01), otherwise low
+ - TMS/nCONFIG high if bit 1 was set (0x02), otherwise low
+ - nCE high if bit 2 was set (0x04), otherwise low
+ - nCS high if bit 3 was set (0x08), otherwise low
+ - TDI/ASDI/DATAO high if bit 4 was set (0x10), otherwise low
+ - Output Enable/LED active if bit 5 was set (0x20), otherwise low
+4. If "Read bit" (0x40) was set, record the state of TDO(CONF_DONE) and
+ DATAOUT/(nSTATUS) pins and put is as a byte( (DATAOUT<<1)|TDO) in the
+ output FIFO _to_ the host.
+
+Byte shift mode
+---------------
+1. Load shift register with byte from host
+2. Do 8 times (i.e. for each bit of the byte; implemented in shift.a51)
+ - if nCS=1, set carry bit from TDO, else set carry bit from DATAOUT
+ (Active Serial mode)
+ - Rotate shift register through carry bit
+ - TDI := Carry bit
+ - Raise TCK, then lower TCK.
+3. If "Read bit" was set when switching into byte shift mode, record the
+ shift register content and put it into the FIFO to the host.
+
+