Browse Source

change to avalon data format

Xiangfu 13 years ago
parent
commit
771d01933e
3 changed files with 126 additions and 160 deletions
  1. 101 110
      driver-avalon.c
  2. 24 48
      driver-avalon.h
  3. 1 2
      fpgautils.c

+ 101 - 110
driver-avalon.c

@@ -34,6 +34,7 @@
 #include "miner.h"
 #include "fpgautils.h"
 #include "driver-avalon.h"
+#include "hexdump.c"
 
 static struct timeval history_sec = { HISTORY_SEC, 0 };
 
@@ -43,40 +44,11 @@ static const char *MODE_LONG_STR = "long";
 static const char *MODE_VALUE_STR = "value";
 static const char *MODE_UNKNOWN_STR = "unknown";
 
-// One for each possible device
-static struct AVALON_INFO **avalon_info;
-
-// Looking for options in --avalon-timing and --avalon-options:
-//
-// Code increments this each time we start to look at a device
-// However, this means that if other devices are checked by
-// the Avalon code (e.g. BFL) they will count in the option offset
-//
-// This, however, is deterministic so that's OK
-//
-// If we were to increment after successfully finding an Avalon
-// that would be random since an Avalon may fail and thus we'd
-// not be able to predict the option order
-//
-// This also assumes that serial_detect() checks them sequentially
-// and in the order specified on the command line
-//
 static int option_offset = -1;
 
+static struct AVALON_INFO **avalon_info;
 struct device_api avalon_api;
 
