Browse Source

Merge branch 'dynclock' into bfgminer

Luke Dashjr 13 years ago
parent
commit
37a64019d1
4 changed files with 49 additions and 20 deletions
  1. 27 15
      driver-cairnsmore.c
  2. 16 5
      driver-icarus.c
  3. 5 0
      dynclock.h
  4. 1 0
      icarus-common.h

+ 27 - 15
driver-cairnsmore.c

@@ -18,7 +18,7 @@
 // This is a general ballpark
 #define CAIRNSMORE1_HASH_TIME 0.0000000024484
 
-#define CAIRNSMORE1_MINIMUM_CLOCK  5
+#define CAIRNSMORE1_MINIMUM_CLOCK  50
 #define CAIRNSMORE1_DEFAULT_CLOCK  200
 #define CAIRNSMORE1_MAXIMUM_CLOCK  200
 
@@ -63,13 +63,15 @@ 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)
+static bool cairnsmore_send_cmd(int fd, uint8_t cmd, uint8_t data, bool probe)
 {
 	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";
+		"bfg0" "\xff\xff\xff\xff" "\xb5\0\0\0";
+	if (unlikely(probe))
+		pkt[61] = '\x01';
 	pkt[32] = 0xda ^ cmd ^ data;
 	pkt[33] = data;
 	pkt[34] = cmd;
@@ -78,12 +80,11 @@ static bool cairnsmore_send_cmd(int fd, uint8_t cmd, uint8_t data)
 
 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))
+	if (!cairnsmore_send_cmd(fd, 0, 1, true))
+		return false;
+	struct timeval tv_start, elapsed;
+	gettimeofday(&tv_start, NULL);
+	if (!cairnsmore_send_cmd(fd, 0, 1, true))
 		return false;
 
 	uint32_t nonce = 0;
@@ -94,19 +95,24 @@ bool cairnsmore_supports_dynclock(int fd)
 			.work_restart_fd = -1,
 		};
 		icarus_gets((unsigned char*)&nonce, fd, &tv_finish, &dummy, 1);
+		timersub(&tv_finish, &tv_start, &elapsed);
 	}
+	applog(LOG_DEBUG, "Cairnsmore dynclock detection... Got %08x in %d.%06ds", nonce, elapsed.tv_sec, elapsed.tv_usec);
 	switch (nonce) {
-		case 0x000b1b5e:  // on big    endian
-		case 0x5e1b0b00:  // on little endian
+		case 0x00949a6f:  // big    endian
+		case 0x6f9a9400:  // little endian
 			// Hashed the command, so it's not supported
 			return false;
 		default:
-			// TODO: nonce from a real job... handle it
+			applog(LOG_WARNING, "Unexpected nonce from dynclock probe: %08x", be32toh(nonce));
+			return false;
 		case 0:
 			return true;
 	}
 }
 
+#define cairnsmore_send_cmd(fd, cmd, data) cairnsmore_send_cmd(fd, cmd, data, false)
+
 static bool cairnsmore_change_clock_func(struct thr_info *thr, int bestM)
 {
 	struct cgpu_info *cm1 = thr->cgpu;
@@ -130,6 +136,7 @@ static bool cairnsmore_init(struct thr_info *thr)
 {
 	struct cgpu_info *cm1 = thr->cgpu;
 	struct ICARUS_INFO *info = cm1->cgpu_data;
+	struct icarus_state *state = thr->cgpu_data;
 
 	if (cairnsmore_supports_dynclock(cm1->device_fd)) {
 		info->dclk_change_clock_func = cairnsmore_change_clock_func;
@@ -143,14 +150,19 @@ static bool cairnsmore_init(struct thr_info *thr)
 		       cm1->api->name, cm1->device_id,
 		       CAIRNSMORE1_DEFAULT_CLOCK, CAIRNSMORE1_MINIMUM_CLOCK, CAIRNSMORE1_MAXIMUM_CLOCK
 		);
+		// The dynamic-clocking firmware connects each FPGA as its own device
+		if (!(info->user_set & 1)) {
+			info->work_division = 1;
+			if (!(info->user_set & 2))
+				info->fpga_count = 1;
+		}
 	} 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;
 	}
