Commit 47a42f97b0e5fbeee63e86a6b2289feb224b06b7

Angus Gratton 2014-03-06T10:36:39

Drillbit: --drillbit-auto parameter for tweakable custom tuning of ASIC speeds

diff --git a/ASIC-README b/ASIC-README
index ed2edff..dafbfb1 100644
--- a/ASIC-README
+++ b/ASIC-README
@@ -466,12 +466,25 @@ Drillbit Systems Devices
 * voltage is ASIC core voltage in millivolts, available values vary per board but
   default is 850 and the recommended maximum is 950 (Bitfury) and 1000 (Avalon.)
 
---drillbit-autotune
+--drillbit-auto <every>:[<gooderr>:<baderr>:<maxerr>]
 
 If supported by firmware and device, this feature allows cgminer to
 automatically tweak each ASIC's clock rate up and down in to achieve
 optimal performance.
 
+* every - only required param, check each ASIC after each block of
+  this many work units. Recommended value 100.
+* gooderr - the "Good" threshold is when less hardware errors than
+  this per "every" work units, the clock rate will be increased.
+  Default value 1.
+* baderr - the "Bad" threshold is when more hardware errors than
+  this per "every" work units, the clock rate will be decreased.
+  Default value 3.
+* maxerr - the "Max" threshold is when more hardware errors than
+  this per "every" work units (including pre-empting before
+  "every" work units is up), the clock rate will be decreased and
+  will not be increased again past this point. Default value 10.
+
 
 BlackArrow Bitfury devices
 
diff --git a/cgminer.c b/cgminer.c
index 39b9d99..d3fce65 100644
--- a/cgminer.c
+++ b/cgminer.c
@@ -209,7 +209,7 @@ char *opt_klondike_options = NULL;
 #endif
 #ifdef USE_DRILLBIT
 char *opt_drillbit_options = NULL;
-bool opt_drillbit_autotune = false;
+char *opt_drillbit_auto = NULL;
 #endif
 char *opt_bab_options = NULL;
 #ifdef USE_BITMINE_A1
@@ -1140,6 +1140,14 @@ static char *set_drillbit_options(const char *arg)
 
 	return NULL;
 }
+
+static char *set_drillbit_auto(const char *arg)
+{
+	opt_set_charp(arg, &opt_drillbit_auto);
+
+	return NULL;
+}
+
 #endif
 
 #ifdef USE_BAB
@@ -1341,11 +1349,11 @@ static struct opt_table opt_config_table[] = {
 			"Automatically disable pools that continually reject shares"),
 #ifdef USE_DRILLBIT
         OPT_WITH_ARG("--drillbit-options",
-                     set_drillbit_options, NULL, NULL,
-                     "Set drillbit options <int|ext>:clock[:clock_divider][:voltage]"),
-        OPT_WITHOUT_ARG("--drillbit-autotune",
-                        opt_set_bool, &opt_drillbit_autotune,
-                        "Enable drillbit automatic clock speed tuning"),
+		set_drillbit_options, NULL, NULL,
+		"Set drillbit options <int|ext>:clock[:clock_divider][:voltage]"),
+        OPT_WITH_ARG("--drillbit-auto",
+		set_drillbit_auto, NULL, NULL,
+		"Enable drillbit automatic tuning <every>:[<gooderr>:<baderr>:<maxerr>]"),
 #endif
 	OPT_WITH_ARG("--expiry|-E",
 		     set_int_0_to_9999, opt_show_intval, &opt_expiry,
@@ -4783,6 +4791,8 @@ void write_config(FILE *fcfg)
 #ifdef USE_DRILLBIT
         if (opt_drillbit_options)
                 fprintf(fcfg, ",\n\"drillbit-options\" : \"%s\"", json_escape(opt_drillbit_options));
+	if (opt_drillbit_auto)
+                fprintf(fcfg, ",\n\"drillbit-auto\" : \"%s\"", json_escape(opt_drillbit_auto));
 #endif
 	if (opt_bab_options)
 		fprintf(fcfg, ",\n\"bab-options\" : \"%s\"", json_escape(opt_bab_options));
diff --git a/driver-drillbit.c b/driver-drillbit.c
index b2037d7..8f42c53 100644
--- a/driver-drillbit.c
+++ b/driver-drillbit.c
@@ -120,6 +120,12 @@ static config_setting *settings;
 
 static void drillbit_empty_buffer(struct cgpu_info *drillbit);
 
+/* Automatic tuning parameters */
+static uint32_t auto_every = 100;
+static uint32_t auto_good = 1;
+static uint32_t auto_bad = 3;
+static uint32_t auto_max = 10;
+
 /* Return a pointer to the chip_info structure for a given chip id, or NULL otherwise */
 static struct drillbit_chip_info *find_chip(struct drillbit_info *info, uint16_t chip_id) {
 	int i;
@@ -529,6 +535,18 @@ static bool drillbit_parse_options(struct cgpu_info *drillbit)
 		if (next_opt)
 			next_opt++;
 	}
+
+	if(opt_drillbit_auto) {
+		sscanf(opt_drillbit_auto, "%d:%d:%d:%d",
+			&auto_every, &auto_good, &auto_bad, &auto_max);
+		if(auto_max < auto_bad) {
+			quithere(1, "Bad drillbit-auto: MAX limit must be greater than BAD limit");
+		}
+		if(auto_bad < auto_good) {
+			quithere(1, "Bad drillbit-auto: GOOD limit must be greater than BAD limit");
+		}
+	}
+
 	return true;
 }
 
