Browse Source

Merge branch 'dev_dynclock' into bfgminer

Conflicts:
	driver-icarus.c
Luke Dashjr 13 years ago
parent
commit
6d7e87f67b
4 changed files with 160 additions and 24 deletions
  1. 1 1
      configure.ac
  2. 115 2
      driver-cairnsmore.c
  3. 31 21
      driver-icarus.c
  4. 13 0
      icarus-common.h

+ 1 - 1
configure.ac

@@ -252,7 +252,7 @@ fi
 AC_CONFIG_SUBDIRS([libblkmaker])
 
 AM_CONDITIONAL([NEED_LIBBLKMAKER], [true])
-AM_CONDITIONAL([NEED_DYNCLOCK], [test x$modminer$ztex != xnono])
+AM_CONDITIONAL([NEED_DYNCLOCK], [test x$icarus$modminer$ztex != xnonono])
 AM_CONDITIONAL([NEED_FPGAUTILS], [test x$icarus$bitforce$modminer$ztex != xnononono])
 AM_CONDITIONAL([HAS_SCRYPT], [test x$scrypt = xyes])
 AM_CONDITIONAL([HAVE_CURSES], [test x$curses = xyes])

+ 115 - 2
driver-cairnsmore.c

@@ -7,6 +7,7 @@
  * any later version.  See COPYING for more details.
  */
 
+#include "dynclock.h"
 #include "fpgautils.h"
 #include "icarus-common.h"
 #include "miner.h"
@@ -16,6 +17,10 @@
 // This is a general ballpark
 #define CAIRNSMORE1_HASH_TIME 0.0000000024484
 
+#define CAIRNSMORE1_MINIMUM_CLOCK  5
+#define CAIRNSMORE1_DEFAULT_CLOCK  200
+#define CAIRNSMORE1_MAXIMUM_CLOCK  200
+
 struct device_api cairnsmore_api;
 
 static bool cairnsmore_detect_one(const char *devpath)
@@ -29,7 +34,7 @@ static bool cairnsmore_detect_one(const char *devpath)
 	info->fpga_count = 2;
 	info->quirk_reopen = false;
 	info->Hs = CAIRNSMORE1_HASH_TIME;
-	info->timing_mode = MODE_SHORT;
+	info->timing_mode = MODE_LONG;
 	info->do_icarus_timing = true;
 
 	if (!icarus_detect_custom(devpath, &cairnsmore_api, info)) {
@@ -54,15 +59,121 @@ static void cairnsmore_detect()
 	serial_detect_auto_byname(&cairnsmore_api, cairnsmore_detect_one, cairnsmore_detect_auto);
 }
 
