Browse Source

Merge branch 'master' into hfa

Conflicts:
	Makefile.am
	api.c
	configure.ac
	miner.h
nfactor-troky
Con Kolivas 11 years ago
parent
commit
c4542f2b96
  1. 12
      Makefile.am
  2. 5
      api.c
  3. 24
      ccan/opt/helpers.d
  4. 26
      ccan/opt/opt.d
  5. 18
      ccan/opt/parse.d
  6. 22
      ccan/opt/usage.d
  7. 5
      cgminer.c
  8. 21
      configure.ac
  9. 5
      driver-klondike.c
  10. 762
      driver-knc-spi-fpga.c
  11. 5
      miner.h

12
Makefile.am

@ -1,10 +1,10 @@
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
JANSSON_INCLUDES= -I$(top_srcdir)/compat/jansson-2.5/src JANSSON_INCLUDES= -I$(top_builddir)/compat/jansson-2.5/src -I$(top_srcdir)/compat/jansson-2.5/src
if WANT_USBUTILS if WANT_USBUTILS
USBUTILS_INCLUDES = -I$(top_srcdir)/compat/libusb-1.0/libusb USBUTILS_INCLUDES = -I$(top_builddir)/compat/libusb-1.0/libusb -I$(top_srcdir)/compat/libusb-1.0/libusb
else else
USBUTILS_INCLUDES = USBUTILS_INCLUDES =
endif endif
@ -72,6 +72,10 @@ if HAS_AVALON
cgminer_SOURCES += driver-avalon.c driver-avalon.h cgminer_SOURCES += driver-avalon.c driver-avalon.h
endif endif
if HAS_KNC
cgminer_SOURCES += driver-knc-spi-fpga.c
endif
if HAS_BFLSC if HAS_BFLSC
cgminer_SOURCES += driver-bflsc.c driver-bflsc.h cgminer_SOURCES += driver-bflsc.c driver-bflsc.h
endif endif
@ -92,10 +96,6 @@ if HAS_ICARUS
cgminer_SOURCES += driver-icarus.c cgminer_SOURCES += driver-icarus.c
endif endif
if HAS_ICARUS
cgminer_SOURCES += driver-icarus.c
endif
if HAS_KLONDIKE if HAS_KLONDIKE
cgminer_SOURCES += driver-klondike.c cgminer_SOURCES += driver-klondike.c
endif endif

5
api.c

@ -29,7 +29,7 @@
#include "miner.h" #include "miner.h"
#include "util.h" #include "util.h"
#if defined(USE_BFLSC) || defined(USE_AVALON) || defined(USE_HASHFAST) || defined(USE_BITFURY) || defined(USE_KLONDIKE) #if defined(USE_BFLSC) || defined(USE_AVALON) || defined(USE_HASHFAST) || defined(USE_BITFURY) || defined(USE_KLONDIKE) || defined(USE_KNC)
#define HAVE_AN_ASIC 1 #define HAVE_AN_ASIC 1
#endif #endif
@ -191,6 +191,9 @@ static const char *DEVICECODE = ""
#ifdef USE_MODMINER #ifdef USE_MODMINER
"MMQ " "MMQ "
#endif #endif
#ifdef USE_KNC
"KnC "
#endif
#ifdef USE_MODMINER #ifdef USE_MODMINER
"MMQ " "MMQ "
#endif #endif

24
ccan/opt/helpers.d

@ -1,24 +0,0 @@
ccan/opt/helpers.o: ccan/opt/helpers.c ccan/opt/opt.h \
ccan/compiler/compiler.h config.h ccan/typesafe_cb/typesafe_cb.h \
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdbool.h \
/usr/include/stdlib.h /usr/include/features.h \
/usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-32.h \
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stddef.h \
/usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
/usr/include/endian.h /usr/include/bits/endian.h \
/usr/include/bits/byteswap.h /usr/include/xlocale.h \
/usr/include/sys/types.h /usr/include/bits/types.h \
/usr/include/bits/typesizes.h /usr/include/time.h \
/usr/include/sys/select.h /usr/include/bits/select.h \
/usr/include/bits/sigset.h /usr/include/bits/time.h \
/usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \
/usr/include/alloca.h /usr/include/string.h /usr/include/errno.h \
/usr/include/bits/errno.h /usr/include/linux/errno.h \
/usr/include/i386-linux-gnu/asm/errno.h /usr/include/asm-generic/errno.h \
/usr/include/asm-generic/errno-base.h /usr/include/stdio.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
ccan/opt/private.h

26
ccan/opt/opt.d

