Browse Source

cointerra: Implement basic miner logic

Luke Dashjr 11 years ago
parent
commit
a5e4220564
2 changed files with 277 additions and 11 deletions
  1. 19 0
      deviceapi.h
  2. 258 11
      driver-cointerra.c

+ 19 - 0
deviceapi.h

@@ -110,4 +110,23 @@ extern FILE *open_bitstream(const char *dname, const char *filename);
 
 extern void close_device_fd(struct thr_info *);
 
+#define set_on_all_procs(dev, attr, nv)  do{  \
+	for (struct cgpu_info *_proc = dev; _proc; _proc = _proc->next_proc)  \
+		_proc->attr = nv;  \
+}while(0)
+
+static inline
+struct cgpu_info *get_proc_by_id(struct cgpu_info * const dev, const int procno)
+{
+	struct cgpu_info *proc = dev;
+	for (int i = 0; i < procno; ++i)
+	{
+		proc = proc->next_proc;
+		if (!proc)
+			return NULL;
+	}
+	return proc;
+}
+
+
 #endif

+ 258 - 11
driver-cointerra.c

@@ -21,9 +21,11 @@
 #define COINTERRA_EP_R  (LIBUSB_ENDPOINT_IN  | 1)
 #define COINTERRA_EP_W  (LIBUSB_ENDPOINT_OUT | 1)
 #define COINTERRA_USB_TIMEOUT  100
+#define COINTERRA_USB_POLL_TIMEOUT  1
 #define COINTERRA_PACKET_SIZE  0x40
 #define COINTERRA_START_SEQ  0x5a,0x5a
 #define COINTERRA_MSG_SIZE  (COINTERRA_PACKET_SIZE - sizeof(cointerra_startseq))
+#define COINTERRA_MSGBODY_SIZE  (COINTERRA_MSG_SIZE - 1)
 
 BFG_REGISTER_DRIVER(cointerra_drv)
 
@@ -48,8 +50,43 @@ enum cointerra_msg_type_in {
 	CMTI_ERRINFO   = 0xa,
 };
 
+enum cointerra_reset_level {
+	CRL_WORK_UPDATE = 1,
+	CRL_NEW_BLOCK   = 2,
+	CRL_INIT        = 3,
+};
+
+struct cointerra_dev_state {
+	libusb_device_handle *usbh;
+	unsigned pipes_per_asic;
+	unsigned pipes_per_die;
+	int works_requested;
+	int next_work_id;
+};
+
 static const uint8_t cointerra_startseq[] = {COINTERRA_START_SEQ};
 