+static bool cairnsmore_send_cmd(int fd, uint8_t cmd, uint8_t data)
+{
+	unsigned char pkt[64] =
+		"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+		"vdi\xb7"
+		"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+		"BFG0" "\xff\xff\xff\xff" "\0\0\0\0";
+	pkt[32] = 0xda ^ cmd ^ data;
+	pkt[33] = data;
+	pkt[34] = cmd;
+	return write(fd, pkt, sizeof(pkt)) == sizeof(pkt);
+}
+
+bool cairnsmore_supports_dynclock(int fd)
+{
+	unsigned char pkts[64] =
+		"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+		"\xe6\x3c\0\xb7"  // Set frequency multiplier to 60 (150 Mhz)
+		"\0\0\0\0\0\0\0\0\0\0\0\0" "BFG0"
+		"\x8b\xdb\x05\x1a" "\xff\xff\xff\xff" "\x00\x00\x1e\xfd";
+	if (write(fd, pkts, sizeof(pkts)) != sizeof(pkts))
+		return false;
+
+	uint32_t nonce = 0;
+	{
+		struct timeval tv_finish;
+		struct thr_info dummy = {
+			.work_restart = false,
+			.work_restart_fd = -1,
+		};
+		icarus_gets((unsigned char*)&nonce, fd, &tv_finish, &dummy, 1);
+	}
+	switch (nonce) {
+		case 0x000b1b5e:  // on big    endian
+		case 0x5e1b0b00:  // on little endian
+			// Hashed the command, so it's not supported
+			return false;
+		default:
+			// TODO: nonce from a real job... handle it
+		case 0:
+			return true;
+	}
+}
+
+static bool cairnsmore_change_clock_func(struct thr_info *thr, int bestM)
+{
+	struct cgpu_info *cm1 = thr->cgpu;
+	struct ICARUS_INFO *info = cm1->cgpu_data;
+
+	if (unlikely(!cairnsmore_send_cmd(cm1->device_fd, 0, bestM)))
+		return false;
+
+	// Adjust Hs expectations for frequency change
+	info->Hs = info->Hs * (double)bestM / (double)info->dclk.freqM;
+
+	char repr[0x10];
+	sprintf(repr, "%s %u", cm1->api->name, cm1->device_id);
+	dclk_msg_freqchange(repr, 2.5 * (double)info->dclk.freqM, 2.5 * (double)bestM, NULL);
+	info->dclk.freqM = bestM;
+
+	return true;
+}
+
+static bool cairnsmore_init(struct thr_info *thr)
+{
+	struct cgpu_info *cm1 = thr->cgpu;
+	struct ICARUS_INFO *info = cm1->cgpu_data;
+
+	if (cairnsmore_supports_dynclock(cm1->device_fd)) {
+		info->dclk_change_clock_func = cairnsmore_change_clock_func;
+
+		dclk_prepare(&info->dclk);
+		info->dclk.freqMaxM = CAIRNSMORE1_MAXIMUM_CLOCK / 2.5;
+		info->dclk.freqM =
+		info->dclk.freqMDefault = CAIRNSMORE1_DEFAULT_CLOCK / 2.5;
+		cairnsmore_send_cmd(cm1->device_fd, 0, info->dclk.freqM);
+		applog(LOG_WARNING, "%s %u: Frequency set to %u Mhz (range: %u-%u)",
+		       cm1->api->name, cm1->device_id,
+		       CAIRNSMORE1_DEFAULT_CLOCK, CAIRNSMORE1_MINIMUM_CLOCK, CAIRNSMORE1_MAXIMUM_CLOCK
+		);
+	} else {
+		applog(LOG_WARNING, "%s %u: Frequency scaling not supported",
+			cm1->api->name, cm1->device_id
+		);
+		// Test failures corrupt the hash state, so next scanhash is a firstrun
+		struct icarus_state *state = thr->cgpu_data;
+		state->firstrun = true;
+	}
+
+	return true;
+}
+
 void convert_icarus_to_cairnsmore(struct cgpu_info *cm1)
 {
 	struct ICARUS_INFO *info = cm1->cgpu_data;
 	info->Hs = CAIRNSMORE1_HASH_TIME;
 	info->fullnonce = info->Hs * (((double)0xffffffff) + 1);
-	info->timing_mode = MODE_SHORT;
+	info->timing_mode = MODE_LONG;
 	info->do_icarus_timing = true;
 	cm1->api = &cairnsmore_api;
 	renumber_cgpu(cm1);
+	cairnsmore_init(cm1->thr[0]);
+}
+
+static struct api_data *cairnsmore_api_extra_device_status(struct cgpu_info *cm1)
+{
+	struct ICARUS_INFO *info = cm1->cgpu_data;
+	struct api_data*root = NULL;
+
+	if (info->dclk.freqM) {
+		double frequency = 2.5 * info->dclk.freqM;
+		root = api_add_freq(root, "Frequency", &frequency, true);
+	}
+
+	return root;
 }
 
 extern struct device_api icarus_api;
@@ -74,4 +185,6 @@ static void cairnsmore_api_init()
 	cairnsmore_api.dname = "cairnsmore";
 	cairnsmore_api.name = "ECM";
 	cairnsmore_api.api_detect = cairnsmore_detect;
