Browse Source

Merge pull request #123 from xiangfu/icarus

Add Icarus Support to Cgminer
Con Kolivas 14 years ago
parent
commit
882e4d85de
5 changed files with 381 additions and 16 deletions
  1. 3 0
      Makefile.am
  2. 11 10
      README
  3. 15 4
      cgminer.c
  4. 19 2
      configure.ac
  5. 333 0
      icarus.c

+ 3 - 0
Makefile.am

@@ -70,3 +70,6 @@ if HAS_BITFORCE
 cgminer_SOURCES += bitforce.c
 endif
 
+if HAS_ICARUS
+cgminer_SOURCES += icarus.c
+endif

+ 11 - 10
README

@@ -50,6 +50,7 @@ CGMiner specific configuration options:
 	--disable-opencl        Override detection and disable building with opencl
 	--disable-adl           Override detection and disable building with adl
 	--enable-bitforce       Compile support for BitForce FPGAs(default disabled)
+	--enable-icarus         Compile support for Icarus Board(default disabled)
 
 Basic *nix build instructions:
 	To build with GPU mining support:
@@ -196,9 +197,9 @@ GPU only options:
 --ndevs|-n          Enumerate number of detected GPUs and exit
 
 
-BitForce only options:
+FPGA mining boards(BitForce, Icarus) only options:
 
---scan-serial|-S <arg> Serial port to probe for BitForce device
+--scan-serial|-S <arg> Serial port to probe for FPGA mining device
 
 
 CPU only options:
@@ -317,14 +318,14 @@ The output line shows the following:
 (5s):1713.6 (avg):1707.8 Mh/s | Q:301  A:729  R:8  HW:0  E:242%  U:22.53/m
 
 Each column is as follows:
-A 5 second exponentially decaying average hash rate
-An all time average hash rate
-The number of requested (Queued) work items from the pools
-The number of Accepted shares
-The number of Rejected shares
-The number of HardWare errors
-The Efficiency defined as number of shares returned / work item
-The Utility defined as the number of shares / minute
+5s:  A 5 second exponentially decaying average hash rate
+avg: An all time average hash rate
+Q:   The number of requested (Queued) work items from the pools
+A:   The number of Accepted shares
+R:   The number of Rejected shares
+HW:  The number of HardWare errors
+E:   The Efficiency defined as number of shares returned / work item
+U:   The Utility defined as the number of shares / minute
 
  GPU 1: 73.5C 2551RPM | 427.3/443.0Mh/s | A:8 R:0 HW:0 U:4.39/m
 

+ 15 - 4
cgminer.c

@@ -399,7 +399,7 @@ static char *set_int_1_to_10(const char *arg, int *i)
 	return set_int_range(arg, i, 1, 10);
 }
 
