Browse Source

antminer: Initial support for the Bitmain AntMiner U1 ASIC
Includes support for identifying the U1 separately from Icarus and Block Erupter
Also includes overclocking via --set-device antminer:clock=xHEX

Nate Woolls 12 years ago
parent
commit
4556d14d57
8 changed files with 343 additions and 27 deletions
  1. 1 0
      Makefile.am
  2. 1 1
      configure.ac
  3. 188 0
      driver-antminer.c
  4. 1 1
      driver-cairnsmore.c
  5. 80 24
      driver-icarus.c
  6. 12 1
      icarus-common.h
  7. 57 0
      util.c
  8. 3 0
      util.h

+ 1 - 0
Makefile.am

@@ -226,6 +226,7 @@ if HAS_ICARUS
 bfgminer_SOURCES += driver-icarus.c icarus-common.h
 bfgminer_SOURCES += driver-cairnsmore.c
 bfgminer_SOURCES += driver-erupter.c
+bfgminer_SOURCES += driver-antminer.c
 endif
 
 if HAS_AVALON

+ 1 - 1
configure.ac

@@ -378,7 +378,7 @@ if test "x$bitforce" = xyes; then
 fi
 AM_CONDITIONAL([HAS_BITFORCE], [test x$bitforce = xyes])
 