+	cairnsmore_api.thread_init = cairnsmore_init;
+	cairnsmore_api.get_api_extra_device_status = cairnsmore_api_extra_device_status;
 }

+ 31 - 21
driver-icarus.c

@@ -54,6 +54,7 @@
   #define HAVE_EPOLL
 #endif
 
+#include "dynclock.h"
 #include "elist.h"
 #include "icarus-common.h"
 #include "miner.h"
@@ -172,7 +173,7 @@ static void rev(unsigned char *s, size_t l)
 #define icarus_open2(devpath, baud, purge)  serial_open(devpath, baud, ICARUS_READ_FAULT_DECISECONDS, purge)
 #define icarus_open(devpath, baud)  icarus_open2(devpath, baud, false)
 
-static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct thr_info *thr, int read_count)
+int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct thr_info *thr, int read_count)
 {
 	ssize_t ret = 0;
 	int rc = 0;
@@ -374,7 +375,8 @@ static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus)
 
 	info->min_data_count = MIN_DATA_COUNT;
 
-	applog(LOG_DEBUG, "Icarus: Init: %d mode=%s read_count=%d Hs=%e",
+	applog(LOG_DEBUG, "%s %u: Init: mode=%s read_count=%d Hs=%e",
+		icarus->api->name,
 		icarus->device_id, timing_mode_str(info->timing_mode), info->read_count, info->Hs);
 }
 
