Browse Source

Merge branch 'dev_x6500' into bfgminer

Conflicts:
	driver-modminer.c
Luke Dashjr 13 years ago
parent
commit
2b9afc779c
13 changed files with 1422 additions and 44 deletions
  1. 6 0
      Makefile.am
  2. 10 0
      compat.h
  3. 30 2
      configure.ac
  4. 3 41
      driver-modminer.c
  5. 529 0
      driver-x6500.c
  6. 85 0
      fpgautils.c
  7. 2 0
      fpgautils.h
  8. 378 0
      ft232r.c
  9. 42 0
      ft232r.h
  10. 270 0
      jtag.c
  11. 43 0
      jtag.h
  12. 21 1
      miner.c
  13. 3 0
      miner.h

+ 6 - 0
Makefile.am

@@ -114,6 +114,12 @@ bitstreamsdir = $(bindir)/bitstreams
 dist_bitstreams_DATA = bitstreams/*
 dist_bitstreams_DATA = bitstreams/*
 endif
 endif
 
 
+if HAS_X6500
+bfgminer_SOURCES += driver-x6500.c ft232r.c ft232r.h jtag.c jtag.h
+bitstreamsdir = $(bindir)/bitstreams
+dist_bitstreams_DATA = bitstreams/*
+endif
+
 if HAS_ZTEX
 if HAS_ZTEX
 bfgminer_SOURCES += driver-ztex.c libztex.c libztex.h
 bfgminer_SOURCES += driver-ztex.c libztex.c libztex.h
 bitstreamsdir = $(bindir)/bitstreams
 bitstreamsdir = $(bindir)/bitstreams

+ 10 - 0
compat.h

@@ -1,6 +1,16 @@
 #ifndef __COMPAT_H__
 #ifndef __COMPAT_H__
 #define __COMPAT_H__
 #define __COMPAT_H__
 
 
+#include "config.h"
+
+// NOTE: Nested preprocessor checks since the latter isn't defined at all without the former
+#ifdef HAVE_LIBUSB
+#	if ! HAVE_DECL_LIBUSB_ERROR_NAME
+		static char my_libusb_error_name_buf[0x10];
+#		define libusb_error_name(x) (sprintf(my_libusb_error_name_buf, "%d", x), my_libusb_error_name_buf)
+#	endif
+#endif
+
 #ifdef WIN32
 #ifdef WIN32
 #include <errno.h>
 #include <errno.h>
 #include <time.h>
 #include <time.h>

+ 30 - 2
configure.ac

@@ -193,10 +193,30 @@ AC_CHECK_LIB(usb-1.0, libusb_init,
 		AC_DEFINE([HAVE_LIBUSB], [1], [Defined to 1 if libusb is wanted])
 		AC_DEFINE([HAVE_LIBUSB], [1], [Defined to 1 if libusb is wanted])
 		USB_LIBS="-lusb-1.0"
 		USB_LIBS="-lusb-1.0"
 		USB_FLAGS=""
 		USB_FLAGS=""
+		AC_CHECK_DECLS([libusb_error_name],[],[],[#include <libusb-1.0/libusb.h>])
 	],
 	],
 	[libusb=no]
 	[libusb=no]
 )
 )
 
 
+AC_ARG_ENABLE([x6500],
+	[AC_HELP_STRING([--disable-x6500],[Compile support for X6500 (default if libusb)])],
+	[x6500=$enableval],
+	[x6500=auto]
+	)
+if test "x$x6500$libusb" = xyesno; then
+	AC_MSG_ERROR([Could not find libusb, required for X6500 support])
+elif test "x$x6500" = xauto; then
+	x6500="$libusb"
+	if test "x$libusb" = xno; then
+		AC_MSG_WARN([Could not find libusb, required for X6500 support])
+		x6500warn=yes
+	fi
+fi
+if test "x$x6500" = xyes; then
+	AC_DEFINE([USE_X6500], [1], [Defined to 1 if X6500 support is wanted])
+fi
+AM_CONDITIONAL([HAS_X6500], [test x$x6500 = xyes])
+
 AC_ARG_ENABLE([ztex],
 AC_ARG_ENABLE([ztex],
 	[AC_HELP_STRING([--disable-ztex],[Compile support for Ztex (default if libusb)])],
 	[AC_HELP_STRING([--disable-ztex],[Compile support for Ztex (default if libusb)])],
 	[ztex=$enableval],
 	[ztex=$enableval],
@@ -291,8 +311,8 @@ fi
 AC_CONFIG_SUBDIRS([libblkmaker])
 AC_CONFIG_SUBDIRS([libblkmaker])
 
 
 AM_CONDITIONAL([NEED_LIBBLKMAKER], [true])
 AM_CONDITIONAL([NEED_LIBBLKMAKER], [true])
-AM_CONDITIONAL([NEED_DYNCLOCK], [test x$icarus$modminer$ztex != xnonono])
-AM_CONDITIONAL([NEED_FPGAUTILS], [test x$icarus$bitforce$modminer$ztex != xnononono])
+AM_CONDITIONAL([NEED_DYNCLOCK], [test x$icarus$modminer$x6500$ztex != xnonono])
+AM_CONDITIONAL([NEED_FPGAUTILS], [test x$icarus$bitforce$modminer$x6500$ztex != xnononono])
 AM_CONDITIONAL([HAS_SCRYPT], [test x$scrypt = xyes])
 AM_CONDITIONAL([HAS_SCRYPT], [test x$scrypt = xyes])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])
 AM_CONDITIONAL([HAVE_CYGWIN], [test x$have_cygwin = xtrue])
 AM_CONDITIONAL([HAVE_CYGWIN], [test x$have_cygwin = xtrue])
@@ -484,6 +504,14 @@ else
 	echo "  ModMiner.FPGAs.......: Disabled"
 	echo "  ModMiner.FPGAs.......: Disabled"
 fi
 fi
 
 
+if test "x$x6500" = xyes; then
+	echo "  X6500.FPGAs..........: Enabled"
+elif test "x$ztexwarn" = xyes; then
+	echo "  X6500.FPGAs..........: Disabled (libusb not found)"
+else
+	echo "  X6500.FPGAs..........: Disabled"
+fi
+
 if test "x$ztex" = xyes; then
 if test "x$ztex" = xyes; then
 	echo "  Ztex.FPGAs...........: Enabled"
 	echo "  Ztex.FPGAs...........: Enabled"
 elif test "x$ztexwarn" = xyes; then
 elif test "x$ztexwarn" = xyes; then

+ 3 - 41
driver-modminer.c

@@ -192,51 +192,13 @@ modminer_fpga_upload_bitstream(struct cgpu_info*modminer)
 	struct modminer_fpga_state *state = modminer->thr[0]->cgpu_data;
 	struct modminer_fpga_state *state = modminer->thr[0]->cgpu_data;
 	fd_set fds;
 	fd_set fds;
 	char buf[0x100];
 	char buf[0x100];
-	unsigned char *ubuf = (unsigned char*)buf;
 	unsigned long len, flen;
 	unsigned long len, flen;
-	char *p;
-	const char *fwfile = BITSTREAM_FILENAME;
 	char fpgaid = 4;  // "all FPGAs"
 	char fpgaid = 4;  // "all FPGAs"
-
-	FILE *f = open_bitstream("modminer", fwfile);
+	FILE *f = open_xilinx_bitstream(modminer, BITSTREAM_FILENAME, &len);
 	if (!f)
 	if (!f)
-		bailout(LOG_ERR, "Error opening ModMiner firmware file %s", fwfile);
-	if (1 != fread(buf, 2, 1, f))
-		bailout(LOG_ERR, "Error reading ModMiner firmware (magic)");
-	if (buf[0] || buf[1] != 9)
-		bailout(LOG_ERR, "ModMiner firmware has wrong magic (9)");
-	if (-1 == fseek(f, 11, SEEK_CUR))
-		bailout(LOG_ERR, "ModMiner firmware seek failed");
-	check_magic('a');
-	read_str("design name");
-	applog(LOG_DEBUG, "ModMiner firmware file %s info:", fwfile);
-	applog(LOG_DEBUG, "  Design name: %s", buf);
-	p = strrchr(buf, ';') ?: buf;
-	p = strrchr(buf, '=') ?: p;
-	if (p[0] == '=')
-		++p;
-	unsigned long fwusercode = (unsigned long)strtoll(p, &p, 16);
-	if (p[0] != '\0')
-		bailout(LOG_ERR, "Bad usercode in ModMiner firmware file");
-	if (fwusercode == 0xffffffff)
-		bailout(LOG_ERR, "ModMiner firmware doesn't support user code");
-	applog(LOG_DEBUG, "  Version: %u, build %u", (fwusercode >> 8) & 0xff, fwusercode & 0xff);
-	check_magic('b');
-	read_str("part number");
-	applog(LOG_DEBUG, "  Part number: %s", buf);
-	check_magic('c');
-	read_str("build date");
-	applog(LOG_DEBUG, "  Build date: %s", buf);
-	check_magic('d');
-	read_str("build time");
-	applog(LOG_DEBUG, "  Build time: %s", buf);
-	check_magic('e');
-	if (1 != fread(buf, 4, 1, f))
-		bailout(LOG_ERR, "Error reading ModMiner firmware (data len)");
-	len = ((unsigned long)ubuf[0] << 24) | ((unsigned long)ubuf[1] << 16) | (ubuf[2] << 8) | ubuf[3];
-	flen = len;
-	applog(LOG_DEBUG, "  Bitstream size: %lu", len);
+		return false;
 
 
+	flen = len;
 	int fd = modminer->device_fd;
 	int fd = modminer->device_fd;
 
 
 	applog(LOG_WARNING, "%s %u: Programming %s... DO NOT EXIT UNTIL COMPLETE", modminer->api->name, modminer->device_id, modminer->device_path);
 	applog(LOG_WARNING, "%s %u: Programming %s... DO NOT EXIT UNTIL COMPLETE", modminer->api->name, modminer->device_id, modminer->device_path);

+ 529 - 0
driver-x6500.c

@@ -0,0 +1,529 @@
+/*
+ * Copyright 2012 Luke Dashjr
+ *
+ * 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 the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include <sys/time.h>
+
+#include <libusb-1.0/libusb.h>
+
+#include "dynclock.h"
+#include "jtag.h"
+#include "logging.h"
+#include "miner.h"
+#include "fpgautils.h"
+#include "ft232r.h"
+
+#define X6500_USB_PRODUCT "X6500 FPGA Miner"
+#define X6500_BITSTREAM_FILENAME "fpgaminer_top_fixed7_197MHz.bit"
+// NOTE: X6500_BITSTREAM_USERID is bitflipped
+#define X6500_BITSTREAM_USERID "\x40\x20\x24\x42"
+#define X6500_MINIMUM_CLOCK    2
+#define X6500_DEFAULT_CLOCK  190
+#define X6500_MAXIMUM_CLOCK  250
+
+struct device_api x6500_api;
+
+#define fromlebytes(ca, j)  (ca[j] | (((uint16_t)ca[j+1])<<8) | (((uint32_t)ca[j+2])<<16) | (((uint32_t)ca[j+3])<<24))
+
+static
+void int2bits(uint32_t n, uint8_t *b, uint8_t bits)
+{
+	uint8_t i;
+	for (i = (bits + 7) / 8; i > 0; )
+		b[--i] = 0;
+	for (i = 0; i < bits; ++i) {
+		if (n & 1)
+			b[i/8] |= 0x80 >> (i % 8);
+		n >>= 1;
+	}
+}
+
+static
+uint32_t bits2int(uint8_t *b, uint8_t bits)
+{
+	uint32_t n, i;
+	n = 0;
+	for (i = 0; i < bits; ++i)
+		if (b[i/8] & (0x80 >> (i % 8)))
+			n |= 1<<i;
+	return n;
+}
+
+static
+void checksum(uint8_t *b, uint8_t bits)
+{
+	uint8_t i;
+	uint8_t checksum = 1;
+	for(i = 0; i < bits; ++i)
+		checksum ^= (b[i/8] & (0x80 >> (i % 8))) ? 1 : 0;
+	if (checksum)
+		b[i/8] |= 0x80 >> (i % 8);
+}
+
+static
+void x6500_jtag_set(struct jtag_port *jp, uint8_t pinoffset)
+{
+	jp->tck = pinoffset << 3;
+	jp->tms = pinoffset << 2;
+	jp->tdi = pinoffset << 1;
+	jp->tdo = pinoffset << 0;
+	jp->ignored = ~(jp->tdo | jp->tdi | jp->tms | jp->tck);
+}
+
+static uint32_t x6500_get_register(struct jtag_port *jp, uint8_t addr);
+
+static
+void x6500_set_register(struct jtag_port *jp, uint8_t addr, uint32_t nv)
+{
+	uint8_t buf[38];
+retry:
+	jtag_write(jp, JTAG_REG_IR, "\x40", 6);
+	int2bits(nv, &buf[0], 32);
+	int2bits(addr, &buf[4], 4);
+	buf[4] |= 8;
+	checksum(buf, 37);
+	jtag_write(jp, JTAG_REG_DR, buf, 38);
+	jtag_run(jp);
+#ifdef DEBUG_X6500_SET_REGISTER
+	if (x6500_get_register(jp, addr) != nv)
+#else
+	if (0)
+#endif
+	{
+		applog(LOG_WARNING, "x6500_set_register failed %x=%08x", addr, nv);
+		goto retry;
+	}
+}
+
+static
+uint32_t x6500_get_register(struct jtag_port *jp, uint8_t addr)
+{
+	uint8_t buf[4];
+	jtag_write(jp, JTAG_REG_IR, "\x40", 6);
+	int2bits(addr, &buf[0], 4);
+	checksum(buf, 5);
+	jtag_write(jp, JTAG_REG_DR, buf, 6);
+	jtag_read (jp, JTAG_REG_DR, buf, 32);
+	jtag_reset(jp);
+	return bits2int(buf, 32);
+}
+
+static bool x6500_foundusb(libusb_device *dev, const char *product, const char *serial)
+{
+	struct cgpu_info *x6500;
+	x6500 = calloc(1, sizeof(*x6500));
+	x6500->api = &x6500_api;
+	mutex_init(&x6500->device_mutex);
+	x6500->device_path = strdup(serial);
+	x6500->deven = DEV_ENABLED;
+	x6500->threads = 2;
+	x6500->name = strdup(product);
+	x6500->cutofftemp = 85;
+	x6500->cgpu_data = dev;
+
+	return add_cgpu(x6500);
+}
+
+static bool x6500_detect_one(const char *serial)
+{
+	return ft232r_detect(X6500_USB_PRODUCT, serial, x6500_foundusb);
+}
+
+static int x6500_detect_auto()
+{
+	return ft232r_detect(X6500_USB_PRODUCT, NULL, x6500_foundusb);
+}
+
+static void x6500_detect()
+{
+	serial_detect_auto(&x6500_api, x6500_detect_one, x6500_detect_auto);
+}
+
+static bool x6500_prepare(struct thr_info *thr)
+{
+	if (thr->device_thread)
+		return true;
+	
+	struct cgpu_info *x6500 = thr->cgpu;
+	mutex_init(&x6500->device_mutex);
+	struct ft232r_device_handle *ftdi = ft232r_open(x6500->cgpu_data);
+	x6500->device_ft232r = NULL;
+	if (!ftdi)
+		return false;
+	if (!ft232r_set_bitmode(ftdi, 0xee, 4))
+		return false;
+	if (!ft232r_purge_buffers(ftdi, FTDI_PURGE_BOTH))
+		return false;
+	x6500->device_ft232r = ftdi;
+	
+	struct jtag_port_a *jtag_a;
+	unsigned char *pdone = calloc(1, sizeof(*jtag_a) + 1);
+	*pdone = 101;
+	jtag_a = (void*)(pdone + 1);
+	jtag_a->ftdi = ftdi;
+	x6500->cgpu_data = jtag_a;
+	
+	return true;
+}
+
+struct x6500_fpga_data {
+	struct jtag_port jtag;
+	struct work prevwork;
+	struct timeval tv_workstart;
+
+	struct dclk_data dclk;
+};
+
+#define bailout2(...) do {  \
+	applog(__VA_ARGS__);  \
+	return false;  \
+} while(0)
+
+static bool
+x6500_fpga_upload_bitstream(struct cgpu_info *x6500, struct jtag_port *jp1)
+{
+	char buf[0x100];
+	unsigned long len, flen;
+	unsigned char *pdone = (unsigned char*)x6500->cgpu_data - 1;
+	struct ft232r_device_handle *ftdi = jp1->a->ftdi;
+
+	FILE *f = open_xilinx_bitstream(x6500, X6500_BITSTREAM_FILENAME, &len);
+	if (!f)
+		return false;
+
+	flen = len;
+
+	applog(LOG_WARNING, "%s %u: Programming %s...",
+	       x6500->api->name, x6500->device_id, x6500->device_path);
+	
+	// "Magic" jtag_port configured to access both FPGAs concurrently
+	struct jtag_port jpt = {
+		.a = jp1->a,
+	};
+	struct jtag_port *jp = &jpt;
+	uint8_t i, j;
+	x6500_jtag_set(jp, 0x11);
+	
+	// Need to reset here despite previous FPGA state, since we are programming all at once
+	jtag_reset(jp);
+	
+	jtag_write(jp, JTAG_REG_IR, "\xd0", 6);  // JPROGRAM
+	// Poll each FPGA status individually since they might not be ready at the same time
+	for (j = 0; j < 2; ++j) {
+		x6500_jtag_set(jp, j ? 0x10 : 1);
+		do {
+			i = 0xd0;  // Re-set JPROGRAM while reading status
+			jtag_read(jp, JTAG_REG_IR, &i, 6);
+		} while (i & 8);
+		applog(LOG_DEBUG, "%s %u.%u: JPROGRAM ready",
+		       x6500->api->name, x6500->device_id, j);
+	}
+	x6500_jtag_set(jp, 0x11);
+	jtag_write(jp, JTAG_REG_IR, "\xa0", 6);  // CFG_IN
+	
+	sleep(1);
+	
+	if (fread(buf, 32, 1, f) != 1)
+		bailout2(LOG_ERR, "%s %u: File underrun programming %s (%d bytes left)", x6500->api->name, x6500->device_id, x6500->device_path, len);
+	jtag_swrite(jp, JTAG_REG_DR, buf, 256);
+	len -= 32;
+	
+	// Put ft232r chip in asynchronous bitbang mode so we don't need to read back tdo
+	// This takes upload time down from about an hour to about 3 minutes
+	if (!ft232r_set_bitmode(ftdi, 0xee, 1))
+		return false;
+	if (!ft232r_purge_buffers(ftdi, FTDI_PURGE_BOTH))
+		return false;
+	jp->a->async = true;
+
+	ssize_t buflen;
+	char nextstatus = 25;
+	while (len) {
+		buflen = len < 32 ? len : 32;
+		if (fread(buf, buflen, 1, f) != 1)
+			bailout2(LOG_ERR, "%s %u: File underrun programming %s (%d bytes left)", x6500->api->name, x6500->device_id, x6500->device_path, len);
+		jtag_swrite_more(jp, buf, buflen * 8, len == (unsigned long)buflen);
+		*pdone = 100 - ((len * 100) / flen);
+		if (*pdone >= nextstatus)
+		{
+			nextstatus += 25;
+			applog(LOG_WARNING, "%s %u: Programming %s... %d%% complete...", x6500->api->name, x6500->device_id, x6500->device_path, *pdone);
+		}
+		len -= buflen;
+	}
+	
+	// Switch back to synchronous bitbang mode
+	if (!ft232r_set_bitmode(ftdi, 0xee, 4))
+		return false;
+	if (!ft232r_purge_buffers(ftdi, FTDI_PURGE_BOTH))
+		return false;
+	jp->a->async = false;
+	jp->a->bufread = 0;
+
+	jtag_write(jp, JTAG_REG_IR, "\x30", 6);  // JSTART
+	for (i=0; i<16; ++i)
+		jtag_run(jp);
+	i = 0xff;  // BYPASS
+	jtag_read(jp, JTAG_REG_IR, &i, 6);
+	if (!(i & 4))
+		return false;
+	
+	applog(LOG_WARNING, "%s %u: Done programming %s", x6500->api->name, x6500->device_id, x6500->device_path);
+	*pdone = 101;
+
+	return true;
+}
+
+static bool x6500_change_clock(struct thr_info *thr, int multiplier)
+{
+	struct x6500_fpga_data *fpga = thr->cgpu_data;
+	struct jtag_port *jp = &fpga->jtag;
+
+	x6500_set_register(jp, 0xD, multiplier * 2);
+	ft232r_flush(jp->a->ftdi);
+	fpga->dclk.freqM = multiplier;
+
+	return true;
+}
+
+static bool x6500_dclk_change_clock(struct thr_info *thr, int multiplier)
+{
+	struct cgpu_info *x6500 = thr->cgpu;
+	char fpgaid = thr->device_thread;
+	struct x6500_fpga_data *fpga = thr->cgpu_data;
+	uint8_t oldFreq = fpga->dclk.freqM;
+
+	mutex_lock(&x6500->device_mutex);
+	if (!x6500_change_clock(thr, multiplier)) {
+		mutex_unlock(&x6500->device_mutex);
+		return false;
+	}
+	mutex_unlock(&x6500->device_mutex);
+
+	char repr[0x10];
+	sprintf(repr, "%s %u.%u", x6500->api->name, x6500->device_id, fpgaid);
+	dclk_msg_freqchange(repr, oldFreq * 2, fpga->dclk.freqM * 2, NULL);
+	return true;
+}
+
+static bool x6500_fpga_init(struct thr_info *thr)
+{
+	struct cgpu_info *x6500 = thr->cgpu;
+	struct ft232r_device_handle *ftdi = x6500->device_ft232r;
+	struct x6500_fpga_data *fpga;
+	struct jtag_port *jp;
+	int fpgaid = thr->device_thread;
+	uint8_t pinoffset = fpgaid ? 0x10 : 1;
+	unsigned char buf[4];
+	int i;
+	
+	if (!ftdi)
+		return false;
+	
+	thread_reportin(thr);  // HACK
+
+	fpga = calloc(1, sizeof(*fpga));
+	jp = &fpga->jtag;
+	jp->a = x6500->cgpu_data;
+	x6500_jtag_set(jp, pinoffset);
+	
+	mutex_lock(&x6500->device_mutex);
+	if (!jtag_reset(jp)) {
+		mutex_unlock(&x6500->device_mutex);
+		applog(LOG_ERR, "%s %u: JTAG reset failed",
+		       x6500->api->name, x6500->device_id);
+		return false;
+	}
+	
+	i = jtag_detect(jp);
+	if (i != 1) {
+		mutex_unlock(&x6500->device_mutex);
+		applog(LOG_ERR, "%s %u: JTAG detect returned %d",
+		       x6500->api->name, x6500->device_id, i);
+		return false;
+	}
+	
+	if (!(1
+	 && jtag_write(jp, JTAG_REG_IR, "\x10", 6)
+	 && jtag_read (jp, JTAG_REG_DR, buf, 32)
+	 && jtag_reset(jp)
+	)) {
+		mutex_unlock(&x6500->device_mutex);
+		applog(LOG_ERR, "%s %u: JTAG error reading user code",
+		       x6500->api->name, x6500->device_id);
+		return false;
+	}
+	
+	if (memcmp(buf, X6500_BITSTREAM_USERID, 4)) {
+		applog(LOG_ERR, "%s %u.%u: FPGA not programmed",
+		       x6500->api->name, x6500->device_id, fpgaid);
+		if (!x6500_fpga_upload_bitstream(x6500, jp))
+			return false;
+	} else
+		applog(LOG_DEBUG, "%s %u.%u: FPGA is already programmed :)",
+		       x6500->api->name, x6500->device_id, fpgaid);
+	
+	thr->cgpu_data = fpga;
+
+	dclk_prepare(&fpga->dclk);
+	fpga->dclk.freqMaxM = X6500_MAXIMUM_CLOCK / 2;
+	x6500_change_clock(thr, X6500_DEFAULT_CLOCK / 2);
+	fpga->dclk.freqMDefault = fpga->dclk.freqM;
+	applog(LOG_WARNING, "%s %u.%u: Frequency set to %u Mhz (range: %u-%u)",
+	       x6500->api->name, x6500->device_id, fpgaid,
+	       fpga->dclk.freqM * 2,
+	       X6500_MINIMUM_CLOCK,
+	       fpga->dclk.freqMaxM * 2);
+
+	mutex_unlock(&x6500->device_mutex);
+	return true;
+}
+
+static void
+get_x6500_statline_before(char *buf, struct cgpu_info *x6500)
+{
+	char info[18] = "               | ";
+
+	unsigned char pdone = *((unsigned char*)x6500->cgpu_data - 1);
+	if (pdone != 101) {
+		sprintf(&info[1], "%3d%%", pdone);
+		info[5] = ' ';
+		strcat(buf, info);
+		return;
+	}
+	strcat(buf, "               | ");
+}
+
+static
+bool x6500_start_work(struct thr_info *thr, struct work *work)
+{
+	struct cgpu_info *x6500 = thr->cgpu;
+	struct x6500_fpga_data *fpga = thr->cgpu_data;
+	struct jtag_port *jp = &fpga->jtag;
+	char fpgaid = thr->device_thread;
+
+	mutex_lock(&x6500->device_mutex);
+
+	for (int i = 1, j = 0; i < 9; ++i, j += 4)
+		x6500_set_register(jp, i, fromlebytes(work->midstate, j));
+
+	for (int i = 9, j = 64; i < 12; ++i, j += 4)
+		x6500_set_register(jp, i, fromlebytes(work->data, j));
+
+	ft232r_flush(jp->a->ftdi);
+
+	gettimeofday(&fpga->tv_workstart, NULL);
+	mutex_unlock(&x6500->device_mutex);
+
+	if (opt_debug) {
+		char *xdata = bin2hex(work->data, 80);
+		applog(LOG_DEBUG, "%s %u.%u: Started work: %s",
+		       x6500->api->name, x6500->device_id, fpgaid, xdata);
+		free(xdata);
+	}
+
+	return true;
+}
+
+static
+int64_t calc_hashes(struct x6500_fpga_data *fpga, struct timeval *tv_now)
+{
+	struct timeval tv_delta;
+	int64_t hashes;
+
+	timersub(tv_now, &fpga->tv_workstart, &tv_delta);
+	hashes = (((int64_t)tv_delta.tv_sec * 1000000) + tv_delta.tv_usec) * fpga->dclk.freqM * 2;
+	if (unlikely(hashes > 0x100000000))
+		hashes = 0x100000000;
+	return hashes;
+}
+
+static
+int64_t x6500_process_results(struct thr_info *thr, struct work *work)
+{
+	struct cgpu_info *x6500 = thr->cgpu;
+	struct x6500_fpga_data *fpga = thr->cgpu_data;
+	struct jtag_port *jtag = &fpga->jtag;
+	char fpgaid = thr->device_thread;
+
+	struct timeval tv_now;
+	int64_t hashes;
+	uint32_t nonce;
+	bool bad;
+	int imm_bad_nonces = 0, imm_nonces = 0;
+
+	while (1) {
+		mutex_lock(&x6500->device_mutex);
+		gettimeofday(&tv_now, NULL);
+		nonce = x6500_get_register(jtag, 0xE);
+		mutex_unlock(&x6500->device_mutex);
+		if (nonce != 0xffffffff) {
+			++imm_nonces;
+			bad = !test_nonce(work, nonce, false);
+			if (!bad) {
+				submit_nonce(thr, work, nonce);
+				applog(LOG_DEBUG, "%s %u.%u: Nonce for current  work: %08lx",
+				       x6500->api->name, x6500->device_id, fpgaid,
+				       (unsigned long)nonce);
+			} else if (test_nonce(&fpga->prevwork, nonce, false)) {
+				submit_nonce(thr, &fpga->prevwork, nonce);
+				applog(LOG_DEBUG, "%s %u.%u: Nonce for PREVIOUS work: %08lx",
+				       x6500->api->name, x6500->device_id, fpgaid,
+				       (unsigned long)nonce);
+			} else {
+				applog(LOG_DEBUG, "%s %u.%u: Nonce with H not zero  : %08lx",
+				       x6500->api->name, x6500->device_id, fpgaid,
+				       (unsigned long)nonce);
+				++hw_errors;
+				++x6500->hw_errors;
+				++imm_bad_nonces;
+			}
+		}
+
+		hashes = calc_hashes(fpga, &tv_now);
+		if (thr->work_restart || hashes >= 0xf0000000)
+			break;
+		usleep(10000);
+		hashes = calc_hashes(fpga, &tv_now);
+		if (thr->work_restart || hashes >= 0xf0000000)
+			break;
+	}
+
+	dclk_gotNonces(&fpga->dclk);
+	if (imm_bad_nonces)
+		dclk_errorCount(&fpga->dclk, ((double)imm_bad_nonces) / (double)imm_nonces);
+	dclk_preUpdate(&fpga->dclk);
+	dclk_updateFreq(&fpga->dclk, x6500_dclk_change_clock, thr);
+
+	memcpy(&fpga->prevwork, work, sizeof(fpga->prevwork));
+
+	return hashes;
+}
+
+static int64_t
+x6500_scanhash(struct thr_info *thr, struct work *work, int64_t __maybe_unused max_nonce)
+{
+	if (!x6500_start_work(thr, work))
+		return -1;
+
+	int64_t hashes = x6500_process_results(thr, work);
+	if (hashes > 0)
+		work->blk.nonce += hashes;
+	return hashes;
+}
+
+struct device_api x6500_api = {
+	.dname = "x6500",
+	.name = "XBS",
+	.api_detect = x6500_detect,
+	.thread_prepare = x6500_prepare,
+	.thread_init = x6500_fpga_init,
+	.get_statline_before = get_x6500_statline_before,
+	.scanhash = x6500_scanhash,
+// 	.thread_shutdown = x6500_fpga_shutdown,
+};

+ 85 - 0
fpgautils.c

@@ -648,3 +648,88 @@ FILE *open_bitstream(const char *dname, const char *filename)
 
 
 	return NULL;
 	return NULL;
 }
 }
+
+#define bailout(...)  do {  \
+	applog(__VA_ARGS__);  \
+	return NULL;  \
+} while(0)
+
+#define check_magic(L)  do {  \
+	if (1 != fread(buf, 1, 1, f))  \
+		bailout(LOG_ERR, "%s %u: Error reading firmware ('%c')",  \
+		        cgpu->api->name, cgpu->device_id, L);  \
+	if (buf[0] != L)  \
+		bailout(LOG_ERR, "%s %u: Firmware has wrong magic ('%c')",  \
+		        cgpu->api->name, cgpu->device_id, L);  \
+} while(0)
+
+#define read_str(eng)  do {  \
+	if (1 != fread(buf, 2, 1, f))  \
+		bailout(LOG_ERR, "%s %u: Error reading firmware (" eng " len)",  \
+		        cgpu->api->name, cgpu->device_id);  \
+	len = (ubuf[0] << 8) | ubuf[1];  \
+	if (len >= sizeof(buf))  \
+		bailout(LOG_ERR, "%s %u: Firmware " eng " too long",  \
+		        cgpu->api->name, cgpu->device_id);  \
+	if (1 != fread(buf, len, 1, f))  \
+		bailout(LOG_ERR, "%s %u: Error reading firmware (" eng ")",  \
+		        cgpu->api->name, cgpu->device_id);  \
+	buf[len] = '\0';  \
+} while(0)
+
+FILE *open_xilinx_bitstream(struct cgpu_info *cgpu, const char *fwfile, unsigned long *out_len)
+{
+	char buf[0x100];
+	unsigned char *ubuf = (unsigned char*)buf;
+	unsigned long len;
+	char *p;
+
+	FILE *f = open_bitstream(cgpu->api->dname, fwfile);
+	if (!f)
+		bailout(LOG_ERR, "%s %u: Error opening firmware file %s",
+		        cgpu->api->name, cgpu->device_id, fwfile);
+	if (1 != fread(buf, 2, 1, f))
+		bailout(LOG_ERR, "%s %u: Error reading firmware (magic)",
+		        cgpu->api->name, cgpu->device_id);
+	if (buf[0] || buf[1] != 9)
+		bailout(LOG_ERR, "%s %u: Firmware has wrong magic (9)",
+		        cgpu->api->name, cgpu->device_id);
+	if (-1 == fseek(f, 11, SEEK_CUR))
+		bailout(LOG_ERR, "%s %u: Firmware seek failed",
+		        cgpu->api->name, cgpu->device_id);
+	check_magic('a');
+	read_str("design name");
+	applog(LOG_DEBUG, "%s %u: Firmware file %s info:",
+	       cgpu->api->name, cgpu->device_id, fwfile);
+	applog(LOG_DEBUG, "  Design name: %s", buf);
+	p = strrchr(buf, ';') ?: buf;
+	p = strrchr(buf, '=') ?: p;
+	if (p[0] == '=')
+		++p;
+	unsigned long fwusercode = (unsigned long)strtoll(p, &p, 16);
+	if (p[0] != '\0')
+		bailout(LOG_ERR, "%s %u: Bad usercode in firmware file",
+		        cgpu->api->name, cgpu->device_id);
+	if (fwusercode == 0xffffffff)
+		bailout(LOG_ERR, "%s %u: Firmware doesn't support user code",
+		        cgpu->api->name, cgpu->device_id);
+	applog(LOG_DEBUG, "  Version: %u, build %u", (fwusercode >> 8) & 0xff, fwusercode & 0xff);
+	check_magic('b');
+	read_str("part number");
+	applog(LOG_DEBUG, "  Part number: %s", buf);
+	check_magic('c');
+	read_str("build date");
+	applog(LOG_DEBUG, "  Build date: %s", buf);
+	check_magic('d');
+	read_str("build time");
+	applog(LOG_DEBUG, "  Build time: %s", buf);
+	check_magic('e');
+	if (1 != fread(buf, 4, 1, f))
+		bailout(LOG_ERR, "%s %u: Error reading firmware (data len)",
+		        cgpu->api->name, cgpu->device_id);
+	len = ((unsigned long)ubuf[0] << 24) | ((unsigned long)ubuf[1] << 16) | (ubuf[2] << 8) | ubuf[3];
+	applog(LOG_DEBUG, "  Bitstream size: %lu", len);
+
+	*out_len = len;
+	return f;
+}

+ 2 - 0
fpgautils.h

@@ -16,6 +16,7 @@
 #include <unistd.h>
 #include <unistd.h>
 
 
 struct device_api;
 struct device_api;
+struct cgpu_info;
 
 
 typedef bool(*detectone_func_t)(const char*);
 typedef bool(*detectone_func_t)(const char*);
 typedef int(*autoscan_func_t)();
 typedef int(*autoscan_func_t)();
@@ -46,5 +47,6 @@ extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char *eol);
 #define serial_close(fd)  close(fd)
 #define serial_close(fd)  close(fd)
 
 
 extern FILE *open_bitstream(const char *dname, const char *filename);
 extern FILE *open_bitstream(const char *dname, const char *filename);
+extern FILE *open_xilinx_bitstream(struct cgpu_info *cgpu, const char *fwfile, unsigned long *out_len);
 
 
 #endif
 #endif

+ 378 - 0
ft232r.c

@@ -0,0 +1,378 @@
+/*
+ * Copyright 2012 Luke Dashjr
+ *
+ * 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 the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <libusb-1.0/libusb.h>
+
+#include "compat.h"
+#include "fpgautils.h"
+#include "ft232r.h"
+#include "logging.h"
+#include "miner.h"
+
+#define FT232R_IDVENDOR   0x0403
+#define FT232R_IDPRODUCT  0x6001
+
+struct ft232r_device_info {
+	libusb_device *libusb_dev;
+	char *product;
+	char *serial;
+};
+
+static struct ft232r_device_info **ft232r_devinfo_list;
+
+void ft232r_scan_free()
+{
+	if (!ft232r_devinfo_list)
+		return;
+
+	struct ft232r_device_info *info;
+
+	for (struct ft232r_device_info **infop = ft232r_devinfo_list; (info = *infop); ++infop) {
+		libusb_unref_device(info->libusb_dev);
+		free(info->product);
+		free(info->serial);
+		free(info);
+	}
+	free(ft232r_devinfo_list);
+	ft232r_devinfo_list = NULL;
+}
+
+void ft232r_scan()
+{
+	ssize_t count, n, i, found = 0;
+	libusb_device **list;
+	struct libusb_device_descriptor desc;
+	libusb_device_handle *handle;
+	struct ft232r_device_info *info;
+	int err;
+	unsigned char buf[0x100];
+
+	ft232r_scan_free();
+
+	count = libusb_get_device_list(NULL, &list);
+	if (unlikely(count < 0)) {
+		applog(LOG_ERR, "ft232r_scan: Error getting USB device list: %s", libusb_error_name(count));
+		ft232r_devinfo_list = calloc(1, sizeof(struct ft232r_device_info *));
+		return;
+	}
+
+	ft232r_devinfo_list = malloc(sizeof(struct ft232r_device_info *) * (count + 1));
+
+	for (i = 0; i < count; ++i) {
+		err = libusb_get_device_descriptor(list[i], &desc);
+		if (unlikely(err)) {
+			applog(LOG_ERR, "ft232r_scan: Error getting device descriptor: %s", libusb_error_name(err));
+			continue;
+		}
+		if (!(desc.idVendor == FT232R_IDVENDOR && desc.idProduct == FT232R_IDPRODUCT))
+			continue;
+
+		err = libusb_open(list[i], &handle);
+		if (unlikely(err)) {
+			applog(LOG_ERR, "ft232r_scan: Error opening device: %s", libusb_error_name(err));
+			continue;
+		}
+
+		n = libusb_get_string_descriptor_ascii(handle, desc.iProduct, buf, sizeof(buf)-1);
+		if (unlikely(n < 0)) {
+			libusb_close(handle);
+			applog(LOG_ERR, "ft232r_scan: Error getting iProduct string: %s", libusb_error_name(n));
+			continue;
+		}
+		buf[n] = '\0';
+		info = malloc(sizeof(struct ft232r_device_info));
+		info->product = strdup((char*)buf);
+
+		n = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, buf, sizeof(buf)-1);
+		libusb_close(handle);
+		if (unlikely(n < 0)) {
+			applog(LOG_ERR, "ft232r_scan: Error getting iSerialNumber string: %s", libusb_error_name(n));
+			n = 0;
+		}
+		buf[n] = '\0';
+		info->serial = strdup((char*)buf);
+		info->libusb_dev = libusb_ref_device(list[i]);
+		ft232r_devinfo_list[found++] = info;
+
+		applog(LOG_DEBUG, "ft232r_scan: Found \"%s\" serial \"%s\"", info->product, info->serial);
+	}
+
+	ft232r_devinfo_list[found] = NULL;
+	libusb_free_device_list(list, 1);
+}
+
+int ft232r_detect(const char *product_needle, const char *serial, foundusb_func_t cb)
+{
+	struct ft232r_device_info *info;
+	int found = 0;
+
+	for (struct ft232r_device_info **infop = ft232r_devinfo_list; (info = *infop); ++infop) {
+		if (!strstr(info->product, product_needle))
+			continue;
+		if (serial && strcmp(serial, info->serial))
+			continue;
+		if (!info->libusb_dev)
+			continue;
+		if (!cb(info->libusb_dev, info->product, info->serial))
+			continue;
+		info->libusb_dev = NULL;
+		++found;
+	}
+
+	return found;
+}
+
+#define FTDI_REQTYPE  (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE)
+#define FTDI_REQTYPE_IN   (FTDI_REQTYPE | LIBUSB_ENDPOINT_IN)
+#define FTDI_REQTYPE_OUT  (FTDI_REQTYPE | LIBUSB_ENDPOINT_OUT)
+
+#define FTDI_REQUEST_RESET           0
+#define FTDI_REQUEST_SET_BAUDRATE    3
+#define FTDI_REQUEST_SET_BITMODE  0x0b
+#define FTDI_REQUEST_GET_PINS     0x0c
+
+#define FTDI_BAUDRATE_3M  0,0
+
+#define FTDI_INDEX       1
+#define FTDI_TIMEOUT  1000
+
+struct ft232r_device_handle {
+	libusb_device_handle *h;
+	uint8_t i;
+	uint8_t o;
+	unsigned char ibuf[256];
+	int ibufLen;
+	uint16_t osz;
+	unsigned char *obuf;
+	uint16_t obufsz;
+};
+
+struct ft232r_device_handle *ft232r_open(libusb_device *dev)
+{
+	// FIXME: Cleanup on errors
+	libusb_device_handle *devh;
+	struct ft232r_device_handle *ftdi;
+
+	if (libusb_open(dev, &devh)) {
+		applog(LOG_ERR, "ft232r_open: Error opening device");
+		return NULL;
+	}
+	libusb_reset_device(devh);
+	libusb_detach_kernel_driver(devh, 0);
+	if (libusb_set_configuration(devh, 1)) {
+		applog(LOG_ERR, "ft232r_open: Error setting configuration");
+		return NULL;
+	}
+	if (libusb_claim_interface(devh, 0)) {
+		applog(LOG_ERR, "ft232r_open: Error claiming interface");
+		return NULL;
+	}
+	if (libusb_control_transfer(devh, FTDI_REQTYPE_OUT, FTDI_REQUEST_SET_BAUDRATE, FTDI_BAUDRATE_3M, NULL, 0, FTDI_TIMEOUT) < 0) {
+		applog(LOG_ERR, "ft232r_open: Error performing control transfer");
+		return NULL;
+	}
+
+	ftdi = calloc(1, sizeof(*ftdi));
+	ftdi->h = devh;
+
+	struct libusb_config_descriptor *cfg;
+	if (libusb_get_config_descriptor(dev, 0, &cfg)) {
+		applog(LOG_ERR, "ft232r_open: Error getting config descriptor");
+		return NULL;
+	}
+	const struct libusb_interface_descriptor *altcfg = &cfg->interface[0].altsetting[0];
+	if (altcfg->bNumEndpoints < 2) {
+		applog(LOG_ERR, "ft232r_open: Too few endpoints");
+		return NULL;
+	}
+	ftdi->i = altcfg->endpoint[0].bEndpointAddress;
+	ftdi->o = altcfg->endpoint[1].bEndpointAddress;
+	ftdi->osz = 0x1000;
+	ftdi->obuf = malloc(ftdi->osz);
+	libusb_free_config_descriptor(cfg);
+
+	return ftdi;
+}
+
+void ft232r_close(struct ft232r_device_handle *dev)
+{
+	libusb_release_interface(dev->h, 0);
+	libusb_reset_device(dev->h);
+	libusb_close(dev->h);
+}
+
+bool ft232r_purge_buffers(struct ft232r_device_handle *dev, enum ft232r_reset_purge purge)
+{
+	if (purge & FTDI_PURGE_RX)
+		if (libusb_control_transfer(dev->h, FTDI_REQTYPE_OUT, FTDI_REQUEST_RESET, FTDI_PURGE_RX, FTDI_INDEX, NULL, 0, FTDI_TIMEOUT))
+			return false;
+	if (purge & FTDI_PURGE_TX)
+		if (libusb_control_transfer(dev->h, FTDI_REQTYPE_OUT, FTDI_REQUEST_RESET, FTDI_PURGE_TX, FTDI_INDEX, NULL, 0, FTDI_TIMEOUT))
+			return false;
+	return true;
+}
+
+bool ft232r_set_bitmode(struct ft232r_device_handle *dev, uint8_t mask, uint8_t mode)
+{
+	if (ft232r_flush(dev) < 0)
+		return false;
+	if (libusb_control_transfer(dev->h, FTDI_REQTYPE_OUT, FTDI_REQUEST_SET_BITMODE, mask, FTDI_INDEX, NULL, 0, FTDI_TIMEOUT))
+		return false;
+	return !libusb_control_transfer(dev->h, FTDI_REQTYPE_OUT, FTDI_REQUEST_SET_BITMODE, (mode << 8) | mask, FTDI_INDEX, NULL, 0, FTDI_TIMEOUT);
+}
+
+static ssize_t ft232r_readwrite(struct ft232r_device_handle *dev, unsigned char endpoint, void *data, size_t count)
+{
+	int transferred;
+	switch (libusb_bulk_transfer(dev->h, endpoint, data, count, &transferred, FTDI_TIMEOUT)) {
+		case LIBUSB_ERROR_TIMEOUT:
+			if (!transferred) {
+				errno = ETIMEDOUT;
+				return -1;
+			}
+		case 0:
+			return transferred;
+		default:
+			errno = EIO;
+			return -1;
+	}
+}
+
+ssize_t ft232r_flush(struct ft232r_device_handle *dev)
+{
+	if (!dev->obufsz)
+		return 0;
+	ssize_t r = ft232r_readwrite(dev, dev->o, dev->obuf, dev->obufsz);
+	if (r == dev->obufsz) {
+		dev->obufsz = 0;
+	} else if (r > 0) {
+		dev->obufsz -= r;
+		memmove(dev->obuf, &dev->obuf[r], dev->obufsz);
+	}
+	return r;
+}
+
+ssize_t ft232r_write(struct ft232r_device_handle *dev, void *data, size_t count)
+{
+	uint16_t bufleft;
+	ssize_t r;
+	
+	bufleft = dev->osz - dev->obufsz;
+	
+	if (count < bufleft) {
+		// Just add to output buffer
+		memcpy(&dev->obuf[dev->obufsz], data, count);
+		dev->obufsz += count;
+		return count;
+	}
+	
+	// Fill up buffer and flush
+	memcpy(&dev->obuf[dev->obufsz], data, bufleft);
+	dev->obufsz += bufleft;
+	r = ft232r_flush(dev);
+	
+	if (unlikely(r <= 0)) {
+		// In this case, no bytes were written supposedly, so remove this data from buffer
+		dev->obufsz -= bufleft;
+		return r;
+	}
+	
+	// Even if not all <bufleft> bytes from this write got out, the remaining are still buffered
+	return bufleft;
+}
+
+typedef ssize_t (*ft232r_rwfunc_t)(struct ft232r_device_handle *, void*, size_t);
+
+static ssize_t ft232r_rw_all(ft232r_rwfunc_t rwfunc, struct ft232r_device_handle *dev, void *data, size_t count)
+{
+	char *p = data;
+	ssize_t writ, total = 0;
+
+	while (count && (writ = rwfunc(dev, p, count)) > 0) {
+		p += writ;
+		count -= writ;
+		total += writ;
+	}
+	return total ?: writ;
+}
+
+ssize_t ft232r_write_all(struct ft232r_device_handle *dev, void *data, size_t count)
+{
+	return ft232r_rw_all(ft232r_write, dev, data, count);
+}
+
+ssize_t ft232r_read(struct ft232r_device_handle *dev, void *data, size_t count)
+{
+	ssize_t r;
+	int adj;
+	
+	// Flush any pending output before reading
+	r = ft232r_flush(dev);
+	if (r < 0)
+		return r;
+	
+	// First 2 bytes of every 0x40 are FTDI status or something
+	while (dev->ibufLen <= 2) {
+		// TODO: Implement a timeout for status byte repeating
+		int transferred = ft232r_readwrite(dev, dev->i, dev->ibuf, sizeof(dev->ibuf));
+		if (transferred <= 0)
+			return transferred;
+		dev->ibufLen = transferred;
+		for (adj = 0x40; dev->ibufLen > adj; adj += 0x40 - 2) {
+			dev->ibufLen -= 2;
+			memmove(&dev->ibuf[adj], &dev->ibuf[adj+2], dev->ibufLen - adj);
+		}
+	}
+	unsigned char *ibufs = &dev->ibuf[2];
+	size_t ibufsLen = dev->ibufLen - 2;
+	
+	if (count > ibufsLen)
+		count = ibufsLen;
+	memcpy(data, ibufs, count);
+	dev->ibufLen -= count;
+	ibufsLen -= count;
+	if (ibufsLen) {
+		memmove(ibufs, &ibufs[count], ibufsLen);
+		applog(LOG_DEBUG, "ft232r_read: %u bytes extra", ibufsLen);
+	}
+	return count;
+}
+
+ssize_t ft232r_read_all(struct ft232r_device_handle *dev, void *data, size_t count)
+{
+	return ft232r_rw_all(ft232r_read, dev, data, count);
+}
+
+bool ft232r_get_pins(struct ft232r_device_handle *dev, uint8_t *pins)
+{
+	return libusb_control_transfer(dev->h, FTDI_REQTYPE_IN, FTDI_REQUEST_GET_PINS, 0, FTDI_INDEX, pins, 1, FTDI_TIMEOUT) == 1;
+}
+
+#if 0
+int main() {
+	libusb_init(NULL);
+	ft232r_scan();
+	ft232r_scan_free();
+	libusb_exit(NULL);
+}
+void applog(int prio, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vprintf(fmt, ap);
+	puts("");
+	va_end(ap);
+}
+#endif

+ 42 - 0
ft232r.h

@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012 Luke Dashjr
+ *
+ * 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 the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#ifndef BFGMINER_FT232R_H
+#define BFGMINER_FT232R_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <libusb-1.0/libusb.h>
+
+enum ft232r_reset_purge {
+	FTDI_PURGE_RX   = 1,
+	FTDI_PURGE_TX   = 2,
+	FTDI_PURGE_BOTH = 3,
+};
+
+struct ft232r_device_handle;
+
+typedef bool(*foundusb_func_t)(libusb_device *, const char *product, const char *serial);
+
+extern void ft232r_scan();
+extern void ft232r_scan_free();
+extern int ft232r_detect(const char *product_needle, const char *serial, foundusb_func_t);
+extern struct ft232r_device_handle *ft232r_open(libusb_device *);
+extern void ft232r_close(struct ft232r_device_handle *);
+extern bool ft232r_purge_buffers(struct ft232r_device_handle *, enum ft232r_reset_purge);
+extern bool ft232r_set_bitmode(struct ft232r_device_handle *, uint8_t mask, uint8_t mode);
+extern ssize_t ft232r_flush(struct ft232r_device_handle *);
+extern ssize_t ft232r_write(struct ft232r_device_handle *, void *data, size_t count);
+extern ssize_t ft232r_write_all(struct ft232r_device_handle *, void *data, size_t count);
+extern ssize_t ft232r_read(struct ft232r_device_handle *, void *buf, size_t count);
+extern ssize_t ft232r_read_all(struct ft232r_device_handle *, void *data, size_t count);
+extern bool ft232r_get_pins(struct ft232r_device_handle *, uint8_t *pins);
+
+#endif

+ 270 - 0
jtag.c

@@ -0,0 +1,270 @@
+/*
+ * Copyright 2012 Luke Dashjr
+ *
+ * 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 the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+// NOTE: This code is based on code Luke-Jr wrote originally for LPC1343CodeBase
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ft232r.h"
+#include "jtag.h"
+#include "logging.h"
+#include "miner.h"
+
+//#define DEBUG_JTAG_CLOCK
+
+#define FTDI_READ_BUFFER_SIZE 100
+
+static
+unsigned char jtag_clock_byte(struct jtag_port *jp, bool tms, bool tdi)
+{
+	return (jp->a->state & jp->ignored)
+	          | (tms ? jp->tms : 0)
+	          | (tdi ? jp->tdi : 0);
+}
+
+// NOTE: The order of tms and tdi here are inverted from LPC1343CodeBase
+bool jtag_clock(struct jtag_port *jp, bool tms, bool tdi, bool *tdo)
+{
+	unsigned char bufsz = tdo ? 3 : 2;
+	unsigned char buf[3];
+	memset(buf, jtag_clock_byte(jp, tms, tdi), sizeof(buf));
+	buf[2] =
+	buf[1] |= jp->tck;
+	if (ft232r_write_all(jp->a->ftdi, buf, bufsz) != bufsz)
+		return false;
+	jp->a->state = buf[2];
+	if (jp->a->async) {
+		if (unlikely(tdo))
+			applog(LOG_WARNING, "jtag_clock: request for tdo in async mode not possible");
+#ifdef DEBUG_JTAG_CLOCK
+		applog(LOG_DEBUG, "%p %02x tms=%d tdi=%d tdo=?async", jp, (unsigned)buf[2], (int)tms, (int)tdi);
+#endif
+		return true;
+	}
+	jp->a->bufread += bufsz;
+	if (jp->a->bufread < FTDI_READ_BUFFER_SIZE - sizeof(buf) && !tdo) {
+		// By deferring unnecessary reads, we can avoid some USB latency
+#ifdef DEBUG_JTAG_CLOCK
+		applog(LOG_DEBUG, "%p %02x tms=%d tdi=%d tdo=?defer", jp, (unsigned)buf[2], (int)tms, (int)tdi);
+#endif
+		return true;
+	}
+#if 0 /* untested */
+	else if (!tdo) {
+		if (ft232r_purge_buffers(jp->a->ftdi, FTDI_PURGE_BOTH)) {
+			jp->bufread = 0;
+#ifdef DEBUG_JTAG_CLOCK
+		applog(LOG_DEBUG, "%p %02x tms=%d tdi=%d tdo=?purge", jp, (unsigned)buf[2], (int)tms, (int)tdi);
+#endif
+			return true;
+		}
+	}
+#endif
+	uint8_t rbufsz = jp->a->bufread;
+	jp->a->bufread = 0;
+	unsigned char rbuf[rbufsz];
+	if (ft232r_read_all(jp->a->ftdi, rbuf, rbufsz) != rbufsz)
+		return false;
+	if (tdo) {
+		*tdo = (rbuf[rbufsz-1] & jp->tdo);
+#ifdef DEBUG_JTAG_CLOCK
+	char *x = bin2hex(rbuf, rbufsz);
+	applog(LOG_DEBUG, "%p %02x tms=%d tdi=%d tdo=%d (%u:%s)", jp, (unsigned)rbuf[rbufsz-1], (int)tms, (int)tdi, (int)(bool)(rbuf[rbufsz-1] & jp->tdo), (unsigned)rbufsz, x);
+	free(x);
+	} else {
+		applog(LOG_DEBUG, "%p %02x tms=%d tdi=%d tdo=?ignore", jp, (unsigned)buf[2], (int)tms, (int)tdi);
+#endif
+	}
+	return true;
+}
+
+static bool jtag_rw_bit(struct jtag_port *jp, void *buf, uint8_t mask, bool tms, bool do_read)
+{
+	uint8_t *byte = buf;
+	bool tdo;
+	if (!jtag_clock(jp, tms, byte[0] & mask, do_read ? &tdo : NULL))
+		return false;
+	if (do_read) {
+		if (tdo)
+			byte[0] |= mask;
+		else
+			byte[0] &= ~mask;
+	}
+	return true;
+}
+
+static inline
+bool getbit(void *data, uint32_t bitnum)
+{
+	unsigned char *cdata = data;
+	div_t d = div(bitnum, 8);
+	unsigned char b = cdata[d.quot];
+	return b & (1<<(7 - d.rem));
+}
+
+static inline
+void setbit(void *data, uint32_t bitnum, bool nv)
+{
+	unsigned char *cdata = data;
+	div_t d = div(bitnum, 8);
+	unsigned char *p = &cdata[d.quot];
+	unsigned char o = (1<<(7 - d.rem));
+	if (nv)
+		*p |= o;
+	else
+		*p &= ~o;
+}
+
+// Expects to start at the Capture step, to handle 0-length gracefully
+bool _jtag_llrw(struct jtag_port *jp, void *buf, size_t bitlength, bool do_read, int stage)
+{
+	uint8_t *data = buf;
+	
+	if (!bitlength)
+		return jtag_clock(jp, true, false, NULL);
+
+	if (stage & 1)
+		if (!jtag_clock(jp, false, false, NULL))
+			return false;
+
+#ifndef DEBUG_JTAG_CLOCK
+	// This alternate implementation is designed to minimize ft232r reads (which are slow)
+	if (do_read) {
+		unsigned char rbuf[FTDI_READ_BUFFER_SIZE];
+		unsigned char wbuf[3];
+		ssize_t rbufsz, bitspending = 0;
+		size_t databitoff = 0, i;
+
+		--bitlength;
+		for (i = 0; i < bitlength; ++i) {
+			wbuf[0] = jtag_clock_byte(jp, false, getbit(data, i));
+			wbuf[1] = wbuf[0] | jp->tck;
+			if (ft232r_write_all(jp->a->ftdi, wbuf, 2) != 2)
+				return false;
+			jp->a->bufread += 2;
+			++bitspending;
+			if (jp->a->bufread > FTDI_READ_BUFFER_SIZE - 2) {
+				// The next bit would overflow, so read now
+				rbufsz = jp->a->bufread;
+				if (ft232r_read_all(jp->a->ftdi, rbuf, rbufsz) != rbufsz)
+					return false;
+				for (ssize_t j = rbufsz - ((bitspending - 1) * 2); j < rbufsz; j += 2)
+					setbit(data, databitoff++, (rbuf[j] & jp->tdo));
+				bitspending = 1;
+				jp->a->bufread = 0;
+			}
+		}
+		// Last bit needs special treatment
+		wbuf[0] = jtag_clock_byte(jp, (stage & 2), getbit(data, i));
+		wbuf[2] = wbuf[1] = wbuf[0] | jp->tck;
+		if (ft232r_write_all(jp->a->ftdi, wbuf, sizeof(wbuf)) != sizeof(wbuf))
+			return false;
+		rbufsz = jp->a->bufread + 3;
+		if (ft232r_read_all(jp->a->ftdi, rbuf, rbufsz) != rbufsz)
+			return false;
+		for (ssize_t j = rbufsz - 1 - (bitspending * 2); j < rbufsz; j += 2)
+			setbit(data, databitoff++, (rbuf[j] & jp->tdo));
+		setbit(data, databitoff++, (rbuf[rbufsz - 1] & jp->tdo));
+		jp->a->bufread = 0;
+		
+		if (stage & 2) {
+			if (!jtag_clock(jp, true, false, NULL))  // Update
+				return false;
+		}
+		
+		return true;
+	}
+#endif
+
+	int i, j;
+	div_t d;
+
+	d = div(bitlength - 1, 8);
+
+	for (i = 0; i < d.quot; ++i) {
+		for (j = 0x80; j; j /= 2) {
+			if (!jtag_rw_bit(jp, &data[i], j, false, do_read))
+				return false;
+		}
+	}
+	for (j = 0; j < d.rem; ++j)
+		if (!jtag_rw_bit(jp, &data[i], 0x80 >> j, false, do_read))
+			return false;
+	if (stage & 2) {
+		if (!jtag_rw_bit(jp, &data[i], 0x80 >> j, true, do_read))
+			return false;
+		if (!jtag_clock(jp, true, false, NULL))  // Update
+			return false;
+	}
+	else
+		if (!jtag_rw_bit(jp, &data[i], 0x80 >> j, false, do_read))
+			return false;
+	return true;
+}
+
+bool jtag_reset(struct jtag_port *jp)
+{
+	for (int i = 0; i < 5; ++i)
+		if (!jtag_clock(jp, true, false, NULL))
+			return false;
+	return jtag_clock(jp, false, false, NULL);
+}
+
+// Returns -1 for failure, -2 for unknown, or zero and higher for number of devices
+ssize_t jtag_detect(struct jtag_port *jp)
+{
+	// TODO: detect more than 1 device
+	int i;
+	bool tdo;
+	
+	if (!(1
+	 && jtag_write(jp, JTAG_REG_IR, "\xff", 8)
+	 && jtag_clock(jp, true , false, NULL)  // Select DR
+	 && jtag_clock(jp, false, false, NULL)  // Capture DR
+	 && jtag_clock(jp, false, false, NULL)  // Shift DR
+	))
+		return -1;
+	for (i = 0; i < 4; ++i)
+		if (!jtag_clock(jp, false, false, NULL))
+			return -1;
+	if (!jtag_clock(jp, false, false, &tdo))
+		return -1;
+	if (tdo)
+		return -1;
+	for (i = 0; i < 4; ++i)
+	{
+		if (!jtag_clock(jp, false, true, &tdo))
+			return -1;
+		if (tdo)
+			break;
+	}
+	if (!jtag_reset(jp))
+		return -1;
+	return i < 2 ? i : -2;
+}
+
+bool _jtag_rw(struct jtag_port *jp, enum jtagreg r, void *buf, size_t bitlength, bool do_read, int stage)
+{
+	if (!jtag_clock(jp, true, false, NULL))  // Select DR
+		return false;
+	if (r == JTAG_REG_IR)
+		if (!jtag_clock(jp, true, false, NULL))  // Select IR
+			return false;
+	if (!jtag_clock(jp, false, false, NULL))  // Capture
+		return false;
+	return _jtag_llrw(jp, buf, bitlength, do_read, stage);  // Exit1
+}
+
+bool jtag_run(struct jtag_port *jp)
+{
+	return jtag_clock(jp, false, false, NULL);
+}

