Browse Source

Merge branch 'dev_dynclock' into bfgminer

Conflicts:
	driver-modminer.c
	libztex.c
Luke Dashjr 13 years ago
parent
commit
422b00db38
8 changed files with 231 additions and 148 deletions
  1. 4 0
      Makefile.am
  2. 1 0
      configure.ac
  3. 57 69
      driver-modminer.c
  4. 23 48
      driver-ztex.c
  5. 88 0
      dynclock.c
  6. 34 0
      dynclock.h
  7. 21 20
      libztex.c
  8. 3 11
      libztex.h

+ 4 - 0
Makefile.am

@@ -78,6 +78,10 @@ if NEED_FPGAUTILS
 bfgminer_SOURCES += fpgautils.c fpgautils.h
 bfgminer_SOURCES += fpgautils.c fpgautils.h
 endif
 endif
 
 
+if NEED_DYNCLOCK
+bfgminer_SOURCES += dynclock.c dynclock.h
+endif
+
 if HAS_BITFORCE
 if HAS_BITFORCE
 bfgminer_SOURCES += driver-bitforce.c
 bfgminer_SOURCES += driver-bitforce.c
 
 

+ 1 - 0
configure.ac

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

+ 57 - 69
driver-modminer.c

@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <unistd.h>
 
 
+#include "dynclock.h"
 #include "logging.h"
 #include "logging.h"
 #include "miner.h"
 #include "miner.h"
 #include "fpgautils.h"
 #include "fpgautils.h"
@@ -34,20 +35,11 @@ struct modminer_fpga_state {
 
 
 	char next_work_cmd[46];
 	char next_work_cmd[46];
 
 
-	unsigned char clock;
-	unsigned char max_clock;
-	// Number of iterations since we last got a nonce
-	int no_nonce_counter;
+	struct dclk_data dclk;
 	// Number of nonces didn't meet pdiff 1, ever
 	// Number of nonces didn't meet pdiff 1, ever
 	int bad_share_counter;
 	int bad_share_counter;
 	// Number of nonces did meet pdiff 1, ever
 	// Number of nonces did meet pdiff 1, ever
 	int good_share_counter;
 	int good_share_counter;
-	// Number of nonces didn't meet pdiff 1, since last clock change
-	int bad_nonce_counter;
-	// Number of nonces total, since last clock change
-	int nonce_counter;
-	// Number of good nonces, since last clock change OR bad nonce
-	int good_nonce_counter;
 	// Time the clock was last reduced due to temperature
 	// Time the clock was last reduced due to temperature
 	time_t last_cutoff_reduced;
 	time_t last_cutoff_reduced;
 
 
@@ -308,6 +300,7 @@ modminer_fpga_prepare(struct thr_info *thr)
 
 
 	struct modminer_fpga_state *state;
 	struct modminer_fpga_state *state;
 	state = thr->cgpu_data = calloc(1, sizeof(struct modminer_fpga_state));
 	state = thr->cgpu_data = calloc(1, sizeof(struct modminer_fpga_state));
+	dclk_prepare(&state->dclk);
 	state->next_work_cmd[0] = '\x08';  // Send Job
 	state->next_work_cmd[0] = '\x08';  // Send Job
 	state->next_work_cmd[1] = thr->device_thread;  // FPGA id
 	state->next_work_cmd[1] = thr->device_thread;  // FPGA id
 
 
@@ -322,53 +315,56 @@ modminer_change_clock(struct thr_info*thr, bool needlock, signed char delta)
 	char fpgaid = thr->device_thread;
 	char fpgaid = thr->device_thread;
 	int fd;
 	int fd;
 	unsigned char cmd[6], buf[1];
 	unsigned char cmd[6], buf[1];
+	unsigned char clk;
 
 
-	cmd[0] = '\x06';  // set clock speed
+	clk = (state->dclk.freqM * 2) + delta;
+
+	cmd[0] = '\x06';  // set frequency
 	cmd[1] = fpgaid;
 	cmd[1] = fpgaid;
-	cmd[2] = state->clock += delta;
+	cmd[2] = clk;
 	cmd[3] = cmd[4] = cmd[5] = '\0';
 	cmd[3] = cmd[4] = cmd[5] = '\0';
 
 
 	if (needlock)
 	if (needlock)
 		mutex_lock(&modminer->device_mutex);
 		mutex_lock(&modminer->device_mutex);
 	fd = modminer->device_fd;
 	fd = modminer->device_fd;
 	if (6 != write(fd, cmd, 6))
 	if (6 != write(fd, cmd, 6))
-		bailout2(LOG_ERR, "%s %u.%u: Error writing (set clock speed)", modminer->api->name, modminer->device_id, fpgaid);
+		bailout2(LOG_ERR, "%s %u.%u: Error writing (set frequency)", modminer->api->name, modminer->device_id, fpgaid);
 	if (serial_read(fd, &buf, 1) != 1)
 	if (serial_read(fd, &buf, 1) != 1)
-		bailout2(LOG_ERR, "%s %u.%u: Error reading (set clock speed)", modminer->api->name, modminer->device_id, fpgaid);
+		bailout2(LOG_ERR, "%s %u.%u: Error reading (set frequency)", modminer->api->name, modminer->device_id, fpgaid);
 	if (needlock)
 	if (needlock)
 		mutex_unlock(&modminer->device_mutex);
 		mutex_unlock(&modminer->device_mutex);
 
 
-	if (!buf[0]) {
-		state->clock -= delta;
-		return false;
-	}
-
-	state->bad_nonce_counter = state->nonce_counter = 0;
-	state->good_nonce_counter = 0;
+	if (buf[0])
+		state->dclk.freqM = clk / 2;
 
 
 	return true;
 	return true;
 }
 }
 
 