@@ -593,10 +595,12 @@ bool icarus_detect_custom(const char *devpath, struct device_api *api, struct IC
 	icarus->threads = 1;
 	add_cgpu(icarus);
 
-	applog(LOG_INFO, "Found Icarus at %s, mark as %d",
-		devpath, icarus->device_id);
+	applog(LOG_INFO, "Found %s %u at %s",
+		icarus->api->name, icarus->device_id,
+		devpath);
 
-	applog(LOG_DEBUG, "Icarus: Init: %d baud=%d work_division=%d fpga_count=%d",
+	applog(LOG_DEBUG, "%s %u: Init: baud=%d work_division=%d fpga_count=%d",
+		icarus->api->name,
 		icarus->device_id, baud, work_division, fpga_count);
 
 	icarus->cgpu_data = info;
@@ -636,14 +640,6 @@ static void icarus_detect()
 	serial_detect(&icarus_api, icarus_detect_one);
 }
 
-struct icarus_state {
-	bool firstrun;
-	struct timeval tv_workstart;
-	struct timeval tv_workfinish;
-	struct work last_work;
-	bool changework;
-};
-
 static bool icarus_prepare(struct thr_info *thr)
 {
 	struct cgpu_info *icarus = thr->cgpu;
@@ -774,6 +770,19 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	tcflush(fd, TCOFLUSH);
 #endif
 
+	memcpy(&nonce, nonce_bin, sizeof(nonce_bin));
+	nonce = be32toh(nonce);
+
+	// Handle dynamic clocking for "subclass" devices
+	// This runs before sending next job, in case it isn't supported
+	if (info->dclk.freqM && likely(!state->firstrun)) {
+		dclk_gotNonces(&info->dclk);
+		if (nonce && !test_nonce(&state->last_work, nonce, false))
+			dclk_errorCount(&info->dclk, 1.0);
+		dclk_preUpdate(&info->dclk);
+		dclk_updateFreq(&info->dclk, info->dclk_change_clock_func, thr);
+	}
+
 	gettimeofday(&state->tv_workstart, NULL);
 
 	ret = icarus_write(fd, ob_bin, sizeof(ob_bin));
@@ -785,7 +794,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	if (opt_debug) {
 		ob_hex = bin2hex(ob_bin, sizeof(ob_bin));
 		if (ob_hex) {
-			applog(LOG_DEBUG, "Icarus %d sent: %s",
+			applog(LOG_DEBUG, "%s %u sent: %s",
+				icarus->api->name,
 				icarus->device_id, ob_hex);
 			free(ob_hex);
 		}
@@ -803,7 +813,6 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	}
 
 	// OK, done starting Icarus's next job... now process the last run's result!
-	memcpy((char *)&nonce, nonce_bin, sizeof(nonce_bin));
 
 	// aborted before becoming idle, get new work
 	if (nonce == 0 && lret) {
@@ -819,7 +828,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 			estimate_hashes = 0xffffffff;
 
 		if (opt_debug) {
-			applog(LOG_DEBUG, "Icarus %d no nonce = 0x%08llx hashes (%ld.%06lds)",
+			applog(LOG_DEBUG, "%s %u no nonce = 0x%08llx hashes (%ld.%06lds)",
+					icarus->api->name,
 					icarus->device_id, estimate_hashes,
 					elapsed.tv_sec, elapsed.tv_usec);
 		}
@@ -827,8 +837,6 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 		return estimate_hashes;
 	}
 
-	nonce = be32toh(nonce);
-
 	curr_hw_errors = icarus->hw_errors;
 	submit_nonce(thr, &state->last_work, nonce);
 	was_hw_error = (curr_hw_errors > icarus->hw_errors);
@@ -839,7 +847,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	hash_count *= info->fpga_count;
 
 	if (opt_debug) {
-		applog(LOG_DEBUG, "Icarus %d nonce = 0x%08x = 0x%08llx hashes (%ld.%06lds)",
+		applog(LOG_DEBUG, "%s %u nonce = 0x%08x = 0x%08llx hashes (%ld.%06lds)",
+				icarus->api->name,
 				icarus->device_id, nonce, hash_count, elapsed.tv_sec, elapsed.tv_usec);
 	}
 
@@ -961,8 +970,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 			else if (info->timing_mode == MODE_SHORT)
 				info->do_icarus_timing = false;
 
-//			applog(LOG_WARNING, "Icarus %d Re-estimate: read_count=%d fullnonce=%fs history count=%d Hs=%e W=%e values=%d hash range=0x%08lx min data count=%u", icarus->device_id, read_count, fullnonce, count, Hs, W, values, hash_count_range, info->min_data_count);
-			applog(LOG_WARNING, "Icarus %d Re-estimate: Hs=%e W=%e read_count=%d fullnonce=%.3fs",
+//			applog(LOG_WARNING, "%s %u Re-estimate: read_count=%d fullnonce=%fs history count=%d Hs=%e W=%e values=%d hash range=0x%08lx min data count=%u", icarus->api->name, icarus->device_id, read_count, fullnonce, count, Hs, W, values, hash_count_range, info->min_data_count);
+			applog(LOG_WARNING, "%s %u Re-estimate: Hs=%e W=%e read_count=%d fullnonce=%.3fs",
+					icarus->api->name,
 					icarus->device_id, Hs, W, read_count, fullnonce);
 		}
 		info->history_count++;

+ 13 - 0
icarus-common.h

@@ -5,6 +5,7 @@
 #include <stdint.h>
 #include <sys/time.h>
 
+#include "dynclock.h"
 #include "miner.h"
 
 // Fraction of a second, USB timeout is measured in
@@ -69,8 +70,20 @@ struct ICARUS_INFO {
 	int fpga_count;
 	uint32_t nonce_mask;
 	bool quirk_reopen;
+
+	dclk_change_clock_func_t dclk_change_clock_func;
+	struct dclk_data dclk;
+};
+
+struct icarus_state {
+	bool firstrun;
+	struct timeval tv_workstart;
+	struct timeval tv_workfinish;
+	struct work last_work;
+	bool changework;
 };
 
 bool icarus_detect_custom(const char *devpath, struct device_api *, struct ICARUS_INFO *);
+extern int icarus_gets(unsigned char *, int fd, struct timeval *tv_finish, struct thr_info *, int read_count);
 
 #endif