-driverlist="$driverlist icarus cairnsmore/icarus erupter/icarus"
+driverlist="$driverlist icarus cairnsmore/icarus erupter/icarus antminer/icarus"
 AC_ARG_ENABLE([icarus],
 	[AC_HELP_STRING([--disable-icarus],[Compile support for Icarus (default enabled)])],
 	[icarus=$enableval],

+ 188 - 0
driver-antminer.c

@@ -0,0 +1,188 @@
+/*
+ * Copyright 2013 Luke Dashjr
+ * Copyright 2013 Nate Woolls
+ * Copyright 2013 Lingchao Xu
+ *
+ * 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 "config.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+#include "miner.h"
+#include "icarus-common.h"
+#include "lowlevel.h"
+#include "lowl-vcom.h"
+#include "deviceapi.h"
+#include "logging.h"
+#include "util.h"
+
+#define ANTMINER_IO_SPEED 115200
+#define ANTMINER_HASH_TIME 0.0000000004761
+
+#define ANTMINER_STATUS_LEN 5
+
+BFG_REGISTER_DRIVER(antminer_drv)
+
+static
+bool antminer_detect_one(const char *devpath)
+{
+	struct device_drv *drv = &antminer_drv;
+	
+	struct ICARUS_INFO *info = calloc(1, sizeof(struct ICARUS_INFO));
+	if (unlikely(!info))
+		quit(1, "Failed to malloc ICARUS_INFO");
+	
+	*info = (struct ICARUS_INFO){
+		.baud = ANTMINER_IO_SPEED,
+		.Hs = ANTMINER_HASH_TIME,
+		.timing_mode = MODE_DEFAULT,
+		.read_size = 5,
+	};
+	
+	if (!icarus_detect_custom(devpath, drv, info))
+	{
+		free(info);
+		return false;
+	}
+	
+	info->read_count = 15;
+	
+	return true;
+}
+
+static
+bool antminer_lowl_probe(const struct lowlevel_device_info * const info)
+{
+	return vcom_lowl_probe_wrapper(info, antminer_detect_one);
+}
+
+static
+char *antminer_get_clock(struct cgpu_info *cgpu, char *replybuf)
+{
+	uint8_t rdreg_buf[4] = {0};
+	unsigned char rebuf[ANTMINER_STATUS_LEN] = {0};
+	
+	struct timeval tv_now;
+	
+	rdreg_buf[0] = 4;
+	rdreg_buf[0] |= 0x80;
+	rdreg_buf[1] = 0;    //16-23
+	rdreg_buf[2] = 0x04; // 8-15
+	rdreg_buf[3] = crc5usb(rdreg_buf, 27);
+	
+	applog(LOG_DEBUG, "%"PRIpreprv": Get clock: %02x%02x%02x%02x", cgpu->proc_repr, rdreg_buf[0], rdreg_buf[1], rdreg_buf[2], rdreg_buf[3]);
+	
+	timer_set_now(&tv_now);
+	int err = icarus_write(cgpu->device_fd, rdreg_buf, sizeof(rdreg_buf));
+	
+	if (err != 0)
+	{
+		sprintf(replybuf, "invalid send get clock: comms error (err=%d)", err);
+		return replybuf;
+	}
+	
+	applog(LOG_DEBUG, "%"PRIpreprv": Get clock: OK", cgpu->proc_repr);
+	
+	memset(rebuf, 0, sizeof(rebuf));
+	err = icarus_gets(rebuf, cgpu->device_fd, &tv_now, NULL, 10, ANTMINER_STATUS_LEN);
+	
+	// Timeout is ok - checking specifically for an error here
+	if (err == ICA_GETS_ERROR)
+	{
+		sprintf(replybuf, "invalid recv get clock: comms error (err=%d)", err);
+		return replybuf;
+	}
+		
+	applog(LOG_DEBUG, "%"PRIpreprv": Get clock: %02x%02x%02x%02x%02x", cgpu->proc_repr, rebuf[0], rebuf[1], rebuf[2], rebuf[3], rebuf[4]);
+	
+	return NULL;
+}
+
+static
+char *antminer_set_clock(struct cgpu_info *cgpu, char *setting, char *replybuf)
+{
+	if (!setting || !*setting)
+		return "missing clock setting";
+	
+	// For now we only allow hex values that use BITMAINtech's lookup table
+	// This means values should be prefixed with an x so that later we can
+	// accept and distinguish decimal values
+	if (setting[0] != 'x')
+	{
+		sprintf(replybuf, "invalid clock: '%s' data must be prefixed with an x", setting);
+		return replybuf;
+	}
+	
+	//remove leading character
+	char *hex_setting = setting + 1;
+
+	uint8_t reg_data[4] = {0};
+	
+	if (!hex2bin(reg_data, hex_setting, strlen(hex_setting) / 2))
+	{
+		sprintf(replybuf, "invalid clock: '%s' data must be a hexidecimal value", hex_setting);
+		return replybuf;
+	}
+	
+	uint8_t cmd_buf[4] = {0};
+	
+	cmd_buf[0] = 2;
+	cmd_buf[0] |= 0x80;
+	cmd_buf[1] = reg_data[0]; //16-23
+	cmd_buf[2] = reg_data[1]; // 8-15
+	cmd_buf[3] = crc5usb(cmd_buf, 27);
+	
+	applog(LOG_DEBUG, "%"PRIpreprv": Set clock: %02x%02x%02x%02x", cgpu->proc_repr, cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3]);
+	
+	int err = icarus_write(cgpu->device_fd, cmd_buf, sizeof(cmd_buf));
+		
+	if (err != 0)
+	{
+		sprintf(replybuf, "invalid send clock: '%s' comms error (err=%d)", setting, err);
+		return replybuf;
+	}
+	
+	applog(LOG_DEBUG, "%"PRIpreprv": Set clock: OK", cgpu->proc_repr);
+	
+	// This is confirmed required in order for the clock change to "take"
+	cgsleep_ms(500);
+		
+	return antminer_get_clock(cgpu, replybuf);
+}
+
+static
+char *antminer_set_device(struct cgpu_info *cgpu, char *option, char *setting, char *replybuf)
+{		
+	if (strcasecmp(option, "clock") == 0)
+	{
+		return antminer_set_clock(cgpu, setting, replybuf);
+	}
+
+	sprintf(replybuf, "Unknown option: %s", option);
+	return replybuf;
+}
+
+static
+void antminer_drv_init()
+{
+	antminer_drv = icarus_drv;
+	antminer_drv.dname = "antminer";
+	antminer_drv.name = "AMU";
+	antminer_drv.lowl_probe = antminer_lowl_probe;
+	antminer_drv.set_device = antminer_set_device,
+	++antminer_drv.probe_priority;
+}
+
+struct device_drv antminer_drv = {
+	.drv_init = antminer_drv_init,
+};

+ 1 - 1
driver-cairnsmore.c

@@ -92,7 +92,7 @@ bool cairnsmore_supports_dynclock(int fd)
 			.work_restart = false,
 			.work_restart_notifier = {-1, -1},
 		};
-		icarus_gets((unsigned char*)&nonce, fd, &tv_finish, &dummy, 1);
+		icarus_gets((unsigned char*)&nonce, fd, &tv_finish, &dummy, 1, ICARUS_DEFAULT_READ_SIZE);
 	}
 	applog(LOG_DEBUG, "Cairnsmore dynclock detection... Got %08x", nonce);
 	switch (nonce) {

+ 80 - 24
driver-icarus.c

@@ -65,17 +65,15 @@
 // The serial I/O speed - Linux uses a define 'B115200' in bits/termios.h
 #define ICARUS_IO_SPEED 115200
 
-// The size of a successful nonce read
-#define ICARUS_READ_SIZE 4
+// The number of bytes in a nonce (always 4)
+// This is NOT the read-size for the Icarus driver
+// That is defined in ICARUS_INFO->read_size
+#define ICARUS_NONCE_SIZE 4
 
-// Ensure the sizes are correct for the Serial read
-#if (ICARUS_READ_SIZE != 4)
-#error ICARUS_READ_SIZE must be 4
-#endif
 #define ASSERT1(condition) __maybe_unused static char sizeof_uint32_t_must_be_4[(condition)?1:-1]
 ASSERT1(sizeof(uint32_t) == 4);
 
-#define ICARUS_READ_TIME(baud) ((double)ICARUS_READ_SIZE * (double)8.0 / (double)(baud))
+#define ICARUS_READ_TIME(baud, read_size) ((double)read_size * (double)8.0 / (double)(baud))
 
 // Defined in deciseconds
 // There's no need to have this bigger, since the overhead/latency of extra work
@@ -184,18 +182,13 @@ static void rev(unsigned char *s, size_t l)
 #define icarus_open2(devpath, baud, purge)  serial_open(devpath, baud, ICARUS_READ_FAULT_DECISECONDS, purge)
 #define icarus_open(devpath, baud)  icarus_open2(devpath, baud, false)
 
-#define ICA_GETS_ERROR -1
-#define ICA_GETS_OK 0
-#define ICA_GETS_RESTART 1
-#define ICA_GETS_TIMEOUT 2
-
-int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct thr_info *thr, int read_count)
+int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct thr_info *thr, int read_count, int read_size)
 {
 	ssize_t ret = 0;
 	int rc = 0;
 	int epollfd = -1;
 	int epoll_timeout = ICARUS_READ_FAULT_DECISECONDS * 100;
-	int read_amount = ICARUS_READ_SIZE;
+	int read_amount = read_size;
 	bool first = true;
 
 #ifdef HAVE_EPOLL
@@ -282,7 +275,7 @@ int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct th
 	}
 }
 
-static int icarus_write(int fd, const void *buf, size_t bufLen)
+int icarus_write(int fd, const void *buf, size_t bufLen)
 {
 	size_t ret;
 
@@ -557,6 +550,27 @@ static void get_options(int this_option_offset, struct ICARUS_INFO *info)
 	}
 }
 
+// Number of bytes remaining after reading a nonce from Icarus
+int icarus_excess_nonce_size(int fd, struct ICARUS_INFO *info)
+{
+	// How big a buffer?
+	int excess_size = info->read_size - ICARUS_NONCE_SIZE;
+
+	// Try to read one more to ensure the device doesn't return
+	// more than we want for this driver
+	excess_size++;
+
+	unsigned char excess_bin[excess_size];
+	// Read excess_size from Icarus
+	struct timeval tv_now;
+	timer_set_now(&tv_now);
+	//icarus_gets(excess_bin, fd, &tv_now, NULL, 1, excess_size);
+	int bytes_read = read(fd, excess_bin, excess_size);
+	// Number of bytes that were still available
+
+	return bytes_read;
+}
+
 bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct ICARUS_INFO *info)
 {
 	int this_option_offset = ++option_offset;
@@ -582,7 +596,7 @@ bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct IC
 
 	const char golden_nonce[] = "000187a2";
 
-	unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE];
+	unsigned char ob_bin[64], nonce_bin[ICARUS_NONCE_SIZE];
 	char nonce_hex[(sizeof(nonce_bin) * 2) + 1];
 
 	get_options(this_option_offset, info);
@@ -598,14 +612,25 @@ bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct IC
 		applog(LOG_DEBUG, "Icarus Detect: Failed to open %s", devpath);
 		return false;
 	}
+	
+	// Set a default so that individual drivers need not specify
+	// e.g. Cairnsmore
+	if (info->read_size == 0)
+		info->read_size = ICARUS_DEFAULT_READ_SIZE;
 
 	hex2bin(ob_bin, golden_ob, sizeof(ob_bin));
 	icarus_write(fd, ob_bin, sizeof(ob_bin));
 	cgtime(&tv_start);
 
 	memset(nonce_bin, 0, sizeof(nonce_bin));
-	icarus_gets(nonce_bin, fd, &tv_finish, NULL, 1);
-
+	// Do not use info->read_size here, instead read exactly ICARUS_NONCE_SIZE
+	// We will then compare the bytes left in fd with info->read_size to determine
+	// if this is a valid device
+	icarus_gets(nonce_bin, fd, &tv_finish, NULL, 1, ICARUS_NONCE_SIZE);
+	
+	// How many bytes were left after reading the above nonce
+	int bytes_left = icarus_excess_nonce_size(fd, info);
+	
 	icarus_close(fd);
 
 	bin2hex(nonce_hex, nonce_bin, sizeof(nonce_bin));
@@ -616,6 +641,16 @@ bool icarus_detect_custom(const char *devpath, struct device_drv *api, struct IC
 			devpath, nonce_hex, golden_nonce);
 		return false;
 	}
+		
+	if (info->read_size - ICARUS_NONCE_SIZE != bytes_left) 
+	{
+		applog(LOG_DEBUG,
+			   "Icarus Detect: "
+			   "Test failed at %s: expected %d bytes, got %d",
+			   devpath, info->read_size, ICARUS_NONCE_SIZE + bytes_left);
+		return false;
+	}
+	
 	applog(LOG_DEBUG,
 		"Icarus Detect: "
 		"Test succeeded at %s: got %s",
@@ -663,6 +698,7 @@ static bool icarus_detect_one(const char *devpath)
 	info->quirk_reopen = 1;
 	info->Hs = ICARUS_REV3_HASH_TIME;
 	info->timing_mode = MODE_DEFAULT;
+	info->read_size = ICARUS_DEFAULT_READ_SIZE;
 
 	if (!icarus_detect_custom(devpath, &icarus_drv, info)) {
 		free(info);
@@ -722,6 +758,10 @@ static bool icarus_init(struct thr_info *thr)
 	if (!info->work_division)
 	{
 		struct timeval tv_finish;
+		
+		// For reading the nonce from Icarus
+		unsigned char res_bin[info->read_size];
+		// For storing the the 32-bit nonce
 		uint32_t res;
 		
 		applog(LOG_DEBUG, "%"PRIpreprv": Work division not specified - autodetecting", icarus->proc_repr);
@@ -734,8 +774,12 @@ static bool icarus_init(struct thr_info *thr)
 			"BFG\0\x64\x61\x01\x1a\xc9\x06\xa9\x51\xfb\x9b\x3c\x73";
 		
 		icarus_write(fd, pkt, sizeof(pkt));
-		if (ICA_GETS_OK == icarus_gets((unsigned char*)&res, fd, &tv_finish, NULL, info->read_count))
+		memset(res_bin, 0, sizeof(res_bin));
+		if (ICA_GETS_OK == icarus_gets(res_bin, fd, &tv_finish, NULL, info->read_count, info->read_size))
+		{
+			memcpy(&res, res_bin, sizeof(res));
 			res = be32toh(res);
+		}
 		else
 			res = 0;
 		
@@ -862,6 +906,10 @@ void handle_identify(struct thr_info * const thr, int ret, const bool was_first_
 	int fd = icarus->device_fd;
 	struct timeval tv_now;
 	double delapsed;
+	
+	// For reading the nonce from Icarus
+	unsigned char nonce_bin[info->read_size];
+	// For storing the the 32-bit nonce
 	uint32_t nonce;
 	
 	if (fd == -1)
@@ -882,9 +930,11 @@ void handle_identify(struct thr_info * const thr, int ret, const bool was_first_
 				break;
 			
 			// Try to get more nonces (ignoring work restart)
-			ret = icarus_gets((void *)&nonce, fd, &tv_now, NULL, (info->fullnonce - delapsed) * 10);
+			memset(nonce_bin, 0, sizeof(nonce_bin));
+			ret = icarus_gets(nonce_bin, fd, &tv_now, NULL, (info->fullnonce - delapsed) * 10, info->read_size);
 			if (ret == ICA_GETS_OK)
 			{
+				memcpy(&nonce, nonce_bin, sizeof(nonce));
 				nonce = be32toh(nonce);
 				submit_nonce(thr, state->last_work, nonce);
 			}
@@ -934,7 +984,6 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 
 	struct ICARUS_INFO *info;
 
-	uint32_t nonce;
 	struct work *nonce_work;
 	int64_t hash_count;
 	struct timeval tv_start = {.tv_sec=0}, elapsed;
@@ -964,6 +1013,11 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 	// Wait for the previous run's result
 	fd = icarus->device_fd;
 	info = icarus->device_data;
+	
+	// For reading the nonce from Icarus
+	unsigned char nonce_bin[info->read_size];
+	// For storing the the 32-bit nonce
+	uint32_t nonce;
 
 	if (unlikely(fd == -1) && !icarus_reopen(icarus, state, &fd))
 		return -1;
@@ -978,8 +1032,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 		{
 			read_count = info->read_count;
 keepwaiting:
-			/* Icarus will return 4 bytes (ICARUS_READ_SIZE) nonces or nothing */
-			ret = icarus_gets((void*)&nonce, fd, &state->tv_workfinish, thr, read_count);
+			/* Icarus will return info->read_size bytes nonces or nothing */
+			memset(nonce_bin, 0, sizeof(nonce_bin));
+			ret = icarus_gets(nonce_bin, fd, &state->tv_workfinish, thr, read_count, info->read_size);
 			switch (ret) {
 				case ICA_GETS_RESTART:
 					// The prepared work is invalid, and the current work is abandoned
@@ -1020,6 +1075,7 @@ keepwaiting:
 
 	if (ret == ICA_GETS_OK)
 	{
+		memcpy(&nonce, nonce_bin, sizeof(nonce));
 		nonce_work = icarus_process_worknonce(state, &nonce);
 		if (likely(nonce_work))
 		{
@@ -1168,7 +1224,7 @@ keepwaiting:
 
 		Ti = (double)(elapsed.tv_sec)
 			+ ((double)(elapsed.tv_usec))/((double)1000000)
-			- ((double)ICARUS_READ_TIME(info->baud));
+			- ((double)ICARUS_READ_TIME(info->baud, info->read_size));
 		Xi = (double)hash_count;
 		history0->sumXiTi += Xi * Ti;
 		history0->sumXi += Xi;

+ 12 - 1
icarus-common.h

@@ -28,6 +28,13 @@
 
 #define NANOSEC 1000000000.0
 
+// Default value for ICARUS_INFO->read_size
+#define ICARUS_DEFAULT_READ_SIZE 4
+
+#define ICA_GETS_ERROR -1
+#define ICA_GETS_OK 0
+#define ICA_GETS_RESTART 1
+#define ICA_GETS_TIMEOUT 2
 
 // Store the last INFO_HISTORY data sets
 // [0] = current data, not yet ready to be included as an estimate
@@ -89,6 +96,9 @@ struct ICARUS_INFO {
 
 	dclk_change_clock_func_t dclk_change_clock_func;
 	struct dclk_data dclk;
+	
+	// Bytes to read from Icarus for nonce
+	int read_size;
 };
 
 struct icarus_state {
@@ -104,6 +114,7 @@ struct icarus_state {
 };
 
 bool icarus_detect_custom(const char *devpath, struct device_drv *, struct ICARUS_INFO *);
-extern int icarus_gets(unsigned char *, int fd, struct timeval *tv_finish, struct thr_info *, int read_count);
+extern int icarus_gets(unsigned char *, int fd, struct timeval *tv_finish, struct thr_info *, int read_count, int read_size);
+extern int icarus_write(int fd, const void *buf, size_t bufLen);
 
 #endif

+ 57 - 0
util.c

@@ -2911,3 +2911,60 @@ void run_cmd(const char *cmd)
 	pthread_t pth;
 	pthread_create(&pth, NULL, cmd_thread, (void*)cmd);
 }
+
+uint8_t crc5usb(unsigned char *ptr, uint8_t len)
+{
+    uint8_t i, j, k;
+    uint8_t crc = 0x1f;
+	
+    uint8_t crcin[5] = {1, 1, 1, 1, 1};
+    uint8_t crcout[5] = {1, 1, 1, 1, 1};
+    uint8_t din = 0;
+	
+    j = 0x80;
+    k = 0;
+	
+    for (i = 0; i < len; i++)
+    {
+    	if (*ptr & j)
+    		din = 1;
+    	else
+    		din = 0;
+		
+    	crcout[0] = crcin[4] ^ din;
+    	crcout[1] = crcin[0];
+    	crcout[2] = crcin[1] ^ crcin[4] ^ din;
+    	crcout[3] = crcin[2];
+    	crcout[4] = crcin[3];
+		
+        j = j >> 1;
+        k++;
+        if (k == 8)
+        {
+            j = 0x80;
+            k = 0;
+            ptr++;
+        }
+        memcpy(crcin, crcout, 5);
+    }
+	
+    crc = 0;
+    if(crcin[4])
+    	crc |= 0x10;
+	
+    if(crcin[3])
+    	crc |= 0x08;
+	
+    if(crcin[2])
+    	crc |= 0x04;
+	
+    if(crcin[1])
+    	crc |= 0x02;
+	
+    if(crcin[0])
+    	crc |= 0x01;
+	
+    return crc;
+}
+
+

+ 3 - 0
util.h

@@ -500,4 +500,7 @@ void maybe_strdup_if_null(const char **p, const char *s)
 extern void run_cmd(const char *cmd);
 
 
+extern uint8_t crc5usb(unsigned char *ptr, uint8_t len);
+
+
 #endif /* __UTIL_H__ */