+ 43 - 0
jtag.h

@@ -0,0 +1,43 @@
+#ifndef BFGMINER_JTAG_H
+#define BFGMINER_JTAG_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <unistd.h>
+
+struct jtag_port_a {
+	struct ft232r_device_handle *ftdi;
+	uint8_t state;
+	bool async;
+	uint8_t bufread;
+};
+
+struct jtag_port {
+	struct jtag_port_a *a;
+	uint8_t tck;
+	uint8_t tms;
+	uint8_t tdi;
+	uint8_t tdo;
+	uint8_t ignored;
+};
+
+enum jtagreg {
+	JTAG_REG_DR,
+	JTAG_REG_IR,
+};
+
+extern bool jtag_clock(struct jtag_port *, bool tms, bool tdi, bool *tdo);
+extern bool _jtag_llrw(struct jtag_port *, void *buf, size_t bitlength, bool do_read, int stage);
+extern bool jtag_reset(struct jtag_port *);
+extern ssize_t jtag_detect(struct jtag_port *);
+extern bool _jtag_rw(struct jtag_port *, enum jtagreg r, void *buf, size_t bitlength, bool do_read, int stage);
+#define jtag_read(jp, r, data, bitlen)  _jtag_rw(jp, r, data, bitlen, true, 0xff)
+#define jtag_sread(jp, r, data, bitlen)  _jtag_rw(jp, r, data, bitlen, true, 1)
+#define jtag_sread_more(jp, data, bitlen, finish)  _jtag_llrw(jp, data, bitlen, true, (finish) ? 2 : 0)
+// Cast is used to accept const data - while it ignores the compiler attribute, it still won't modify the data
+#define jtag_write(jp, r, data, bitlen)  _jtag_rw(jp, r, (void*)data, bitlen, false, 0xff)
+#define jtag_swrite(jp, r, data, bitlen)  _jtag_rw(jp, r, (void*)data, bitlen, false, 1)
+#define jtag_swrite_more(jp, data, bitlen, finish)  _jtag_llrw(jp, (void*)data, bitlen, false, (finish) ? 2 : 0)
+extern bool jtag_run(struct jtag_port *);
+
+#endif

