Commit e811c4dc77215916c8cbf48452202870c9788a30

Con Kolivas 2013-06-26T00:15:04

Add an avalon-auto option which enables dynamic overclocking based on hardware error rate for maximum effective hashrate.

diff --git a/ASIC-README b/ASIC-README
index ff758ab..821c21b 100644
--- a/ASIC-README
+++ b/ASIC-README
@@ -84,15 +84,17 @@ degrees.
 
 Avalon commands:
 
+--avalon-auto       Adjust avalon overclock frequency dynamically for best hashrate
 --avalon-cutoff <arg> Set avalon overheat cut off temperature (default: 60)
 --avalon-options <arg> Set avalon options baud:miners:asic:timeout:freq
 --avalon-temp <arg> Set avalon target temperature (default: 50)
 
-eg:
---avalon-cutoff 65
 
-This will cut off the avalon should it get up to 65 degrees and will then
-re-enable it when it gets to the target temperature as specified by avalon-temp.
+Avalon auto will enable dynamic overclocking gradually increasing and
+decreasing the frequency till the highest hashrate that keeps hardware errors
+around 1% is achieved. This WILL run your avalon beyond its normal specification
+so the usual warnings apply. When avalon-auto is enabled, the avalon-options
+for frequency and timeout are used as the starting point only.
 
 eg:
 --avalon-temp 55
@@ -104,6 +106,12 @@ options" entry in the web interface if you do not have a direct way of setting
 it.
 
 eg:
+--avalon-cutoff 65
+
+This will cut off the avalon should it get up to 65 degrees and will then
+re-enable it when it gets to the target temperature as specified by avalon-temp.
+
+eg:
 --avalon-options 115200:24:10:45:282
 
 The values are baud : miners : asic count : timeout : frequency.
diff --git a/README b/README
index 5b94fe5..4612372 100644
--- a/README
+++ b/README
@@ -233,6 +233,7 @@ See SCRYPT-README for more information regarding litecoin mining.
 ASIC and FPGA mining boards (BFL ASIC, BitForce, Icarus, ModMiner, Ztex)
 only options:
 
+--avalon-auto       Adjust avalon overclock frequency dynamically for best hashrate
 --avalon-cutoff <arg> Set avalon overheat cut off temperature (default: 60)
 --avalon-options <arg> Set avalon options baud:miners:asic:timeout:freq
 --avalon-temp <arg> Set avalon target temperature (default: 50)
diff --git a/cgminer.c b/cgminer.c
index ae12bbb..a514cab 100644
--- a/cgminer.c
+++ b/cgminer.c
@@ -1057,6 +1057,9 @@ static struct opt_table opt_config_table[] = {
 		     opt_hidden),
 #endif
 #ifdef USE_AVALON
+	OPT_WITHOUT_ARG("--avalon-auto",
+			opt_set_bool, &opt_avalon_auto,
+			"Adjust avalon overclock frequency dynamically for best hashrate"),
 	OPT_WITH_ARG("--avalon-cutoff",
 		     set_int_0_to_100, opt_show_intval, &opt_avalon_overheat,
 		     "Set avalon overheat cut off temperature"),
diff --git a/driver-avalon.c b/driver-avalon.c
index b6c79e8..4efc783 100644
--- a/driver-avalon.c
+++ b/driver-avalon.c
@@ -42,6 +42,8 @@
 
 int opt_avalon_temp = AVALON_TEMP_TARGET;
 int opt_avalon_overheat = AVALON_TEMP_OVERHEAT;
+bool opt_avalon_auto;
+
 static int option_offset = -1;
 struct device_drv avalon_drv;
 
@@ -51,6 +53,7 @@ static int avalon_init_task(struct avalon_task *at,
 			    uint8_t miner_num, uint8_t nonce_elf,
 			    uint8_t gate_miner, int frequency)
 {
+	uint16_t *lefreq16;
 	uint8_t *buf;
 	static bool first = true;
 
@@ -98,37 +101,8 @@ static int avalon_init_task(struct avalon_task *at,
 	buf[9] = 0x01;
 	buf[10] = 0x00;
 	buf[11] = 0x00;
-	switch (frequency) {
-		case 256:
-			buf[6] = 0x03;
-			buf[7] = 0x08;
-			break;
-		default:
-		case 270:
-			buf[6] = 0x73;
-			buf[7] = 0x08;
-			break;
-		case 282:
-			buf[6] = 0xd3;
-			buf[7] = 0x08;
-			break;
-		case 300:
-			buf[6] = 0x63;
-			buf[7] = 0x09;
-			break;
-		case 325:
-			buf[6] = 0x28;
-			buf[7] = 0x0a;
-			break;
-		case 350:
-			buf[6] = 0xf0;
-			buf[7] = 0x0a;
-			break;
-		case 375:
-			buf[6] = 0xb8;
-			buf[7] = 0x0b;
-			break;
-	}
+	lefreq16 = (uint16_t *)&buf[6];
+	*lefreq16 = htole16(frequency * 8);
 
 	return 0;
 }
@@ -731,6 +705,11 @@ static void avalon_parse_results(struct cgpu_info *avalon, struct avalon_info *i
 				mutex_lock(&info->lock);
 				if (!info->nonces++)
 					gettemp = true;
+				info->auto_nonces++;
+				mutex_unlock(&info->lock);
+			} else if (opt_avalon_auto) {
+				mutex_lock(&info->lock);
+				info->auto_hw++;
 				mutex_unlock(&info->lock);
 			}
 
@@ -842,6 +821,31 @@ static void avalon_rotate_array(struct cgpu_info *avalon)
 		avalon->work_array = 0;
 }
 
