Line 1... |
Line 1... |
/* cable_xpc_dlc9.c - Xilinx Platform Cable (DLC9) driver for the Advanced JTAG Bridge
|
/* cable_xpc_dlc9.c - Xilinx Platform Cable (DLC9) driver for the Advanced JTAG Bridge
|
Copyright (C) 2008 - 2010 Nathan Yawn, nathan.yawn@opencores.org
|
Copyright (C) 2008 - 2010 Nathan Yawn, nathan.yawn@opencores.org
|
|
Copyright (C) 2008 Kolja Waschk (UrJTAG project)
|
|
|
|
CPLD mode for burst transfers added by:
|
|
Copyright (C) 2011 Raul Fajardo, rfajardo@opencores.org
|
|
adapted from xc3sprog/ioxpc.cpp:
|
|
Copyright (C) 2009-2011 Uwe Bonnes bon@elektron.ikp.physik.tu-darmstadt.de
|
|
|
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
the Free Software Foundation; either version 2 of the License, or
|
the Free Software Foundation; either version 2 of the License, or
|
(at your option) any later version.
|
(at your option) any later version.
|
Line 16... |
Line 22... |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
|
#include <stdio.h>
|
#include <stdio.h>
|
#include <sys/types.h>
|
#include <sys/types.h>
|
#include <unistd.h> // for usleep()
|
|
#include <stdlib.h> // for sleep()
|
#include <stdlib.h> // for sleep()
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
#include "usb.h" // libusb header
|
#include "usb.h" // libusb header
|
|
|
#include "cable_xpc_dlc9.h"
|
#include "cable_xpc_dlc9.h"
|
|
#include "utilities.h"
|
#include "errcodes.h"
|
#include "errcodes.h"
|
|
|
|
/*
|
|
* The dynamic switch between FX2 and CPLD modes works fine. If a switch is required, the functions:
|
|
* static int cable_xpcusb_fx2_init();
|
|
* static int cable_xpcusb_cpld_init();
|
|
* can be called. The variable cpld_ctrl can tell if the CPLD is active (1) or FX2 (0).
|
|
*
|
|
* The functions accessing the cable in this driver always check the cpld_ctrl variable and adapt the
|
|
* cable mode accordingly.
|
|
*
|
|
* Therefore, we can arbitrarily define bit and stream functionality from different modes without
|
|
* concern. Out_func and inout_func are not provided under CPLD mode because it always complete
|
|
* a bit transfer by toggling the clock twice 0-1-0 providing a write and a read.
|
|
*
|
|
* When using stream functionality of the CPLD, and bit functionality of FX2, also cable_common_read_write_bit
|
|
* works. The cable_xpcusb_read_write_bit is not necessary then. However, it is still necessary when using:
|
|
* cable_common_write_stream
|
|
* cable_common_read_stream
|
|
*/
|
|
|
|
#define debug(...) //fprintf(stderr, __VA_ARGS__ )
|
|
|
|
//#define CLASSIC
|
|
#define CPLDBITFUNC
|
|
|
|
#ifdef CLASSIC
|
jtag_cable_t dlc9_cable_driver = {
|
jtag_cable_t dlc9_cable_driver = {
|
.name ="xpc_usb" ,
|
.name ="xpc_usb" ,
|
.inout_func = cable_xpcusb_inout,
|
.inout_func = cable_xpcusb_inout,
|
.out_func = cable_xpcusb_out,
|
.out_func = cable_xpcusb_out,
|
.init_func = cable_xpcusb_init,
|
.init_func = cable_xpcusb_init,
|
Line 38... |
Line 71... |
.stream_inout_func = cable_common_read_stream,
|
.stream_inout_func = cable_common_read_stream,
|
.flush_func = NULL,
|
.flush_func = NULL,
|
.opts = "",
|
.opts = "",
|
.help = "no options\n",
|
.help = "no options\n",
|
};
|
};
|
|
#else
|
|
jtag_cable_t dlc9_cable_driver = {
|
|
.name ="xpc_usb" ,
|
|
.inout_func = cable_xpcusb_inout,
|
|
.out_func = cable_xpcusb_out,
|
|
.init_func = cable_xpcusb_init,
|
|
.opt_func = cable_xpcusb_opt,
|
|
#ifdef CPLDBITFUNC
|
|
.bit_out_func = cable_xpcusb_cpld_write_bit,
|
|
.bit_inout_func = cable_xpcusb_cpld_readwrite_bit,
|
|
#else
|
|
.bit_out_func = cable_common_write_bit,
|
|
.bit_inout_func = cable_common_read_write_bit,
|
|
#endif
|
|
.stream_out_func = cable_xpcusb_write_stream,
|
|
.stream_inout_func = cable_xpcusb_readwrite_stream,
|
|
.flush_func = NULL,
|
|
.opts = "",
|
|
.help = "no options\n",
|
|
};
|
|
#endif
|
|
|
#define USB_TIMEOUT 500
|
#define USB_TIMEOUT 500
|
|
|
// Note that this is based on the UrJTAG driver for the XPC-USB,
|
|
// which was 'experimental' at the time.
|
|
// It only supports bit-bang mode, and therefore will not be fast.
|
|
|
|
// USB constants for the DLC9
|
// USB constants for the DLC9
|
#define XPCUSB_VID 0x3fd
|
#define XPCUSB_VID 0x3fd
|
#define XPCUSB_PID 0x08
|
#define XPCUSB_PID 0x08
|
|
|
// Bit meanings in the command byte sent to the DLC9
|
// Bit meanings in the command byte sent to the DLC9
|
Line 57... |
Line 107... |
#define XPCUSB_CMD_TDO 0x01
|
#define XPCUSB_CMD_TDO 0x01
|
#define XPCUSB_CMD_TMS 0x02
|
#define XPCUSB_CMD_TMS 0x02
|
#define XPCUSB_CMD_TCK 0x04
|
#define XPCUSB_CMD_TCK 0x04
|
#define XPCUSB_CMD_PROG 0x08
|
#define XPCUSB_CMD_PROG 0x08
|
|
|
|
/*
|
|
* send max 4096 bytes to CPLD
|
|
* this is equal to 8192 TDI plus 8192 TDO bits
|
|
*/
|
|
#define CPLD_MAX_BYTES (1<<12)
|
|
|
|
/*
|
|
* Buffer has to hold 8192 bits for write, each 2 bytes hold 4 bits for write, so this has to be 4096
|
|
* Buffer has to hold 8192 bits for read, each byte holds 8 bits for read, so this has to be 1024
|
|
* Therefore, buffer size -> CPLD_MAX_BYTES
|
|
*/
|
|
typedef struct
|
|
{
|
|
int in_bits;
|
|
int out_bits;
|
|
uint8_t buf[CPLD_MAX_BYTES];
|
|
} xpc_ext_transfer_state_t;
|
|
|
|
|
|
static struct usb_device *device = NULL;
|
|
static usb_dev_handle *h_device = NULL;
|
|
static int cpld_ctrl = 0;
|
|
|
|
static const uint32_t endianess_test = 1;
|
|
#define is_bigendian() ( (*(uint8_t*)&endianess_test) == 0 )
|
|
|
|
static int cable_xpcusb_open_cable(void);
|
|
static void cable_xpcusb_close_cable(void);
|
|
static int cable_xpcusb_fx2_init();
|
|
static int cable_xpcusb_cpld_init();
|
|
|
|
|
static struct usb_device *device;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
/*----- Functions for the Xilinx Platform Cable USB (Model DLC9) */
|
/*----- Functions for the Xilinx Platform Cable USB (Model DLC9) */
|
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
static int xpcu_request_28(struct usb_dev_handle *xpcu, int value)
|
static 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, USB_TIMEOUT)<0)
|
|
{
|
|
fprintf(stderr, "Error sending usb_control_msg(0x28.x)\n");
|
|
return APP_ERR_USB;
|
|
}
|
|
|
|
return APP_ERR_NONE;
|
if(usb_control_msg(xpcu, 0x40, 0xB0, 0x0028, value, NULL, 0, 1000)<0)
|
}
|
|
|
|
|
|
static int xpcu_raise_ioa5(struct usb_dev_handle *xpcu)
|
|
{
|
|
if(usb_control_msg(xpcu, 0x40, 0xB0, 0x0018, 0x0000, NULL, 0, USB_TIMEOUT)<0)
|
|
{
|
{
|
fprintf(stderr, "Error sending usb_control_msg(0x18.0x00) (raise IOA.5{\n");
|
perror("usb_control_msg(0x28.x)");
|
return APP_ERR_USB;
|
return -1;
|
}
|
}
|
|
|
return APP_ERR_NONE;
|
return 0;
|
}
|
}
|
|
|
static int xpcu_select_gpio(struct usb_dev_handle *xpcu, int chain)
|
static int xpcu_select_gpio(struct usb_dev_handle *xpcu, int chain)
|
{
|
{
|
if(usb_control_msg(xpcu, 0x40, 0xB0, 0x0052, chain, NULL, 0, USB_TIMEOUT)<0)
|
if(usb_control_msg(xpcu, 0x40, 0xB0, 0x0052, chain, NULL, 0, USB_TIMEOUT)<0)
|
Line 101... |
Line 169... |
}
|
}
|
|
|
return APP_ERR_NONE;
|
return APP_ERR_NONE;
|
}
|
}
|
|
|
static int xpcu_read_firmware_version(struct usb_dev_handle *xpcu, uint16_t *buf)
|
static int xpcu_write_gpio(struct usb_dev_handle *xpcu, uint8_t bits)
|
{
|
{
|
if(usb_control_msg(xpcu, 0xC0, 0xB0, 0x0050, 0x0000, (char*)buf, 2, USB_TIMEOUT)<0)
|
if(usb_control_msg(xpcu, 0x40, 0xB0, 0x0030, bits, NULL, 0, 1000)<0)
|
{
|
{
|
fprintf(stderr,"Error sending usb_control_msg(0x50.0) (read_firmware_version)\n");
|
perror("usb_control_msg(0x30.0x00) (write port E)");
|
return APP_ERR_USB;
|
return -1;
|
}
|
}
|
return APP_ERR_NONE;
|
|
|
return 0;
|
}
|
}
|
|
|
|
|
static int xpcu_read_cpld_version(struct usb_dev_handle *xpcu, uint16_t *buf)
|
static int xpcu_read_cpld_version(struct usb_dev_handle *xpcu, uint16_t *buf)
|
{
|
{
|
if(usb_control_msg(xpcu, 0xC0, 0xB0, 0x0050, 0x0001, (char*)buf, 2, USB_TIMEOUT)<0)
|
if(usb_control_msg(xpcu, 0xC0, 0xB0, 0x0050, 0x0001, (char*)buf, 2, 1000)<0)
|
{
|
{
|
fprintf(stderr, "Error sending usb_control_msg(0x50.1) (read_cpld_version)\n");
|
perror("usb_control_msg(0x50.1) (read_cpld_version)");
|
return APP_ERR_USB;
|
return -1;
|
}
|
}
|
return APP_ERR_NONE;
|
return 0;
|
}
|
}
|
|
|
|
|
static int xpcusb_enumerate_bus(void)
|
static int xpcu_read_firmware_version(struct usb_dev_handle *xpcu, uint16_t *buf)
|
{
|
{
|
int flag; // for USB bus scanning stop condition
|
if(usb_control_msg(xpcu, 0xC0, 0xB0, 0x0050, 0x0000, (char*)buf, 2, 1000)<0)
|
struct usb_bus *bus; // pointer on the USB bus
|
{
|
|
perror("usb_control_msg(0x50.0) (read_firmware_version)");
|
// board detection
|
return -1;
|
usb_init();
|
}
|
usb_find_busses();
|
|
usb_find_devices();
|
|
|
|
flag = 0;
|
return 0;
|
|
}
|
|
|
for (bus = usb_get_busses(); bus; bus = bus->next)
|
static int xpcu_output_enable(struct usb_dev_handle *xpcu, int enable)
|
{
|
|
for (device = bus->devices; device; device = device->next)
|
|
{
|
{
|
if (device->descriptor.idVendor == XPCUSB_VID &&
|
if(usb_control_msg(xpcu, 0x40, 0xB0, enable ? 0x18 : 0x10, 0, NULL, 0, 1000)<0)
|
device->descriptor.idProduct == XPCUSB_PID)
|
|
{
|
{
|
flag = 1;
|
perror("usb_control_msg(0x10/0x18)");
|
fprintf(stderr, "Found Xilinx Platform Cable USB (DLC9)\n");
|
return -1;
|
return APP_ERR_NONE;
|
|
}
|
}
|
|
|
|
return 0;
|
}
|
}
|
if (flag)
|
|
break;
|
/*
|
|
* === 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.
|
|
* (see xpcusb_add_bit_for_ext_transfer)
|
|
*
|
|
* After the bulk write, if any of the bits 12..15 was set in any word
|
|
* (see xpcusb_add_bit_for_ext_transfer), a bulk_read shall follow to collect
|
|
* the TDO data.
|
|
*/
|
|
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;
|
}
|
}
|
|
|
fprintf(stderr, "Failed to find Xilinx Platform Cable USB\n");
|
#if VERBOSE
|
return APP_ERR_CABLENOTFOUND;
|
{
|
|
int i;
|
|
printf("\n###\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<in_len;i++) printf("%02X%s", in[i], (i+1<in_len)?",":"");
|
|
printf("\", ");
|
}
|
}
|
|
#endif
|
|
|
|
if(usb_bulk_write(xpcu, 0x02, (char*)in, in_len, 1000)<0)
|
|
{
|
|
fprintf(stderr, "\nusb_bulk_write error(shift): %s\n", strerror(errno));
|
|
fprintf(stderr, "Burst length: %d\n", in_len);
|
|
return -1;
|
|
}
|
|
|
int cable_xpcusb_init()
|
if(out_len > 0 && out != NULL)
|
{
|
{
|
int err = APP_ERR_NONE;
|
if(usb_bulk_read(xpcu, 0x86, (char*)out, out_len, 1000)<0)
|
|
{
|
|
printf("\nusb_bulk_read error(shift): %s\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
|
// Process to reset the XPC USB (DLC9)
|
#if VERBOSE
|
if(err |= xpcusb_enumerate_bus()) {
|
{
|
return err;
|
int i;
|
|
printf("\"");
|
|
for(i=0;i<out_len;i++) printf("%02X%s", out[i], (i+1<out_len)?",":"");
|
|
printf("\")\n");
|
}
|
}
|
|
#endif
|
|
|
usb_dev_handle *h_device = usb_open(device);
|
return 0;
|
|
}
|
|
|
if(h_device == NULL)
|
/*
|
|
* 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.
|
|
* */
|
|
static void xpcusb_add_bit_for_ext_transfer(xpc_ext_transfer_state_t *xts, uint8_t toggle_tclk, uint8_t tms, uint8_t tdi, uint8_t sample_tdo)
|
{
|
{
|
fprintf(stderr, "Init failed to open USB device for reset\n");
|
int bit_idx = (xts->in_bits & 3);
|
return APP_ERR_USB;
|
int buf_idx = (xts->in_bits - bit_idx) >> 1;
|
|
|
|
debug("add_bit, in = %i, bit_idx = %i, buf_idx = %i\n", tdi, bit_idx, buf_idx);
|
|
|
|
if(bit_idx == 0)
|
|
{
|
|
xts->buf[buf_idx] = 0;
|
|
xts->buf[buf_idx+1] = 0;
|
}
|
}
|
|
|
if(usb_reset(h_device) != APP_ERR_NONE)
|
xts->in_bits++;
|
fprintf(stderr, "Failed to reset XPC-USB\n");
|
|
|
|
usb_close(h_device);
|
if(tdi) xts->buf[buf_idx] |= (0x01<<bit_idx);
|
|
|
// Wait for reset!!!
|
if(tms) xts->buf[buf_idx] |= (0x10<<bit_idx);
|
sleep(1);
|
|
|
|
// Do device initialization
|
if ( toggle_tclk ) xts->buf[buf_idx+1] |= (0x01<<bit_idx);
|
if(err |= xpcusb_enumerate_bus())
|
|
return err;
|
|
|
|
h_device = usb_open(device);
|
if(sample_tdo)
|
if(h_device == NULL)
|
|
{
|
{
|
fprintf(stderr, "Init failed to open USB device for initialization\n");
|
xts->buf[buf_idx+1] |= (0x10<<bit_idx);
|
return APP_ERR_USB;
|
xts->out_bits++;
|
|
}
|
}
|
}
|
|
|
// set the configuration
|
/*
|
if (usb_set_configuration(h_device, device->config->bConfigurationValue))
|
* TDO data is shifted in from MSB to LSB and transferred 32-bit little-endian.
|
|
* In a "full" word with 32 TDO bits, the earliest one reached bit 0.
|
|
* The earliest of 31 bits however would be bit 1. A 17 bit transfer has the LSB
|
|
* as the MSB of uint16_t[0], other bits are in uint16_t[1].
|
|
*
|
|
* However, if the last packet is smaller than 16, only 2 bytes are transferred.
|
|
* If there's only one TDO bit, it arrives as the MSB of the 16-bit word, uint16_t[0].
|
|
* uint16_t[1] is then skipped.
|
|
*
|
|
* For full 32 bits blocks, the data is aligned. The last non 32-bits block arrives
|
|
* non-aligned and has to be re-aligned. Half-words (16-bits) transfers have to be
|
|
* re-aligned too.
|
|
*/
|
|
static int xpcusb_do_ext_transfer(xpc_ext_transfer_state_t *xts, uint32_t * tdostream)
|
{
|
{
|
usb_close(h_device);
|
int i, r;
|
fprintf(stderr, "USB-reset failed to set configuration\n");
|
int in_len, out_len;
|
return APP_ERR_USB;
|
int shift, bit_num, bit_val;
|
}
|
uint32_t aligned_32bitwords, aligned_bytes;
|
|
uint32_t out_done;
|
|
|
while (usb_claim_interface(h_device, device->config->interface->altsetting->bInterfaceNumber));
|
//cpld expects data (tdi) to be in 16 bit words
|
|
in_len = 2 * (xts->in_bits >> 2);
|
|
if ((xts->in_bits & 3) != 0) in_len += 2;
|
|
|
// DO DEVICE-SPECIFIC INIT HERE
|
//cpld returns the read data (tdo) in 32 bit words
|
// Don't mess with the order here, it's easy to break.
|
out_len = 2 * (xts->out_bits >> 4);
|
|
if ((xts->out_bits & 15) != 0) out_len += 2;
|
|
|
// Maybe set the clock speed?
|
r = xpcu_shift (h_device, 0xA6, xts->in_bits, in_len, xts->buf, out_len, xts->buf);
|
if(xpcu_request_28(h_device, 0x11) != APP_ERR_NONE) {
|
|
fprintf(stderr, "Request 28 (set clock speed?) failed.\n");
|
|
}
|
|
|
|
// Set internal TCK,TMS,TDO to 0
|
if(r >= 0 && xts->out_bits > 0 && tdostream != NULL)
|
if(usb_control_msg(h_device, 0x40, 0xB0, 0x0030, 0x08, NULL, 0, USB_TIMEOUT)!= APP_ERR_NONE) {
|
{
|
fprintf(stderr, "usb_control_msg(0x30.0x00) (write port E) failed\n");
|
aligned_32bitwords = xts->out_bits/32;
|
|
aligned_bytes = aligned_32bitwords*4;
|
|
if ( is_bigendian() ) //these data is aligned as little-endian
|
|
{
|
|
for (i=0; i<aligned_bytes; i++)
|
|
{
|
|
if ( i%4 == 0 )
|
|
tdostream[i/4] = 0;
|
|
tdostream[i/4] |= xts->buf[i] << (i%4)*8;
|
}
|
}
|
|
}
|
|
else
|
|
memcpy(tdostream, xts->buf, aligned_bytes); //these data is already little-endian
|
|
|
// Read firmware version (constant embedded in firmware)
|
out_done = aligned_bytes*8;
|
uint16_t buf;
|
|
|
|
if(xpcu_read_firmware_version(h_device, &buf) != APP_ERR_NONE) {
|
//This data is not aligned
|
fprintf(stderr, "Failed to read firmware version.\n");
|
if (xts->out_bits % 32)
|
}
|
{
|
else {
|
shift = xts->out_bits % 16; //we can also receive a 16-bit word in which case
|
printf("firmware version = 0x%04X (%u)\n", buf, buf);
|
if (shift) //the MSB starts in the least significant 16 bit word
|
}
|
shift = 16 - shift; //and it shifts the same way for 32 bit if
|
|
//out_bits > 16 and ( shift = 32 - out_bits % 32 )
|
|
|
// Read CPLD version (uses the internal GPIF interface)
|
debug("out_done %d shift %d\n", out_done, shift);
|
if(xpcu_read_cpld_version(h_device, &buf) != APP_ERR_NONE) {
|
for (i= aligned_bytes*8; i <xts->out_bits; i++)
|
fprintf(stderr, "Failed to read CPLD version.\n");
|
|
}
|
|
else
|
|
{
|
{
|
printf("cable CPLD version = 0x%04X (%u)\n", buf, buf);
|
bit_num = i + shift;
|
if(buf == 0) {
|
bit_val = xts->buf[bit_num/8] & (1<<(bit_num%8));
|
printf("Warning: version '0' can't be correct. Please try resetting the cable\n");
|
if(!(out_done % 32))
|
|
tdostream[out_done/32] = 0;
|
|
if (bit_val)
|
|
tdostream[out_done/32] |= (1<<(out_done%32));
|
|
out_done++;
|
}
|
}
|
}
|
}
|
|
|
// Set IOA bit 5, which enables output buffers
|
|
if(xpcu_raise_ioa5(h_device) != APP_ERR_NONE) {
|
|
fprintf(stderr, "Failed to enable XPC output buffers\n");
|
|
}
|
}
|
|
|
// access external chain for normal operation
|
xts->in_bits = 0;
|
if(xpcu_select_gpio(h_device, 0) != APP_ERR_NONE) {
|
xts->out_bits = 0;
|
fprintf(stderr, "Failed to select external JTAG chain\n");
|
|
}
|
|
|
|
// Init all done, release cable
|
return r;
|
if (usb_release_interface(h_device, device->config->interface->altsetting->bInterfaceNumber)){
|
|
usb_close(h_device);
|
|
fprintf(stderr, "USB-out failed to release interface\n");
|
|
return APP_ERR_USB;
|
|
}
|
}
|
|
|
usb_close(h_device);
|
|
|
|
return APP_ERR_NONE;
|
|
}
|
|
|
|
|
|
int cable_xpcusb_out(uint8_t value)
|
int cable_xpcusb_out(uint8_t value)
|
{
|
{
|
int rv; // to catch return values of functions
|
int rv; // to catch return values of functions
|
usb_dev_handle *h_device; // handle on the ubs device
|
//usb_dev_handle *h_device; // handle on the ubs device
|
uint8_t out;
|
uint8_t out;
|
|
|
// open the device
|
// open the device, if necessary
|
h_device = usb_open(device);
|
|
if (h_device == NULL){
|
if (h_device == NULL){
|
usb_close(h_device);
|
rv = cable_xpcusb_open_cable();
|
fprintf(stderr, "USB-out failed to open device\n");
|
if(rv != APP_ERR_NONE) return rv;
|
return APP_ERR_USB;
|
|
}
|
}
|
|
|
// set the configuration
|
if ( cpld_ctrl )
|
if (usb_set_configuration(h_device, device->config->bConfigurationValue))
|
|
{
|
{
|
usb_close(h_device);
|
rv = cable_xpcusb_fx2_init();
|
fprintf(stderr, "USB-out failed to set configuration\n");
|
if ( rv != APP_ERR_NONE) return rv;
|
return APP_ERR_USB;
|
|
}
|
}
|
|
|
// wait until device is ready
|
|
while (usb_claim_interface(h_device, device->config->interface->altsetting->bInterfaceNumber));
|
|
|
|
// send the buffer
|
// send the buffer
|
// Translate to USB blaster protocol
|
// Translate to USB blaster protocol
|
out = 0;
|
out = 0;
|
if(value & TCLK_BIT)
|
if(value & TCLK_BIT)
|
out |= XPCUSB_CMD_TCK;
|
out |= XPCUSB_CMD_TCK;
|
Line 296... |
Line 440... |
if(value & TMS_BIT)
|
if(value & TMS_BIT)
|
out |= XPCUSB_CMD_TMS;
|
out |= XPCUSB_CMD_TMS;
|
|
|
out |= XPCUSB_CMD_PROG; // Set output PROG (always necessary)
|
out |= XPCUSB_CMD_PROG; // Set output PROG (always necessary)
|
|
|
/* debug
|
|
if(value & TDI_BIT)
|
|
printf("Write 1\n");
|
|
else
|
|
printf("Write 0\n");
|
|
*/
|
|
|
|
rv = usb_control_msg(h_device, 0x40, 0xB0, 0x0030, out, NULL, 0, USB_TIMEOUT);
|
rv = usb_control_msg(h_device, 0x40, 0xB0, 0x0030, out, NULL, 0, USB_TIMEOUT);
|
if (rv < 0){
|
if (rv < 0){
|
fprintf(stderr, "\nFailed to send a write control message (rv = %d):\n%s\n", rv, usb_strerror());
|
fprintf(stderr, "\nFailed to send a write control message (rv = %d):\n%s\n", rv, usb_strerror());
|
|
cable_xpcusb_close_cable();
|
|
return APP_ERR_USB;
|
}
|
}
|
|
|
// release the interface cleanly
|
|
if (usb_release_interface(h_device, device->config->interface->altsetting->bInterfaceNumber)){
|
|
fprintf(stderr, "Warning: failed to release usb interface after write\n");
|
|
}
|
|
|
|
// close the device
|
|
usb_close(h_device);
|
|
return APP_ERR_NONE;
|
return APP_ERR_NONE;
|
}
|
}
|
|
|
|
|
int cable_xpcusb_inout(uint8_t value, uint8_t *inval)
|
int cable_xpcusb_inout(uint8_t value, uint8_t *inval)
|
{
|
{
|
int rv; // to catch return values of functions
|
int rv; // to catch return values of functions
|
usb_dev_handle *h_device; // handle on the usb device
|
//usb_dev_handle *h_device; // handle on the usb device
|
char ret = 0;
|
char ret = 0;
|
uint8_t out;
|
uint8_t out;
|
|
|
|
// open the device, if necessary
|
|
if(h_device == NULL) {
|
|
rv = cable_xpcusb_open_cable();
|
|
if(rv != APP_ERR_NONE) return rv;
|
|
}
|
|
|
|
if ( cpld_ctrl )
|
|
{
|
|
rv = cable_xpcusb_fx2_init();
|
|
if ( rv != APP_ERR_NONE) return rv;
|
|
}
|
|
|
// Translate to USB blaster protocol
|
// Translate to USB blaster protocol
|
out = 0;
|
out = 0;
|
if(value & TCLK_BIT)
|
if(value & TCLK_BIT)
|
out |= XPCUSB_CMD_TCK;
|
out |= XPCUSB_CMD_TCK;
|
if(value & TDI_BIT)
|
if(value & TDI_BIT)
|
Line 337... |
Line 480... |
if(value & TMS_BIT)
|
if(value & TMS_BIT)
|
out |= XPCUSB_CMD_TMS;
|
out |= XPCUSB_CMD_TMS;
|
|
|
out |= XPCUSB_CMD_PROG; // Set output PROG (always necessary)
|
out |= XPCUSB_CMD_PROG; // Set output PROG (always necessary)
|
|
|
// open the device
|
|
h_device = usb_open(device);
|
|
if (h_device == NULL){
|
|
usb_close(h_device);
|
|
return APP_ERR_USB;
|
|
}
|
|
|
|
// set the configuration
|
|
if (usb_set_configuration(h_device, device->config->bConfigurationValue)){
|
|
usb_close(h_device);
|
|
return APP_ERR_USB;
|
|
}
|
|
|
|
// wait until device is ready
|
|
while (usb_claim_interface(h_device, device->config->interface->altsetting->bInterfaceNumber));
|
|
|
|
// Send the output
|
// Send the output
|
rv = usb_control_msg(h_device, 0x40, 0xB0, 0x0030, out, NULL, 0, USB_TIMEOUT);
|
rv = usb_control_msg(h_device, 0x40, 0xB0, 0x0030, out, NULL, 0, USB_TIMEOUT);
|
if (rv < 0){
|
if (rv < 0){
|
fprintf(stderr, "\nFailed to send a write control message (rv = %x):\n%s\n", rv, usb_strerror());
|
fprintf(stderr, "\nFailed to send a write control message (rv = %x):\n%s\n", rv, usb_strerror());
|
goto usbblaster_in_fail;
|
cable_xpcusb_close_cable();
|
|
return APP_ERR_USB;
|
}
|
}
|
|
|
|
|
// receive the response
|
// receive the response
|
rv = usb_control_msg(h_device, 0xC0, 0xB0, 0x0038, 0, (char*)&ret, 1, USB_TIMEOUT);
|
rv = usb_control_msg(h_device, 0xC0, 0xB0, 0x0038, 0, (char*)&ret, 1, USB_TIMEOUT);
|
if (rv < 0){
|
if (rv < 0){
|
fprintf(stderr, "\nFailed to execute a read control message:\n%s\n", usb_strerror());
|
fprintf(stderr, "\nFailed to execute a read control message:\n%s\n", usb_strerror());
|
goto usbblaster_in_fail;
|
cable_xpcusb_close_cable();
|
}
|
|
|
|
|
|
// release the interface cleanly
|
|
if (usb_release_interface(h_device, device->config->interface->altsetting->bInterfaceNumber)){
|
|
fprintf(stderr, "Warning: failed to release USB interface after read\n");
|
|
usb_close(h_device);
|
|
return APP_ERR_USB;
|
return APP_ERR_USB;
|
}
|
}
|
|
|
// close the device
|
|
usb_close(h_device);
|
|
|
|
/* debug
|
|
if(value & TDI_BIT)
|
|
printf("Write 1, ");
|
|
else
|
|
printf("Write 0, ");
|
|
*/
|
|
|
|
if(ret & XPCUSB_CMD_TDO)
|
if(ret & XPCUSB_CMD_TDO)
|
*inval = 1;
|
*inval = 1;
|
else
|
else
|
*inval = 0;
|
*inval = 0;
|
|
|
//printf("Read 0\n");
|
|
return APP_ERR_NONE;
|
return APP_ERR_NONE;
|
|
|
usbblaster_in_fail:
|
|
usb_release_interface(h_device, device->config->interface->altsetting->bInterfaceNumber);
|
|
usb_close(h_device);
|
|
return APP_ERR_USB;
|
|
}
|
}
|
|
|
|
|
// Xilinx couldn't be like everyone else. Oh, no.
|
// Xilinx couldn't be like everyone else. Oh, no.
|
// For some reason, "set data/drop TCK" then "read data/raise TCK" won't work.
|
// For some reason, "set data/drop TCK" then "read data/raise TCK" won't work.
|
// So we have our very own bit read/write function. @whee.
|
// So we have our very own bit read/write function. @whee.
|
int cable_xpcusb_read_write_bit(uint8_t packet_out, uint8_t *bit_in) {
|
int cable_xpcusb_read_write_bit(uint8_t packet_out, uint8_t *bit_in) {
|
uint8_t data = TRST_BIT; // TRST is active low, don't clear unless /set/ in 'packet'
|
uint8_t data = TRST_BIT; // TRST is active low, don't clear unless /set/ in 'packet'
|
Line 420... |
Line 523... |
|
|
return err;
|
return err;
|
}
|
}
|
|
|
|
|
|
int cable_xpcusb_cpld_write_bit(uint8_t value)
|
|
{
|
|
uint32_t out;
|
|
out = (value & TDO) ? 1:0;
|
|
return cable_xpcusb_write_stream(&out, 1, value & TMS);
|
|
}
|
|
|
|
int cable_xpcusb_cpld_readwrite_bit(uint8_t value, uint8_t *inval)
|
|
{
|
|
int r;
|
|
uint32_t out;
|
|
uint32_t in;
|
|
out = (value & TDO) ? 1:0;
|
|
r = cable_xpcusb_readwrite_stream(&out, &in, 1, value & TMS);
|
|
if ( r < 0 )
|
|
return r;
|
|
*inval = in & 0x1;
|
|
return APP_ERR_NONE;
|
|
}
|
|
|
|
int cable_xpcusb_write_stream(uint32_t *outstream, int len_bits, int set_last_bit)
|
|
{
|
|
return cable_xpcusb_readwrite_stream(outstream, NULL, len_bits, set_last_bit);
|
|
}
|
|
|
|
/*
|
|
* Care has to be taken that the number of bits to be transferred
|
|
* is NOT a multiple of 4. The CPLD doesn't seem to handle that well.
|
|
*/
|
|
int cable_xpcusb_readwrite_stream(uint32_t *outstream, uint32_t *instream, int len_bits, int set_last_bit)
|
|
{
|
|
int i;
|
|
int ret = APP_ERR_NONE;
|
|
uint32_t bitval;
|
|
xpc_ext_transfer_state_t xts;
|
|
uint8_t tms, tdi, sample_tdo, toggle_clock;
|
|
|
|
// open the device, if necessary
|
|
if(h_device == NULL) {
|
|
ret = cable_xpcusb_open_cable();
|
|
if(ret != APP_ERR_NONE) return ret;
|
|
}
|
|
|
|
if ( !cpld_ctrl )
|
|
{
|
|
ret = cable_xpcusb_cpld_init();
|
|
if ( ret != APP_ERR_NONE) return ret;
|
|
}
|
|
|
|
debug("cable_xpcusb_write_stream(), len_bits = 0x%X, set_last_bit = %i\n", len_bits, set_last_bit);
|
|
|
|
xts.in_bits = 0;
|
|
xts.out_bits = 0;
|
|
|
|
tms = 0;
|
|
tdi = 0;
|
|
toggle_clock = 1;
|
|
sample_tdo = 1; //automatically ignored if xts.out == NULL
|
|
|
|
for (i = 0; i < len_bits && ret == APP_ERR_NONE; i++)
|
|
{
|
|
if ( outstream )
|
|
bitval = outstream[i/32] & (1<<(i%32));
|
|
else
|
|
bitval = 0;
|
|
|
|
tms = ( i == len_bits - 1 ) ? set_last_bit:0;
|
|
tdi = bitval ? 1:0;
|
|
|
|
debug("Adding bit for transfer, bitval = %i, set_tms = %i\n", tdi, tms);
|
|
xpcusb_add_bit_for_ext_transfer(&xts, toggle_clock, tms, tdi, sample_tdo);
|
|
|
|
if ( xts.in_bits == (2*CPLD_MAX_BYTES - 1) )
|
|
{
|
|
debug("Reached %i bits, doing transfer\n", (2*CPLD_MAX_BYTES - 1));
|
|
ret = xpcusb_do_ext_transfer(&xts, instream);
|
|
}
|
|
}
|
|
|
|
if((xts.in_bits > 0) && (ret == APP_ERR_NONE))
|
|
{
|
|
/* CPLD doesn't like multiples of 4; add one dummy bit */
|
|
if((xts.in_bits & 3) == 0)
|
|
{
|
|
debug("Adding dummy bit\n");
|
|
xpcusb_add_bit_for_ext_transfer(&xts, 0, 0, 0, 0);
|
|
}
|
|
debug("Doing final transfer of sequence\n");
|
|
ret = xpcusb_do_ext_transfer(&xts, instream);
|
|
}
|
|
|
|
if(ret != APP_ERR_NONE)
|
|
{
|
|
fprintf(stderr, "Cable will block until next power reset\n");
|
|
fprintf(stderr, "Closing connection to cable.\n");
|
|
cable_xpcusb_close_cable();
|
|
fprintf(stderr, "Aborting adv_jtag_bridge.\n");
|
|
exit(1);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int cable_xpcusb_open_cable(void)
|
|
{
|
|
int if_not_claimed = 1;
|
|
timeout_timer timer;
|
|
|
|
fprintf(stderr, "XPC USB driver opening cable\n");
|
|
// open the device (assumes 'device' has already been set/populated)
|
|
h_device = usb_open(device);
|
|
if (h_device == NULL){
|
|
fprintf(stderr, "XPC USB driver failed to open device\n");
|
|
return APP_ERR_USB;
|
|
}
|
|
|
|
// set the configuration
|
|
if (usb_set_configuration(h_device, device->config->bConfigurationValue))
|
|
{
|
|
usb_close(h_device);
|
|
h_device = NULL;
|
|
fprintf(stderr, "XPC USB driver failed to set configuration\n");
|
|
return APP_ERR_USB;
|
|
}
|
|
|
|
if ( create_timer(&timer) )
|
|
{
|
|
fprintf(stderr, "Failed to create timer\n");
|
|
// fall back to infinite wait
|
|
while (usb_claim_interface(h_device, device->config->interface->altsetting->bInterfaceNumber));
|
|
}
|
|
else
|
|
{
|
|
|
|
while (if_not_claimed && !timedout(&timer) )
|
|
if_not_claimed = usb_claim_interface(h_device, device->config->interface->altsetting->bInterfaceNumber);
|
|
|
|
if ( timedout(&timer) )
|
|
{
|
|
fprintf(stderr, "Claiming interface timed out...\n");
|
|
return APP_ERR_USB;
|
|
}
|
|
}
|
|
|
|
return APP_ERR_NONE;
|
|
}
|
|
|
|
|
|
static void cable_xpcusb_close_cable(void)
|
|
{
|
|
fprintf(stderr, "XPC USB driver closing cable\n");
|
|
if(h_device != NULL) {
|
|
// release the interface cleanly
|
|
if (usb_release_interface(h_device, device->config->interface->altsetting->bInterfaceNumber)){
|
|
fprintf(stderr, "Warning: failed to release usb interface\n");
|
|
}
|
|
|
|
// close the device
|
|
usb_close(h_device);
|
|
h_device = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
int cable_xpcusb_opt(int c, char *str)
|
int cable_xpcusb_opt(int c, char *str)
|
{
|
{
|
fprintf(stderr, "Unknown parameter '%c'\n", c);
|
fprintf(stderr, "Unknown parameter '%c'\n", c);
|
return APP_ERR_BAD_PARAM;
|
return APP_ERR_BAD_PARAM;
|
}
|
}
|
Line 431... |
Line 700... |
jtag_cable_t *cable_xpcusb_get_driver(void)
|
jtag_cable_t *cable_xpcusb_get_driver(void)
|
{
|
{
|
return &dlc9_cable_driver;
|
return &dlc9_cable_driver;
|
}
|
}
|
|
|
No newline at end of file
|
No newline at end of file
|
|
static int xpcusb_enumerate_bus(void)
|
|
{
|
|
int flag; // for USB bus scanning stop condition
|
|
struct usb_bus *bus; // pointer on the USB bus
|
|
|
|
// board detection
|
|
usb_init();
|
|
usb_find_busses();
|
|
usb_find_devices();
|
|
|
|
flag = 0;
|
|
|
|
for (bus = usb_get_busses(); bus; bus = bus->next)
|
|
{
|
|
for (device = bus->devices; device; device = device->next)
|
|
{
|
|
if (device->descriptor.idVendor == XPCUSB_VID &&
|
|
device->descriptor.idProduct == XPCUSB_PID)
|
|
{
|
|
flag = 1;
|
|
fprintf(stderr, "Found Xilinx Platform Cable USB (DLC9)\n");
|
|
return APP_ERR_NONE;
|
|
}
|
|
}
|
|
if (flag)
|
|
break;
|
|
}
|
|
|
|
fprintf(stderr, "Failed to find Xilinx Platform Cable USB\n");
|
|
return APP_ERR_CABLENOTFOUND;
|
|
}
|
|
|
|
|
|
|
|
static int xpcu_common_init( struct usb_dev_handle *xpcu )
|
|
{
|
|
int r;
|
|
|
|
r = xpcu_request_28(xpcu, 0x11);
|
|
if (r>=0)
|
|
r = xpcu_write_gpio(xpcu, 8);
|
|
|
|
if (r<0)
|
|
cable_xpcusb_close_cable();
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
static int cable_xpcusb_fx2_init()
|
|
{
|
|
int r;
|
|
|
|
r = xpcu_select_gpio(h_device, 0);
|
|
if ( r < 0 ) fprintf(stderr, "Error setting FX2 mode\n");
|
|
cpld_ctrl = 0;
|
|
|
|
return APP_ERR_NONE;
|
|
}
|
|
|
|
static int cable_xpcusb_cpld_init()
|
|
{
|
|
int r;
|
|
uint8_t zero[2] = {0,0};
|
|
|
|
r = xpcu_request_28(h_device, 0x11);
|
|
if (r >= 0) r = xpcu_output_enable(h_device, 1);
|
|
else fprintf(stderr, "First xpcu_request_28 failed!\n");
|
|
if (r >= 0) r = xpcu_shift(h_device, 0xA6, 2, 2, zero, 0, NULL);
|
|
else fprintf(stderr, "xpcu_output_enable failed!\n");
|
|
if (r >= 0) r = xpcu_request_28(h_device, 0x12);
|
|
else fprintf(stderr, "xpcu_shift for init failed!\n");
|
|
if(r < 0) fprintf(stderr, "second xpcu_request_28 failed!\n");
|
|
|
|
cpld_ctrl = 1;
|
|
|
|
return APP_ERR_NONE;
|
|
}
|
|
|
|
|
|
int cable_xpcusb_init()
|
|
{
|
|
int r = APP_ERR_NONE;
|
|
uint16_t buf;
|
|
// Process to reset the XPC USB (DLC9)
|
|
if(r |= xpcusb_enumerate_bus()) {
|
|
return r;
|
|
}
|
|
|
|
//usb_dev_handle *
|
|
h_device = usb_open(device);
|
|
|
|
if(h_device == NULL)
|
|
{
|
|
fprintf(stderr, "Init failed to open USB device for reset\n");
|
|
return APP_ERR_USB;
|
|
}
|
|
|
|
if(usb_reset(h_device) != APP_ERR_NONE)
|
|
fprintf(stderr, "Failed to reset XPC-USB\n");
|
|
|
|
usb_close(h_device);
|
|
h_device = NULL;
|
|
|
|
// Wait for reset!!!
|
|
sleep(1);
|
|
|
|
// Do device initialization
|
|
if(r |= xpcusb_enumerate_bus())
|
|
return r;
|
|
|
|
r = cable_xpcusb_open_cable();
|
|
if ( r )
|
|
{
|
|
fprintf(stderr, "Open cable failed\n");
|
|
return APP_ERR_USB;
|
|
}
|
|
|
|
r = xpcu_common_init(h_device);
|
|
|
|
/* Read firmware version (constant embedded in firmware) */
|
|
|
|
if (r>=0) r = xpcu_read_firmware_version(h_device, &buf);
|
|
if (r>=0)
|
|
{
|
|
printf("firmware version = 0x%04X (%u)\n", buf, buf);
|
|
}
|
|
|
|
/* Read CPLD version (via GPIF) */
|
|
|
|
if (r>=0) r = xpcu_read_cpld_version(h_device, &buf);
|
|
if (r>=0)
|
|
{
|
|
printf("cable CPLD version = 0x%04X (%u)\n", buf, buf);
|
|
if(buf == 0)
|
|
{
|
|
printf("Warning: version '0' can't be correct. Please try resetting the cable\n");
|
|
r = -1;
|
|
}
|
|
}
|
|
|
|
if (r<0)
|
|
cable_xpcusb_close_cable();
|
|
|
|
r = cable_xpcusb_cpld_init();
|
|
if (r<0)
|
|
cable_xpcusb_close_cable();
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
|
No newline at end of file
|
No newline at end of file
|