Browse Source

cairnsmore: Implement dynamic clocking support for Glasswalker's bitstream

Luke Dashjr 13 years ago
parent
commit
9c77beb6ad
4 changed files with 86 additions and 2 deletions
  1. 1 1
      configure.ac
  2. 68 0
      driver-cairnsmore.c
  3. 13 1
      driver-icarus.c
  4. 4 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])

+ 68 - 0
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)
@@ -54,6 +59,55 @@ static void cairnsmore_detect()
 	serial_detect_auto_byname(cairnsmore_api.dname, cairnsmore_detect_one, cairnsmore_detect_auto);
 }
 
+static bool cairnsmore_send_cmd(struct cgpu_info *cm1, uint8_t cmd, uint8_t data)
+{
+	int fd = cm1->device_fd;
+	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);
+}
+
+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, 0, bestM)))
+		return false;
+	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;
+
+	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, 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
+	);
+
+	return true;
+}
+
 void convert_icarus_to_cairnsmore(struct cgpu_info *cm1)
 {
 	struct ICARUS_INFO *info = cm1->cgpu_data;
@@ -63,6 +117,18 @@ void convert_icarus_to_cairnsmore(struct cgpu_info *cm1)
 	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;
+
+	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 +140,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;
 }

+ 13 - 1
driver-icarus.c

@@ -54,6 +54,7 @@
   #define HAVE_EPOLL
 #endif
 
+#include "dynclock.h"
 #include "elist.h"
 #include "fpgautils.h"
 #include "icarus-common.h"
@@ -773,6 +774,18 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	tcflush(fd, TCOFLUSH);
 #endif
 
+	memcpy(&nonce, nonce_bin, sizeof(nonce_bin));
+
+	// 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));
@@ -802,7 +815,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) {

+ 4 - 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,6 +70,9 @@ 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;
 };
 
 bool icarus_detect_custom(const char *devpath, struct device_api *, struct ICARUS_INFO *);