+static
+int cointerra_write_msg(libusb_device_handle * const usbh, const char * const repr, const uint8_t msgtype, const void * const msgbody, const unsigned timeout)
+{
+	uint8_t buf[COINTERRA_PACKET_SIZE], *p;
+	memcpy(buf, cointerra_startseq, sizeof(cointerra_startseq));
+	p = &buf[sizeof(cointerra_startseq)];
+	pk_u8(p, 0, msgtype);
+	memcpy(&p[1], msgbody, COINTERRA_MSGBODY_SIZE);
+	
+	int e, xfer;
+	
+	e = libusb_bulk_transfer(usbh, COINTERRA_EP_W, buf, sizeof(buf), &xfer, timeout);
+	if (e)
+		return e;
+	
+	if (xfer != COINTERRA_PACKET_SIZE)
+		return LIBUSB_ERROR_OTHER;
+	
+	return 0;
+}
+
 static
 int cointerra_read_msg(uint8_t * const out_msgtype, uint8_t * const out, libusb_device_handle * const usbh, const char * const repr, const unsigned timeout)
 {
@@ -71,6 +108,22 @@ int cointerra_read_msg(uint8_t * const out_msgtype, uint8_t * const out, libusb_
 	return 0;
 }
 
+static
+int cointerra_request(libusb_device_handle * const usbh, const uint8_t msgtype, uint16_t interval_cs)
+{
+	uint8_t buf[COINTERRA_MSGBODY_SIZE] = {0};
+	pk_u16le(buf, 0, msgtype);
+	pk_u16le(buf, 2, interval_cs);
+	return cointerra_write_msg(usbh, cointerra_drv.dname, CMTO_REQUEST, buf, COINTERRA_USB_TIMEOUT);
+}
+
+static
+int cointerra_reset(libusb_device_handle * const usbh, const enum cointerra_reset_level crl)
+{
+	uint8_t buf[COINTERRA_MSGBODY_SIZE] = { crl };
+	return cointerra_write_msg(usbh, cointerra_drv.dname, CMTO_RESET, buf, COINTERRA_USB_TIMEOUT);
+}
+
 static
 bool cointerra_lowl_match(const struct lowlevel_device_info * const info)
 {
@@ -94,23 +147,17 @@ bool cointerra_lowl_probe(const struct lowlevel_device_info * const info)
 	
 	unsigned pipes;
 	{
-		uint8_t buf[COINTERRA_PACKET_SIZE] = {
-			COINTERRA_START_SEQ,
-			CMTO_REQUEST,
-			CMTI_INFO, 0,
-		};
-		int xfer;
-		uint8_t msgtype;
-		
-		e = libusb_bulk_transfer(usbh, COINTERRA_EP_W, buf, sizeof(buf), &xfer, COINTERRA_USB_TIMEOUT);
+		e = cointerra_request(usbh, CMTI_INFO, 0);
 		if (e)
-			return e;
+			goto err;
 		
+		uint8_t msgtype;
+		uint8_t buf[COINTERRA_MSG_SIZE];
 		while (true)
 		{
 			e = cointerra_read_msg(&msgtype, buf, usbh, cointerra_drv.dname, COINTERRA_USB_TIMEOUT);
 			if (e)
-				return e;
+				goto err;
 			if (msgtype == CMTI_INFO)
 				break;
 			// FIXME: Timeout if we keep getting packets we don't care about
@@ -135,6 +182,200 @@ bool cointerra_lowl_probe(const struct lowlevel_device_info * const info)
 		.deven = DEV_ENABLED,
 	};
 	return add_cgpu(cgpu);
+
+err:
+	libusb_close(usbh);
+	return false;
+}
+
+static
+bool cointerra_init(struct thr_info * const master_thr)
+{
+	struct cgpu_info * const dev = master_thr->cgpu;
+	struct lowlevel_device_info * const info = dev->device_data;
+	struct cointerra_dev_state * const devstate = malloc(sizeof(*devstate));
+	int e;
+	
+	dev->device_data = devstate;
+	*devstate = (struct cointerra_dev_state){
+		.pipes_per_die = 120,
+		.pipes_per_asic = 240,
+	};
+	e = libusb_open(info->lowl_data, &devstate->usbh);
+	if (e)
+		applogr(false, LOG_ERR, "%s: Failed to open %s: %s",
+		        dev->dev_repr, info->devid, bfg_strerror(e, BST_LIBUSB));
+	libusb_device_handle * const usbh = devstate->usbh;
+	
+	// Request regular status updates
+	cointerra_request(usbh, CMTI_STATUS, 0x83d);
+	
+	cointerra_reset(usbh, CRL_INIT);
+	
+	// Queue is full until device asks for work
+	set_on_all_procs(dev, thr[0]->queue_full, true);
+	
+	timer_set_delay_from_now(&master_thr->tv_poll, 100000);
+	
+	return true;
+}
+
+static
+bool cointerra_queue_append(struct thr_info * const thr, struct work * const work)
+{
+	struct cgpu_info * const dev = thr->cgpu->device;
+	struct thr_info * const master_thr = dev->thr[0];
+	struct cointerra_dev_state * const devstate = dev->device_data;
+	uint8_t buf[COINTERRA_MSGBODY_SIZE] = {0};
+	int e;
+	
+	if (unlikely(!devstate->works_requested))
+	{
+		set_on_all_procs(dev, thr[0]->queue_full, true);
+		return false;
+	}
+	
+	work->device_id = devstate->next_work_id;
+	
+	pk_u16be(buf, 0, work->device_id);
+	swap32yes(&buf[   6],  work->midstate  , 0x20 / 4);
+	swap32yes(&buf[0x26], &work->data[0x40],  0xc / 4);
+	pk_u16le(buf, 50, 0);  // ntime roll limit
+	pk_u16le(buf, 52, 0x20);  // number of zero bits in results
+	e = cointerra_write_msg(devstate->usbh, cointerra_drv.dname, CMTO_WORK, buf, COINTERRA_USB_TIMEOUT);
+	if (e)
+		return false;
+	
+	HASH_ADD_INT(master_thr->work, device_id, work);
+	++devstate->next_work_id;
+	if (!--devstate->works_requested)
+		set_on_all_procs(dev, thr[0]->queue_full, true);
+	
+	return true;
+}
+
+static
+void cointerra_queue_flush(struct thr_info * const thr)
+{
+}
+
+static
+bool cointerra_poll_msg(struct thr_info * const master_thr)
+{
+	struct cgpu_info * const dev = master_thr->cgpu, *proc;
+	struct thr_info *mythr;
+	struct cointerra_dev_state * const devstate = dev->device_data;
+	int e;
+	uint8_t msgtype;
+	uint8_t buf[COINTERRA_MSGBODY_SIZE];
+	
+	e = cointerra_read_msg(&msgtype, buf, devstate->usbh, dev->dev_repr, COINTERRA_USB_POLL_TIMEOUT);
+	if (e)
+		return false;
+	
+	switch (msgtype)
+	{
+		case CMTI_WORKREQ:
+		{
+			devstate->works_requested = upk_u16le(buf, 0);
+			const bool qf = !devstate->works_requested;
+			set_on_all_procs(dev, thr[0]->queue_full, qf);
+			break;
+		}
+		case CMTI_MATCH:
+		{
+			struct work *work;
+			const int workid = upk_u16be(buf, 0);
+			const int die = buf[2], asic = buf[3], pipeno = buf[5];
+			const unsigned procno = (asic * devstate->pipes_per_asic) + (die * devstate->pipes_per_die) + pipeno;
+			const uint32_t timeoff = upk_u32le(buf, 42);
+			const uint32_t nonce = upk_u32le(buf, 57);
+			
+			proc = get_proc_by_id(dev, procno) ?: dev;
+			mythr = proc->thr[0];
+			
+			HASH_FIND_INT(master_thr->work, &workid, work);
+			if (unlikely(!work))
+			{
+				applog(LOG_WARNING, "%"PRIpreprv": Got %s message about unknown work 0x%x",
+				       proc->proc_repr, "nonce found", workid);
+				inc_hw_errors3(mythr, NULL, &nonce, 1.);
+				break;
+			}
+			
+			submit_noffset_nonce(mythr, work, nonce, timeoff);
+			
+			// hashes_done must be counted by matches because cointerra devices do not provide a way to know which pipe completed matchless work
+			hashes_done2(mythr, 0x100000000, NULL);
+			
+			break;
+		}
+		case CMTI_WORKDONE:
+		{
+			const int workid = upk_u16be(buf, 0);
+			struct work *work;
+			HASH_FIND_INT(master_thr->work, &workid, work);
+			if (unlikely(!work))
+			{
+				applog(LOG_WARNING, "%s: Got %s message about unknown work 0x%x",
+				       dev->dev_repr, "work done", workid);
+				inc_hw_errors_only(master_thr);
+				break;
+			}
+			HASH_DEL(master_thr->work, work);
+			free_work(work);
+			break;
+		}
+		case CMTI_STATUS:
+		{
+			proc = dev;
+			for (int i = 0; i < 0x10; i += 2)
+			{
+				const float celcius = upk_u16le(buf, i) * 0.01;
+				for (int j = 0; j < devstate->pipes_per_die; ++j)
+				{
+					proc->temp = celcius;
+					proc = proc->next_proc;
+					if (unlikely(!proc))
+						goto die_temps_done;
+				}
+			}
+die_temps_done:
+			// TODO: ambient temps, fan, voltage, etc
+			;
+		}
+		case CMTI_SETTINGS:
+		case CMTI_INFO:
+		case CMTI_LOGMSG:
+		case CMTI_RESETDONE:
+		case CMTI_ERRINFO:
+			break;
+	}
+	
+	return true;
+}
+
+static
+void cointerra_poll(struct thr_info * const master_thr)
+{
+	struct cgpu_info * const dev = master_thr->cgpu;
+	struct timeval tv_timeout;
+	timer_set_delay_from_now(&tv_timeout, 10000);
+	while (true)
+	{
+		if (!cointerra_poll_msg(master_thr))
+		{
+			applog(LOG_DEBUG, "%s poll: No more messages", dev->dev_repr);
+			break;
+		}
+		if (timer_passed(&tv_timeout, NULL))
+		{
+			applog(LOG_DEBUG, "%s poll: 10ms timeout met", dev->dev_repr);
+			break;
+		}
+	}
+	
+	timer_set_delay_from_now(&master_thr->tv_poll, 100000);
 }
 
 struct device_drv cointerra_drv = {
@@ -143,4 +384,10 @@ struct device_drv cointerra_drv = {
 	
 	.lowl_match = cointerra_lowl_match,
 	.lowl_probe = cointerra_lowl_probe,
+	
+	.minerloop = minerloop_queue,
+	.thread_init = cointerra_init,
+	.queue_append = cointerra_queue_append,
+	.queue_flush = cointerra_queue_flush,
+	.poll = cointerra_poll,
 };