-static bool
-modminer_reduce_clock(struct thr_info*thr, bool needlock)
+static bool modminer_dclk_change_clock(struct thr_info*thr, int multiplier)
 {
 {
+	struct cgpu_info *modminer = thr->cgpu;
+	char fpgaid = thr->device_thread;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	struct modminer_fpga_state *state = thr->cgpu_data;
-
-	if (state->clock <= MODMINER_MINIMUM_CLOCK)
+	uint8_t oldFreq = state->dclk.freqM;
+	signed char delta = (multiplier - oldFreq) * 2;
+	if (unlikely(!modminer_change_clock(thr, true, delta)))
 		return false;
 		return false;
 
 
-	return modminer_change_clock(thr, needlock, -2);
+	char repr[0x10];
+	sprintf(repr, "%s %u.%u", modminer->api->name, modminer->device_id, fpgaid);
+	dclk_msg_freqchange(repr, oldFreq * 2, state->dclk.freqM * 2, NULL);
+	return true;
 }
 }
 
 
 static bool
 static bool
-modminer_increase_clock(struct thr_info*thr, bool needlock)
+modminer_reduce_clock(struct thr_info*thr, bool needlock)
 {
 {
 	struct modminer_fpga_state *state = thr->cgpu_data;
 	struct modminer_fpga_state *state = thr->cgpu_data;
 
 
-	if (state->clock >= state->max_clock)
+	if (state->dclk.freqM <= MODMINER_MINIMUM_CLOCK / 2)
 		return false;
 		return false;
 
 
-	return modminer_change_clock(thr, needlock, 2);
+	return modminer_change_clock(thr, needlock, -2);
 }
 }
 
 
 static bool _modminer_get_nonce(struct cgpu_info*modminer, char fpgaid, uint32_t*nonce)
 static bool _modminer_get_nonce(struct cgpu_info*modminer, char fpgaid, uint32_t*nonce)
@@ -423,27 +419,28 @@ modminer_fpga_init(struct thr_info *thr)
 		applog(LOG_DEBUG, "%s %u.%u: FPGA is already programmed :)", modminer->api->name, modminer->device_id, fpgaid);
 		applog(LOG_DEBUG, "%s %u.%u: FPGA is already programmed :)", modminer->api->name, modminer->device_id, fpgaid);
 	state->pdone = 101;
 	state->pdone = 101;
 
 
-	state->clock = MODMINER_MAXIMUM_CLOCK + 2;  // Will be reduced by 2 immediately
+	state->dclk.freqM = MODMINER_MAXIMUM_CLOCK / 2 + 1;  // Will be reduced immediately
 	while (1) {
 	while (1) {
-		if (state->clock <= MODMINER_MINIMUM_CLOCK)
-			bailout2(LOG_ERR, "%s %u.%u: Hit minimum trying to find acceptable clock speeds", modminer->api->name, modminer->device_id, fpgaid);
-		state->clock -= 2;
+		if (state->dclk.freqM <= MODMINER_MINIMUM_CLOCK / 2)
+			bailout2(LOG_ERR, "%s %u.%u: Hit minimum trying to find acceptable frequencies", modminer->api->name, modminer->device_id, fpgaid);
+		--state->dclk.freqM;
 		if (!modminer_change_clock(thr, false, 0))
 		if (!modminer_change_clock(thr, false, 0))
 			// MCU rejected assignment
 			// MCU rejected assignment
 			continue;
 			continue;
 		if (!_modminer_get_nonce(modminer, fpgaid, &nonce))
 		if (!_modminer_get_nonce(modminer, fpgaid, &nonce))
-			bailout2(LOG_ERR, "%s %u.%u: Error detecting acceptable clock speeds", modminer->api->name, modminer->device_id, fpgaid);
+			bailout2(LOG_ERR, "%s %u.%u: Error detecting acceptable frequencies", modminer->api->name, modminer->device_id, fpgaid);
 		if (!memcmp(&nonce, "\x00\xff\xff\xff", 4))
 		if (!memcmp(&nonce, "\x00\xff\xff\xff", 4))
 			// MCU took assignment, but disabled FPGA
 			// MCU took assignment, but disabled FPGA
 			continue;
 			continue;
 		break;
 		break;
 	}
 	}
-	state->max_clock = state->clock;
-	if (MODMINER_DEFAULT_CLOCK < state->clock) {
-		if (!modminer_change_clock(thr, false, -(state->clock - MODMINER_DEFAULT_CLOCK)))
-			applog(LOG_WARNING, "%s %u.%u: Failed to set desired initial clock speed of %u", modminer->api->name, modminer->device_id, fpgaid, MODMINER_DEFAULT_CLOCK);
+	state->dclk.freqMaxM = state->dclk.freqM;
+	if (MODMINER_DEFAULT_CLOCK / 2 < state->dclk.freqM) {
+		if (!modminer_change_clock(thr, false, -(state->dclk.freqM * 2 - MODMINER_DEFAULT_CLOCK)))
+			applog(LOG_WARNING, "%s %u.%u: Failed to set desired initial frequency of %u", modminer->api->name, modminer->device_id, fpgaid, MODMINER_DEFAULT_CLOCK);
 	}
 	}