+ 21 - 1
miner.c

@@ -55,6 +55,10 @@
 #include "driver-opencl.h"
 #include "driver-opencl.h"
 #include "bench_block.h"
 #include "bench_block.h"
 
 
+#ifdef USE_X6500
+#include "ft232r.h"
+#endif
+
 #if defined(unix)
 #if defined(unix)
 	#include <errno.h>
 	#include <errno.h>
 	#include <fcntl.h>
 	#include <fcntl.h>
@@ -68,7 +72,7 @@
 #if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_MODMINER)
 #if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_MODMINER)
 #	define USE_FPGA
 #	define USE_FPGA
 #	define USE_FPGA_SERIAL
 #	define USE_FPGA_SERIAL
-#elif defined(USE_ZTEX)
+#elif defined(USE_ZTEX) || defined(USE_X6500)
 #	define USE_FPGA
 #	define USE_FPGA
 #endif
 #endif
 
 
@@ -6387,6 +6391,10 @@ extern struct device_api icarus_api;
 extern struct device_api modminer_api;
 extern struct device_api modminer_api;
 #endif
 #endif
 
 
+#ifdef USE_X6500
+extern struct device_api x6500_api;
+#endif
+
 #ifdef USE_ZTEX
 #ifdef USE_ZTEX
 extern struct device_api ztex_api;
 extern struct device_api ztex_api;
 #endif
 #endif