-static inline void rev(uint8_t *s, size_t l)
-{
-	size_t i, j;
-	uint8_t t;
-
-	for (i = 0, j = l - 1; i < j; i++, j--) {
-		t = s[i];
-		s[i] = s[j];
-		s[j] = t;
-	}
-}
-
 static int avalon_gets(uint8_t *buf, int fd, struct timeval *tv_finish,
 		       struct thr_info *thr, int read_count)
 {
@@ -85,13 +57,14 @@ static int avalon_gets(uint8_t *buf, int fd, struct timeval *tv_finish,
 	int read_amount = AVALON_READ_SIZE;
 	bool first = true;
 
-	/* FIXME: we should set RTS to 0 and CTS to be 1, before read? */
-	int done = avalon_task_done(fd);
+	int full = avalon_buffer_full(fd);
 	if (opt_debug)
-		applog(LOG_DEBUG, "Avalon: Finished all task? %s", done ? "Yes" : "no");
-	if (done) {
-		/* FIXME: return here. ask new task */
-		;
+		applog(LOG_DEBUG, "Avalon: Buffer full: %s",
+		       full == AVA_BUFFER_FULL? "Yes" : "no");
+	if (full == AVA_BUFFER_EMPTY) {
+		if (opt_debug)
+			applog(LOG_DEBUG, "Avalon: Finished hash!");
+		return AVA_GETS_DONE;
 	}
 
 	/* Read reply 1 byte at a time to get earliest tv_finish */
@@ -100,7 +73,7 @@ static int avalon_gets(uint8_t *buf, int fd, struct timeval *tv_finish,
 		if (ret < 0)
 			return AVA_GETS_ERROR;
 
-		if (first)
+		if (first && tv_finish != NULL)
 			gettimeofday(tv_finish, NULL);
 
 		if (ret >= read_amount)
@@ -154,6 +127,7 @@ static int avalon_decode_nonce(struct work **work, uint32_t *nonce,
 	/* FIXME: should be modify to avalon data format */
 	memcpy((uint8_t *)nonce, nonce_bin, AVALON_READ_SIZE);
 #if !defined (__BIG_ENDIAN__) && !defined(MIPSEB)
+	/* FIXME: there should be a rev() not just 32bit */
 	*nonce = swab32(*nonce);
 #endif
 
@@ -169,50 +143,105 @@ static int avalon_decode_nonce(struct work **work, uint32_t *nonce,
 	return i;
 }
 
-static inline void avalon_create_task(uint8_t *ob_bin, struct work *work)
+static int avalon_init_task(struct avalon_task *at,
+			    uint8_t reset, uint8_t ff, uint8_t fan,
+			    uint8_t timeout, uint8_t chip_num,
+			    uint8_t miner_num)
 {
-	memset(ob_bin, 0, AVALON_WRITE_SIZE);
-	memcpy(ob_bin, work->midstate, 32);
-	memcpy(ob_bin + 52, work->data + 64, 12);
-	rev(ob_bin, 32);
-	rev(ob_bin + 52, 12);
+	if (!at)
+		return -1;
+
+	memset(at, 0, sizeof(struct avalon_task));
+
+	at->reset = reset ? 1 : 0;
+	at->flush_fifo = ff ? 1: 0;
+
+	at->fan_eft = fan ? 1 : 0;	/* 1: fan_pwm_data */
+	at->fan_pwm_data = fan ? (0xFF & fan) : 0xFF;	/* by default: 0xFF */
+
+	/* 1: timeout_data miner_num, chip_num */
+	at->timer_eft = timeout ? 1 : 0;
+	at->timer_eft = chip_num ? 1 : 0;
+	at->timer_eft = miner_num ? 1 : 0;
+
+	at->timeout_data = timeout ? timeout : 0x27; 	/* by default: 0x27 */
+	at->chip_num = chip_num ? chip_num : 0xA;	/* by default: 0x0A */
+	at->miner_num = miner_num ? miner_num : 0x18;	/* by default: 0x18 */
+
+	/* FIXME: Not support nonce range yet */
+	at->nonce_elf = 0;	/* 1: nonce_range*/
+	
+	if (opt_debug) {
+		applog(LOG_DEBUG, "Avalon: Task:");
+		hexdump((uint8_t *)at, sizeof(struct avalon_task));
+	}
+
+	return 0;
 }
 
-static int avalon_send_task(int fd, const void *buf, size_t bufLen)
+static inline void avalon_create_task(struct avalon_task *at, struct work *work)
 {
-	size_t ret;
-	char *ob_hex = NULL;
+	memcpy(at->midstate, work->midstate, 32);
+	rev((uint8_t *)at->midstate, 32);
 
-	/* FIXME: we should set RTS to 1 and wait CTS became 1, before write? */
-	int empty = avalon_buffer_empty(fd);
-	if (empty < 0)
-		return AVA_SEND_ERROR;
+	memcpy(at->data, work->data + 64, 12);
+	rev((uint8_t *)at->data, 12);
 
-	if (!empty) {
-		 /* FIXME: the buffer was full; return AVA_SEND_FULL; */
-		;
+	if (opt_debug) {
+		applog(LOG_DEBUG, "Avalon: Task + work:");
+		hexdump((uint8_t *)at, sizeof(struct avalon_task));
 	}
+}
+
+static int avalon_send_task(int fd, const struct avalon_task *at)
+{
+	size_t ret;
+	struct timespec p;
 
 	if (opt_debug) {
-		ob_hex = bin2hex(buf, bufLen);
-		applog(LOG_DEBUG, "Avalon: Sent %s", ob_hex);
-		free(ob_hex);
+		applog(LOG_DEBUG, "Avalon: Sent:");
+		hexdump((uint8_t *)at, sizeof(struct avalon_task));
 	}
 
-	ret = write(fd, buf, bufLen);
-	if (unlikely(ret != bufLen))
+	ret = write(fd, (uint8_t *)at, AVALON_WRITE_SIZE);
+	if (unlikely(ret != AVALON_WRITE_SIZE))
 		return AVA_SEND_ERROR;
 
-	/* From the document. avalon needs some time space between two write */
-	struct timespec p;
 	p.tv_sec = 0;
-	/* FIXME:  */
-	p.tv_nsec = 0;
+	p.tv_nsec = AVALON_SEND_WORK_PITCH;
 	nanosleep(&p, NULL);
 
 	return AVA_SEND_OK;
 }
 
+static int avalon_reset(int fd)
+{
+	const char golden_nonce[] = "000187a2";
+	uint8_t nonce_bin[AVALON_READ_SIZE];
+	char *nonce_hex;
+
+
+	struct avalon_task at;
+	avalon_init_task(&at, 1, 0, 0, 0, 0, 0);
+	avalon_send_task(fd, &at);
+
+	memset(nonce_bin, 0, sizeof(nonce_bin));
+	avalon_gets(nonce_bin, fd, NULL, NULL, 10/* set to 1s now */);
+
+	nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin));
+	if (strncmp(nonce_hex, golden_nonce, 8)) {
+		applog(LOG_ERR, "Avalon: Reset failed! not a Avalon?");
+		free(nonce_hex);
+		return 1;
+	}
+	free(nonce_hex);
+
+	/* FIXME: add more avalon info base on return */
+	applog(LOG_DEBUG, "Avalon: Reset succeeded");
+
+	return 0;
+}
+
 static void do_avalon_close(struct thr_info *thr)
 {
 	struct cgpu_info *avalon = thr->cgpu;
@@ -450,27 +479,10 @@ static void get_options(int this_option_offset, int *baud, int *work_division,
 
 static bool avalon_detect_one(const char *devpath)
 {
-	// Block 171874 nonce = (0xa2870100) = 0x000187a2
-	// N.B. golden_ob MUST take less time to calculate
-	//	than the timeout set in avalon_open()
-	//	This one takes ~0.53ms on Rev3 Avalon
-	const char golden_ob[] =
-		"4679ba4ec99876bf4bfe086082b40025"
-		"4df6c356451471139a3afa71e48f544a"
-		"00000000000000000000000000000000"
-		"0000000087320b1a1426674f2fa722ce";
-
-	const char golden_nonce[] = "000187a2";
-	const uint32_t golden_nonce_val = 0x000187a2;
-
-	uint8_t ob_bin[AVALON_WRITE_SIZE], nonce_bin[AVALON_READ_SIZE];
-	char *nonce_hex;
-
 	struct AVALON_INFO *info;
-	struct timeval tv_start, tv_finish;
-	int fd;
-
+	int fd, ret;
 	int baud, work_division, asic_count;
+
 	int this_option_offset = ++option_offset;
 	get_options(this_option_offset, &baud, &work_division, &asic_count);
 
@@ -481,29 +493,11 @@ static bool avalon_detect_one(const char *devpath)
 		return false;
 	}
 
-	hex2bin(ob_bin, golden_ob, sizeof(ob_bin));
-	avalon_send_task(fd, ob_bin, sizeof(ob_bin));
-	gettimeofday(&tv_start, NULL);
-
-	memset(nonce_bin, 0, sizeof(nonce_bin));
-	/* FIXME: how much time on avalon finish reset */ 
-	avalon_gets(nonce_bin, fd, &tv_finish, NULL, 10/* set to 1s now */);
-
+	ret = avalon_reset(fd);
 	avalon_close(fd);
 
-	nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin));
-	if (strncmp(nonce_hex, golden_nonce, 8)) {
-		applog(LOG_ERR,
-			"Avalon Detect: "
-			"Test failed at %s: get %s, should: %s",
-			devpath, nonce_hex, golden_nonce);
-		free(nonce_hex);
+	if (ret)
 		return false;
-	}
-	applog(LOG_DEBUG,
-	       "Avalon Detect: Test succeeded at %s: got %s",
-	       devpath, nonce_hex);
-	free(nonce_hex);
 
 	/* We have a real Avalon! */
 	struct cgpu_info *avalon;
@@ -517,15 +511,13 @@ static bool avalon_detect_one(const char *devpath)
 			      sizeof(struct AVALON_INFO *) *
 			      (total_devices + 1));
 
-	applog(LOG_INFO, "Found Avalon at %s, mark as %d",
+	applog(LOG_INFO, "Avalon Detect: Found at %s, mark as %d",
 	       devpath, avalon->device_id);
 
 	applog(LOG_DEBUG,
 	       "Avalon: Init: %d baud=%d work_division=%d asic_count=%d",
 		avalon->device_id, baud, work_division, asic_count);
 
-	// Since we are adding a new device on the end it
-	// needs to always be allocated
 	avalon_info[avalon->device_id] = (struct AVALON_INFO *)
 		malloc(sizeof(struct AVALON_INFO));
 	if (unlikely(!(avalon_info[avalon->device_id])))
@@ -540,9 +532,6 @@ static bool avalon_detect_one(const char *devpath)
 	info->work_division = work_division;
 	info->asic_count = asic_count;
 	info->nonce_mask = mask(work_division);
-	info->golden_hashes =
-		(golden_nonce_val & info->nonce_mask) * asic_count;
-	timersub(&tv_finish, &tv_start, &(info->golden_tv));
 
 	set_timing_mode(this_option_offset, avalon);
 
@@ -585,8 +574,9 @@ static int64_t avalon_scanhash(struct thr_info *thr, struct work **work,
 	int ret;
 
 	struct AVALON_INFO *info;
+	struct avalon_task at;
 
-	uint8_t ob_bin[AVALON_WRITE_SIZE], nonce_bin[AVALON_READ_SIZE];
+	uint8_t nonce_bin[AVALON_READ_SIZE];
 	uint32_t nonce;
 	int64_t hash_count;
 	int i, work_i;
@@ -623,8 +613,9 @@ static int64_t avalon_scanhash(struct thr_info *thr, struct work **work,
 #endif
 	/* Write task to device one by one */
 	for (i = 0; i < AVALON_GET_WORK_COUNT; i++) {
-		avalon_create_task(ob_bin, work[i]);
-		ret = avalon_send_task(fd, ob_bin, AVALON_WRITE_SIZE);
+		avalon_init_default_task(&at);
+		avalon_create_task(&at, work[i]);
+		ret = avalon_send_task(fd, &at);
 		if (ret == AVA_SEND_ERROR) {
 			do_avalon_close(thr);
 			applog(LOG_ERR, "AVA%i: Comms error",

+ 24 - 48
driver-avalon.h

@@ -20,9 +20,9 @@ struct avalon_task {
 	uint8_t timeout_data;
 	uint8_t miner_num;	// Word[0]
 
-	uint8_t nonce_elf	:1;
-	uint32_t miner_ctrl	:31;
-	uint32_t pad0;		//Word[2:1]
+	uint8_t nonce_elf		:1;
+	uint32_t pad0_miner_ctrl	:31;
+	uint32_t pad1_miner_ctrl;	//Word[2:1]
 
 	uint32_t midstate[8];
 	uint32_t data[3];
@@ -50,10 +50,6 @@ struct AVALON_HISTORY {
 
 enum timing_mode { MODE_DEFAULT, MODE_SHORT, MODE_LONG, MODE_VALUE };
 
-// Store the last INFO_HISTORY data sets
-// [0] = current data, not yet ready to be included as an estimate
-// Each new data set throws the last old set off the end thus
-// keeping a ongoing average of recent data
 #define INFO_HISTORY 10
 
 struct AVALON_INFO {
@@ -90,13 +86,12 @@ struct AVALON_INFO {
 };
 
 #define AVALON_MINER_THREADS 1
-/* FIXME: should be ~20 */
-#define AVALON_GET_WORK_COUNT 1
+#define AVALON_GET_WORK_COUNT 20
 
 #define AVALON_IO_SPEED 115200
-/* FIXME:  The size of a successful task-write and nonce-read */
-#define AVALON_WRITE_SIZE 64
-#define AVALON_READ_SIZE 4
+#define AVALON_SEND_WORK_PITCH 8000
+#define AVALON_WRITE_SIZE (sizeof(struct avalon_task))
+#define AVALON_READ_SIZE (sizeof(struct avalon_result))
 
 // Ensure the sizes are correct for the Serial read
 #define ASSERT1(condition) __maybe_unused static char sizeof_uint32_t_must_be_4[(condition)?1:-1]
@@ -122,40 +117,6 @@ ASSERT1(sizeof(uint32_t) == 4);
 #define AVALON_REV3_HASH_TIME 0.0000000026316
 #define NANOSEC 1000000000.0
 
-// Avalon Rev3 doesn't send a completion message when it finishes
-// the full nonce range, so to avoid being idle we must abort the
-// work (by starting a new work) shortly before it finishes
-//
-// Thus we need to estimate 2 things:
-//	1) How many hashes were done if the work was aborted
-//	2) How high can the timeout be before the Avalon is idle,
-//		to minimise the number of work started
-//	We set 2) to 'the calculated estimate' - 1
-//	to ensure the estimate ends before idle
-//
-// The simple calculation used is:
-//	Tn = Total time in seconds to calculate n hashes
-//	Hs = seconds per hash
-//	Xn = number of hashes
-//	W  = code overhead per work
-//
-// Rough but reasonable estimate:
-//	Tn = Hs * Xn + W	(of the form y = mx + b)
-//
-// Thus:
-//	Line of best fit (using least squares)
-//
-//	Hs = (n*Sum(XiTi)-Sum(Xi)*Sum(Ti))/(n*Sum(Xi^2)-Sum(Xi)^2)
-//	W = Sum(Ti)/n - (Hs*Sum(Xi))/n
-//
-// N.B. W is less when aborting work since we aren't waiting for the reply
-//	to be transferred back (AVALON_READ_TIME)
-//	Calculating the hashes aborted at n seconds is thus just n/Hs
-//	(though this is still a slight overestimate due to code delays)
-//
-
-// Both below must be exceeded to complete a set of data
-// Minimum how long after the first, the last data point must be
 #define HISTORY_SEC 60
 // Minimum how many points a single AVALON_HISTORY should have
 #define MIN_DATA_COUNT 5
@@ -177,8 +138,23 @@ ASSERT1(sizeof(uint32_t) == 4);
 #define avalon_open2(devpath, baud, purge)  serial_open(devpath, baud, AVALON_RESET_FAULT_DECISECONDS, purge)
 #define avalon_open(devpath, baud)  avalon_open2(devpath, baud, false)
 
+#define avalon_init_default_task(at) avalon_init_task(at, 1, 0, 0, 0, 0, 0)
 #define avalon_close(fd) close(fd)
 
-#define avalon_buffer_empty(fd)	get_serial_cts(fd)
-#define avalon_task_done(fd)	get_serial_cts(fd)
+#define AVA_BUFFER_FULL 0
+#define AVA_BUFFER_EMPTY 1
+#define avalon_buffer_full(fd)	get_serial_cts(fd)
+
+static inline void rev(uint8_t *s, size_t l)
+{
+	size_t i, j;
+	uint8_t t;
+
+	for (i = 0, j = l - 1; i < j; i++, j--) {
+		t = s[i];
+		s[i] = s[j];
+		s[j] = t;
+	}
+}
+
 #endif	/* AVALON_H */

+ 1 - 2
fpgautils.c

@@ -400,8 +400,7 @@ int serial_open(const char *devpath, unsigned long baud, signed short timeout, b
 	my_termios.c_cflag |= CS8;
 	my_termios.c_cflag |= CREAD;
 #ifdef USE_AVALON
-	/* FIXME: change to |= CRTSCTS */
-	my_termios.c_cflag &= ~CRTSCTS;
+	my_termios.c_cflag |= CRTSCTS;
 #endif
 	my_termios.c_cflag |= CLOCAL;
 	my_termios.c_cflag &= ~(CSIZE | PARENB);