+static void avalon_set_timeout(struct avalon_info *info)
+{
+	info->timeout = AVALON_TIMEOUT_FACTOR / info->frequency;
+}
+
+static void avalon_inc_freq(struct avalon_info *info)
+{
+	info->frequency += 2;
+	if (info->frequency > AVALON_MAX_FREQUENCY)
+		info->frequency = AVALON_MAX_FREQUENCY;
+	avalon_set_timeout(info);
+	applog(LOG_NOTICE, "Avalon increasing frequency to %d, timeout %d",
+	       info->frequency, info->timeout);
+}
+
+static void avalon_dec_freq(struct avalon_info *info)
+{
+	info->frequency -= 1;
+	if (info->frequency < AVALON_MIN_FREQUENCY)
+		info->frequency = AVALON_MIN_FREQUENCY;
+	avalon_set_timeout(info);
+	applog(LOG_NOTICE, "Avalon decreasing frequency to %d, timeout %d",
+	       info->frequency, info->timeout);
+}
+
 static void *avalon_send_tasks(void *userdata)
 {
 	struct cgpu_info *avalon = (struct cgpu_info *)userdata;
@@ -860,6 +864,24 @@ static void *avalon_send_tasks(void *userdata)
 		while (avalon_buffer_full(avalon))
 			cgsem_wait(&info->write_sem);
 
+		if (opt_avalon_auto && info->auto_queued >= AVALON_AUTO_CYCLE) {
+			mutex_lock(&info->lock);
+			if (info->auto_nonces >= (AVALON_AUTO_CYCLE * 19 / 20) &&
+			    info->auto_nonces <= (AVALON_AUTO_CYCLE * 21 / 20)) {
+				int total = info->auto_nonces + info->auto_hw;
+
+				/* Try to keep hw errors ~1-1.5% */
+				if (info->auto_hw * 100 < total)
+					avalon_inc_freq(info);
+				else if (info->auto_hw * 66 > total)
+					avalon_dec_freq(info);
+			}
+			info->auto_queued =
+			info->auto_nonces =
+			info->auto_hw = 0;
+			mutex_unlock(&info->lock);
+		}
+
 		mutex_lock(&info->qlock);
 		start_count = avalon->work_array * avalon_get_work_count;
 		end_count = start_count + avalon_get_work_count;
@@ -877,11 +899,15 @@ static void *avalon_send_tasks(void *userdata)
 						info->timeout, info->asic_count,
 						info->miner_count, 1, 0, info->frequency);
 				avalon_create_task(&at, avalon->works[i]);
+				info->auto_queued++;
 			} else {
 				idled++;
 				avalon_init_task(&at, 0, 0, info->fan_pwm,
 						info->timeout, info->asic_count,
 						info->miner_count, 1, 1, info->frequency);
+				/* Reset the auto_queued count if we end up
+				 * idling any miners. */
+				info->auto_queued = 0;
 			}
 
 			ret = avalon_send_task(&at, avalon);
diff --git a/driver-avalon.h b/driver-avalon.h
index 7a0df4d..0de31c8 100644
--- a/driver-avalon.h
+++ b/driver-avalon.h
@@ -25,15 +25,21 @@
 #define AVALON_FAN_FACTOR 120
 #define AVALON_DEFAULT_FAN_MAX_PWM 0xA0 /* 100% */
 #define AVALON_DEFAULT_FAN_MIN_PWM 0x20 /*  20% */
+
 #define AVALON_TEMP_TARGET 50
 #define AVALON_TEMP_HYSTERESIS 3
 #define AVALON_TEMP_OVERHEAT 60
 
 #define AVALON_DEFAULT_TIMEOUT 0x2D
+#define AVALON_MIN_FREQUENCY 256
+#define AVALON_MAX_FREQUENCY 450
+#define AVALON_TIMEOUT_FACTOR 12000
 #define AVALON_DEFAULT_FREQUENCY 282
 #define AVALON_DEFAULT_MINER_NUM 0x20
 #define AVALON_DEFAULT_ASIC_NUM 0xA
 
+#define AVALON_AUTO_CYCLE 1024
+
 #define AVALON_FTDI_READSIZE 510
 #define AVALON_USB_PACKETSIZE 512
 #define AVALON_READBUF_SIZE 8192
@@ -118,6 +124,10 @@ struct avalon_info {
 	cgsem_t write_sem;
 	int nonces;
 
+	int auto_queued;
+	int auto_nonces;
+	int auto_hw;
+
 	bool idle;
 	bool reset;
 	bool overheat;
@@ -142,6 +152,7 @@ ASSERT1(sizeof(uint32_t) == 4);
 extern struct avalon_info **avalon_info;
 extern int opt_avalon_temp;
 extern int opt_avalon_overheat;
+extern bool opt_avalon_auto;
 
 #endif /* USE_AVALON */
 #endif	/* AVALON_H */