@@ -6470,6 +6478,9 @@ int main(int argc, char *argv[])
 #ifdef HAVE_LIBUSB
 #ifdef HAVE_LIBUSB
         libusb_init(NULL);
         libusb_init(NULL);
 #endif
 #endif
+#ifdef USE_X6500
+	ft232r_scan();
+#endif
 
 
 	mutex_init(&hash_lock);
 	mutex_init(&hash_lock);
 	mutex_init(&qd_lock);
 	mutex_init(&qd_lock);
@@ -6674,6 +6685,11 @@ int main(int argc, char *argv[])
 		modminer_api.api_detect();
 		modminer_api.api_detect();
 #endif
 #endif
 
 
+#ifdef USE_X6500
+	if (!opt_scrypt)
+		x6500_api.api_detect();
+#endif
+
 #ifdef USE_ZTEX
 #ifdef USE_ZTEX
 	if (!opt_scrypt)
 	if (!opt_scrypt)
 		ztex_api.api_detect();
 		ztex_api.api_detect();
@@ -6683,6 +6699,10 @@ int main(int argc, char *argv[])
 	cpu_api.api_detect();
 	cpu_api.api_detect();
 #endif
 #endif
 
 
+#ifdef USE_X6500
+	ft232r_scan_free();
+#endif
+
 	for (i = 0; i < total_devices; ++i)
 	for (i = 0; i < total_devices; ++i)
 		if (!devices[i]->devtype)
 		if (!devices[i]->devtype)
 			devices[i]->devtype = "PGA";
 			devices[i]->devtype = "PGA";

+ 3 - 0
miner.h

@@ -377,6 +377,9 @@ struct cgpu_info {
 		struct libztex_device *device_ztex;
 		struct libztex_device *device_ztex;
 #endif
 #endif
 		int device_fd;
 		int device_fd;
+#ifdef USE_X6500
+		struct ft232r_device_handle *device_ft232r;
+#endif
 	};
 	};
 #ifdef USE_BITFORCE
 #ifdef USE_BITFORCE
 	struct timeval work_start_tv;
 	struct timeval work_start_tv;