-#ifdef USE_BITFORCE
+#if defined(USE_BITFORCE) || defined(USE_ICARUS)
 static char *add_serial(char *arg)
 {
 	string_elist_add(arg, &scan_devices);
@@ -661,7 +661,7 @@ static struct opt_table opt_config_table[] = {
 	OPT_WITHOUT_ARG("--disable-gpu|-G",
 			opt_set_bool, &opt_nogpu,
 			"Disable GPU mining even if suitable devices exist"),
-#if defined(WANT_CPUMINE) && (defined(HAVE_OPENCL) || defined(USE_BITFORCE))
+#if defined(WANT_CPUMINE) && (defined(HAVE_OPENCL) || defined(USE_BITFORCE) || defined(USE_ICARUS))
 	OPT_WITHOUT_ARG("--enable-cpu|-C",
 			opt_set_bool, &opt_usecpu,
 			"Enable CPU mining with other mining (default: no CPU mining if other devices exist)"),
@@ -776,10 +776,10 @@ 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
+#if defined(USE_BITFORCE) || defined(USE_ICARUS)
 	OPT_WITH_ARG("--scan-serial|-S",
 		     add_serial, NULL, NULL,
-		     "Serial port to probe for BitForce device"),
+		     "Serial port to probe for FPGA Mining device"),
 #endif
 	OPT_WITH_ARG("--scan-time|-s",
 		     set_int_0_to_9999, opt_show_intval, &opt_scantime,
@@ -960,6 +960,9 @@ static char *opt_verusage_and_exit(const char *extra)
 #endif
 #ifdef USE_BITFORCE
 		"bitforce "
+#endif
+#ifdef USE_ICARUS
+		"icarus "
 #endif
 		"mining support.\n"
 		, packagename);
@@ -4157,6 +4160,10 @@ struct device_api cpu_api = {
 extern struct device_api bitforce_api;
 #endif
 
+#ifdef USE_ICARUS
+extern struct device_api icarus_api;
+#endif
+
 
 static int cgminer_id_count = 0;
 
@@ -4311,6 +4318,10 @@ int main (int argc, char *argv[])
 	bitforce_api.api_detect();
 #endif
 
+#ifdef USE_ICARUS
+	icarus_api.api_detect();
+#endif
+
 #ifdef WANT_CPUMINE
 	cpu_api.api_detect();
 #endif

+ 19 - 2
configure.ac

@@ -196,6 +196,17 @@ if test "x$bitforce" = xyes; then
 fi
 AM_CONDITIONAL([HAS_BITFORCE], [test x$bitforce = xyes])
 
+icarus="no"
+
+AC_ARG_ENABLE([icarus],
+	[AC_HELP_STRING([--enable-icarus],[Compile support for Icarus (default disabled)])],
+	[icarus=$enableval]
+	)
+if test "x$icarus" = xyes; then
+	AC_DEFINE([USE_ICARUS], [1], [Defined to 1 if Icarus support is wanted])
+fi
+AM_CONDITIONAL([HAS_ICARUS], [test x$icarus = xyes])
+
 AC_SEARCH_LIBS(addstr, ncurses pdcurses, ,
         AC_MSG_ERROR([Could not find curses library - please install libncurses-dev or pdcurses-dev]))
 
@@ -338,13 +349,13 @@ if test "x$opencl" != xno; then
 		echo "  OpenCL...............: FOUND. GPU mining support enabled"
 	else
 		echo "  OpenCL...............: NOT FOUND. GPU mining support DISABLED"
-		if test "x$cpumining$bitforce" = xnono; then
+		if test "x$cpumining$bitforce$icarus" = xnonono; then
 			AC_MSG_ERROR([No mining configured in])
 		fi
 	fi
 else
 	echo "  OpenCL...............: Detection overrided. GPU mining support DISABLED"
-	if test "x$cpumining$bitforce" = xnono; then
+	if test "x$cpumining$bitforce$icarus" = xnonono; then
 		AC_MSG_ERROR([No mining configured in])
 	fi
 fi
@@ -366,6 +377,12 @@ else
 	echo "  BitForce.FPGAs.......: Disabled"
 fi
 
+if test "x$icarus" = xyes; then
+	echo "  Icarus.FPGAs.........: Enabled"
+else
+	echo "  Icarus.FPGAs.........: Disabled"
+fi
+
 echo
 if test "x$cpumining" = xyes; then
 	echo "  CPU Mining...........: Enabled"

+ 333 - 0
icarus.c

@@ -0,0 +1,333 @@
+/*
+ * Copyright 2012 Luke Dashjr
+ * Copyright 2012 Xiangfu <xiangfu@openmobilefree.com>
+ *
+ * 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.
+ */
+
+/*
+ * Those code should be works fine with V2 and V3 bitstream of Icarus.
+ * Operation:
+ *   No detection implement.
+ *   Input: 64B = 32B midstate + 20B fill bytes + last 12 bytes of block head.
+ *   Return: send back 32bits immediately when Icarus found a valid nonce.
+ *           no query protocol implemented here, if no data send back in ~11.3
+ *           seconds (full cover time on 32bit nonce range by 380MH/s speed)
+ *           just send another work.
+ * Notice:
+ *   1. Icarus will start calculate when you push a work to them, even they
+ *      are busy.
+ *   2. The 2 FPGAs on Icarus will distribute the job, one will calculate the
+ *      0 ~ 7FFFFFFF, another one will cover the 80000000 ~ FFFFFFFF.
+ *   3. It's possible for 2 FPGAs both find valid nonce in the meantime, the 2
+ *      valid nonce will all be send back.
+ *   4. Icarus will stop work when: a valid nonce has been found or 32 bits
+ *      nonce range is completely calculated.
+ */
+
+#include <limits.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#ifndef WIN32
+  #include <termios.h>
+  #include <sys/stat.h>
+  #include <fcntl.h>
+  #ifndef O_CLOEXEC
+    #define O_CLOEXEC 0
+  #endif
+#else
+  #include <windows.h>
+  #include <io.h>
+#endif
+
+#include "elist.h"
+#include "miner.h"
+
+#define ICARUS_READ_FAULT_COUNT	(8)
+
+static int icarus_read_count;
+static int icarus_write_fault;
+
+struct device_api icarus_api;
+
+static void rev(unsigned char *s, size_t l)
+{
+	size_t i, j;
+	unsigned char t;
+
+	for (i = 0, j = l - 1; i < j; i++, j--) {
+		t = s[i];
+		s[i] = s[j];
+		s[j] = t;
+	}
+}
+
+static int icarus_open(const char *devpath)
+{
+#ifndef WIN32
+	struct termios my_termios;
+
+	int serialfd = open(devpath, O_RDWR | O_CLOEXEC | O_NOCTTY);
+
+	if (serialfd == -1)
+		return -1;
+
+	tcgetattr(serialfd, &my_termios);
+	my_termios.c_cflag = B115200;
+	my_termios.c_cflag |= CS8;
+	my_termios.c_cflag |= CREAD;
+	my_termios.c_cflag |= CLOCAL;
+	my_termios.c_cflag &= ~(CSIZE | PARENB);
+
+	my_termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK |
+				ISTRIP | INLCR | IGNCR | ICRNL | IXON);
+	my_termios.c_oflag &= ~OPOST;
+	my_termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+	my_termios.c_cc[VTIME] = 10; /* block 1 second */
+	my_termios.c_cc[VMIN] = 0;
+	tcsetattr(serialfd, TCSANOW, &my_termios);
+
+	tcflush(serialfd, TCOFLUSH);
+	tcflush(serialfd, TCIFLUSH);
+
+	return serialfd;
+#else
+	HANDLE hSerial = CreateFile(devpath, GENERIC_READ | GENERIC_WRITE, 0,
+				    NULL, OPEN_EXISTING, 0, NULL);
+	if (unlikely(hSerial == INVALID_HANDLE_VALUE))
+		return -1;
+	/* TODO: Needs setup read block time. just like VTIME = 10 */
+	return _open_osfhandle((LONG)hSerial, 0);
+#endif
+}
+
+static void icarus_gets(char *buf, size_t bufLen, int fd)
+{
+	ssize_t ret = 0;
+
+	icarus_read_count = 0;
+
+	while (bufLen) {
+		ret = read(fd, buf, 1);
+		if (ret == 1) {
+			bufLen--;
+			buf++;
+			continue;
+		}
+
+		icarus_read_count++;
+		if (icarus_read_count == ICARUS_READ_FAULT_COUNT) {
+			applog(LOG_WARNING,
+			       "Icarus Read: No data in %d seconds",
+			       ICARUS_READ_FAULT_COUNT);
+			break;
+		}
+	}
+}
+
+static void icarus_write(int fd, const void *buf, size_t bufLen)
+{
+	ssize_t ret;
+
+	ret = write(fd, buf, bufLen);
+	if (unlikely(ret != bufLen))
+		icarus_write_fault = 1;
+}
+
+#define icarus_close(fd) close(fd)
+
+static bool icarus_detect_one(const char *devpath)
+{
+	int fd;
+
+	const unsigned char golden_ob[] =
+		"2db907f9cb4eb938ded904f4832c4331"
+		"0380e3aeb54364057e7fec5157bfc533"
+		"00000000000000000000000080000000"
+		"00000000a58e091ac342724e7c3dc346";
+	const unsigned char golden_nonce[] = "063c5e01";
+
+	char ob_bin[64], nonce_bin[4];
+	char *nonce_hex;
+
+	if (total_devices == MAX_DEVICES)
+		return false;
+
+	fd = icarus_open(devpath);
+	if (unlikely(fd == -1)) {
+		applog(LOG_ERR, "Icarus Detect: Failed to open %s", devpath);
+		return false;
+	}
+
+	hex2bin(ob_bin, golden_ob, sizeof(ob_bin));
+	icarus_write(fd, ob_bin, sizeof(ob_bin));
+
+	memset(nonce_bin, 0, sizeof(nonce_bin));
+	icarus_gets(nonce_bin, sizeof(nonce_bin), fd);
+
+	icarus_close(fd);
+
+	nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin));
+	if (nonce_hex) {
+		if (strncmp(nonce_hex, golden_nonce, 8)) {
+			applog(LOG_ERR, 
+			       "Icarus Detect: "
+			       "Test failed at %s : get %s, should: %s",
+			       devpath, nonce_hex, golden_nonce);
+			free(nonce_hex);
+			return false;
+		}
+		free(nonce_hex);
+	} else
+		return false;
+
+	/* We have a real Icarus! */
+	struct cgpu_info *icarus;
+	icarus = calloc(1, sizeof(struct cgpu_info));
+	icarus->api = &icarus_api;
+	icarus->device_id = total_devices;
+	icarus->device_path = strdup(devpath);
+	icarus->threads = 1;
+	devices[total_devices++] = icarus;
+
+	icarus_write_fault = 0;
+
+	return true;
+}
+
+static void icarus_detect()
+{
+	struct string_elist *iter, *tmp;
+
+	list_for_each_entry_safe(iter, tmp, &scan_devices, list) {
+		if (icarus_detect_one(iter->string))
+			string_elist_del(iter);
+	}
+}
+
+static bool icarus_prepare(struct thr_info *thr)
+{
+	struct cgpu_info *icarus = thr->cgpu;
+
+	struct timeval now;
+
+	int fd = icarus_open(icarus->device_path);
+	if (unlikely(-1 == fd)) {
+		applog(LOG_ERR, "Failed to open Icarus on %s",
+		       icarus->device_path);
+		return false;
+	}
+
+	icarus->device_fd = fd;
+
+	applog(LOG_INFO, "Opened Icarus on %s", icarus->device_path);
+	gettimeofday(&now, NULL);
+	get_datestamp(icarus->init, &now);
+
+	return true;
+}
+
+static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work,
+				uint64_t max_nonce)
+{
+	struct cgpu_info *icarus;
+	int fd;
+
+	unsigned char ob_bin[64], nonce_bin[4];
+	unsigned char *ob_hex, *nonce_hex;
+	uint32_t nonce;
+	uint32_t hash_count;
+	time_t t;
+
+	icarus = thr->cgpu;
+	fd = icarus->device_fd;
+
+	memset(ob_bin, 0, sizeof(ob_bin));
+	memcpy(ob_bin, work->midstate, 32);
+	memcpy(ob_bin + 52, work->data + 64, 12);
+	rev(ob_bin, 32);
+	rev(ob_bin + 52, 12);
+#ifndef WIN32
+	tcflush(fd, TCOFLUSH);
+#endif
+	icarus_write(fd, ob_bin, sizeof(ob_bin));
+	if (icarus_write_fault)
+		return 0;	/* This should never happen */
+
+	ob_hex = bin2hex(ob_bin, sizeof(ob_bin));
+	if (ob_hex) {
+		t = time(NULL);
+		applog(LOG_DEBUG, "Icarus send : %s", ob_hex);
+		free(ob_hex);
+	}
+
+	/* Icarus will return 8 bytes nonces or nothing */
+	memset(nonce_bin, 0, sizeof(nonce_bin));
+	icarus_gets(nonce_bin, sizeof(nonce_bin), fd);
+
+	nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin));
+	if (nonce_hex) {
+		t = time(NULL) - t;
+		applog(LOG_DEBUG, "Icarus return (elapse %d seconds): %s",
+		       t, nonce_hex);
+		free(nonce_hex);
+	}
+
+	memcpy((char *)&nonce, nonce_bin, sizeof(nonce_bin));
+
+        if (nonce == 0 && icarus_read_count == ICARUS_READ_FAULT_COUNT)
+                return 0xffffffff;
+
+#ifndef __BIG_ENDIAN__
+	nonce = swab32(nonce);
+#endif
+	work->blk.nonce = 0xffffffff;
+	submit_nonce(thr, work, nonce);
+
+	hash_count = (nonce & 0x7fffffff);
+        if (hash_count == 0)
+		hash_count = 2;
+        else {
+                if (hash_count++ == 0x7fffffff)
+                        hash_count = 0xffffffff;
+                else
+                        hash_count <<= 1;
+        }
+
+        return hash_count;
+}
+
+static void icarus_shutdown(struct thr_info *thr)
+{
+	struct cgpu_info *icarus;
+	int fd;
+
+	if (thr->cgpu) {
+		icarus = thr->cgpu;
+
+		if (icarus->device_path)
+			free(icarus->device_path);
+
+		close(icarus->device_fd);
+
+		devices[icarus->device_id] = NULL;
+		free(icarus);
+
+		thr->cgpu = NULL;
+	}
+}
+
+struct device_api icarus_api = {
+	.name = "Icarus",
+	.api_detect = icarus_detect,
+	.thread_prepare = icarus_prepare,
+	.scanhash = icarus_scanhash,
+	.thread_shutdown = icarus_shutdown,
+};