@ -1,26 +0,0 @@
ccan/opt/opt.o: ccan/opt/opt.c ccan/opt/opt.h ccan/compiler/compiler.h \
config.h ccan/typesafe_cb/typesafe_cb.h \
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdbool.h \
/usr/include/stdlib.h /usr/include/features.h \
/usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-32.h \
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stddef.h \
/usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
/usr/include/endian.h /usr/include/bits/endian.h \
/usr/include/bits/byteswap.h /usr/include/xlocale.h \
/usr/include/sys/types.h /usr/include/bits/types.h \
/usr/include/bits/typesizes.h /usr/include/time.h \
/usr/include/sys/select.h /usr/include/bits/select.h \
/usr/include/bits/sigset.h /usr/include/bits/time.h \
/usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \
/usr/include/alloca.h /usr/include/string.h /usr/include/errno.h \
/usr/include/bits/errno.h /usr/include/linux/errno.h \
/usr/include/i386-linux-gnu/asm/errno.h /usr/include/asm-generic/errno.h \
/usr/include/asm-generic/errno-base.h /usr/include/stdio.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
/usr/include/err.h /usr/include/assert.h \
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdint.h \
/usr/include/stdint.h /usr/include/bits/wchar.h ccan/opt/private.h

18
ccan/opt/parse.d

@ -1,18 +0,0 @@
ccan/opt/parse.o: ccan/opt/parse.c ccan/opt/opt.h \
ccan/compiler/compiler.h config.h ccan/typesafe_cb/typesafe_cb.h \
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdbool.h \
/usr/include/stdlib.h /usr/include/features.h \
/usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-32.h \
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stddef.h \
/usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
/usr/include/endian.h /usr/include/bits/endian.h \
/usr/include/bits/byteswap.h /usr/include/xlocale.h \
/usr/include/sys/types.h /usr/include/bits/types.h \
/usr/include/bits/typesizes.h /usr/include/time.h \
/usr/include/sys/select.h /usr/include/bits/select.h \
/usr/include/bits/sigset.h /usr/include/bits/time.h \
/usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \
/usr/include/alloca.h /usr/include/string.h /usr/include/assert.h \
ccan/opt/private.h

22
ccan/opt/usage.d

@ -1,22 +0,0 @@
ccan/opt/usage.o: ccan/opt/usage.c ccan/opt/opt.h \
ccan/compiler/compiler.h config.h ccan/typesafe_cb/typesafe_cb.h \
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdbool.h \
/usr/include/stdlib.h /usr/include/features.h \
/usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-32.h \
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stddef.h \
/usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
/usr/include/endian.h /usr/include/bits/endian.h \
/usr/include/bits/byteswap.h /usr/include/xlocale.h \
/usr/include/sys/types.h /usr/include/bits/types.h \
/usr/include/bits/typesizes.h /usr/include/time.h \
/usr/include/sys/select.h /usr/include/bits/select.h \
/usr/include/bits/sigset.h /usr/include/bits/time.h \
/usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \
/usr/include/alloca.h /usr/include/string.h /usr/include/stdio.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/include/stdint.h \
/usr/include/stdint.h /usr/include/bits/wchar.h ccan/opt/private.h

5
cgminer.c

