diff --git a/jtag/ChangeLog b/jtag/ChangeLog index 3a67027b..e6fea8a9 100644 --- a/jtag/ChangeLog +++ b/jtag/ChangeLog @@ -1,3 +1,8 @@ +2008-03-01 Kolja Waschk + + * src/tap/cable/xpc.c: Implemented much faster access method using GPIF + transfers via vendor request 0xA6. + 2008-02-28 Kolja Waschk * doc/UrJTAG.txt: Concretized list of supported USB cables. diff --git a/jtag/src/tap/cable/xpc.c b/jtag/src/tap/cable/xpc.c index ae12216a..6d26ad97 100644 --- a/jtag/src/tap/cable/xpc.c +++ b/jtag/src/tap/cable/xpc.c @@ -35,13 +35,54 @@ #include "usbconn.h" #include "usbconn/libusb.h" -/* ----------------------------------------------------------------- */ +#include +#include -int xpcu_select_gpio(struct usb_dev_handle *xpcu, int int_or_ext ) +/* Connectivity on Spartan-3E starter kit: + * + * = FX2 Port A = + * + * IOA.0 => green LED (0=off) + * IOA.1 => red LED (0=off) + * IOA.2 is tied to VCC via R25 on my board + * IOA.3 isn't connected + * IOA.4 => CPLD pin 85 (reset?) + * IOA.5 => CPLD pin 86, eventually OE? + * IOA.6 => CPLD pin 83 (reset?) + * IOA.7 => CPLD pin 49 (reset?) + * + * = FX2 Port C = + * + * probably used as GPIFADR 0..7, to CPLD + * + * = FX2 Port E = + * + * IOE.3 => CPLD TCK + * IOE.4 => CPLD TMS + * IOE.5 => CPLD TDO + * IOE.6 => CPLD TDI + */ + +/* ---------------------------------------------------------------------- */ + +int xpcu_output_enable(struct usb_dev_handle *xpcu, int enable) { - if(usb_control_msg(xpcu, 0x40, 0xB0, 0x0052, int_or_ext, NULL, 0, 1000)<0) + if(usb_control_msg(xpcu, 0x40, 0xB0, enable ? 0x18 : 0x10, 0, NULL, 0, 1000)<0) { - perror("usb_control_msg(0x52.x) (select gpio)"); + perror("usb_control_msg(0x10/0x18)"); + return -1; + } + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +int xpcu_bit_reverse(struct usb_dev_handle *xpcu, uint8_t bits_in, uint8_t *bits_out) +{ + if(usb_control_msg(xpcu, 0xC0, 0xB0, 0x0020, bits_in, (char*)bits_out, 1, 1000)<0) + { + perror("usb_control_msg(0x20.x) (bit reverse)"); return -1; } @@ -52,7 +93,7 @@ int xpcu_select_gpio(struct usb_dev_handle *xpcu, int int_or_ext ) int xpcu_request_28(struct usb_dev_handle *xpcu, int value) { - /* Maybe clock speed setting? */ + /* Typical values seen during autodetection of chain configuration: 0x11, 0x12 */ if(usb_control_msg(xpcu, 0x40, 0xB0, 0x0028, value, NULL, 0, 1000)<0) { @@ -115,6 +156,107 @@ int xpcu_read_firmware_version(struct usb_dev_handle *xpcu, uint16_t *buf) return 0; } +/* ----------------------------------------------------------------- */ + +int xpcu_select_gpio(struct usb_dev_handle *xpcu, int int_or_ext ) +{ + if(usb_control_msg(xpcu, 0x40, 0xB0, 0x0052, int_or_ext, NULL, 0, 1000)<0) + { + perror("usb_control_msg(0x52.x) (select gpio)"); + return -1; + } + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +/* === A6 transfer (TDI/TMS/TCK/RDO) === + * + * Vendor request 0xA6 initiates a quite universal shift operation. The data + * is passed directly to the CPLD as 16-bit words. + * + * The argument N in the request specifies the number of state changes/bits. + * + * State changes are described by the following bulk write. It consists + * of ceil(N/4) little-endian 16-bit words, each describing up to 4 changes: + * + * Care has to be taken that N is NOT a multiple of 4. + * The CPLD doesn't seem to handle that well. + * + * Bit 0: Value for first TDI to shift out. + * Bit 1: Second TDI. + * Bit 2: Third TDI. + * Bit 3: Fourth TDI. + * + * Bit 4: Value for first TMS to shift out. + * Bit 5: Second TMS. + * Bit 6: Third TMS. + * Bit 7: Fourth TMS. + * + * Bit 8: Whether to raise/lower TCK for first bit. + * Bit 9: Same for second bit. + * Bit 10: Third bit. + * Bit 11: Fourth bit. + * + * Bit 12: Whether to read TDO for first bit + * Bit 13: Same for second bit. + * Bit 14: Third bit. + * Bit 15: Fourth bit. + * + * After the bulk write, if any of the bits 12..15 was set in any word, a + * bulk_read shall follow to collect the TDO data. + * + * TDO data is shifted in from MSB. In a "full" word with 16 TDO bits, the + * earliest one reached bit 0. The earliest of 15 bits however would be bit 0, + * and if there's only one TDO bit, it arrives as the MSB of the word. + */ + +static int +xpcu_shift(struct usb_dev_handle *xpcu, int reqno, int bits, int in_len, uint8_t *in, int out_len, uint8_t *out ) +{ + if(usb_control_msg(xpcu, 0x40, 0xB0, reqno, bits, NULL, 0, 1000)<0) + { + perror("usb_control_msg(x.x) (shift)"); + return -1; + } + +#if 0 + printf("###\n"); + printf("reqno = %02X\n", reqno); + printf("bits = %d\n", bits); + printf("in_len = %d, in_len*2 = %d\n", in_len, in_len * 2); + printf("out_len = %d, out_len*8 = %d\n", out_len, out_len * 8); + + printf("a6_display(\"%02X\", \"", bits); + for(i=0;i 0 && out != NULL) + { + if(usb_bulk_read(xpcu, 0x86, (char*)out, out_len, 1000)<0) + { + printf("\nusb_bulk_read error(shift): %s\n", strerror(errno)); + return -1; + } + } + +#if 0 + printf("\""); + for(i=0;ilink.usb->params))->handle; + + if (xpcu_request_28(xpcu, 0x11)<0) if (xpcu_select_gpio(xpcu, 1)<0) return -1; + if (xpcu_output_enable(xpcu, 1)<0) return -1; + if (xpcu_shift(xpcu, 0xA6, 2, 2, zero, 0, NULL)<0) return -1; + if (xpcu_read_gpio(xpcu, &iostate)<0) return -1; + if (xpcu_request_28(xpcu, 0x12)<0) return -1; return 0; } @@ -250,6 +400,175 @@ xpc_set_trst( cable_t *cable, int trst ) return 1; } +/* ---------------------------------------------------------------------- */ + +static int last_tdo; + +static void +xpc_ext_clock( cable_t *cable, int tms, int tdi, int n ) +{ + int i; + uint8_t tdo[2]; + uint8_t clock[2]; + struct usb_dev_handle *xpcu; + + clock[0] = (tms?0x10:0) | (tdi?0x01:0); + clock[1] = 0x11; /* clock'n read */ + + xpcu = ((libusb_param_t*)(cable->link.usb->params))->handle; + + for(i=0;iin_bits >> 2); + if ((xts->in_bits & 3) != 0) in_len += 2; + + out_len = 2 * (xts->out_bits >> 4); + if ((xts->out_bits & 15) != 0) out_len += 2; + + r = xpcu_shift (xts->xpcu, 0xA6, xts->in_bits, in_len, xts->in, out_len, xts->in); + + if(r>=0 && xts->out != NULL) + { + int out_idx = 0; + int out_rem = xts->out_bits; + + while (out_rem > 0) + { + uint32_t mask, rxw; + + rxw = (xts->in[out_idx+1]<<8) | xts->in[out_idx]; + + mask = (out_rem >= 16) ? 1 : (1<<(16 - out_rem)); + + for(;mask <= 32768 && out_rem > 0; mask <<= 1) + { + xts->out[xts->out_done++] = (rxw & mask) ? 1 : 0; + out_rem--; + } + + out_idx += 2; + } + } + + xts->in_bits = 0; + xts->out_bits = 0; + + return r; +} + +/* ---------------------------------------------------------------------- */ + +static void +xpcu_add_bit_for_ext_transfer( xpc_ext_transfer_state_t *xts, char in, char is_real ) +{ + int bit_idx = (xts->in_bits & 3); + int buf_idx = (xts->in_bits - bit_idx) >> 1; + + if(bit_idx == 0) + { + xts->in[buf_idx] = 0; + xts->in[buf_idx+1] = 0; + }; + + xts->in_bits++; + + if(is_real) + { + if(in) xts->in[buf_idx] |= (0x01<out) + { + xts->in[buf_idx+1] |= (0x11<out_bits++; + } + else + { + xts->in[buf_idx+1] |= (0x01<link.usb->params))->handle; + xts.out = (uint8_t*)out; + xts.in_bits = 0; + xts.out_bits = 0; + xts.out_done = 0; + + for(i=0,j=0; i=0; i++) + { + xpcu_add_bit_for_ext_transfer( &xts, in[i], 1 ); + if(xts.in_bits == (4*XPC_A6_CHUNKSIZE*4 - 1)) + { + j = xpcu_do_ext_transfer( &xts ); + } + }; + + if(xts.in_bits > 0 && j>=0) + { + /* CPLD doesn't like multiples of 4; add one dummy bit */ + if((xts.in_bits & 3) == 0) + { + xpcu_add_bit_for_ext_transfer( &xts, 0, 0 ); + } + j = xpcu_do_ext_transfer( &xts ); + } + + return j; +} + +/* ---------------------------------------------------------------------- */ + + cable_driver_t xpc_int_cable_driver = { "xpc_int", N_("Xilinx Platform Cable USB internal chain"), @@ -285,9 +604,9 @@ cable_driver_t xpc_ext_cable_driver = { xpc_ext_init, generic_usbconn_done, generic_set_frequency, - xpc_clock, - xpc_get_tdo, - generic_transfer, + xpc_ext_clock, + xpc_ext_get_tdo, + xpc_ext_transfer, xpc_set_trst, generic_get_trst, generic_flush_using_transfer,