@@ -565,6 +583,7 @@ static struct cgpu_info *drillbit_detect_one(struct libusb_device *dev, struct u
 	info->chips = calloc(sizeof(struct drillbit_chip_info), info->num_chips);
 	for (i = 0; i < info->num_chips; i++) {
 		info->chips[i].chip_id = i;
+		info->chips[i].auto_max = 999;
 	}
 
 	/* Send reset request */
@@ -654,37 +673,51 @@ static bool drillbit_checkresults(struct thr_info *thr, struct work *work, uint3
 }
 
 /* Check if this ASIC should be tweaked up or down in clock speed */
-static void drillbit_check_autotune(struct thr_info *thr, struct drillbit_chip_info *chip)
+static void drillbit_check_auto(struct thr_info *thr, struct drillbit_chip_info *chip)
 {
 	struct cgpu_info *drillbit = thr->cgpu;
 	AutoTuneRequest request;
 	char buf[SZ_SERIALISED_AUTOTUNEREQUEST+1];
 	int amount;
 	float ratio;
+	bool tune_up, tune_down;
 
-	/* autotune tries to keep ratio of HW errors within these margins:
-	   every RETUNE_EVERY results (errors and successes totalled, error count should be between
-	   the low and high limits given.
+	/*
+	  Only check automatic tuning every "auto_every" work units,
+	  or if the error count exceeds the 'max' count
 	*/
-	const uint32_t TUNE_UP_EVERY = 100;
-	const uint32_t ERROR_LOW = 1;
-	const uint32_t ERROR_HIGH = 3;
-
-	/* Check auto parameters */
-	if(chip->success_auto + chip->error_auto < TUNE_UP_EVERY && chip->error_auto < ERROR_HIGH*2)
+	if(chip->success_auto + chip->error_auto < auto_every &&
+		(chip->error_auto < auto_max))
 		return;
 
-	ratio = 100 * (float)chip->error_auto / (chip->success_auto + chip->error_auto);
-	applog(LOG_DEBUG, "Chip id %d has error ratio %3.1f%%", chip->chip_id, ratio);
+	tune_up = chip->error_auto < auto_good && chip->auto_delta < chip->auto_max;
+	tune_down = chip->error_auto > auto_bad;
+
+
+	drvlog(tune_up||tune_down ? LOG_NOTICE : LOG_DEBUG,
+		"Chip id %d has %d/%d error rate %s", chip->chip_id, chip->error_auto,
+		chip->error_auto + chip->success_auto,
+		tune_up ? " - tuning up" : tune_down ? " - tuning down" : " - no change");
 
-	if(chip->error_auto < ERROR_LOW || chip->error_auto > ERROR_HIGH) {
+	if(tune_up || tune_down) {
 		/* Value should be tweaked */
 		buf[0] = 'A';
 		request.chip_id = chip->chip_id;
-		request.increase_clock = chip->error_auto < ERROR_LOW;
+		request.increase_clock = tune_up;
 		serialise_autotune_request(&buf[1], &request);
 		usb_write_timeout(drillbit, buf, sizeof(buf), &amount, TIMEOUT, C_BF_AUTOTUNE);
 		usb_read_simple_response(drillbit, 'A', C_BF_AUTOTUNE);
+		if(tune_up) {
+			chip->auto_delta++;
+		} else {
+			chip->auto_delta--;
+			if(chip->error_auto >= auto_max
+				&& chip->success_count + chip->error_count > auto_every) {
+				drvlog(LOG_ERR, "Chip id %d capping auto delta at max %d",chip->chip_id,
+					chip->auto_delta);
+				chip->auto_max = chip->auto_delta;
+			}
+		}
 	}
 
 	chip->success_auto = 0;
@@ -799,8 +832,8 @@ static int check_for_results(struct thr_info *thr)
 		else
 			chip->state = IDLE; // Uh-oh, we're totally out of work for this ASIC!
 
-		if(opt_drillbit_autotune && info->protocol_version >= 4)
-			drillbit_check_autotune(thr, chip);
+		if(opt_drillbit_auto && info->protocol_version >= 4)
+			drillbit_check_auto(thr, chip);
 	}
 
 cleanup:
diff --git a/driver-drillbit.h b/driver-drillbit.h
index 373a3c7..b386107 100644
--- a/driver-drillbit.h
+++ b/driver-drillbit.h
@@ -49,6 +49,8 @@ struct drillbit_chip_info {
   uint32_t work_sent_count;
   uint32_t success_auto;
   uint32_t error_auto;
+  int auto_delta;
+  int auto_max;
 };
 
 #endif /* BITFURY_H */
diff --git a/miner.h b/miner.h
index 40d88ea..4f74f67 100644
--- a/miner.h
+++ b/miner.h
@@ -982,7 +982,7 @@ extern char *opt_klondike_options;
 #endif
 #ifdef USE_DRILLBIT
 extern char *opt_drillbit_options;
-extern bool opt_drillbit_autotune;
+extern char *opt_drillbit_auto;
 #endif
 #ifdef USE_BAB
 extern char *opt_bab_options;