@ -1598,6 +1598,9 @@ extern const char *opt_argv0;
static char *opt_verusage_and_exit(const char *extra) static char *opt_verusage_and_exit(const char *extra)
{ {
printf("%s\nBuilt with " printf("%s\nBuilt with "
#ifdef USE_KNC
"KnC "
#endif
#ifdef USE_AVALON #ifdef USE_AVALON
"avalon " "avalon "
#endif #endif
@ -5117,7 +5120,7 @@ static void hashmeter(int thr_id, struct timeval *diff,
local_secs = (double)total_diff.tv_sec + ((double)total_diff.tv_usec / 1000000.0); local_secs = (double)total_diff.tv_sec + ((double)total_diff.tv_usec / 1000000.0);
decay_time(&total_rolling, local_mhashes_done / local_secs, local_secs); decay_time(&total_rolling, local_mhashes_done / local_secs, local_secs);
global_hashrate = roundl(total_rolling) * 1000000; global_hashrate = llround(total_rolling) * 1000000;
timersub(&total_tv_end, &total_tv_start, &total_diff); timersub(&total_tv_end, &total_tv_start, &total_diff);
total_secs = (double)total_diff.tv_sec + total_secs = (double)total_diff.tv_sec +

21
configure.ac

@ -232,6 +232,17 @@ if test "x$avalon" = xyes; then
fi fi
AM_CONDITIONAL([HAS_AVALON], [test x$avalon = xyes]) AM_CONDITIONAL([HAS_AVALON], [test x$avalon = xyes])
knc="no"
AC_ARG_ENABLE([knc],
[AC_HELP_STRING([--enable-knc],[Compile support for KnC miners (default disabled)])],
[knc=$enableval]
)
if test "x$knc" = xyes; then
AC_DEFINE([USE_KNC], [1], [Defined to 1 if KnC miner support is wanted])
fi
AM_CONDITIONAL([HAS_KNC], [test x$knc = xyes])
bflsc="no" bflsc="no"
AC_ARG_ENABLE([bflsc], AC_ARG_ENABLE([bflsc],
@ -526,14 +537,14 @@ if test "x$opencl" != xno; then
else else
echo " OpenCL...............: NOT FOUND. GPU mining support DISABLED" echo " OpenCL...............: NOT FOUND. GPU mining support DISABLED"
if test "x$avalon$bitforce$bitfury$icarus$ztex$modminer$bflsc$hashfast$klondike" = xnonononononononono; then if test "x$avalon$bitforce$bitfury$icarus$modminer$bflsc$hashfast$klondike$knc" = xnonononononononono; then
AC_MSG_ERROR([No mining configured in]) AC_MSG_ERROR([No mining configured in])
fi fi
echo " scrypt...............: Disabled (needs OpenCL)" echo " scrypt...............: Disabled (needs OpenCL)"
fi fi
else else
echo " OpenCL...............: Detection overrided. GPU mining support DISABLED" echo " OpenCL...............: Detection overrided. GPU mining support DISABLED"
if test "x$avalon$bitforce$bitfury$icarus$ztex$modminer$bflsc$hashfast$klondike" = xnonononononononono; then if test "x$avalon$bitforce$bitfury$icarus$modminer$bflsc$hashfast$klondike$knc" = xnonononononononono; then
AC_MSG_ERROR([No mining configured in]) AC_MSG_ERROR([No mining configured in])
fi fi
echo " scrypt...............: Disabled (needs OpenCL)" echo " scrypt...............: Disabled (needs OpenCL)"
@ -562,6 +573,12 @@ else
echo " BFL.ASICs............: Disabled" echo " BFL.ASICs............: Disabled"
fi fi
if test "x$knc" = xyes; then
echo " KnC.miners...........: Enabled"
else
echo " KnC.miners...........: Disabled"
fi
if test "x$bitforce" = xyes; then if test "x$bitforce" = xyes; then
echo " BitForce.FPGAs.......: Enabled" echo " BitForce.FPGAs.......: Enabled"
else else

5
driver-klondike.c

@ -821,6 +821,7 @@ static bool klondike_detect_one(struct libusb_device *dev, struct usb_find_devic
break; break;
update_usb_stats(klncgpu); update_usb_stats(klncgpu);
applog(LOG_DEBUG, "Klondike cgpu added"); applog(LOG_DEBUG, "Klondike cgpu added");
rwlock_init(&klninfo->stat_lock);
cglock_init(&klninfo->klist_lock); cglock_init(&klninfo->klist_lock);
return true; return true;
} }
@ -1017,7 +1018,7 @@ static void *klondike_get_replies(void *userdata)
if (klninfo->jobque[dev].flushed == false) if (klninfo->jobque[dev].flushed == false)
idle = true; idle = true;
slaves = klninfo->status[0].kline.ws.slavecount; slaves = klninfo->status[0].kline.ws.slavecount;
rd_lock(&(klninfo->stat_lock)); rd_unlock(&(klninfo->stat_lock));
if (idle) if (idle)
applog(LOG_WARNING, "%s%i:%d went idle before work was sent", applog(LOG_WARNING, "%s%i:%d went idle before work was sent",
klncgpu->drv->name, klncgpu->drv->name,
@ -1026,7 +1027,7 @@ static void *klondike_get_replies(void *userdata)
} }
wr_lock(&(klninfo->stat_lock)); wr_lock(&(klninfo->stat_lock));
klninfo->jobque[dev].flushed = false; klninfo->jobque[dev].flushed = false;
wr_lock(&(klninfo->stat_lock)); wr_unlock(&(klninfo->stat_lock));
} }
case KLN_CMD_STATUS: case KLN_CMD_STATUS:
case KLN_CMD_ABORT: case KLN_CMD_ABORT:

762
driver-knc-spi-fpga.c

@ -0,0 +1,762 @@
/*
* cgminer driver for KnCminer devices
*
* Copyright 2013 Con Kolivas <kernel@kolivas.org>
* Copyright 2013 KnCminer
*
* 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 <stdlib.h>
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include "logging.h"
#include "miner.h"
#define MAX_SPIS 1
#define MAX_BYTES_IN_SPI_XSFER 4096
/* /dev/spidevB.C, where B = bus, C = chipselect */
#define SPI_DEVICE_TEMPLATE "/dev/spidev%d.%d"
#define SPI_MODE (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH)
#define SPI_BITS_PER_WORD 32
#define SPI_MAX_SPEED 3000000
#define SPI_DELAY_USECS 0
/* Max number of ASICs permitted on one SPI device */
#define MAX_ASICS 6
/* How many hardware errors in a row before disabling the core */
#define HW_ERR_LIMIT 10
#define DISA_ERR_LIMIT 3
#define MAX_ACTIVE_WORKS (192 * 2 * 6 * 2)
#define WORK_MIDSTATE_WORDS 8
#define WORK_DATA_WORDS 3
#define WORK_STALE_US 60000000
/* Keep core disabled for no longer than 15 minutes */
#define CORE_DISA_PERIOD_US (15 * 60 * 1000000)
struct spidev_context {
int fd;
uint32_t speed;
uint16_t delay;
uint8_t mode;
uint8_t bits;
};
struct spi_request {
#define CMD_NOP 0
#define CMD_GET_VERSION 1
#define CMD_SUBMIT_WORK 2
#define CMD_FLUSH_QUEUE 3
#define WORK_ID_MASK 0x7FFF
#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
uint32_t cmd :4;
uint32_t rsvd :1; /* set to zero */
uint32_t queue_id :12;
uint32_t work_id :15;
#else
uint32_t work_id :15;
uint32_t queue_id :12;
uint32_t rsvd :1; /* set to zero */
uint32_t cmd :4;
#endif
uint32_t midstate[WORK_MIDSTATE_WORDS];
uint32_t data[WORK_DATA_WORDS];
};
struct spi_response {
#define RESPONSE_TYPE_NOP 0
#define RESPONSE_TYPE_NONCE_FOUND 1
#define RESPONSE_TYPE_WORK_DONE 2
#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
uint32_t type :2;
uint32_t asic :3;
uint32_t queue_id :12;
uint32_t work_id :15;
#else
uint32_t work_id :15;
uint32_t queue_id :12;
uint32_t asic :3;
uint32_t type :2;
#endif
uint32_t nonce;
uint32_t core;
};
#define MAX_REQUESTS_IN_BATCH ( MAX_BYTES_IN_SPI_XSFER / \
sizeof(struct spi_request) \
)
static struct spi_request spi_txbuf[MAX_REQUESTS_IN_BATCH];
#define MAX_RESPONSES_IN_BATCH ( (sizeof(spi_txbuf) - 12) / \
sizeof(struct spi_response) \
)
struct spi_rx_t {
#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
uint32_t rsvd_1 :31;
uint32_t response_queue_full :1;
#else
uint32_t response_queue_full :1;
uint32_t rsvd_1 :31;
#endif
#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
uint32_t rsvd_2 :16;
uint32_t works_accepted :16;
#else
uint32_t works_accepted :16;
uint32_t rsvd_2 :16;
#endif
uint32_t rsvd_3;
struct spi_response responses[MAX_RESPONSES_IN_BATCH];
};
static struct spi_rx_t spi_rxbuf;
struct active_work {
struct work *work;
uint32_t work_id;
struct timeval begin;
};
struct core_disa_data {
struct timeval disa_begin;
uint8_t asic;
uint8_t core;
};
struct knc_state {
struct spidev_context *ctx;
int devices;
uint32_t salt;
uint32_t next_work_id;
/* read - last read item, next is at (read + 1) mod BUFSIZE
* write - next write item, last written at (write - 1) mod BUFSIZE
* When buffer is empty, read + 1 == write
* Buffer full condition: read == write
*/
int read_q, write_q;
#define KNC_QUEUED_BUFFER_SIZE (MAX_REQUESTS_IN_BATCH + 1)
struct active_work queued_fifo[KNC_QUEUED_BUFFER_SIZE];
int read_a, write_a;
#define KNC_ACTIVE_BUFFER_SIZE (MAX_ACTIVE_WORKS + 1)
struct active_work active_fifo[KNC_ACTIVE_BUFFER_SIZE];
uint8_t hwerrs[MAX_ASICS * 256];
uint8_t disa_cnt[MAX_ASICS * 256];
uint32_t hwerr_work_id[MAX_ASICS * 256];
int read_d, write_d;
#define KNC_DISA_CORES_SIZE (MAX_ASICS * 256)
struct core_disa_data disa_cores_fifo[KNC_DISA_CORES_SIZE];
pthread_mutex_t lock;
};
static inline bool knc_queued_fifo_full(struct knc_state *knc)
{
return (knc->read_q == knc->write_q);
}
static inline bool knc_active_fifo_full(struct knc_state *knc)
{
return (knc->read_a == knc->write_a);
}
static inline void knc_queued_fifo_inc_idx(int *idx)
{
if (unlikely(*idx >= ((int)KNC_QUEUED_BUFFER_SIZE - 1)))
*idx = 0;
else
++(*idx);
}
static inline void knc_active_fifo_inc_idx(int *idx)
{
if (unlikely(*idx >= (KNC_ACTIVE_BUFFER_SIZE - 1)))
*idx = 0;
else
++(*idx);
}
static inline void knc_disa_cores_fifo_inc_idx(int *idx)
{
if (unlikely(*idx >= (KNC_DISA_CORES_SIZE - 1)))
*idx = 0;
else
++(*idx);
}
/* Find SPI device with index idx, init it */
static struct spidev_context *spi_new(int idx)
{
struct spidev_context *ctx;
char dev_fname[PATH_MAX];
if (NULL == (ctx = malloc(sizeof(struct spidev_context)))) {
applog(LOG_ERR, "KnC spi: Out of memory");
goto l_exit_error;
}
ctx->mode = SPI_MODE;
ctx->bits = SPI_BITS_PER_WORD;
ctx->speed = SPI_MAX_SPEED;
ctx->delay = SPI_DELAY_USECS;
ctx->fd = -1;
sprintf(dev_fname, SPI_DEVICE_TEMPLATE,
idx, /* bus */
0 /* chipselect */
);
if (0 > (ctx->fd = open(dev_fname, O_RDWR))) {
applog(LOG_ERR, "KnC spi: Can not open SPI device %s: %m",
dev_fname);
goto l_free_exit_error;
}
/*
* spi mode
*/
if (0 > ioctl(ctx->fd, SPI_IOC_WR_MODE, &ctx->mode))
goto l_ioctl_error;
if (0 > ioctl(ctx->fd, SPI_IOC_RD_MODE, &ctx->mode))
goto l_ioctl_error;
/*
* bits per word
*/
if (0 > ioctl(ctx->fd, SPI_IOC_WR_BITS_PER_WORD, &ctx->bits))
goto l_ioctl_error;
if (0 > ioctl(ctx->fd, SPI_IOC_RD_BITS_PER_WORD, &ctx->bits))
goto l_ioctl_error;
/*
* max speed hz
*/
if (0 > ioctl(ctx->fd, SPI_IOC_WR_MAX_SPEED_HZ, &ctx->speed))
goto l_ioctl_error;
if (0 > ioctl(ctx->fd, SPI_IOC_RD_MAX_SPEED_HZ, &ctx->speed))
goto l_ioctl_error;
applog(LOG_INFO, "KnC spi: device %s uses mode %hhu, bits %hhu, speed %u",
dev_fname, ctx->mode, ctx->bits, ctx->speed);
return ctx;
l_ioctl_error:
applog(LOG_ERR, "KnC spi: ioctl error on SPI device %s: %m", dev_fname);
close(ctx->fd);
l_free_exit_error:
free(ctx);
l_exit_error:
return NULL;
}
static void spi_free(struct spidev_context *ctx)
{
if (NULL == ctx)
return;
close(ctx->fd);
free(ctx);
}
static int spi_transfer(struct spidev_context *ctx, uint8_t *txbuf,
uint8_t *rxbuf, int len)
{
struct spi_ioc_transfer xfr;
int ret;
memset(rxbuf, 0xff, len);
ret = len;
xfr.tx_buf = (unsigned long)txbuf;
xfr.rx_buf = (unsigned long)rxbuf;
xfr.len = len;
xfr.speed_hz = ctx->speed;
xfr.delay_usecs = ctx->delay;
xfr.bits_per_word = ctx->bits;
xfr.cs_change = 0;
xfr.pad = 0;
if (1 > (ret = ioctl(ctx->fd, SPI_IOC_MESSAGE(1), &xfr)))
applog(LOG_ERR, "KnC spi xfer: ioctl error on SPI device: %m");
return ret;
}
static void disable_core(uint8_t asic, uint8_t core)
{
char str[256];
snprintf(str, sizeof(str), "i2cset -y 2 0x2%hhu %hhu 0", asic, core);
if (0 != WEXITSTATUS(system(str)))
applog(LOG_ERR, "KnC: system call failed");
}
static void enable_core(uint8_t asic, uint8_t core)
{
char str[256];
snprintf(str, sizeof(str), "i2cset -y 2 0x2%hhu %hhu 1", asic, core);
if (0 != WEXITSTATUS(system(str)))
applog(LOG_ERR, "KnC: system call failed");
}
static int64_t timediff(const struct timeval *a, const struct timeval *b)
{
struct timeval diff;
timersub(a, b, &diff);
return diff.tv_sec * 1000000 + diff.tv_usec;
}
static void knc_check_disabled_cores(struct knc_state *knc)
{
struct core_disa_data *core;
int next_read_d, cidx;
struct timeval now;
int64_t us;
next_read_d = knc->read_d;
knc_disa_cores_fifo_inc_idx(&next_read_d);
if (next_read_d == knc->write_d)
return; /* queue empty */
core = &knc->disa_cores_fifo[next_read_d];
gettimeofday(&now, NULL);
us = timediff(&now, &core->disa_begin);
if ((us >= 0) && (us < CORE_DISA_PERIOD_US))
return; /* latest disabled core still not expired */
cidx = core->asic * 256 + core->core;
enable_core(core->asic, core->core);
knc->hwerrs[cidx] = 0;
applog(LOG_NOTICE,
"KnC: core %u-%u was enabled back from disabled state",
core->asic, core->core);
knc->read_d = next_read_d;
}
static void knc_work_from_queue_to_spi(struct knc_state *knc,
struct active_work *q_work,
struct spi_request *spi_req)
{
uint32_t *buf_from, *buf_to;
int i;
spi_req->cmd = CMD_SUBMIT_WORK;
spi_req->queue_id = 0; /* at the moment we have one and only queue #0 */
spi_req->work_id = (knc->next_work_id ^ knc->salt) & WORK_ID_MASK;
q_work->work_id = spi_req->work_id;
++(knc->next_work_id);
buf_to = spi_req->midstate;
buf_from = (uint32_t *)q_work->work->midstate;
for (i = 0; i < WORK_MIDSTATE_WORDS; ++i)
buf_to[i] = le32toh(buf_from[8 - i - 1]);
buf_to = spi_req->data;
buf_from = (uint32_t *)&(q_work->work->data[16 * 4]);
for (i = 0; i < WORK_DATA_WORDS; ++i)
buf_to[i] = le32toh(buf_from[3 - i - 1]);
}
static int64_t knc_process_response(struct thr_info *thr, struct cgpu_info *cgpu,
struct spi_rx_t *rxbuf)
{
struct knc_state *knc = cgpu->device_data;
int submitted, successful, i, num_sent;
int next_read_q, next_read_a;
struct timeval now;
struct work *work;
int64_t us;
num_sent = knc->write_q - knc->read_q - 1;
if (knc->write_q <= knc->read_q)
num_sent += KNC_QUEUED_BUFFER_SIZE;
/* Actually process SPI response */
if (rxbuf->works_accepted) {
applog(LOG_DEBUG, "KnC spi: raw response %08X %08X",
((uint32_t *)rxbuf)[0], ((uint32_t *)rxbuf)[1]);
applog(LOG_DEBUG,
"KnC spi: response, accepted %u (from %u), full %u",
rxbuf->works_accepted, num_sent,
rxbuf->response_queue_full);
}
/* move works_accepted number of items from queued_fifo to active_fifo */
gettimeofday(&now, NULL);
submitted = 0;
for (i = 0; i < rxbuf->works_accepted; ++i) {
next_read_q = knc->read_q;
knc_queued_fifo_inc_idx(&next_read_q);
if ((next_read_q == knc->write_q) || knc_active_fifo_full(knc))
break;
memcpy(&knc->active_fifo[knc->write_a],
&knc->queued_fifo[next_read_q],
sizeof(struct active_work));
knc->active_fifo[knc->write_a].begin = now;
knc->queued_fifo[next_read_q].work = NULL;
knc->read_q = next_read_q;
knc_active_fifo_inc_idx(&knc->write_a);
++submitted;
}
if (submitted != rxbuf->works_accepted) {
applog(LOG_ERR,
"KnC: accepted by FPGA %u works, but only %d submitted",
rxbuf->works_accepted, submitted);
}
/* check for completed works and calculated nonces */
gettimeofday(&now, NULL);
successful = 0;
for (i = 0; i < (int)MAX_RESPONSES_IN_BATCH; ++i) {
if ((rxbuf->responses[i].type != RESPONSE_TYPE_NONCE_FOUND) &&
(rxbuf->responses[i].type != RESPONSE_TYPE_WORK_DONE))
continue;
applog(LOG_DEBUG, "KnC spi: raw response %08X %08X",
((uint32_t *)&rxbuf->responses[i])[0],
((uint32_t *)&rxbuf->responses[i])[1]);
applog(LOG_DEBUG, "KnC spi: response, T:%u C:%u-%u Q:%u W:%u",
rxbuf->responses[i].type,
rxbuf->responses[i].asic, rxbuf->responses[i].core,
rxbuf->responses[i].queue_id,
rxbuf->responses[i].work_id);
/* Find active work with matching ID */
next_read_a = knc->read_a;
knc_active_fifo_inc_idx(&next_read_a);
while (next_read_a != knc->write_a) {
if (knc->active_fifo[next_read_a].work_id ==
rxbuf->responses[i].work_id)
break;
/* check for stale works */
us = timediff(&now,
&knc->active_fifo[next_read_a].begin);
if ((us < 0) || (us >= WORK_STALE_US)) {
applog(LOG_DEBUG,
"KnC spi: remove stale work %u",
knc->active_fifo[next_read_a].work_id);
work = knc->active_fifo[next_read_a].work;
knc_active_fifo_inc_idx(&knc->read_a);
work_completed(cgpu, work);
if (next_read_a != knc->read_a) {
memcpy(&(knc->active_fifo[next_read_a]),
&(knc->active_fifo[knc->read_a]),
sizeof(struct active_work));
}
knc->active_fifo[knc->read_a].work = NULL;
}
knc_active_fifo_inc_idx(&next_read_a);
}
if (next_read_a == knc->write_a)
continue;
applog(LOG_DEBUG, "KnC spi: response work %u found",
rxbuf->responses[i].work_id);
work = knc->active_fifo[next_read_a].work;
if (rxbuf->responses[i].type == RESPONSE_TYPE_NONCE_FOUND) {
if (NULL != thr) {
int cidx = rxbuf->responses[i].asic * 256 +
rxbuf->responses[i].core;
if (submit_nonce(thr, work,
rxbuf->responses[i].nonce)) {
if (cidx < (int)sizeof(knc->hwerrs)) {
knc->hwerrs[cidx] = 0;
knc->disa_cnt[cidx] = 0;
knc->hwerr_work_id[cidx] = 0xFFFFFFFF;
}
successful++;
} else {
if ((cidx < (int)sizeof(knc->hwerrs)) &&
(knc->hwerr_work_id[cidx] != rxbuf->responses[i].work_id)) {
knc->hwerr_work_id[cidx] = rxbuf->responses[i].work_id;
if (++(knc->hwerrs[cidx]) >= HW_ERR_LIMIT) {
struct core_disa_data *core;
core = &knc->disa_cores_fifo[knc->write_d];
core->disa_begin = now;
core->asic = rxbuf->responses[i].asic;
core->core = rxbuf->responses[i].core;
disable_core(core->asic, core->core);
if (++(knc->disa_cnt[cidx]) >= DISA_ERR_LIMIT) {
applog(LOG_WARNING,
"KnC: core %u-%u was disabled permanently", core->asic, core->core);
} else {
applog(LOG_WARNING,
"KnC: core %u-%u was disabled due to %u HW errors in a row",
core->asic, core->core, HW_ERR_LIMIT);
knc_disa_cores_fifo_inc_idx(&knc->write_d);
}
}
}
};
}
continue;
}
/* Work completed */
knc_active_fifo_inc_idx(&knc->read_a);
work_completed(cgpu, work);
if (next_read_a != knc->read_a) {
memcpy(&(knc->active_fifo[next_read_a]),
&(knc->active_fifo[knc->read_a]),
sizeof(struct active_work));
}
knc->active_fifo[knc->read_a].work = NULL;
}
return ((uint64_t)successful) * 0x100000000UL;
}
/* Send flush command via SPI */
static int _internal_knc_flush_fpga(struct knc_state *knc)
{
int len;
spi_txbuf[0].cmd = CMD_FLUSH_QUEUE;
spi_txbuf[0].queue_id = 0; /* at the moment we have one and only queue #0 */
len = spi_transfer(knc->ctx, (uint8_t *)spi_txbuf,
(uint8_t *)&spi_rxbuf, sizeof(struct spi_request));
if (len != sizeof(struct spi_request))
return -1;
len /= sizeof(struct spi_response);
return len;
}
static bool knc_detect_one(struct spidev_context *ctx)
{
/* Scan device for ASICs */
int chip_id, devices = 0;
struct cgpu_info *cgpu;
struct knc_state *knc;
for (chip_id = 0; chip_id < MAX_ASICS; ++chip_id) {
/* TODO: perform the ASIC test/detection */
++devices;
}
if (!devices) {
applog(LOG_INFO, "SPI detected, but not KnCminer ASICs");
return false;
}
applog(LOG_INFO, "Found a KnC miner with %d ASICs", devices);
cgpu = calloc(1, sizeof(*cgpu));
knc = calloc(1, sizeof(*knc));
if (!cgpu || !knc) {
applog(LOG_ERR, "KnC miner detected, but failed to allocate memory");
return false;
}
knc->ctx = ctx;
knc->devices = devices;
knc->read_q = 0;
knc->write_q = 1;
knc->read_a = 0;
knc->write_a = 1;
knc->read_d = 0;
knc->write_d = 1;
knc->salt = rand();
mutex_init(&knc->lock);
memset(knc->hwerr_work_id, 0xFF, sizeof(knc->hwerr_work_id));
_internal_knc_flush_fpga(knc);
cgpu->drv = &knc_drv;
cgpu->name = "KnCminer";
cgpu->threads = 1; // .. perhaps our number of devices?
cgpu->device_data = knc;
add_cgpu(cgpu);
return true;
}
// http://www.concentric.net/~Ttwang/tech/inthash.htm
static unsigned long mix(unsigned long a, unsigned long b, unsigned long c)
{
a = a - b; a = a - c; a = a ^ (c >> 13);
b = b - c; b = b - a; b = b ^ (a << 8);
c = c - a; c = c - b; c = c ^ (b >> 13);
a = a - b; a = a - c; a = a ^ (c >> 12);
b = b - c; b = b - a; b = b ^ (a << 16);
c = c - a; c = c - b; c = c ^ (b >> 5);
a = a - b; a = a - c; a = a ^ (c >> 3);
b = b - c; b = b - a; b = b ^ (a << 10);
c = c - a; c = c - b; c = c ^ (b >> 15);
return c;
}
/* Probe devices and register with add_cgpu */
void knc_detect(bool __maybe_unused hotplug)
{
int idx;
srand(mix(clock(), time(NULL), getpid()));
/* Loop through all possible SPI interfaces */
for (idx = 0; idx < MAX_SPIS; ++idx) {
struct spidev_context *ctx = spi_new(idx + 1);
if (ctx != NULL) {
if (!knc_detect_one(ctx))
spi_free(ctx);
}
}
}
/* return value is number of nonces that have been checked since
* previous call
*/
static int64_t knc_scanwork(struct thr_info *thr)
{
struct cgpu_info *cgpu = thr->cgpu;
struct knc_state *knc = cgpu->device_data;
int len, num, next_read_q;
int64_t ret;
applog(LOG_DEBUG, "KnC running scanwork");
knc_check_disabled_cores(knc);
/* Prepare tx buffer */
memset(spi_txbuf, 0, sizeof(spi_txbuf));
num = 0;
mutex_lock(&knc->lock);
next_read_q = knc->read_q;
knc_queued_fifo_inc_idx(&next_read_q);
while (next_read_q != knc->write_q) {
knc_work_from_queue_to_spi(knc, &knc->queued_fifo[next_read_q],
&spi_txbuf[num]);
knc_queued_fifo_inc_idx(&next_read_q);
++num;
}
/* knc->read_q is advanced in knc_process_response, not here */
len = spi_transfer(knc->ctx, (uint8_t *)spi_txbuf,
(uint8_t *)&spi_rxbuf, sizeof(spi_txbuf));
if (len != sizeof(spi_rxbuf)) {
ret = -1;
goto out_unlock;
}
applog(LOG_DEBUG, "KnC spi: %d works in request", num);
ret = knc_process_response(thr, cgpu, &spi_rxbuf);
out_unlock:
mutex_unlock(&knc->lock);
return ret;
}
static bool knc_queue_full(struct cgpu_info *cgpu)
{
struct knc_state *knc = cgpu->device_data;
int queue_full = false;
struct work *work;
applog(LOG_DEBUG, "KnC running queue full");
mutex_lock(&knc->lock);
if (knc_queued_fifo_full(knc)) {
queue_full = true;
goto out_unlock;
}
work = get_queued(cgpu);
if (!work)
goto out_unlock;
knc->queued_fifo[knc->write_q].work = work;
knc_queued_fifo_inc_idx(&(knc->write_q));
if (knc_queued_fifo_full(knc))
queue_full = true;
out_unlock:
mutex_unlock(&knc->lock);
return queue_full;
}
static void knc_flush_work(struct cgpu_info *cgpu)
{
struct knc_state *knc = cgpu->device_data;
int len, next_read_q, next_read_a;
struct work *work;
applog(LOG_ERR, "KnC running flushwork");
mutex_lock(&knc->lock);
/* Drain queued works */
next_read_q = knc->read_q;
knc_queued_fifo_inc_idx(&next_read_q);
while (next_read_q != knc->write_q) {
work = knc->queued_fifo[next_read_q].work;
work_completed(cgpu, work);
knc->queued_fifo[next_read_q].work = NULL;
knc->read_q = next_read_q;
knc_queued_fifo_inc_idx(&next_read_q);
}
/* Drain active works */
next_read_a = knc->read_a;
knc_active_fifo_inc_idx(&next_read_a);
while (next_read_a != knc->write_a) {
work = knc->active_fifo[next_read_a].work;
work_completed(cgpu, work);
knc->active_fifo[next_read_a].work = NULL;
knc->read_a = next_read_a;
knc_active_fifo_inc_idx(&next_read_a);
}
len = _internal_knc_flush_fpga(knc);
if (len > 0)
knc_process_response(NULL, cgpu, &spi_rxbuf);
mutex_unlock(&knc->lock);
}
struct device_drv knc_drv = {
.drv_id = DRIVER_knc,
.dname = "KnCminer",
.name = "KnC",
.drv_detect = knc_detect, // Probe for devices, add with add_cgpu
.hash_work = hash_queued_work,
.scanwork = knc_scanwork,
.queue_full = knc_queue_full,
.flush_work = knc_flush_work,
};

5
miner.h

@ -223,8 +223,12 @@ static inline int fsync (int fd)
#define semtimedop(SEM, SOPS, VAL, TIMEOUT) semop(SEM, SOPS, VAL) #define semtimedop(SEM, SOPS, VAL, TIMEOUT) semop(SEM, SOPS, VAL)
#endif #endif
#ifndef MIN
#define MIN(x, y) ((x) > (y) ? (y) : (x)) #define MIN(x, y) ((x) > (y) ? (y) : (x))
#endif
#ifndef MAX
#define MAX(x, y) ((x) > (y) ? (x) : (y)) #define MAX(x, y) ((x) > (y) ? (x) : (y))
#endif
/* Put avalon last to make it the last device it tries to detect to prevent it /* Put avalon last to make it the last device it tries to detect to prevent it
* trying to claim same chip but different devices. Adding a device here will * trying to claim same chip but different devices. Adding a device here will
@ -240,6 +244,7 @@ static inline int fsync (int fd)
DRIVER_ADD_COMMAND(bitfury) \ DRIVER_ADD_COMMAND(bitfury) \
DRIVER_ADD_COMMAND(hashfast) \ DRIVER_ADD_COMMAND(hashfast) \
DRIVER_ADD_COMMAND(klondike) \ DRIVER_ADD_COMMAND(klondike) \
DRIVER_ADD_COMMAND(knc) \
DRIVER_ADD_COMMAND(avalon) DRIVER_ADD_COMMAND(avalon)
#define DRIVER_PARSE_COMMANDS(DRIVER_ADD_COMMAND) \ #define DRIVER_PARSE_COMMANDS(DRIVER_ADD_COMMAND) \

Loading…
Cancel
Save