-	applog(LOG_WARNING, "%s %u.%u: Setting clock speed to %u (range: %u-%u)", modminer->api->name, modminer->device_id, fpgaid, state->clock, MODMINER_MINIMUM_CLOCK, state->max_clock);
+	state->dclk.freqMDefault = state->dclk.freqM;
+	applog(LOG_WARNING, "%s %u.%u: Frequency set to %u Mhz (range: %u-%u)", modminer->api->name, modminer->device_id, fpgaid, state->dclk.freqM * 2, MODMINER_MINIMUM_CLOCK, state->dclk.freqMaxM * 2);
 
 
 	mutex_unlock(&modminer->device_mutex);
 	mutex_unlock(&modminer->device_mutex);
 
 
@@ -507,14 +504,10 @@ get_modminer_api_extra_device_status(struct cgpu_info*modminer)
 
 
 		if (state->temp)
 		if (state->temp)
 			json_object_set(o, "Temperature", json_integer(state->temp));
 			json_object_set(o, "Temperature", json_integer(state->temp));
-		json_object_set(o, "Frequency", json_real((double)state->clock * 1000000.));
-		json_object_set(o, "Max Frequency", json_real((double)state->max_clock * 1000000.));
-		json_object_set(o, "_No Nonce Counter", json_integer(state->no_nonce_counter));
+		json_object_set(o, "Frequency", json_real((double)state->dclk.freqM * 2 * 1000000.));
+		json_object_set(o, "Max Frequency", json_real((double)state->dclk.freqMaxM * 2 * 1000000.));
 		json_object_set(o, "Hardware Errors", json_integer(state->bad_share_counter));
 		json_object_set(o, "Hardware Errors", json_integer(state->bad_share_counter));
 		json_object_set(o, "Valid Nonces", json_integer(state->good_share_counter));
 		json_object_set(o, "Valid Nonces", json_integer(state->good_share_counter));
-		json_object_set(o, "_Bad Nonce Counter", json_integer(state->bad_nonce_counter));
-		json_object_set(o, "_Nonce Counter", json_integer(state->nonce_counter));
-		json_object_set(o, "_Good Nonce Counter", json_integer(state->good_nonce_counter));
 
 
 		root = api_add_json(root, k[i], o, false);
 		root = api_add_json(root, k[i], o, false);
 		json_decref(o);
 		json_decref(o);
@@ -584,6 +577,7 @@ modminer_process_results(struct thr_info*thr)
 	char cmd[2], temperature;
 	char cmd[2], temperature;
 	uint32_t nonce;
 	uint32_t nonce;
 	long iter;
 	long iter;
+	int immediate_bad_nonces = 0, immediate_nonces = 0;
 	bool bad;
 	bool bad;
 	cmd[0] = '\x0a';
 	cmd[0] = '\x0a';
 	cmd[1] = fpgaid;
 	cmd[1] = fpgaid;
@@ -612,8 +606,14 @@ modminer_process_results(struct thr_info*thr)
 				time_t now = time(NULL);
 				time_t now = time(NULL);
 				if (state->last_cutoff_reduced != now) {
 				if (state->last_cutoff_reduced != now) {
 					state->last_cutoff_reduced = now;
 					state->last_cutoff_reduced = now;
+					int oldFreq = state->dclk.freqM;
 					if (modminer_reduce_clock(thr, false))
 					if (modminer_reduce_clock(thr, false))
-						applog(LOG_WARNING, "%s %u.%u: Drop clock speed to %u (temp: %d)", modminer->api->name, modminer->device_id, fpgaid, state->clock, temperature);
+						applog(LOG_NOTICE, "%s %u.%u: Frequency %s from %u to %u Mhz (temp: %d)",
+						       modminer->api->name, modminer->device_id, fpgaid,
+						       (oldFreq > state->dclk.freqM ? "dropped" : "raised "),
+						       oldFreq * 2, state->dclk.freqM * 2,
+						       temperature
+						);
 				}
 				}
 			}
 			}
 		}
 		}
