|
|
@@ -17,10 +17,10 @@
|
|
|
#include <stdio.h>
|
|
|
#include <sys/time.h>
|
|
|
#include <sys/types.h>
|
|
|
-#include <sys/select.h>
|
|
|
#include <dirent.h>
|
|
|
#include <unistd.h>
|
|
|
#ifndef WIN32
|
|
|
+ #include <sys/select.h>
|
|
|
#include <termios.h>
|
|
|
#include <sys/stat.h>
|
|
|
#include <fcntl.h>
|
|
|
@@ -28,6 +28,7 @@
|
|
|
#define O_CLOEXEC 0
|
|
|
#endif
|
|
|
#else
|
|
|
+ #include "compat.h"
|
|
|
#include <windows.h>
|
|
|
#include <io.h>
|
|
|
#endif
|
|
|
@@ -38,9 +39,10 @@
|
|
|
#include "fpgautils.h"
|
|
|
#include "driver-avalon.h"
|
|
|
#include "hexdump.c"
|
|
|
+#include "util.h"
|
|
|
|
|
|
static int option_offset = -1;
|
|
|
-struct avalon_info **avalon_info;
|
|
|
+struct avalon_info **avalon_infos;
|
|
|
struct device_drv avalon_drv;
|
|
|
|
|
|
static int avalon_init_task(struct avalon_task *at,
|
|
|
@@ -172,7 +174,7 @@ static int avalon_send_task(int fd, const struct avalon_task *at,
|
|
|
buf[4] = tt;
|
|
|
#endif
|
|
|
if (likely(avalon)) {
|
|
|
- info = avalon_info[avalon->device_id];
|
|
|
+ info = avalon_infos[avalon->device_id];
|
|
|
delay = nr_len * 10 * 1000000000ULL;
|
|
|
delay = delay / info->baud;
|
|
|
}
|
|
|
@@ -180,7 +182,7 @@ static int avalon_send_task(int fd, const struct avalon_task *at,
|
|
|
if (at->reset)
|
|
|
nr_len = 1;
|
|
|
if (opt_debug) {
|
|
|
- applog(LOG_DEBUG, "Avalon: Sent(%d):", nr_len);
|
|
|
+ applog(LOG_DEBUG, "Avalon: Sent(%u):", (unsigned int)nr_len);
|
|
|
hexdump((uint8_t *)buf, nr_len);
|
|
|
}
|
|
|
ret = write(fd, buf, nr_len);
|
|
|
@@ -202,30 +204,40 @@ static int avalon_send_task(int fd, const struct avalon_task *at,
|
|
|
return AVA_SEND_BUFFER_EMPTY;
|
|
|
}
|
|
|
|
|
|
-static int avalon_gets(int fd, uint8_t *buf, int read_count,
|
|
|
- struct thr_info *thr, struct timeval *tv_finish)
|
|
|
+static inline int avalon_gets(int fd, uint8_t *buf, struct thr_info *thr,
|
|
|
+ struct timeval *tv_finish)
|
|
|
{
|
|
|
- ssize_t ret = 0;
|
|
|
- int rc = 0;
|
|
|
int read_amount = AVALON_READ_SIZE;
|
|
|
bool first = true;
|
|
|
+ ssize_t ret = 0;
|
|
|
|
|
|
while (true) {
|
|
|
- struct timeval timeout = {0, 100000};
|
|
|
+ struct timeval timeout;
|
|
|
fd_set rd;
|
|
|
|
|
|
+ if (unlikely(thr->work_restart)) {
|
|
|
+ applog(LOG_DEBUG, "Avalon: Work restart");
|
|
|
+ return AVA_GETS_RESTART;
|
|
|
+ }
|
|
|
+
|
|
|
+ timeout.tv_sec = 0;
|
|
|
+ timeout.tv_usec = 100000;
|
|
|
+
|
|
|
FD_ZERO(&rd);
|
|
|
FD_SET(fd, &rd);
|
|
|
ret = select(fd + 1, &rd, NULL, NULL, &timeout);
|
|
|
- if (unlikely(ret < 0))
|
|
|
+ if (unlikely(ret < 0)) {
|
|
|
+ applog(LOG_ERR, "Avalon: Error %d on select in avalon_gets", errno);
|
|
|
return AVA_GETS_ERROR;
|
|
|
+ }
|
|
|
if (ret) {
|
|
|
ret = read(fd, buf, read_amount);
|
|
|
- if (unlikely(ret < 0))
|
|
|
+ if (unlikely(ret < 0)) {
|
|
|
+ applog(LOG_ERR, "Avalon: Error %d on read in avalon_gets", errno);
|
|
|
return AVA_GETS_ERROR;
|
|
|
+ }
|
|
|
if (likely(first)) {
|
|
|
- if (likely(tv_finish))
|
|
|
- gettimeofday(tv_finish, NULL);
|
|
|
+ cgtime(tv_finish);
|
|
|
first = false;
|
|
|
}
|
|
|
if (likely(ret >= read_amount))
|
|
|
@@ -235,43 +247,23 @@ static int avalon_gets(int fd, uint8_t *buf, int read_count,
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- rc++;
|
|
|
- if (rc >= read_count) {
|
|
|
- if (opt_debug) {
|
|
|
- applog(LOG_WARNING,
|
|
|
- "Avalon: No data in %.2f seconds",
|
|
|
- (float)rc/(float)AVALON_TIME_FACTOR);
|
|
|
- }
|
|
|
- return AVA_GETS_TIMEOUT;
|
|
|
- }
|
|
|
-
|
|
|
- if (thr && thr->work_restart) {
|
|
|
- if (opt_debug) {
|
|
|
- applog(LOG_WARNING,
|
|
|
- "Avalon: Work restart at %.2f seconds",
|
|
|
- (float)(rc)/(float)AVALON_TIME_FACTOR);
|
|
|
- }
|
|
|
+ if (unlikely(thr->work_restart)) {
|
|
|
+ applog(LOG_DEBUG, "Avalon: Work restart");
|
|
|
return AVA_GETS_RESTART;
|
|
|
}
|
|
|
+
|
|
|
+ return AVA_GETS_TIMEOUT;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static int avalon_get_result(int fd, struct avalon_result *ar,
|
|
|
struct thr_info *thr, struct timeval *tv_finish)
|
|
|
{
|
|
|
- struct cgpu_info *avalon;
|
|
|
- struct avalon_info *info;
|
|
|
uint8_t result[AVALON_READ_SIZE];
|
|
|
- int ret, read_count = AVALON_RESET_FAULT_DECISECONDS * AVALON_TIME_FACTOR;
|
|
|
-
|
|
|
- if (likely(thr)) {
|
|
|
- avalon = thr->cgpu;
|
|
|
- info = avalon_info[avalon->device_id];
|
|
|
- read_count = info->read_count;
|
|
|
- }
|
|
|
+ int ret;
|
|
|
|
|
|
memset(result, 0, AVALON_READ_SIZE);
|
|
|
- ret = avalon_gets(fd, result, read_count, thr, tv_finish);
|
|
|
+ ret = avalon_gets(fd, result, thr, tv_finish);
|
|
|
|
|
|
if (ret == AVA_GETS_OK) {
|
|
|
if (opt_debug) {
|
|
|
@@ -284,35 +276,65 @@ static int avalon_get_result(int fd, struct avalon_result *ar,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static int avalon_decode_nonce(struct thr_info *thr, struct work **work,
|
|
|
- struct avalon_result *ar, uint32_t *nonce)
|
|
|
+static bool avalon_decode_nonce(struct thr_info *thr, struct avalon_result *ar,
|
|
|
+ uint32_t *nonce)
|
|
|
{
|
|
|
struct cgpu_info *avalon;
|
|
|
struct avalon_info *info;
|
|
|
- int avalon_get_work_count, i;
|
|
|
-
|
|
|
- if (unlikely(!work))
|
|
|
- return -1;
|
|
|
+ struct work *work;
|
|
|
|
|
|
avalon = thr->cgpu;
|
|
|
- info = avalon_info[avalon->device_id];
|
|
|
- avalon_get_work_count = info->miner_count;
|
|
|
+ if (unlikely(!avalon->works))
|
|
|
+ return false;
|
|
|
|
|
|
- for (i = 0; i < avalon_get_work_count; i++) {
|
|
|
- if (work[i] &&
|
|
|
- !memcmp(ar->data, work[i]->data + 64, 12) &&
|
|
|
- !memcmp(ar->midstate, work[i]->midstate, 32))
|
|
|
- break;
|
|
|
- }
|
|
|
- if (i == avalon_get_work_count)
|
|
|
- return -1;
|
|
|
+ work = find_queued_work_bymidstate(avalon, (char *)ar->midstate, 32,
|
|
|
+ (char *)ar->data, 64, 12);
|
|
|
+ if (!work)
|
|
|
+ return false;
|
|
|
|
|
|
- info->matching_work[i]++;
|
|
|
+ info = avalon_infos[avalon->device_id];
|
|
|
+ info->matching_work[work->subid]++;
|
|
|
*nonce = htole32(ar->nonce);
|
|
|
+ submit_nonce(thr, work, *nonce);
|
|
|
|
|
|
- applog(LOG_DEBUG, "Avalon: match to work[%d](%p): %d",i, work[i],
|
|
|
- info->matching_work[i]);
|
|
|
- return i;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static void avalon_get_reset(int fd, struct avalon_result *ar)
|
|
|
+{
|
|
|
+ int read_amount = AVALON_READ_SIZE;
|
|
|
+ uint8_t result[AVALON_READ_SIZE];
|
|
|
+ struct timeval timeout = {1, 0};
|
|
|
+ ssize_t ret = 0, offset = 0;
|
|
|
+ fd_set rd;
|
|
|
+
|
|
|
+ memset(result, 0, AVALON_READ_SIZE);
|
|
|
+ memset(ar, 0, AVALON_READ_SIZE);
|
|
|
+ FD_ZERO(&rd);
|
|
|
+ FD_SET(fd, &rd);
|
|
|
+ ret = select(fd + 1, &rd, NULL, NULL, &timeout);
|
|
|
+ if (unlikely(ret < 0)) {
|
|
|
+ applog(LOG_WARNING, "Avalon: Error %d on select in avalon_get_reset", errno);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!ret) {
|
|
|
+ applog(LOG_WARNING, "Avalon: Timeout on select in avalon_get_reset");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ do {
|
|
|
+ ret = read(fd, result + offset, read_amount);
|
|
|
+ if (unlikely(ret < 0)) {
|
|
|
+ applog(LOG_WARNING, "Avalon: Error %d on read in avalon_get_reset", errno);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ read_amount -= ret;
|
|
|
+ offset += ret;
|
|
|
+ } while (read_amount > 0);
|
|
|
+ if (opt_debug) {
|
|
|
+ applog(LOG_DEBUG, "Avalon: get:");
|
|
|
+ hexdump((uint8_t *)result, AVALON_READ_SIZE);
|
|
|
+ }
|
|
|
+ memcpy((uint8_t *)ar, result, AVALON_READ_SIZE);
|
|
|
}
|
|
|
|
|
|
static int avalon_reset(int fd, struct avalon_result *ar)
|
|
|
@@ -333,7 +355,7 @@ static int avalon_reset(int fd, struct avalon_result *ar)
|
|
|
if (ret == AVA_SEND_ERROR)
|
|
|
return 1;
|
|
|
|
|
|
- avalon_get_result(fd, ar, NULL, NULL);
|
|
|
+ avalon_get_reset(fd, ar);
|
|
|
|
|
|
buf = (uint8_t *)ar;
|
|
|
/* Sometimes there is one extra 0 byte for some reason in the buffer,
|
|
|
@@ -367,7 +389,7 @@ static void avalon_idle(struct cgpu_info *avalon)
|
|
|
struct avalon_task at;
|
|
|
|
|
|
int fd = avalon->device_fd;
|
|
|
- struct avalon_info *info = avalon_info[avalon->device_id];
|
|
|
+ struct avalon_info *info = avalon_infos[avalon->device_id];
|
|
|
int avalon_get_work_count = info->miner_count;
|
|
|
|
|
|
i = 0;
|
|
|
@@ -560,19 +582,19 @@ static bool avalon_detect_one(const char *devpath)
|
|
|
* return false; */
|
|
|
}
|
|
|
|
|
|
- avalon_info = realloc(avalon_info,
|
|
|
- sizeof(struct avalon_info *) *
|
|
|
- (total_devices + 1));
|
|
|
+ avalon_infos = realloc(avalon_infos,
|
|
|
+ sizeof(struct avalon_info *) *
|
|
|
+ (total_devices + 1));
|
|
|
|
|
|
applog(LOG_INFO, "Avalon Detect: Found at %s, mark as %d",
|
|
|
devpath, avalon->device_id);
|
|
|
|
|
|
- avalon_info[avalon->device_id] = (struct avalon_info *)
|
|
|
+ avalon_infos[avalon->device_id] = (struct avalon_info *)
|
|
|
malloc(sizeof(struct avalon_info));
|
|
|
- if (unlikely(!(avalon_info[avalon->device_id])))
|
|
|
- quit(1, "Failed to malloc avalon_info");
|
|
|
+ if (unlikely(!(avalon_infos[avalon->device_id])))
|
|
|
+ quit(1, "Failed to malloc avalon_infos");
|
|
|
|
|
|
- info = avalon_info[avalon->device_id];
|
|
|
+ info = avalon_infos[avalon->device_id];
|
|
|
|
|
|
memset(info, 0, sizeof(struct avalon_info));
|
|
|
|
|
|
@@ -580,8 +602,6 @@ static bool avalon_detect_one(const char *devpath)
|
|
|
info->miner_count = miner_count;
|
|
|
info->asic_count = asic_count;
|
|
|
info->timeout = timeout;
|
|
|
- info->read_count = ((float)info->timeout * AVALON_HASH_TIME_FACTOR *
|
|
|
- AVALON_TIME_FACTOR) / (float)info->miner_count;
|
|
|
|
|
|
info->fan_pwm = AVALON_DEFAULT_FAN_MIN_PWM;
|
|
|
info->temp_max = 0;
|
|
|
@@ -595,14 +615,11 @@ static bool avalon_detect_one(const char *devpath)
|
|
|
info->temp_old = 0;
|
|
|
info->frequency = frequency;
|
|
|
|
|
|
- /* Do something for failed reset ? */
|
|
|
- if (0) {
|
|
|
- /* Set asic to idle mode after detect */
|
|
|
- avalon_idle(avalon);
|
|
|
- avalon->device_fd = -1;
|
|
|
+ /* Set asic to idle mode after detect */
|
|
|
+ avalon_idle(avalon);
|
|
|
+ avalon->device_fd = -1;
|
|
|
|
|
|
- avalon_close(fd);
|
|
|
- }
|
|
|
+ avalon_close(fd);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
@@ -623,7 +640,7 @@ static void avalon_init(struct cgpu_info *avalon)
|
|
|
|
|
|
avalon->device_fd = -1;
|
|
|
fd = avalon_open(avalon->device_path,
|
|
|
- avalon_info[avalon->device_id]->baud);
|
|
|
+ avalon_infos[avalon->device_id]->baud);
|
|
|
if (unlikely(fd == -1)) {
|
|
|
applog(LOG_ERR, "Avalon: Failed to open on %s",
|
|
|
avalon->device_path);
|
|
|
@@ -636,7 +653,6 @@ static void avalon_init(struct cgpu_info *avalon)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- avalon->status = LIFE_INIT;
|
|
|
avalon->device_fd = fd;
|
|
|
__avalon_init(avalon);
|
|
|
}
|
|
|
@@ -644,43 +660,53 @@ static void avalon_init(struct cgpu_info *avalon)
|
|
|
static bool avalon_prepare(struct thr_info *thr)
|
|
|
{
|
|
|
struct cgpu_info *avalon = thr->cgpu;
|
|
|
+ struct avalon_info *info = avalon_infos[avalon->device_id];
|
|
|
struct timeval now;
|
|
|
|
|
|
+ free(avalon->works);
|
|
|
+ avalon->works = calloc(info->miner_count * sizeof(struct work *),
|
|
|
+ AVALON_ARRAY_SIZE);
|
|
|
+ if (!avalon->works)
|
|
|
+ quit(1, "Failed to calloc avalon works in avalon_prepare");
|
|
|
if (avalon->device_fd == -1)
|
|
|
avalon_init(avalon);
|
|
|
else
|
|
|
__avalon_init(avalon);
|
|
|
|
|
|
- gettimeofday(&now, NULL);
|
|
|
+ cgtime(&now);
|
|
|
get_datestamp(avalon->init, &now);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-static void avalon_free_work(struct thr_info *thr, struct work **work)
|
|
|
+static void avalon_free_work(struct thr_info *thr)
|
|
|
{
|
|
|
struct cgpu_info *avalon;
|
|
|
struct avalon_info *info;
|
|
|
+ struct work **works;
|
|
|
int i;
|
|
|
|
|
|
- if (unlikely(!work))
|
|
|
- return;
|
|
|
-
|
|
|
avalon = thr->cgpu;
|
|
|
- info = avalon_info[avalon->device_id];
|
|
|
+ avalon->queued = 0;
|
|
|
+ if (unlikely(!avalon->works))
|
|
|
+ return;
|
|
|
+ works = avalon->works;
|
|
|
+ info = avalon_infos[avalon->device_id];
|
|
|
|
|
|
- for (i = 0; i < info->miner_count; i++)
|
|
|
- if (likely(work[i])) {
|
|
|
- free_work(work[i]);
|
|
|
- work[i] = NULL;
|
|
|
+ for (i = 0; i < info->miner_count * 4; i++) {
|
|
|
+ if (works[i]) {
|
|
|
+ work_completed(avalon, works[i]);
|
|
|
+ works[i] = NULL;
|
|
|
}
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static void do_avalon_close(struct thr_info *thr)
|
|
|
{
|
|
|
struct avalon_result ar;
|
|
|
struct cgpu_info *avalon = thr->cgpu;
|
|
|
- struct avalon_info *info = avalon_info[avalon->device_id];
|
|
|
+ struct avalon_info *info = avalon_infos[avalon->device_id];
|
|
|
|
|
|
+ avalon_free_work(thr);
|
|
|
nmsleep(1000);
|
|
|
avalon_reset(avalon->device_fd, &ar);
|
|
|
avalon_idle(avalon);
|
|
|
@@ -688,16 +714,10 @@ static void do_avalon_close(struct thr_info *thr)
|
|
|
avalon->device_fd = -1;
|
|
|
|
|
|
info->no_matching_work = 0;
|
|
|
- avalon_free_work(thr, info->bulk0);
|
|
|
- avalon_free_work(thr, info->bulk1);
|
|
|
- avalon_free_work(thr, info->bulk2);
|
|
|
- avalon_free_work(thr, info->bulk3);
|
|
|
}
|
|
|
|
|
|
static inline void record_temp_fan(struct avalon_info *info, struct avalon_result *ar, float *temp_avg)
|
|
|
{
|
|
|
- int max;
|
|
|
-
|
|
|
info->fan0 = ar->fan0 * AVALON_FAN_FACTOR;
|
|
|
info->fan1 = ar->fan1 * AVALON_FAN_FACTOR;
|
|
|
info->fan2 = ar->fan2 * AVALON_FAN_FACTOR;
|
|
|
@@ -718,24 +738,14 @@ static inline void record_temp_fan(struct avalon_info *info, struct avalon_resul
|
|
|
info->temp2 = 0 - ((~ar->temp2 & 0x7f) + 1);
|
|
|
}
|
|
|
|
|
|
- *temp_avg = info->temp2;
|
|
|
-
|
|
|
- max = info->temp_max;
|
|
|
- if (info->temp0 > max)
|
|
|
- max = info->temp0;
|
|
|
- if (info->temp1 > max)
|
|
|
- max = info->temp1;
|
|
|
- if (info->temp2 > max)
|
|
|
- max = info->temp2;
|
|
|
- if (max >= 100) { /* FIXME: fix the root cause on fpga controller firmware */
|
|
|
- if (opt_debug) {
|
|
|
- applog(LOG_DEBUG, "Avalon: temp_max: %d", max);
|
|
|
- hexdump((uint8_t *)ar, AVALON_READ_SIZE);
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
+ *temp_avg = info->temp2 > info->temp1 ? info->temp2 : info->temp1;
|
|
|
|
|
|
- info->temp_max = max;
|
|
|
+ if (info->temp0 > info->temp_max)
|
|
|
+ info->temp_max = info->temp0;
|
|
|
+ if (info->temp1 > info->temp_max)
|
|
|
+ info->temp_max = info->temp1;
|
|
|
+ if (info->temp2 > info->temp_max)
|
|
|
+ info->temp_max = info->temp2;
|
|
|
}
|
|
|
|
|
|
static inline void adjust_fan(struct avalon_info *info)
|
|
|
@@ -756,29 +766,61 @@ static inline void adjust_fan(struct avalon_info *info)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static int64_t avalon_scanhash(struct thr_info *thr, struct work **work,
|
|
|
- __maybe_unused int64_t max_nonce)
|
|
|
+/* We use a replacement algorithm to only remove references to work done from
|
|
|
+ * the buffer when we need the extra space for new work. */
|
|
|
+static bool avalon_fill(struct cgpu_info *avalon)
|
|
|
+{
|
|
|
+ int subid, slot, mc = avalon_infos[avalon->device_id]->miner_count;
|
|
|
+ struct work *work;
|
|
|
+
|
|
|
+ if (avalon->queued >= mc)
|
|
|
+ return true;
|
|
|
+ work = get_queued(avalon);
|
|
|
+ if (unlikely(!work))
|
|
|
+ return false;
|
|
|
+ subid = avalon->queued++;
|
|
|
+ work->subid = subid;
|
|
|
+ slot = avalon->work_array * mc + subid;
|
|
|
+ if (likely(avalon->works[slot]))
|
|
|
+ work_completed(avalon, avalon->works[slot]);
|
|
|
+ avalon->works[slot] = work;
|
|
|
+ if (avalon->queued >= mc)
|
|
|
+ return true;
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static void avalon_rotate_array(struct cgpu_info *avalon)
|
|
|
+{
|
|
|
+ avalon->queued = 0;
|
|
|
+ if (++avalon->work_array >= AVALON_ARRAY_SIZE)
|
|
|
+ avalon->work_array = 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int64_t avalon_scanhash(struct thr_info *thr)
|
|
|
{
|
|
|
struct cgpu_info *avalon;
|
|
|
- int fd, ret, full;
|
|
|
+ struct work **works;
|
|
|
+ int fd, ret = AVA_GETS_OK, full;
|
|
|
|
|
|
struct avalon_info *info;
|
|
|
struct avalon_task at;
|
|
|
struct avalon_result ar;
|
|
|
- int i, work_i0, work_i1, work_i2, work_i3;
|
|
|
+ int i;
|
|
|
int avalon_get_work_count;
|
|
|
+ int start_count, end_count;
|
|
|
|
|
|
struct timeval tv_start, tv_finish, elapsed;
|
|
|
uint32_t nonce;
|
|
|
int64_t hash_count;
|
|
|
static int first_try = 0;
|
|
|
- int result_count, result_wrong;
|
|
|
+ int result_wrong;
|
|
|
|
|
|
avalon = thr->cgpu;
|
|
|
- info = avalon_info[avalon->device_id];
|
|
|
+ works = avalon->works;
|
|
|
+ info = avalon_infos[avalon->device_id];
|
|
|
avalon_get_work_count = info->miner_count;
|
|
|
|
|
|
- if (unlikely(avalon->device_fd == -1))
|
|
|
+ if (unlikely(avalon->device_fd == -1)) {
|
|
|
if (!avalon_prepare(thr)) {
|
|
|
applog(LOG_ERR, "AVA%i: Comms error(open)",
|
|
|
avalon->device_id);
|
|
|
@@ -786,35 +828,25 @@ static int64_t avalon_scanhash(struct thr_info *thr, struct work **work,
|
|
|
/* fail the device if the reopen attempt fails */
|
|
|
return -1;
|
|
|
}
|
|
|
+ }
|
|
|
fd = avalon->device_fd;
|
|
|
#ifndef WIN32
|
|
|
tcflush(fd, TCOFLUSH);
|
|
|
#endif
|
|
|
|
|
|
- for (i = 0; i < avalon_get_work_count; i++) {
|
|
|
- info->bulk0[i] = info->bulk1[i];
|
|
|
- info->bulk1[i] = info->bulk2[i];
|
|
|
- info->bulk2[i] = info->bulk3[i];
|
|
|
- info->bulk3[i] = work[i];
|
|
|
- applog(LOG_DEBUG, "Avalon: bulk0/1/2 buffer [%d]: %p, %p, %p, %p",
|
|
|
- i, info->bulk0[i], info->bulk1[i], info->bulk2[i], info->bulk3[i]);
|
|
|
- }
|
|
|
-
|
|
|
- i = 0;
|
|
|
+ start_count = avalon->work_array * avalon_get_work_count;
|
|
|
+ end_count = start_count + avalon_get_work_count;
|
|
|
+ i = start_count;
|
|
|
while (true) {
|
|
|
avalon_init_task(&at, 0, 0, info->fan_pwm,
|
|
|
info->timeout, info->asic_count,
|
|
|
info->miner_count, 1, 0, info->frequency);
|
|
|
- avalon_create_task(&at, work[i]);
|
|
|
+ avalon_create_task(&at, works[i]);
|
|
|
ret = avalon_send_task(fd, &at, avalon);
|
|
|
if (unlikely(ret == AVA_SEND_ERROR ||
|
|
|
(ret == AVA_SEND_BUFFER_EMPTY &&
|
|
|
- (i + 1 == avalon_get_work_count) &&
|
|
|
+ (i + 1 == end_count) &&
|
|
|
first_try))) {
|
|
|
- avalon_free_work(thr, info->bulk0);
|
|
|
- avalon_free_work(thr, info->bulk1);
|
|
|
- avalon_free_work(thr, info->bulk2);
|
|
|
- avalon_free_work(thr, info->bulk3);
|
|
|
do_avalon_close(thr);
|
|
|
applog(LOG_ERR, "AVA%i: Comms error(buffer)",
|
|
|
avalon->device_id);
|
|
|
@@ -824,12 +856,13 @@ static int64_t avalon_scanhash(struct thr_info *thr, struct work **work,
|
|
|
avalon_init(avalon);
|
|
|
return 0; /* This should never happen */
|
|
|
}
|
|
|
- if (ret == AVA_SEND_BUFFER_EMPTY && (i + 1 == avalon_get_work_count)) {
|
|
|
+ if (ret == AVA_SEND_BUFFER_EMPTY && (i + 1 == end_count)) {
|
|
|
first_try = 1;
|
|
|
+ avalon_rotate_array(avalon);
|
|
|
return 0xffffffff;
|
|
|
}
|
|
|
|
|
|
- work[i]->blk.nonce = 0xffffffff;
|
|
|
+ works[i]->blk.nonce = 0xffffffff;
|
|
|
|
|
|
if (ret == AVA_SEND_BUFFER_FULL)
|
|
|
break;
|
|
|
@@ -840,14 +873,11 @@ static int64_t avalon_scanhash(struct thr_info *thr, struct work **work,
|
|
|
first_try = 0;
|
|
|
|
|
|
elapsed.tv_sec = elapsed.tv_usec = 0;
|
|
|
- gettimeofday(&tv_start, NULL);
|
|
|
+ cgtime(&tv_start);
|
|
|
|
|
|
- result_count = 0;
|
|
|
result_wrong = 0;
|
|
|
hash_count = 0;
|
|
|
while (true) {
|
|
|
- work_i0 = work_i1 = work_i2 = work_i3 = -1;
|
|
|
-
|
|
|
full = avalon_buffer_full(fd);
|
|
|
applog(LOG_DEBUG, "Avalon: Buffer full: %s",
|
|
|
((full == AVA_BUFFER_FULL) ? "Yes" : "No"));
|
|
|
@@ -856,194 +886,96 @@ static int64_t avalon_scanhash(struct thr_info *thr, struct work **work,
|
|
|
|
|
|
ret = avalon_get_result(fd, &ar, thr, &tv_finish);
|
|
|
if (unlikely(ret == AVA_GETS_ERROR)) {
|
|
|
- avalon_free_work(thr, info->bulk0);
|
|
|
- avalon_free_work(thr, info->bulk1);
|
|
|
- avalon_free_work(thr, info->bulk2);
|
|
|
- avalon_free_work(thr, info->bulk3);
|
|
|
do_avalon_close(thr);
|
|
|
applog(LOG_ERR,
|
|
|
"AVA%i: Comms error(read)", avalon->device_id);
|
|
|
dev_error(avalon, REASON_DEV_COMMS_ERROR);
|
|
|
return 0;
|
|
|
}
|
|
|
+ if (unlikely(ret == AVA_GETS_RESTART))
|
|
|
+ break;
|
|
|
if (unlikely(ret == AVA_GETS_TIMEOUT)) {
|
|
|
timersub(&tv_finish, &tv_start, &elapsed);
|
|
|
applog(LOG_DEBUG, "Avalon: no nonce in (%ld.%06lds)",
|
|
|
- elapsed.tv_sec, elapsed.tv_usec);
|
|
|
+ (long)elapsed.tv_sec, (long)elapsed.tv_usec);
|
|
|
continue;
|
|
|
}
|
|
|
- if (unlikely(ret == AVA_GETS_RESTART)) {
|
|
|
- avalon_free_work(thr, info->bulk0);
|
|
|
- avalon_free_work(thr, info->bulk1);
|
|
|
- avalon_free_work(thr, info->bulk2);
|
|
|
- avalon_free_work(thr, info->bulk3);
|
|
|
- break;
|
|
|
+
|
|
|
+ if (!avalon_decode_nonce(thr, &ar, &nonce)) {
|
|
|
+ info->no_matching_work++;
|
|
|
+ result_wrong++;
|
|
|
+
|
|
|
+ if (unlikely(result_wrong >= avalon_get_work_count))
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (opt_debug) {
|
|
|
+ timersub(&tv_finish, &tv_start, &elapsed);
|
|
|
+ applog(LOG_DEBUG,"Avalon: no matching work: %d"
|
|
|
+ " (%ld.%06lds)", info->no_matching_work,
|
|
|
+ (long)elapsed.tv_sec, (long)elapsed.tv_usec);
|
|
|
+ }
|
|
|
+ continue;
|
|
|
}
|
|
|
- result_count++;
|
|
|
-
|
|
|
- work_i0 = avalon_decode_nonce(thr, info->bulk0, &ar, &nonce);
|
|
|
- if (work_i0 < 0) {
|
|
|
- work_i1 = avalon_decode_nonce(thr, info->bulk1, &ar, &nonce);
|
|
|
- if (work_i1 < 0) {
|
|
|
- work_i2 = avalon_decode_nonce(thr, info->bulk2, &ar, &nonce);
|
|
|
- if (work_i2 < 0) {
|
|
|
- work_i3 = avalon_decode_nonce(thr, info->bulk3, &ar, &nonce);
|
|
|
- if (work_i3 < 0) {
|
|
|
- info->no_matching_work++;
|
|
|
- result_wrong++;
|
|
|
-
|
|
|
- if (opt_debug) {
|
|
|
- timersub(&tv_finish, &tv_start, &elapsed);
|
|
|
- applog(LOG_DEBUG,"Avalon: no matching work: %d"
|
|
|
- " (%ld.%06lds)", info->no_matching_work,
|
|
|
- elapsed.tv_sec, elapsed.tv_usec);
|
|
|
- }
|
|
|
- continue;
|
|
|
- } else
|
|
|
- submit_nonce(thr, info->bulk3[work_i3], nonce);
|
|
|
- } else
|
|
|
- submit_nonce(thr, info->bulk2[work_i2], nonce);
|
|
|
- } else
|
|
|
- submit_nonce(thr, info->bulk1[work_i1], nonce);
|
|
|
- } else
|
|
|
- submit_nonce(thr, info->bulk0[work_i0], nonce);
|
|
|
-
|
|
|
- hash_count += nonce;
|
|
|
+
|
|
|
+ hash_count += 0xffffffff;
|
|
|
if (opt_debug) {
|
|
|
timersub(&tv_finish, &tv_start, &elapsed);
|
|
|
applog(LOG_DEBUG,
|
|
|
- "Avalon: nonce = 0x%08x = 0x%08llx hashes "
|
|
|
- "(%ld.%06lds)", nonce, hash_count,
|
|
|
+ "Avalon: nonce = 0x%08x = 0x%08"PRIx64" hashes "
|
|
|
+ "(%ld.%06lds)", nonce, (uint64_t)hash_count,
|
|
|
elapsed.tv_sec, elapsed.tv_usec);
|
|
|
}
|
|
|
}
|
|
|
- if (result_wrong && result_count == result_wrong) {
|
|
|
- /* This mean FPGA controller give all wrong result
|
|
|
- * try to reset the Avalon */
|
|
|
- avalon_free_work(thr, info->bulk0);
|
|
|
- avalon_free_work(thr, info->bulk1);
|
|
|
- avalon_free_work(thr, info->bulk2);
|
|
|
- avalon_free_work(thr, info->bulk3);
|
|
|
+ if (hash_count && avalon->results < AVALON_ARRAY_SIZE)
|
|
|
+ avalon->results++;
|
|
|
+ if (unlikely((result_wrong >= avalon_get_work_count) ||
|
|
|
+ (!hash_count && ret != AVA_GETS_RESTART && --avalon->results < 0))) {
|
|
|
+ /* Look for all invalid results, or consecutive failure
|
|
|
+ * to generate any results suggesting the FPGA
|
|
|
+ * controller has screwed up. */
|
|
|
do_avalon_close(thr);
|
|
|
applog(LOG_ERR,
|
|
|
- "AVA%i: FPGA controller mess up", avalon->device_id);
|
|
|
+ "AVA%i: FPGA controller messed up, %d wrong results",
|
|
|
+ avalon->device_id, result_wrong);
|
|
|
dev_error(avalon, REASON_DEV_COMMS_ERROR);
|
|
|
- do_avalon_close(thr);
|
|
|
nmsleep(1000);
|
|
|
avalon_init(avalon);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- avalon_free_work(thr, info->bulk0);
|
|
|
-
|
|
|
- record_temp_fan(info, &ar, &(avalon->temp));
|
|
|
- applog(LOG_INFO,
|
|
|
- "Avalon: Fan1: %d/m, Fan2: %d/m, Fan3: %d/m\t"
|
|
|
- "Temp1: %dC, Temp2: %dC, Temp3: %dC, TempMAX: %dC",
|
|
|
- info->fan0, info->fan1, info->fan2,
|
|
|
- info->temp0, info->temp1, info->temp2, info->temp_max);
|
|
|
- info->temp_history_index++;
|
|
|
- info->temp_sum += info->temp2;
|
|
|
- applog(LOG_DEBUG, "Avalon: temp_index: %d, temp_count: %d, temp_old: %d",
|
|
|
- info->temp_history_index, info->temp_history_count, info->temp_old);
|
|
|
- if (info->temp_history_index == info->temp_history_count) {
|
|
|
- adjust_fan(info);
|
|
|
- info->temp_history_index = 0;
|
|
|
- info->temp_sum = 0;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * FIXME: Each work split to 10 pieces, each piece send to a
|
|
|
- * asic(256MHs). one work can be mulit-nonce back. it is not
|
|
|
- * easy calculate correct hash on such situation. so I simplely
|
|
|
- * add each nonce to hash_count. base on Utility/m hash_count*2
|
|
|
- * give a very good result.
|
|
|
- *
|
|
|
- * Any patch will be great.
|
|
|
- */
|
|
|
- return (hash_count * 2);
|
|
|
-}
|
|
|
-
|
|
|
-// minerloop_scanhash hacked to handle Avalon's many processors
|
|
|
-static
|
|
|
-void minerloop_avalon(struct thr_info *mythr)
|
|
|
-{
|
|
|
- const int thr_id = mythr->id;
|
|
|
- struct cgpu_info *cgpu = mythr->cgpu;
|
|
|
- struct device_drv *api = cgpu->drv;
|
|
|
- struct timeval tv_start, tv_end;
|
|
|
- struct timeval tv_hashes;
|
|
|
- uint32_t max_nonce = api->can_limit_work ? api->can_limit_work(mythr) : 0xffffffff;
|
|
|
- int64_t hashes;
|
|
|
- struct avalon_info *info = avalon_info[cgpu->device_id];
|
|
|
- int i;
|
|
|
- int avalon_get_work_count = info->miner_count;
|
|
|
- struct work **work = calloc(1,
|
|
|
- avalon_get_work_count * sizeof(struct work *));
|
|
|
- if (!work)
|
|
|
- quit(1, "Faile on Avalon calloc");
|
|
|
- const bool primary = (!mythr->device_thread) || mythr->primary_thread;
|
|
|
-
|
|
|
- while (1) {
|
|
|
- mythr->work_restart = false;
|
|
|
- for (i = 0; i < avalon_get_work_count; i++)
|
|
|
- request_work(mythr);
|
|
|
- for (i = 0; i < avalon_get_work_count; i++) {
|
|
|
- work[i] = get_work(mythr);
|
|
|
- work[i]->blk.nonce = 0;
|
|
|
+ avalon_rotate_array(avalon);
|
|
|
+
|
|
|
+ if (hash_count) {
|
|
|
+ record_temp_fan(info, &ar, &(avalon->temp));
|
|
|
+ applog(LOG_INFO,
|
|
|
+ "Avalon: Fan1: %d/m, Fan2: %d/m, Fan3: %d/m\t"
|
|
|
+ "Temp1: %dC, Temp2: %dC, Temp3: %dC, TempMAX: %dC",
|
|
|
+ info->fan0, info->fan1, info->fan2,
|
|
|
+ info->temp0, info->temp1, info->temp2, info->temp_max);
|
|
|
+ info->temp_history_index++;
|
|
|
+ info->temp_sum += avalon->temp;
|
|
|
+ applog(LOG_DEBUG, "Avalon: temp_index: %d, temp_count: %d, temp_old: %d",
|
|
|
+ info->temp_history_index, info->temp_history_count, info->temp_old);
|
|
|
+ if (info->temp_history_index == info->temp_history_count) {
|
|
|
+ adjust_fan(info);
|
|
|
+ info->temp_history_index = 0;
|
|
|
+ info->temp_sum = 0;
|
|
|
}
|
|
|
- for (i = 0; i < avalon_get_work_count; i++) {
|
|
|
- if (api->prepare_work && !api->prepare_work(mythr, work[i])) {
|
|
|
- applog(LOG_ERR, "work prepare failed, exiting "
|
|
|
- "mining thread %d", thr_id);
|
|
|
- break;
|
|
|
- }
|
|
|
- gettimeofday(&(work[i]->tv_work_start), NULL);
|
|
|
- }
|
|
|
-
|
|
|
- do {
|
|
|
- thread_reportin(mythr);
|
|
|
- gettimeofday(&tv_start, NULL);
|
|
|
- hashes = api->scanhash_queue(mythr, work, max_nonce);
|
|
|
- gettimeofday(&tv_end, NULL);
|
|
|
- thread_reportin(mythr);
|
|
|
-
|
|
|
- timersub(&tv_end, &tv_start, &tv_hashes);
|
|
|
- if (!hashes_done(mythr, hashes, &tv_hashes, api->can_limit_work ? &max_nonce : NULL))
|
|
|
- goto disabled;
|
|
|
-
|
|
|
- if (unlikely(mythr->work_restart)) {
|
|
|
- /* Apart from device_thread 0, we stagger the
|
|
|
- * starting of every next thread to try and get
|
|
|
- * all devices busy before worrying about
|
|
|
- * getting work for their extra threads */
|
|
|
- if (!primary) {
|
|
|
- struct timespec rgtp;
|
|
|
-
|
|
|
- rgtp.tv_sec = 0;
|
|
|
- rgtp.tv_nsec = 250 * mythr->device_thread * 1000000;
|
|
|
- nanosleep(&rgtp, NULL);
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- if (unlikely(mythr->pause || cgpu->deven != DEV_ENABLED))
|
|
|
-disabled:
|
|
|
- mt_disable(mythr);
|
|
|
- } while (false);
|
|
|
}
|
|
|
- free(work);
|
|
|
+
|
|
|
+ /* This hashmeter is just a utility counter based on returned shares */
|
|
|
+ return hash_count;
|
|
|
}
|
|
|
|
|
|
-static struct api_data *avalon_drv_stats(struct cgpu_info *cgpu)
|
|
|
+static struct api_data *avalon_api_stats(struct cgpu_info *cgpu)
|
|
|
{
|
|
|
struct api_data *root = NULL;
|
|
|
- struct avalon_info *info = avalon_info[cgpu->device_id];
|
|
|
+ struct avalon_info *info = avalon_infos[cgpu->device_id];
|
|
|
+ int i;
|
|
|
|
|
|
root = api_add_int(root, "baud", &(info->baud), false);
|
|
|
root = api_add_int(root, "miner_count", &(info->miner_count),false);
|
|
|
root = api_add_int(root, "asic_count", &(info->asic_count), false);
|
|
|
- root = api_add_int(root, "read_count", &(info->read_count), false);
|
|
|
root = api_add_int(root, "timeout", &(info->timeout), false);
|
|
|
root = api_add_int(root, "frequency", &(info->frequency), false);
|
|
|
|
|
|
@@ -1057,30 +989,12 @@ static struct api_data *avalon_drv_stats(struct cgpu_info *cgpu)
|
|
|
root = api_add_int(root, "temp_max", &(info->temp_max), false);
|
|
|
|
|
|
root = api_add_int(root, "no_matching_work", &(info->no_matching_work), false);
|
|
|
- root = api_add_int(root, "matching_work_count1", &(info->matching_work[0]), false);
|
|
|
- root = api_add_int(root, "matching_work_count2", &(info->matching_work[1]), false);
|
|
|
- root = api_add_int(root, "matching_work_count3", &(info->matching_work[2]), false);
|
|
|
- root = api_add_int(root, "matching_work_count4", &(info->matching_work[3]), false);
|
|
|
- root = api_add_int(root, "matching_work_count5", &(info->matching_work[4]), false);
|
|
|
- root = api_add_int(root, "matching_work_count6", &(info->matching_work[5]), false);
|
|
|
- root = api_add_int(root, "matching_work_count7", &(info->matching_work[6]), false);
|
|
|
- root = api_add_int(root, "matching_work_count8", &(info->matching_work[7]), false);
|
|
|
- root = api_add_int(root, "matching_work_count9", &(info->matching_work[8]), false);
|
|
|
- root = api_add_int(root, "matching_work_count10", &(info->matching_work[9]), false);
|
|
|
- root = api_add_int(root, "matching_work_count11", &(info->matching_work[10]), false);
|
|
|
- root = api_add_int(root, "matching_work_count12", &(info->matching_work[11]), false);
|
|
|
- root = api_add_int(root, "matching_work_count13", &(info->matching_work[12]), false);
|
|
|
- root = api_add_int(root, "matching_work_count14", &(info->matching_work[13]), false);
|
|
|
- root = api_add_int(root, "matching_work_count15", &(info->matching_work[14]), false);
|
|
|
- root = api_add_int(root, "matching_work_count16", &(info->matching_work[15]), false);
|
|
|
- root = api_add_int(root, "matching_work_count17", &(info->matching_work[16]), false);
|
|
|
- root = api_add_int(root, "matching_work_count18", &(info->matching_work[17]), false);
|
|
|
- root = api_add_int(root, "matching_work_count19", &(info->matching_work[18]), false);
|
|
|
- root = api_add_int(root, "matching_work_count20", &(info->matching_work[19]), false);
|
|
|
- root = api_add_int(root, "matching_work_count21", &(info->matching_work[20]), false);
|
|
|
- root = api_add_int(root, "matching_work_count22", &(info->matching_work[21]), false);
|
|
|
- root = api_add_int(root, "matching_work_count23", &(info->matching_work[22]), false);
|
|
|
- root = api_add_int(root, "matching_work_count24", &(info->matching_work[23]), false);
|
|
|
+ for (i = 0; i < info->miner_count; i++) {
|
|
|
+ char mcw[24];
|
|
|
+
|
|
|
+ sprintf(mcw, "match_work_count%d", i + 1);
|
|
|
+ root = api_add_int(root, mcw, &(info->matching_work[i]), false);
|
|
|
+ }
|
|
|
|
|
|
return root;
|
|
|
}
|
|
|
@@ -1095,9 +1009,10 @@ struct device_drv avalon_drv = {
|
|
|
.name = "AVA",
|
|
|
.drv_detect = avalon_detect,
|
|
|
.thread_prepare = avalon_prepare,
|
|
|
- .minerloop = minerloop_avalon,
|
|
|
- .scanhash_queue = avalon_scanhash,
|
|
|
- .get_api_stats = avalon_drv_stats,
|
|
|
+ .minerloop = hash_queued_work,
|
|
|
+ .queue_full = avalon_fill,
|
|
|
+ .scanwork = avalon_scanhash,
|
|
|
+ .get_api_stats = avalon_api_stats,
|
|
|
.reinit_device = avalon_init,
|
|
|
.thread_shutdown = avalon_shutdown,
|
|
|
};
|