jtag-mpsse

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

commit 7b0db42e5617040a6873d7af5736bfd6c9fcd570
Author: Brian Swetland <swetland@frotz.net>
Date:   Sun, 20 Apr 2014 19:31:49 -0700

initial commit

Doesn't do a whole lot yet, but basic IR/DR interactions are working
along with a bus enumeration whatsit.

Diffstat:
A.gitignore | 2++
AMakefile | 15+++++++++++++++
Ajtag-mpsse.c | 535+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ajtag.c | 41+++++++++++++++++++++++++++++++++++++++++
Ajtag.h | 37+++++++++++++++++++++++++++++++++++++
Azynq.h | 32++++++++++++++++++++++++++++++++
6 files changed, 662 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +.*.swp +*.o diff --git a/Makefile b/Makefile @@ -0,0 +1,15 @@ + +CFLAGS := -g -Wall -O2 +LIBS := -lusb-1.0 + +all: jtag + +jtag.c: jtag.h +jtag-mpsse.c: jtag.h + +JTAG2232_OBJS := jtag.o jtag-mpsse.o +jtag: $(JTAG2232_OBJS) + $(CC) -o jtag $(JTAG2232_OBJS) $(LIBS) + +clean:: + rm -f jtag *.o diff --git a/jtag-mpsse.c b/jtag-mpsse.c @@ -0,0 +1,535 @@ +/* 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 <unistd.h> +#include <string.h> +#include <ctype.h> + +#include "jtag.h" + +#include <libusb-1.0/libusb.h> + +struct device_info { + u32 idcode; + u32 idmask; + u32 irsize; + const char *name; +}; + +#define ZYNQID(code) ((0x1B<<21)|(0x9<<17)|((code)<<12)|(0x49<<1)|1) +#define ZYNQMASK 0x0FFFFFFF + +struct device_info LIBRARY[] = { + { 0x4ba00477, 0xFFFFFFFF, 4, "Cortex A9" }, + { ZYNQID(0x02), ZYNQMASK, 6, "xc7x010" }, + { ZYNQID(0x1b), ZYNQMASK, 6, "xc7x015" }, + { ZYNQID(0x07), ZYNQMASK, 6, "xc7x020" }, + { ZYNQID(0x0c), ZYNQMASK, 6, "xc7x030" }, + { ZYNQID(0x11), ZYNQMASK, 6, "xc7x045" }, +}; + +struct device_info *identify_device(u32 idcode) { + unsigned n; + for (n = 0; n < sizeof(LIBRARY) / sizeof(LIBRARY[0]); n++) { + if ((idcode & LIBRARY[n].idmask) == LIBRARY[n].idcode) { + return LIBRARY + n; + } + } + return NULL; +} + +#define TRACE_USB 0 +#define TRACE_JTAG 0 + +#define DEVICE_MAX 16 + +struct jtag_handle { + struct libusb_device_handle *udev; + u8 ep_in; + u8 ep_out; + + u8 read_buffer[512]; + u8 *read_ptr; + u32 read_count; + u32 read_size; + + u32 dr_pre; + u32 dr_post; + u32 ir_pre; + u32 ir_post; + + u32 dev_count; + u32 dev_idcode[DEVICE_MAX]; + u32 dev_irsize[DEVICE_MAX]; + struct device_info *dev_info[DEVICE_MAX]; + + u32 active_irsize; + u32 active_idcode; +}; + +static int usb_open(JTAG *jtag, unsigned vid, unsigned pid) { + struct libusb_device_handle *udev; + + 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; + } + + libusb_detach_kernel_driver(udev, 0); + + if (libusb_claim_interface(udev, 0) < 0) { + fprintf(stderr,"cannot claim interface\n"); + return -1; + } + jtag->udev = udev; + jtag->ep_in = 0x81; + jtag->ep_out = 0x02; + return 0; +} + + +#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(struct libusb_device_handle *udev, + 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 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 + +int ftdi_reset(struct libusb_device_handle *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; +} + +int ftdi_mpsse_enable(struct libusb_device_handle *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; +} + +/* TODO: handle smaller packet size for lowspeed version of the part */ +/* TODO: multi-packet reads */ +/* TODO: asynch/background reads */ +static int ftdi_read(JTAG *jtag, unsigned char *buffer, int count, int timeout) { + int xfer; + while (count > 0) { + if (jtag->read_count >= count) { + memcpy(buffer, jtag->read_ptr, count); + jtag->read_count -= count; + jtag->read_ptr += count; + return 0; + } + if (jtag->read_count > 0) { + memcpy(buffer, jtag->read_ptr, jtag->read_count); + count -= jtag->read_count; + jtag->read_count = 0; + } + xfer = usb_bulk(jtag->udev, jtag->ep_in, + jtag->read_buffer, jtag->read_size, 1000); + if (xfer < 0) + return -1; + if (xfer < 2) + return -1; + /* discard header */ + jtag->read_ptr = jtag->read_buffer + 2; + jtag->read_count = xfer - 2; + } + return 0; +} + +static unsigned char bitxfermask[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; + +static int jtag_move(JTAG *jtag, int count, unsigned bits){ + unsigned char buf[1024]; + unsigned char *p = buf; + unsigned xfer; + + if (count > 32) + return -1; + + while (count > 0) { + xfer = (count > 6) ? 6 : count; + *p++ = 0x4b; + *p++ = xfer - 1; + *p++ = bits & bitxfermask[xfer]; + bits >>= xfer; + count -= xfer; + } + if (usb_bulk(jtag->udev, jtag->ep_out, buf, p - buf, 1000) != (p - buf)) + return -1; + return 0; +} + +int _jtag_shift(JTAG *jtag, int count, u64 bits, u64 *out, + int movecount, unsigned movebits) { + unsigned char buf[1024]; + unsigned char *p = buf; + unsigned xfer, bytes, readbytes, iobytes, readbits; + + if (count > 64) + return -1; + + if (movecount) { + /* if we're doing a joint shift-move, the last bit is + * shifted during the first bit of the move + */ + count--; + } + + if (count <= 0) + return -1; + + bytes = count / 8; + readbytes = bytes; + iobytes = bytes; + + if (bytes) { + count -= (bytes * 8); + *p++ = out ? 0x39 : 0x19; + *p++ = bytes - 1; + *p++ = 0; + while (bytes > 0) { + *p++ = bits & 0xff; + bits >>= 8; + bytes--; + } + } + readbits = count; + while (count > 0) { + xfer = (count > 6) ? 6 : count; + *p++ = out ? 0x3b : 0x1b; + *p++ = xfer - 1; + *p++ = bits & bitxfermask[xfer]; + bits >>= xfer; + count -= xfer; + iobytes++; + } + + if (movecount) { + if (movebits > 6) + return -1; /* TODO */ + *p++ = out ? 0x6b : 0x4b; + *p++ = movebits; + *p++ = ((bits & 1) << 7) | (movebits & 0x3F); + iobytes++; + } + + /* if we're doing readback demand an ioflush */ + if (out) + *p++ = 0x87; + + if (usb_bulk(jtag->udev, jtag->ep_out, buf, p - buf, 1000) != (p - buf)) + return -1; + + if (out) { + u64 n = 0; + unsigned shift = 0; + if (ftdi_read(jtag, buf, iobytes, 1000)) + return -1; + p = buf; + while (readbytes-- > 0) { + n |= (((u64) *p++) << shift); + shift += 8; + } + while (readbits > 0) { + xfer = (readbits > 6) ? 6 : readbits; + n |= ((*p++ & bitxfermask[xfer]) << shift); + shift <<= xfer; + readbits -= xfer; + } + if (movecount) { + n |= ((*p++ & bitxfermask[movecount]) << shift); + } + *out = n; + } + return 0; +} + +#define ONES(n) (0xFFFFFFFFFFFFFFFFULL >> ((64 - (n)))) + +static int jtag_shift_dr(JTAG *jtag, int count, u64 bits, + u64 *out, int movecount, unsigned movebits) { + int r; + bits <<= jtag->dr_pre; + r = _jtag_shift(jtag, count + jtag->dr_pre + jtag->dr_post, + bits, out, movecount, movebits); + if (out) { + *out = (*out >> jtag->dr_pre) & ONES(count); + } + return r; +} + +static int jtag_shift_ir(JTAG *jtag, int count, u64 bits, + int movecount, unsigned movebits) { + bits |= (ONES(jtag->ir_post) << count); + bits <<= jtag->ir_pre; + bits |= ONES(jtag->ir_pre); + return _jtag_shift(jtag, count + jtag->ir_pre + jtag->ir_post, + bits, NULL , movecount, movebits); +} + +/* 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 MOVE_NONE 0,0 +#define MOVE_ANY_TO_RESET_IDLE 8,0b01111111 +#define MOVE_IDLE_TO_SHIFTDR 3,0b001 +#define MOVE_IDLE_TO_SHIFTIR 4,0b0011 +#define MOVE_SHIFTxR_TO_IDLE 2,0b011 + + +void jtag_close(JTAG *jtag) { +} + +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, 0x20, 0x30, // set high state and dir +}; + +JTAG *jtag_open(void) { + int r; + JTAG *jtag = malloc(sizeof(JTAG)); + if (!jtag) + return NULL; + memset(jtag, 0, sizeof(JTAG)); + jtag->read_size = sizeof(jtag->read_buffer); + + r = usb_open(jtag, 0x0403, 0x6010); + if (r < 0) + goto fail; + if (ftdi_reset(jtag->udev)) + goto fail; + if (ftdi_mpsse_enable(jtag->udev)) + goto fail; + if (usb_bulk(jtag->udev, jtag->ep_out, + mpsse_init, sizeof(mpsse_init), 1000) != sizeof(mpsse_init)) + goto fail; + return jtag; + +fail: + jtag_close(jtag); + free(jtag); + return NULL; +} + +#define ENUM_MAGIC (0x00005038aaaaaaFFULL) + +/* TODO: attempt to probe for IR size if devices are not in library. + * On RESET the IR must have 1 in bit0 and 0 in bit1. + * The other bits are undefined. + */ +int jtag_enumerate(JTAG *jtag) { + u64 u; + unsigned n; + + jtag_move(jtag, MOVE_ANY_TO_RESET_IDLE); + jtag_move(jtag, MOVE_IDLE_TO_SHIFTIR); + /* BYPASS is always all 1s -- shift a pile of BYPASS opcodes into the chain */ + _jtag_shift(jtag, 64, 0xFFFFFFFFFFFFFFFFULL, NULL, MOVE_NONE); + _jtag_shift(jtag, 64, 0xFFFFFFFFFFFFFFFFULL, NULL, MOVE_NONE); + _jtag_shift(jtag, 64, 0xFFFFFFFFFFFFFFFFULL, NULL, MOVE_NONE); + _jtag_shift(jtag, 64, 0xFFFFFFFFFFFFFFFFULL, NULL, MOVE_NONE); + _jtag_shift(jtag, 64, 0xFFFFFFFFFFFFFFFFULL, NULL, MOVE_NONE); + _jtag_shift(jtag, 64, 0xFFFFFFFFFFFFFFFFULL, NULL, MOVE_NONE); + _jtag_shift(jtag, 64, 0xFFFFFFFFFFFFFFFFULL, NULL, MOVE_NONE); + _jtag_shift(jtag, 64, 0xFFFFFFFFFFFFFFFFULL, NULL, MOVE_SHIFTxR_TO_IDLE); + jtag_move(jtag, MOVE_IDLE_TO_SHIFTDR); + /* BYPASS registers should be one 0 bit each. + * Shift a pattern in and try to find it after all the 0s. + * If we can't find it, there must be >16 devices on the chain + * and/or they have enormous instruction registers. + */ + _jtag_shift(jtag, 64, ENUM_MAGIC, &u, MOVE_SHIFTxR_TO_IDLE); + for (n = 0; n < DEVICE_MAX; n++) { + if (!(u & 1)) { + u >>= 1; + continue; + } + if (u == ENUM_MAGIC) { + jtag->dev_count = n; + fprintf(stderr, "jtag: found %d devices\n", jtag->dev_count); + goto okay; + } + } + fprintf(stderr,"jtag: more than %d devices?!\n", DEVICE_MAX); + return -1; + +okay: + jtag_move(jtag, MOVE_ANY_TO_RESET_IDLE); + /* should put IDCODE in IR of everyone */ + jtag_move(jtag, MOVE_IDLE_TO_SHIFTDR); + for (n = 0; n < jtag->dev_count; n++) { + _jtag_shift(jtag, 32, 0xFFFFFFFF, &u, MOVE_NONE); + if ((u & 1) == 0) { + fprintf(stderr, "jtag: device %2d has no idcode\n", n); + return -1; + } + jtag->dev_idcode[n] = (u32) u; + jtag->dev_info[n] = identify_device((u32) u); + if (jtag->dev_info[n] == NULL) { + fprintf(stderr, "jtag: device %2d idcode %08x unknown IR size\n", + n, (u32) u); + return -1; + } + jtag->dev_irsize[n] = jtag->dev_info[n]->irsize; + fprintf(stderr, "jtag: device %2d idcode %08x irsize %2d '%s'\n", + n, jtag->dev_idcode[n], jtag->dev_irsize[n], + jtag->dev_info[n]->name); + } + return jtag->dev_count; +} + +int jtag_select(JTAG *jtag, u32 idcode) { + u32 irsize = 0; + u32 ir_pre = 0; + u32 ir_post = 0; + u32 dr_pre = 0; + u32 dr_post = 0; + int found = 0; + unsigned n; + + for (n = 0; n < jtag->dev_count; n++) { + u32 sz; + if (idcode == jtag->dev_idcode[n]) { + irsize = jtag->dev_irsize[n]; + found = 1; + continue; + } + sz = jtag->dev_irsize[n]; + if (sz > 32) + return -1; + if (found) { + ir_post += sz; + dr_post += 1; + } else { + ir_pre += sz; + dr_pre += 1; + } + } + if (!found) { + fprintf(stderr,"device id %08x not found in chain\n", idcode); + return -1; + } + + jtag->active_idcode = idcode; + jtag->active_irsize = irsize; + fprintf(stderr,"jtag: select idcode %08x\n", idcode); + fprintf(stderr,"jtag: TDI -> irpost(%d) -> iract(%d) -> irpre(%d) -> TDO\n", + ir_post, irsize, ir_pre); + + jtag->dr_pre = dr_pre; + jtag->dr_post = dr_post; + jtag->ir_pre = ir_pre; + jtag->ir_post = ir_post; + + return jtag_move(jtag, MOVE_ANY_TO_RESET_IDLE); +} + +int jtag_ir_wr(JTAG *jtag, u32 ir) { + if (jtag_move(jtag, MOVE_IDLE_TO_SHIFTIR)) + return -1; + if (jtag_shift_ir(jtag, jtag->active_irsize, ir, MOVE_SHIFTxR_TO_IDLE)) + return -1; + return 0; +} + +int jtag_dr_io(JTAG *jtag, u32 bitcount, u64 wdata, u64 *rdata) { + if (jtag_move(jtag, MOVE_IDLE_TO_SHIFTDR)) + return -1; + if (jtag_shift_dr(jtag, bitcount, wdata, rdata, MOVE_SHIFTxR_TO_IDLE)) + return -1; + return 0; +} + diff --git a/jtag.c b/jtag.c @@ -0,0 +1,41 @@ +/* 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 "jtag.h" +#include "zynq.h" + +int main(int argc, char **argv) { + JTAG *jtag; + unsigned n; + u64 u; + + if (!(jtag = jtag_open())) + return -1; + if ((n = jtag_enumerate(jtag)) <= 0) + return -1; + + if (jtag_select(jtag, 0x4ba00477)) + return -1; + + if (jtag_ir_wr(jtag, DAP_IDCODE)) + return -1; + if (jtag_dr_io(jtag, 32, 0, &u)) + return -1; + + fprintf(stderr,"idcode? %08lx\n", u); + return 0; +} diff --git a/jtag.h b/jtag.h @@ -0,0 +1,37 @@ +/* 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_H_ +#define _JTAG_H_ + +#include <stdint.h> + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint8_t u8; + +typedef struct jtag_handle JTAG; + +JTAG *jtag_open(void); +void jtag_close(JTAG *jtag); + +int jtag_enumerate(JTAG *jtag); +u32 jtag_get_nth_idcode(JTAG *jtag, u32 n); +int jtag_select(JTAG *jtag, u32 idcode); + +int jtag_ir_wr(JTAG *jtag, u32 ir); +int jtag_dr_io(JTAG *jtag, u32 bitcount, u64 wdata, u64 *rdata); + +#endif diff --git a/zynq.h b/zynq.h @@ -0,0 +1,32 @@ + +/* ARM DAP Controller (4bit IR) */ +#define DAP_IR_SIZE 4 + +#define DAP_ABORT 0x08 +#define DAP_DPACC 0x0A +#define DAP_APACC 0x0B +#define DAP_IDCODE 0x0E +#define DAP_BYPASS 0x0F + + +/* Xilinx TAP Controller (6bit IR) */ +#define XIL_IR_SIZE 6 + +#define XIL_EXTEST 0x00 +#define XIL_SAMPLE 0x01 +#define XIL_USER1 0x02 +#define XIL_USER2 0x03 +#define XIL_USER3 0x22 +#define XIL_USER4 0x23 +#define XIL_CFG_OUT 0x04 +#define XIL_CFG_IN 0x05 +#define XIL_USERCODE 0x08 +#define XIL_IDCODE 0x09 +#define XIL_ISC_ENABLE 0x10 +#define XIL_ISC_PROGRAM 0x11 +#define XIL_ISC_PROGRAM_SECURITY 0x12 +#define XIL_ISC_NOOP 0x14 +#define XIL_ISC_READ 0x1B +#define XIL_ISC_DISABLE 0x17 +#define XIL_BYPASS 0x3F +