@@ -625,9 +625,8 @@ modminer_process_results(struct thr_info*thr)
 			safebailout();
 			safebailout();
 		mutex_unlock(&modminer->device_mutex);
 		mutex_unlock(&modminer->device_mutex);
 		if (memcmp(&nonce, "\xff\xff\xff\xff", 4)) {
 		if (memcmp(&nonce, "\xff\xff\xff\xff", 4)) {
-			state->no_nonce_counter = 0;
-			++state->nonce_counter;
 			bad = !test_nonce(work, nonce, false);
 			bad = !test_nonce(work, nonce, false);
+			++immediate_nonces;
 			if (!bad)
 			if (!bad)
 				applog(LOG_DEBUG, "%s %u.%u: Nonce for current  work: %02x%02x%02x%02x",
 				applog(LOG_DEBUG, "%s %u.%u: Nonce for current  work: %02x%02x%02x%02x",
 				       modminer->api->name, modminer->device_id, fpgaid,
 				       modminer->api->name, modminer->device_id, fpgaid,
@@ -645,11 +644,6 @@ modminer_process_results(struct thr_info*thr)
 			{
 			{
 				++state->good_share_counter;
 				++state->good_share_counter;
 				submit_nonce(thr, work, nonce);
 				submit_nonce(thr, work, nonce);
-				++state->good_nonce_counter;
-				if (state->good_nonce_counter >= 0x10 && ((!state->temp) || state->temp < modminer->cutofftemp - 2)) {
-					if (modminer_increase_clock(thr, true))
-						applog(LOG_NOTICE, "%s %u.%u: Raise clock speed to %u", modminer->api->name, modminer->device_id, fpgaid, state->clock);
-				}
 			}
 			}
 			else {
 			else {
 				applog(LOG_DEBUG, "%s %u.%u: Nonce with H not zero  : %02x%02x%02x%02x",
 				applog(LOG_DEBUG, "%s %u.%u: Nonce with H not zero  : %02x%02x%02x%02x",
@@ -658,23 +652,9 @@ modminer_process_results(struct thr_info*thr)
 				++hw_errors;
 				++hw_errors;
 				++modminer->hw_errors;
 				++modminer->hw_errors;
 				++state->bad_share_counter;
 				++state->bad_share_counter;
-				++state->bad_nonce_counter;
-				state->good_nonce_counter = 0;
-				if (state->bad_nonce_counter * 50 > 500 + state->nonce_counter)
-				{
-					// Only reduce clocks if hardware errors are more than ~2% of results
-					int pchwe = state->bad_nonce_counter * 100 / state->nonce_counter;
-					if (modminer_reduce_clock(thr, true))
-						applog(LOG_WARNING, "%s %u.%u: Drop clock speed to %u (%d%% hw err)", modminer->api->name, modminer->device_id, fpgaid, state->clock, pchwe);
-				}
+				++immediate_bad_nonces;
 			}
 			}
 		}
 		}
-		else
-		if (++state->no_nonce_counter > 0x20000) {
-			state->no_nonce_counter = 0;
-			if (modminer_reduce_clock(thr, true))
-				applog(LOG_WARNING, "%s %u.%u: Drop clock speed to %u (no nonces)", modminer->api->name, modminer->device_id, fpgaid, state->clock);
-		}
 		if (work_restart(thr) || !--iter)
 		if (work_restart(thr) || !--iter)
 			break;
 			break;
 		usleep(1000);
 		usleep(1000);
@@ -688,7 +668,7 @@ modminer_process_results(struct thr_info*thr)
 	gettimeofday(&tv_workend, NULL);
 	gettimeofday(&tv_workend, NULL);
 	timersub(&tv_workend, &state->tv_workstart, &elapsed);
 	timersub(&tv_workend, &state->tv_workstart, &elapsed);
 
 
-	uint64_t hashes = (uint64_t)state->clock * (((uint64_t)elapsed.tv_sec * 1000000) + elapsed.tv_usec);
+	uint64_t hashes = (uint64_t)state->dclk.freqM * 2 * (((uint64_t)elapsed.tv_sec * 1000000) + elapsed.tv_usec);
 	if (hashes > 0xffffffff)
 	if (hashes > 0xffffffff)
 	{
 	{
 		applog(LOG_WARNING, "%s %u.%u: Finished work before new one sent", modminer->api->name, modminer->device_id, fpgaid);
 		applog(LOG_WARNING, "%s %u.%u: Finished work before new one sent", modminer->api->name, modminer->device_id, fpgaid);
@@ -699,6 +679,14 @@ modminer_process_results(struct thr_info*thr)
 	else
 	else
 		hashes -= state->hashes;
 		hashes -= state->hashes;
 	state->hashes += hashes;
 	state->hashes += hashes;
+
+	dclk_gotNonces(&state->dclk);
+	if (immediate_bad_nonces)
+		dclk_errorCount(&state->dclk, ((double)immediate_bad_nonces) / (double)immediate_nonces);
+	dclk_preUpdate(&state->dclk);
+	if (!dclk_updateFreq(&state->dclk, modminer_dclk_change_clock, thr))
+		{}  // TODO: handle error
+
 	return hashes;
 	return hashes;
 }
 }
 
 

+ 23 - 48
driver-ztex.c

@@ -27,6 +27,7 @@
 #include <unistd.h>
 #include <unistd.h>
 #include <sha2.h>
 #include <sha2.h>
 
 
+#include "dynclock.h"
 #include "fpgautils.h"
 #include "fpgautils.h"
 #include "miner.h"
 #include "miner.h"
 #include "libztex.h"
 #include "libztex.h"
@@ -121,52 +122,29 @@ static void ztex_detect()
 	noserial_detect(&ztex_api, ztex_autodetect);
 	noserial_detect(&ztex_api, ztex_autodetect);
 }
 }
 
 
-static bool ztex_updateFreq(struct libztex_device* ztex)
+static bool ztex_change_clock_func(struct thr_info *thr, int bestM)
 {
 {
-	int i, maxM, bestM;
-	double bestR, r;
-
-	for (i = 0; i < ztex->freqMaxM; i++)
-		if (ztex->maxErrorRate[i + 1] * i < ztex->maxErrorRate[i] * (i + 20))
-			ztex->maxErrorRate[i + 1] = ztex->maxErrorRate[i] * (1.0 + 20.0 / i);
-
-	maxM = 0;
-	while (maxM < ztex->freqMDefault && ztex->maxErrorRate[maxM + 1] < LIBZTEX_MAXMAXERRORRATE)
-		maxM++;
-	while (maxM < ztex->freqMaxM && ztex->errorWeight[maxM] > 150 && ztex->maxErrorRate[maxM + 1] < LIBZTEX_MAXMAXERRORRATE)
-		maxM++;
-
-	bestM = 0;
-	bestR = 0;
-	for (i = 0; i <= maxM; i++) {
-		r = (i + 1 + (i == ztex->freqM? LIBZTEX_ERRORHYSTERESIS: 0)) * (1 - ztex->maxErrorRate[i]);
-		if (r > bestR) {
-			bestM = i;
-			bestR = r;
-		}
-	}
+	struct libztex_device *ztex = thr->cgpu->device_ztex;
 
 
-	if (bestM != ztex->freqM) {
-		ztex_selectFpga(ztex);
-		libztex_setFreq(ztex, bestM);
-		ztex_releaseFpga(ztex);
-	}
+	ztex_selectFpga(ztex);
+	libztex_setFreq(ztex, bestM);
+	ztex_releaseFpga(ztex);
 
 
-	maxM = ztex->freqMDefault;
-	while (maxM < ztex->freqMaxM && ztex->errorWeight[maxM + 1] > 100)
-		maxM++;
-	if ((bestM < (1.0 - LIBZTEX_OVERHEATTHRESHOLD) * maxM) && bestM < maxM - 1) {
+	return true;
+}
+
+static bool ztex_updateFreq(struct thr_info *thr)
+{
+	struct libztex_device *ztex = thr->cgpu->device_ztex;
+	bool rv = dclk_updateFreq(&ztex->dclk, ztex_change_clock_func, thr);
+	if (unlikely(!rv)) {
 		ztex_selectFpga(ztex);
 		ztex_selectFpga(ztex);
 		libztex_resetFpga(ztex);
 		libztex_resetFpga(ztex);
 		ztex_releaseFpga(ztex);
 		ztex_releaseFpga(ztex);
-		applog(LOG_ERR, "%s: frequency drop of %.1f%% detect. This may be caused by overheating. FPGA is shut down to prevent damage.",
-		       ztex->repr, (1.0 - 1.0 * bestM / maxM) * 100);
-		return false;
 	}
 	}
-	return true;
+	return rv;
 }
 }
 
 
-
 static bool ztex_checkNonce(struct libztex_device *ztex,
 static bool ztex_checkNonce(struct libztex_device *ztex,
                             struct work *work,
                             struct work *work,
                             struct libztex_hash_data *hdata)
                             struct libztex_hash_data *hdata)
@@ -191,7 +169,7 @@ static bool ztex_checkNonce(struct libztex_device *ztex,
 	sha2(swap, 80, hash1, false);
 	sha2(swap, 80, hash1, false);
 	sha2(hash1, 32, hash2, false);
 	sha2(hash1, 32, hash2, false);
 	if (htobe32(hash2_32[7]) != ((hdata->hash7 + 0x5be0cd19) & 0xFFFFFFFF)) {
 	if (htobe32(hash2_32[7]) != ((hdata->hash7 + 0x5be0cd19) & 0xFFFFFFFF)) {
-		ztex->errorCount[ztex->freqM] += 1.0 / ztex->numNonces;
+		dclk_errorCount(&ztex->dclk, 1.0 / ztex->numNonces);
 		applog(LOG_DEBUG, "%s: checkNonce failed for %0.8X", ztex->repr, hdata->nonce);
 		applog(LOG_DEBUG, "%s: checkNonce failed for %0.8X", ztex->repr, hdata->nonce);
 		return false;
 		return false;
 	}
 	}
@@ -285,8 +263,7 @@ static int64_t ztex_scanhash(struct thr_info *thr, struct work *work,
 			break;
 			break;
 		}
 		}
 
 
-		ztex->errorCount[ztex->freqM] *= 0.995;
-		ztex->errorWeight[ztex->freqM] = ztex->errorWeight[ztex->freqM] * 0.995 + 1.0;
+		dclk_gotNonces(&ztex->dclk);
  
  
 		for (i = 0; i < ztex->numNonces; i++) {
 		for (i = 0; i < ztex->numNonces; i++) {
 			nonce = hdata[i].nonce;
 			nonce = hdata[i].nonce;
@@ -330,11 +307,9 @@ static int64_t ztex_scanhash(struct thr_info *thr, struct work *work,
 
 
 	}
 	}
 
 
-	ztex->errorRate[ztex->freqM] = ztex->errorCount[ztex->freqM] /	ztex->errorWeight[ztex->freqM] * (ztex->errorWeight[ztex->freqM] < 100? ztex->errorWeight[ztex->freqM] * 0.01: 1.0);
-	if (ztex->errorRate[ztex->freqM] > ztex->maxErrorRate[ztex->freqM])
-		ztex->maxErrorRate[ztex->freqM] = ztex->errorRate[ztex->freqM];
+	dclk_preUpdate(&ztex->dclk);
 
 
-	if (!ztex_updateFreq(ztex)) {
+	if (!ztex_updateFreq(thr)) {
 		// Something really serious happened, so mark this thread as dead!
 		// Something really serious happened, so mark this thread as dead!
 		free(lastnonce);
 		free(lastnonce);
 		free(backlog);
 		free(backlog);
@@ -372,7 +347,7 @@ get_ztex_api_extra_device_status(struct cgpu_info *ztex)
 	struct libztex_device *ztexr = ztex->device_ztex;
 	struct libztex_device *ztexr = ztex->device_ztex;
 
 
 	if (ztexr) {
 	if (ztexr) {
-		double frequency = ztexr->freqM1 * (ztexr->freqM + 1);
+		double frequency = ztexr->freqM1 * (ztexr->dclk.freqM + 1);
 		root = api_add_freq(root, "Frequency", &frequency, true);
 		root = api_add_freq(root, "Frequency", &frequency, true);
 	}
 	}
 
 
@@ -392,9 +367,9 @@ static bool ztex_prepare(struct thr_info *thr)
 	if (libztex_configureFpga(ztex) != 0)
 	if (libztex_configureFpga(ztex) != 0)
 		return false;
 		return false;
 	ztex_releaseFpga(ztex);
 	ztex_releaseFpga(ztex);
-	ztex->freqM = ztex->freqMaxM+1;;
-	//ztex_updateFreq(ztex);
-	libztex_setFreq(ztex, ztex->freqMDefault);
+	ztex->dclk.freqM = ztex->dclk.freqMaxM+1;;
+	//ztex_updateFreq(thr);
+	libztex_setFreq(ztex, ztex->dclk.freqMDefault);
 	applog(LOG_DEBUG, "%s: prepare", ztex->repr);
 	applog(LOG_DEBUG, "%s: prepare", ztex->repr);
 	return true;
 	return true;
 }
 }

+ 88 - 0
dynclock.c

@@ -0,0 +1,88 @@
+/*
+ * Copyright 2012 Luke Dashjr
+ * Copyright 2012 nelisky.btc@gmail.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 3 of the License, or (at your option)
+ * any later version.  See COPYING for more details.
+ */
+
+#include "dynclock.h"
+#include "miner.h"
+
+void dclk_prepare(struct dclk_data *data)
+{
+	memset(data, 0, sizeof(*data));
+}
+
+void dclk_msg_freqchange(const char *repr, int oldFreq, int newFreq, const char *tail)
+{
+	applog(LOG_NOTICE, "%s: Frequency %s from %u to %u Mhz%s",
+	       repr,
+	       (oldFreq > newFreq ? "dropped" : "raised "),
+	       oldFreq, newFreq,
+	       tail ?: ""
+	);
+}
+
+bool dclk_updateFreq(struct dclk_data *data, dclk_change_clock_func_t changeclock, struct thr_info *thr)
+{
+	struct cgpu_info *cgpu = thr->cgpu;
+	int i, maxM, bestM;
+	double bestR, r;
+	bool rv = true;
+
+	for (i = 0; i < data->freqMaxM; i++)
+		if (data->maxErrorRate[i + 1] * i < data->maxErrorRate[i] * (i + 20))
+			data->maxErrorRate[i + 1] = data->maxErrorRate[i] * (1.0 + 20.0 / i);
+
+	maxM = 0;
+	while (maxM < data->freqMDefault && data->maxErrorRate[maxM + 1] < DCLK_MAXMAXERRORRATE)
+		maxM++;
+	while (maxM < data->freqMaxM && data->errorWeight[maxM] > 150 && data->maxErrorRate[maxM + 1] < DCLK_MAXMAXERRORRATE)
+		maxM++;
+
+	bestM = 0;
+	bestR = 0;
+	for (i = 0; i <= maxM; i++) {
+		r = (i + 1 + (i == data->freqM? DCLK_ERRORHYSTERESIS: 0)) * (1 - data->maxErrorRate[i]);
+		if (r > bestR) {
+			bestM = i;
+			bestR = r;
+		}
+	}
+
+	if (bestM != data->freqM) {
+		rv = changeclock(thr, bestM);
+	}
+
+	maxM = data->freqMDefault;
+	while (maxM < data->freqMaxM && data->errorWeight[maxM + 1] > 100)
+		maxM++;
+	if ((bestM < (1.0 - DCLK_OVERHEATTHRESHOLD) * maxM) && bestM < maxM - 1) {
+		applog(LOG_ERR, "%s %u: frequency drop of %.1f%% detect. This may be caused by overheating. FPGA is shut down to prevent damage.",
+		       cgpu->api->name, cgpu->device_id,
+		       (1.0 - 1.0 * bestM / maxM) * 100);
+		return false;
+	}
+	return rv;
+}
+
+void dclk_gotNonces(struct dclk_data *data)
+{
+	data->errorCount[data->freqM] *= 0.995;
+	data->errorWeight[data->freqM] = data->errorWeight[data->freqM] * 0.995 + 1.0;
+}
+
+void dclk_errorCount(struct dclk_data *data, double portion)
+{
+	data->errorCount[data->freqM] += portion;
+}
+
+void dclk_preUpdate(struct dclk_data *data)
+{
+	data->errorRate[data->freqM] = data->errorCount[data->freqM] / data->errorWeight[data->freqM] * (data->errorWeight[data->freqM] < 100 ? data->errorWeight[data->freqM] * 0.01 : 1.0);
+	if (data->errorRate[data->freqM] > data->maxErrorRate[data->freqM])
+		data->maxErrorRate[data->freqM] = data->errorRate[data->freqM];
+}

+ 34 - 0
dynclock.h

@@ -0,0 +1,34 @@
+#ifndef DYNCLOCK_H
+#define DYNCLOCK_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct thr_info;
+
+#define DCLK_MAXMAXERRORRATE 0.05
+#define DCLK_ERRORHYSTERESIS 0.1
+#define DCLK_OVERHEATTHRESHOLD 0.4
+
+struct dclk_data {
+	uint8_t freqM;
+	uint8_t freqMaxM;
+	uint8_t freqMDefault;
+
+	double errorCount[256];
+	double errorWeight[256];
+	double errorRate[256];
+	double maxErrorRate[256];
+};
+
+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);
+
+extern void dclk_prepare(struct dclk_data *data);
+extern void dclk_gotNonces(struct dclk_data *);
+extern void dclk_errorCount(struct dclk_data *, double portion);
+extern void dclk_preUpdate(struct dclk_data *data);
+extern bool dclk_updateFreq(struct dclk_data *, dclk_change_clock_func_t changeclock, struct thr_info *);
+
+#endif

+ 21 - 20
libztex.c

@@ -23,6 +23,7 @@
 #include <stdio.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <unistd.h>
 
 
+#include "dynclock.h"
 #include "miner.h"
 #include "miner.h"
 #include "fpgautils.h"
 #include "fpgautils.h"
 #include "libztex.h"
 #include "libztex.h"
@@ -371,23 +372,29 @@ int libztex_selectFpga(struct libztex_device *ztex) {
 
 
 int libztex_setFreq(struct libztex_device *ztex, uint16_t freq) {
 int libztex_setFreq(struct libztex_device *ztex, uint16_t freq) {
 	int cnt;
 	int cnt;
-	uint16_t oldfreq = ztex->freqM;
+	uint16_t oldfreq = ztex->dclk.freqM;
 
 
-	if (freq > ztex->freqMaxM)
-		freq = ztex->freqMaxM;
+	if (freq > ztex->dclk.freqMaxM)
+		freq = ztex->dclk.freqMaxM;
 
 
 	cnt = libusb_control_transfer(ztex->hndl, 0x40, 0x83, freq, 0, NULL, 0, 500);
 	cnt = libusb_control_transfer(ztex->hndl, 0x40, 0x83, freq, 0, NULL, 0, 500);
 	if (unlikely(cnt < 0)) {
 	if (unlikely(cnt < 0)) {
 		applog(LOG_ERR, "Ztex check device: Failed to set frequency with err %d", cnt);
 		applog(LOG_ERR, "Ztex check device: Failed to set frequency with err %d", cnt);
 		return cnt;
 		return cnt;
 	}
 	}
-	ztex->freqM = freq;
-	if (oldfreq > ztex->freqMaxM) 
-		applog(LOG_WARNING, "%s: Frequency set to %0.2f Mhz",
-		       ztex->repr, ztex->freqM1 * (ztex->freqM + 1));
+	ztex->dclk.freqM = freq;
+	if (oldfreq > ztex->dclk.freqMaxM)
+		applog(LOG_WARNING, "%s: Frequency set to %u Mhz (range: %u-%u)",
+		       ztex->repr,
+		       (unsigned)(ztex->freqM1 * (ztex->dclk.freqM + 1)),
+		       (unsigned)ztex->freqM1,
+		       (unsigned)(ztex->freqM1 * (ztex->dclk.freqMaxM + 1))
+		);
 	else
 	else
-		applog(LOG_WARNING, "%s: Frequency change from %0.2f to %0.2f Mhz",
-		       ztex->repr, ztex->freqM1 * (oldfreq + 1), ztex->freqM1 * (ztex->freqM + 1));
+		dclk_msg_freqchange(ztex->repr,
+		                    ztex->freqM1 * (oldfreq + 1),
+		                    ztex->freqM1 * (ztex->dclk.freqM + 1),
+		                    NULL);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -411,6 +418,7 @@ int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** zt
 	unsigned char buf[64];
 	unsigned char buf[64];
 
 
 	newdev = malloc(sizeof(struct libztex_device));
 	newdev = malloc(sizeof(struct libztex_device));
+	dclk_prepare(&newdev->dclk);
 	newdev->bitFileName = NULL;
 	newdev->bitFileName = NULL;
 	newdev->numberOfFpgas = -1;
 	newdev->numberOfFpgas = -1;
 	newdev->valid = false;
 	newdev->valid = false;
@@ -510,15 +518,15 @@ int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** zt
 	newdev->numNonces = buf[1] + 1;
 	newdev->numNonces = buf[1] + 1;
 	newdev->offsNonces = ((buf[2] & 255) | ((buf[3] & 255) << 8)) - 10000;
 	newdev->offsNonces = ((buf[2] & 255) | ((buf[3] & 255) << 8)) - 10000;
 	newdev->freqM1 = ((buf[4] & 255) | ((buf[5] & 255) << 8) ) * 0.01;
 	newdev->freqM1 = ((buf[4] & 255) | ((buf[5] & 255) << 8) ) * 0.01;
-	newdev->freqMaxM = (buf[7] & 255);
-	newdev->freqM = (buf[6] & 255);
-	newdev->freqMDefault = newdev->freqM;
+	newdev->dclk.freqMaxM = (buf[7] & 255);
+	newdev->dclk.freqM = (buf[6] & 255);
+	newdev->dclk.freqMDefault = newdev->dclk.freqM;
 	newdev->suspendSupported = (buf[0] == 5);
 	newdev->suspendSupported = (buf[0] == 5);
 	newdev->hashesPerClock = buf[0] > 2? (((buf[8] & 255) | ((buf[9] & 255) << 8)) + 1) / 128.0: 1.0;
 	newdev->hashesPerClock = buf[0] > 2? (((buf[8] & 255) | ((buf[9] & 255) << 8)) + 1) / 128.0: 1.0;
 	newdev->extraSolutions = buf[0] > 4? buf[10]: 0;
 	newdev->extraSolutions = buf[0] > 4? buf[10]: 0;
 	
 	
 	applog(LOG_DEBUG, "PID: %d numNonces: %d offsNonces: %d freqM1: %f freqMaxM: %d freqM: %d suspendSupported: %s hashesPerClock: %f extraSolutions: %d",
 	applog(LOG_DEBUG, "PID: %d numNonces: %d offsNonces: %d freqM1: %f freqMaxM: %d freqM: %d suspendSupported: %s hashesPerClock: %f extraSolutions: %d",
-	                 buf[0], newdev->numNonces, newdev->offsNonces, newdev->freqM1, newdev->freqMaxM, newdev->freqM, newdev->suspendSupported ? "T": "F", 
+	                 buf[0], newdev->numNonces, newdev->offsNonces, newdev->freqM1, newdev->dclk.freqMaxM, newdev->dclk.freqM, newdev->suspendSupported ? "T": "F",
 	                 newdev->hashesPerClock, newdev->extraSolutions);
 	                 newdev->hashesPerClock, newdev->extraSolutions);
 
 
 	if (buf[0] < 4) {
 	if (buf[0] < 4) {
@@ -527,13 +535,6 @@ int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** zt
 		applog(LOG_WARNING, "HASHES_PER_CLOCK not defined, assuming %0.2f", newdev->hashesPerClock);
 		applog(LOG_WARNING, "HASHES_PER_CLOCK not defined, assuming %0.2f", newdev->hashesPerClock);
 	}
 	}
 
 
-	for (cnt=0; cnt < 255; cnt++) {
-		newdev->errorCount[cnt] = 0;
-		newdev->errorWeight[cnt] = 0;
-		newdev->errorRate[cnt] = 0;
-		newdev->maxErrorRate[cnt] = 0;
-	}
-
 	newdev->usbbus = libusb_get_bus_number(dev);
 	newdev->usbbus = libusb_get_bus_number(dev);
 	newdev->usbaddress = libusb_get_device_address(dev);
 	newdev->usbaddress = libusb_get_device_address(dev);
 	sprintf(newdev->repr, "ZTEX %s-1", newdev->snString);
 	sprintf(newdev->repr, "ZTEX %s-1", newdev->snString);

+ 3 - 11
libztex.h

@@ -24,16 +24,14 @@
 
 
 #include <libusb-1.0/libusb.h>
 #include <libusb-1.0/libusb.h>
 
 
+#include "dynclock.h"
+
 #define LIBZTEX_MAX_DESCRIPTORS 512
 #define LIBZTEX_MAX_DESCRIPTORS 512
 #define LIBZTEX_SNSTRING_LEN 10
 #define LIBZTEX_SNSTRING_LEN 10
 
 
 #define LIBZTEX_IDVENDOR 0x221A
 #define LIBZTEX_IDVENDOR 0x221A
 #define LIBZTEX_IDPRODUCT 0x0100
 #define LIBZTEX_IDPRODUCT 0x0100
 
 
-#define LIBZTEX_MAXMAXERRORRATE 0.05
-#define LIBZTEX_ERRORHYSTERESIS 0.1
-#define LIBZTEX_OVERHEATTHRESHOLD 0.4
-
 struct libztex_fpgastate {
 struct libztex_fpgastate {
 	bool fpgaConfigured;
 	bool fpgaConfigured;
 	unsigned char fpgaChecksum;
 	unsigned char fpgaChecksum;
@@ -61,18 +59,12 @@ struct libztex_device {
 	uint8_t numNonces;
 	uint8_t numNonces;
 	uint16_t offsNonces;
 	uint16_t offsNonces;
 	double freqM1;	
 	double freqM1;	
-	uint8_t freqM;
-	uint8_t freqMaxM;
-	uint8_t freqMDefault;
 	char* bitFileName;
 	char* bitFileName;
 	bool suspendSupported;
 	bool suspendSupported;
 	double hashesPerClock;
 	double hashesPerClock;
 	uint8_t extraSolutions;
 	uint8_t extraSolutions;
 
 
-	double errorCount[256];
-	double errorWeight[256];
-	double errorRate[256];
-	double maxErrorRate[256];
+	struct dclk_data dclk;
 
 
 	int numberOfFpgas;
 	int numberOfFpgas;
 	int selectedFpga;
 	int selectedFpga;