jtag

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 30dc81288d66ebb99639b0f0dc3f9f79d6c64d19
Author: Brian Swetland <swetland@frotz.net>
Date:   Mon, 20 Feb 2012 22:13:58 -0800

initial commit

Diffstat:
AMakefile | 21+++++++++++++++++++++
AREADME | 20++++++++++++++++++++
Ajinfo.c | 23+++++++++++++++++++++++
Ajload.c | 46++++++++++++++++++++++++++++++++++++++++++++++
Ajtag-virtual.c | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ajtag.c | 214+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ajtag.h | 34++++++++++++++++++++++++++++++++++
Ausb-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. + +