+	// Commands corrupt the hash state, so next scanhash is a firstrun
+	state->firstrun = true;
 
 	return true;
 }

+ 16 - 5
driver-icarus.c

@@ -468,6 +468,7 @@ static void get_options(int this_option_offset, struct ICARUS_INFO *info)
 				*(colon2++) = '\0';
 
 			if (*colon) {
+				info->user_set |= 1;
 				tmp = atoi(colon);
 				if (tmp == 1 || tmp == 2 || tmp == 4 || tmp == 8) {
 					*work_division = tmp;
@@ -484,6 +485,7 @@ static void get_options(int this_option_offset, struct ICARUS_INFO *info)
 					*(colon++) = '\0';
 
 			  if (*colon2) {
+				info->user_set |= 2;
 				tmp = atoi(colon2);
 				if (tmp > 0 && tmp <= *work_division)
 					*fpga_count = tmp;
@@ -726,6 +728,13 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	// Prepare the next work immediately
 	memcpy(ob_bin, work->midstate, 32);
 	memcpy(ob_bin + 52, work->data + 64, 12);
+	if (!(memcmp(&ob_bin[56], "\xff\xff\xff\xff", 4)
+	   || memcmp(&ob_bin, "\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", 32))) {
+		// This sequence is used on cairnsmore bitstreams for commands, NEVER send it otherwise
+		applog(LOG_WARNING, "%s %u: Received job attempting to send a command, corrupting it!",
+		       icarus->api->name, icarus->device_id);
+		ob_bin[56] = 0;
+	}
 	rev(ob_bin, 32);
 	rev(ob_bin + 52, 12);
 
@@ -774,11 +783,13 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	nonce = be32toh(nonce);
 
 	// Handle dynamic clocking for "subclass" devices
-	// This runs before sending next job, in case it isn't supported
+	// This needs to run before sending next job, since it hashes the command too
 	if (info->dclk.freqM && likely(!state->firstrun)) {
-		dclk_gotNonces(&info->dclk);
+		int qsec = ((4 * elapsed.tv_sec) + (elapsed.tv_usec / 250000)) ?: 1;
+		for (int n = qsec; n; --n)
+			dclk_gotNonces(&info->dclk);
 		if (nonce && !test_nonce(&state->last_work, nonce, false))
-			dclk_errorCount(&info->dclk, 1.0);
+			dclk_errorCount(&info->dclk, qsec);
 		dclk_preUpdate(&info->dclk);
 		dclk_updateFreq(&info->dclk, info->dclk_change_clock_func, thr);
 	}
@@ -970,8 +981,8 @@ 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, "%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",
+//			applog(LOG_DEBUG, "%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_DEBUG, "%s %u Re-estimate: Hs=%e W=%e read_count=%d fullnonce=%.3fs",
 					icarus->api->name,
 					icarus->device_id, Hs, W, read_count, fullnonce);
 		}

+ 5 - 0
dynclock.h

@@ -25,10 +25,15 @@ typedef bool (*dclk_change_clock_func_t)(struct thr_info *, int multiplier);
 
 extern void dclk_msg_freqchange(const char *, int oldFreq, int newFreq, const char *tail);
 
+// Called to initialize dclk_data at startup
 extern void dclk_prepare(struct dclk_data *data);
+// Called for every quarter of a second to age error rate info
 extern void dclk_gotNonces(struct dclk_data *);
+// Called for errors (1.0 "portion" is a quarter second)
 extern void dclk_errorCount(struct dclk_data *, double portion);
+// Called after a nonce range is completed to update actual error rate
 extern void dclk_preUpdate(struct dclk_data *data);
+// Called after a nonce range is completed, and error rate updated, to make actual clock adjustments
 extern bool dclk_updateFreq(struct dclk_data *, dclk_change_clock_func_t changeclock, struct thr_info *);
 
 #endif

+ 1 - 0
icarus-common.h

@@ -70,6 +70,7 @@ struct ICARUS_INFO {
 	int fpga_count;
 	uint32_t nonce_mask;
 	bool quirk_reopen;
+	uint8_t user_set;
 
 	dclk_change_clock_func_t dclk_change_clock_func;
 	struct dclk_data dclk;