Browse Source

BitForce FPGA support

cgminer will scan for and mine BitForce FPGAs on USB ports by providing the
new --scan-serial <device> option, or autodetect them by searching
/dev/serial/by-id for *BitFORCE_SHA256*
Luke Dashjr 14 years ago
parent
commit
5dfc8b694f
5 changed files with 295 additions and 4 deletions
  1. 4 0
      Makefile.am
  2. 219 0
      bitforce.c
  3. 14 2
      configure.ac
  4. 27 2
      main.c
  5. 31 0
      miner.h

+ 4 - 0
Makefile.am

@@ -60,3 +60,7 @@ AM_CFLAGS	= -DHAS_YASM
 endif
 endif
 endif
+
+if USE_BITFORCE
+cgminer_SOURCES += bitforce.c
+endif

+ 219 - 0
bitforce.c

@@ -0,0 +1,219 @@
+/*
+ * 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 2 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include <limits.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "elist.h"
+#include "miner.h"
+
+
+struct device_api bitforce_api;
+
+static bool bitforce_detect_one(const char *devpath)
+{
+	char pdevbuf[0x100];
+	int i = 0;
+
+	if (total_devices == MAX_DEVICES)
+		return false;
+
+	FILE *fileDev = fopen(devpath, "r+b");
+	if (unlikely(!fileDev))
+	{
+		applog(LOG_DEBUG, "BitForce Detect: Failed to open %s", devpath);
+		return false;
+	}
+	setbuf(fileDev, NULL);
+	fprintf(fileDev, "ZGX");
+	if (!fgets(pdevbuf, sizeof(pdevbuf), fileDev))
+	{
+		applog(LOG_ERR, "Error reading from BitForce (ZGX)");
+		return 0;
+	}
+	fclose(fileDev);
+	if (unlikely(!strstr(pdevbuf, "SHA256")))
+	{
+		applog(LOG_DEBUG, "BitForce Detect: Didn't recognize BitForce on %s", devpath);
+		return false;
+	}
+
+	// We have a real BitForce!
+	struct cgpu_info *bitforce;
+	bitforce = calloc(1, sizeof(*bitforce));
+	devices[total_devices++] = bitforce;
+	bitforce->api = &bitforce_api;
+	bitforce->device_id = i++;
+	bitforce->device_path = strdup(devpath);
+	bitforce->enabled = true;
+	bitforce->threads = 1;
+
+	return true;
+}
+
+static void bitforce_detect_auto()
+{
+	DIR *D;
+	struct dirent *de;
+	const char udevdir[] = "/dev/serial/by-id";
+	char devpath[sizeof(udevdir) + 1 + NAME_MAX];
+	char *devfile = devpath + sizeof(udevdir);
+
+	D = opendir(udevdir);
+	if (!D)
+		return;
+	memcpy(devpath, udevdir, sizeof(udevdir) - 1);
+	devpath[sizeof(udevdir) - 1] = '/';
+	while ( (de = readdir(D)) ) {
+		if (!strstr(de->d_name, "BitFORCE_SHA256"))
+			continue;
+		strcpy(devfile, de->d_name);
+		bitforce_detect_one(devpath);
+	}
+	closedir(D);
+}
+
+static void bitforce_detect()
+{
+	struct string_elist *iter, *tmp;
+
+	list_for_each_entry_safe(iter, tmp, &scan_devices, list) {
+		if (bitforce_detect_one(iter->string))
+			string_elist_del(iter);
+	}
+
+	bitforce_detect_auto();
+}
+
+static bool bitforce_thread_prepare(struct thr_info *thr)
+{
+	struct cgpu_info *bitforce = thr->cgpu;
+
+	struct timeval now;
+
+	FILE *fileDev = fopen(bitforce->device_path, "r+b");
+	if (unlikely(!fileDev))
+	{
+		applog(LOG_ERR, "Failed to open BitForce on %s", bitforce->device_path);
+		return false;
+	}
+
+	{
+		int nDevFD = fileno(fileDev);
+		struct termios pattr;
+		tcgetattr(nDevFD, &pattr);
+		pattr.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
+		pattr.c_oflag &= ~OPOST;
+		pattr.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+		pattr.c_cflag &= ~(CSIZE | PARENB);
+		pattr.c_cflag |= CS8;
+		tcsetattr(nDevFD, TCSANOW, &pattr);
+	}
+	setbuf(fileDev, NULL);
+	bitforce->device_file = fileDev;
+
+	applog(LOG_INFO, "Opened BitForce on %s", bitforce->device_path);
+	gettimeofday(&now, NULL);
+	get_datestamp(bitforce->init, &now);
+
+	return true;
+}
+
+static uint64_t bitforce_scanhash(struct thr_info *thr, struct work *work, uint64_t max_nonce)
+{
+	struct cgpu_info *bitforce = thr->cgpu;
+	FILE *fileDev = bitforce->device_file;
+
+	char pdevbuf[0x100];
+	unsigned char ob[61] = ">>>>>>>>12345678901234567890123456789012123456789012>>>>>>>>";
+	int i;
+	char *pnoncebuf;
+	uint32_t nonce;
+
+	fprintf(fileDev, "ZDX");
+	if (!fgets(pdevbuf, sizeof(pdevbuf), fileDev)) {
+		applog(LOG_ERR, "Error reading from BitForce (ZDX)");
+		return 0;
+	}
+	if (unlikely(pdevbuf[0] != 'O' || pdevbuf[1] != 'K'))
+	{
+		applog(LOG_ERR, "BitForce ZDX reports: %s", pdevbuf);
+		return 0;
+	}
+
+	memcpy(ob + 8, work->midstate, 32);
+	memcpy(ob + 8 + 32, work->data + 64, 12);
+	fwrite(ob, 60, 1, fileDev);
+	applog(LOG_DEBUG, "BitForce block data: %s", bin2hex(ob + 8, 44));
+
+	if (!fgets(pdevbuf, sizeof(pdevbuf), fileDev))
+	{
+		applog(LOG_ERR, "Error reading from BitForce (block data)");
+		return 0;
+	}
+	if (unlikely(pdevbuf[0] != 'O' || pdevbuf[1] != 'K'))
+	{
+		applog(LOG_ERR, "BitForce block data reports: %s", pdevbuf);
+		return 0;
+	}
+
+	usleep(4500000);
+	i = 4500;
+	while (1) {
+		fprintf(fileDev, "ZFX");
+		if (!fgets(pdevbuf, sizeof(pdevbuf), fileDev))
+		{
+			applog(LOG_ERR, "Error reading from BitForce (ZFX)");
+			return 0;
+		}
+		if (pdevbuf[0] != 'B')
+		    break;
+		usleep(10000);
+		i += 10;
+	}
+	applog(LOG_DEBUG, "BitForce waited %dms until %s\n", i, pdevbuf);
+	work->blk.nonce = 0xffffffff;
+	if (pdevbuf[2] == '-')
+		return 0xffffffff;
+	else
+	if (strncasecmp(pdevbuf, "NONCE-FOUND", 11)) {
+		applog(LOG_ERR, "BitForce result reports: %s", pdevbuf);
+		return 0;
+	}
+
+	pnoncebuf = &pdevbuf[12];
+
+	while (1) {
+		hex2bin((void*)&nonce, pnoncebuf, 4);
+#ifndef __BIG_ENDIAN__
+		nonce = swab32(nonce);
+#endif
+
+		submit_nonce(thr, work, nonce);
+		if (pnoncebuf[8] != ',')
+			break;
+		pnoncebuf += 9;
+	}
+
+	return 0xffffffff;
+}
+
+struct device_api bitforce_api = {
+	.name = "BFL",
+	.api_detect = bitforce_detect,
+	// .reinit_device = TODO
+	.thread_prepare = bitforce_thread_prepare,
+	.scanhash = bitforce_scanhash,
+};

+ 14 - 2
configure.ac

@@ -181,6 +181,15 @@ else
 	DLOPEN_FLAGS=""
 fi
 
+bitforce=yes
+AC_ARG_ENABLE([bitforce],
+	[AC_HELP_STRING([--disable-bitforce],[Don't compile support for BitForce FPGAs])],
+	[bitforce=$enableval]
+)
+if test "x$bitforce" != xno; then
+	AC_DEFINE([USE_BITFORCE], [1], [Defined to 1 if BitForce support is wanted.])
+fi
+
 AC_SEARCH_LIBS(addstr, ncurses pdcurses, ,
         AC_MSG_ERROR([Could not find curses library - please install libncurses-dev or pdcurses-dev]))
 
@@ -190,6 +199,7 @@ AC_CHECK_LIB(pdcurses, addstr, PDCURSES_LIBS=-lpdcurses)
 AM_CONDITIONAL([WANT_JANSSON], [test x$request_jansson = xtrue])
 AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue])
 AM_CONDITIONAL([HAVE_x86_64], [test x$have_x86_64 = xtrue])
+AM_CONDITIONAL([USE_BITFORCE], [test x$bitforce != xno])
 
 if test x$request_jansson = xtrue
 then
@@ -312,18 +322,20 @@ echo
 echo "Configuration Options Summary:"
 echo
 
+echo "  BitForce.FPGAs.......: $bitforce"
+
 if test "x$opencl" != xno; then
 	if test $found_opencl = 1; then
 		echo "  OpenCL...............: FOUND. GPU mining support enabled"
 	else
 		echo "  OpenCL...............: NOT FOUND. GPU mining support DISABLED"
-		if test "x$cpumining" != xyes; then
+		if test "x$cpumining$bitforce" = xnono; then
 			AC_MSG_ERROR([No mining configured in])
 		fi
 	fi
 else
 	echo "  OpenCL...............: Detection overrided. GPU mining support DISABLED"
-	if test "x$cpumining" != xyes; then
+	if test "x$cpumining$bitforce" = xnono; then
 		AC_MSG_ERROR([No mining configured in])
 	fi
 fi

+ 27 - 2
main.c

@@ -216,6 +216,7 @@ static bool opt_restart = true;
 static bool opt_nogpu;
 #endif
 
+struct list_head scan_devices;
 int nDevs;
 static int opt_g_threads = 2;
 static signed int devices_enabled = 0;
@@ -1003,6 +1004,12 @@ static char *set_float_0_to_99(const char *arg, float *f)
 	return NULL;
 }
 
+static char *add_serial(char *arg)
+{
+	string_elist_add(arg, &scan_devices);
+	return NULL;
+}
+
 static char *set_devices(char *arg)
 {
 	int i = strtol(arg, &arg, 0);
@@ -1670,6 +1677,11 @@ static struct opt_table opt_config_table[] = {
 	OPT_WITHOUT_ARG("--round-robin",
 		     set_rr, &pool_strategy,
 		     "Change multipool strategy from failover to round robin on failure"),
+#ifdef USE_BITFORCE
+	OPT_WITH_ARG("--scan-serial|-S",
+		     add_serial, NULL, NULL,
+		     "Serial port to probe for BitForce device"),
+#endif
 	OPT_WITH_ARG("--scan-time|-s",
 		     set_int_0_to_9999, opt_show_intval, &opt_scantime,
 		     "Upper bound on time spent scanning current work, in seconds"),
@@ -4340,6 +4352,7 @@ static inline bool abandon_work(int thr_id, struct work *work, struct timeval *w
 {
 	if (wdiff->tv_sec > opt_scantime ||
 	    work->blk.nonce >= MAXTHREADS - hashes ||
+	    hashes >= 0xfffffffe ||
 	    stale_work(work, false))
 		return true;
 	return false;
@@ -4432,7 +4445,7 @@ static void *miner_thread(void *userdata)
 				}
 			}
 
-			if (sdiff.tv_sec < cycle) {
+			if (unlikely(sdiff.tv_sec < cycle)) {
 				if (likely(!api->can_limit_work || max_nonce == 0xffffffff))
 					continue;
 
@@ -5386,7 +5399,7 @@ static void cpu_detect()
 	#endif /* !WIN32 */
 
 	if (opt_n_threads < 0 || !forced_n_threads) {
-		if (nDevs && !opt_usecpu)
+		if (total_devices && !opt_usecpu)
 			opt_n_threads = 0;
 		else
 			opt_n_threads = num_processors;
@@ -5803,6 +5816,12 @@ struct device_api opencl_api = {
 };
 #endif
 
+
+#ifdef USE_BITFORCE
+extern struct device_api bitforce_api;
+#endif
+
+
 static int cgminer_id_count = 0;
 
 void enable_device(struct cgpu_info *cgpu)
@@ -5877,6 +5896,8 @@ int main (int argc, char *argv[])
 	HASH_ADD_STR(blocks, hash, block);
 	strcpy(current_block, block->hash);
 
+	INIT_LIST_HEAD(&scan_devices);
+
 	memset(gpus, 0, sizeof(gpus));
 	for (i = 0; i < MAX_GPUDEVICES; i++)
 		gpus[i].dynamic = true;
@@ -5946,6 +5967,10 @@ int main (int argc, char *argv[])
 		opencl_api.api_detect();
 #endif
 
+#ifdef USE_BITFORCE
+	bitforce_api.api_detect();
+#endif
+
 #ifdef WANT_CPUMINE
 	cpu_api.api_detect();
 #endif

+ 31 - 0
miner.h

@@ -236,6 +236,9 @@ struct cgpu_info {
 	int cgminer_id;
 	struct device_api *api;
 	int device_id;
+	char *device_path;
+	FILE *device_file;
+
 	bool enabled;
 	int accepted;
 	int rejected;
@@ -297,6 +300,32 @@ struct thr_info {
 extern int thr_info_create(struct thr_info *thr, pthread_attr_t *attr, void *(*start) (void *), void *arg);
 extern void thr_info_cancel(struct thr_info *thr);
 
+
+struct string_elist {
+	char *string;
+	bool free_me;
+
+	struct list_head list;
+};
+
+static inline void string_elist_add(const char *s, struct list_head *head)
+{
+	struct string_elist *n;
+
+	n = calloc(1, sizeof(*n));
+	n->string = strdup(s);
+	n->free_me = true;
+	list_add_tail(&n->list, head);
+}
+
+static inline void string_elist_del(struct string_elist *item)
+{
+	if (item->free_me)
+		free(item->string);
+	list_del(&item->list);
+}
+
+
 static inline uint32_t swab32(uint32_t v)
 {
 	return bswap_32(v);
@@ -468,6 +497,7 @@ extern void api(void);
 #define MAX_DEVICES 32
 #define MAX_POOLS (32)
 
+extern struct list_head scan_devices;
 extern int nDevs;
 extern int opt_n_threads;
 extern int num_processors;
@@ -581,6 +611,7 @@ enum cl_kernel {
 	KL_PHATK,
 };
 
+extern void get_datestamp(char *, struct timeval *);
 bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce);
 extern void wlogprint(const char *f, ...);
 extern int curses_int(const char *query);