Browse Source

MMQ new initialisation (that works) and clocking control

nfactor-troky
Kano 12 years ago
parent
commit
fc14b2ee07
  1. 46
      FPGA-README
  2. 4
      cgminer.c
  3. 356
      driver-modminer.c
  4. 93
      fpgautils.c
  5. 43
      fpgautils.h
  6. 12
      miner.h

46
FPGA-README

@ -2,7 +2,48 @@
This README contains extended details about FPGA mining with cgminer This README contains extended details about FPGA mining with cgminer
Bitforce ModMinerQuad (MMQ)
------------------
The mining bitstream does not survive a power cycle, so cgminer will upload
it, if it needs to, before it starts mining
You must make sure you have an approriate firmware in your MMQ
Read here for official details of changing the firmware:
http://wiki.btcfpga.com/index.php?title=Firmware
The basics of changing the firmware are:
Join the 2 left pads of the "RESET" pad with wire and the led will dim
Without dicsonnecting the "RESET", join the 2 left pads of the "ISP" pad
with a wire and it will stay dim
Release "RESET" then release "ISP" and is should still be dim
Unplug the USB and when you plug it back in it will show up as a mass
storage device
Linux: (as one single line):
mcopy -i /dev/disk/by-id/usb-NXP_LPC134X_IFLASH_ISP000000000-0:0
modminer091012.bin ::/firmware.bin
Windows: delete the MSD device file firmware.bin and copy in the new one
rename the new file and put it under the same name 'firmware.bin'
Disconnect the USB correctly (so writes are flushed first)
Join and then disconnect "RESET" and then plug the USB back in and it's done
Best to update to one of the latest 2 listed below if you don't already
have one of them in your MMQ
The current latest different firmware are:
Latest for support of normal or TLM bitstream:
http://btcfpga.com/files/firmware/modminer092612-TLM.bin
Latest with only normal bitstream support (Temps/HW Fix):
http://btcfpga.com/files/firmware/modminer091012.bin
The code is currently tested on the modminer091012.bin firmware.
This comment will be updated when others have been tested
Bitforce (BFL)
--------------
--bfl-range Use nonce range on bitforce devices if supported --bfl-range Use nonce range on bitforce devices if supported
@ -37,7 +78,8 @@ the MH/s value reported with the changed firmware - and the MH/s reported
will be less than the firmware speed since you lose work on every block change. will be less than the firmware speed since you lose work on every block change.
Icarus Icarus (ICA)
------------
There are two hidden options in cgminer when Icarus support is compiled in: There are two hidden options in cgminer when Icarus support is compiled in:

4
cgminer.c

@ -4870,6 +4870,10 @@ static bool hashtest(struct thr_info *thr, struct work *work)
thr->cgpu->api->name, thr->cgpu->device_id); thr->cgpu->api->name, thr->cgpu->device_id);
hw_errors++; hw_errors++;
thr->cgpu->hw_errors++; thr->cgpu->hw_errors++;
if (thr->cgpu->api->hw_error)
thr->cgpu->api->hw_error(thr);
return false; return false;
} }

356
driver-modminer.c

@ -1,4 +1,5 @@
/* /*
* Copyright 2012 Andrew Smith
* Copyright 2012 Luke Dashjr * Copyright 2012 Luke Dashjr
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@ -12,6 +13,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <math.h>
#include "logging.h" #include "logging.h"
#include "miner.h" #include "miner.h"
@ -21,10 +23,31 @@
#define BITSTREAM_FILENAME "fpgaminer_top_fixed7_197MHz.ncd" #define BITSTREAM_FILENAME "fpgaminer_top_fixed7_197MHz.ncd"
#define BISTREAM_USER_ID "\2\4$B" #define BISTREAM_USER_ID "\2\4$B"
#define MODMINER_CUTOFF_TEMP 60.0
#define MODMINER_OVERHEAT_TEMP 50.0
#define MODMINER_OVERHEAT_CLOCK -10
#define MODMINER_HW_ERROR_PERCENT 0.75
#define MODMINER_MAX_CLOCK 220
#define MODMINER_DEF_CLOCK 200
#define MODMINER_MIN_CLOCK 160
#define MODMINER_CLOCK_DOWN -2
#define MODMINER_CLOCK_SET 0
#define MODMINER_CLOCK_UP 2
// Maximum how many good shares in a row means clock up
// 96 is ~34m22s at 200MH/s
#define MODMINER_TRY_UP 96
// Initially how many good shares in a row means clock up
// This is doubled each down clock until it reaches MODMINER_TRY_UP
// 6 is ~2m9s at 200MH/s
#define MODMINER_EARLY_UP 6
struct device_api modminer_api; struct device_api modminer_api;
static inline bool static inline bool _bailout(int fd, struct cgpu_info *modminer, int prio, const char *fmt, ...)
_bailout(int fd, struct cgpu_info*modminer, int prio, const char *fmt, ...)
{ {
if (fd != -1) if (fd != -1)
serial_close(fd); serial_close(fd);
@ -39,42 +62,112 @@ _bailout(int fd, struct cgpu_info*modminer, int prio, const char *fmt, ...)
va_end(ap); va_end(ap);
return false; return false;
} }
#define bailout(...) return _bailout(fd, NULL, __VA_ARGS__);
static bool // 45 noops sent when detecting, in case the device was left in "start job" reading
modminer_detect_one(const char *devpath) static const char NOOP[] = "\0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
{
int fd = serial_open(devpath, 0, 10, true);
if (unlikely(fd == -1))
bailout(LOG_DEBUG, "ModMiner detect: failed to open %s", devpath);
static bool modminer_detect_one(const char *devpath)
{
char buf[0x100]; char buf[0x100];
char *devname;
ssize_t len; ssize_t len;
int fd;
#ifdef WIN32
fd = serial_open(devpath, 0, 10, true);
if (fd < 0) {
applog(LOG_ERR, "ModMiner detect: failed to open %s", devpath);
return false;
}
// Sending 45 noops, just in case the device was left in "start job" reading (void)(write(fd, NOOP, sizeof(NOOP)-1) ?:0);
(void)(write(fd, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 45) ?:0);
while (serial_read(fd, buf, sizeof(buf)) > 0) while (serial_read(fd, buf, sizeof(buf)) > 0)
; ;
if (1 != write(fd, "\x01", 1)) // Get version // Version
bailout(LOG_DEBUG, "ModMiner detect: write failed on %s (get version)", devpath); if (1 != write(fd, "\x01", 1)) {
applog(LOG_ERR, "ModMiner detect: version request failed on %s (%d)", devpath, errno);
goto shin;
}
len = serial_read(fd, buf, sizeof(buf)-1); len = serial_read(fd, buf, sizeof(buf)-1);
if (len < 1) if (len < 1) {
bailout(LOG_DEBUG, "ModMiner detect: no response to version request from %s", devpath); applog(LOG_ERR, "ModMiner detect: no version reply on %s (%d)", devpath, errno);
goto shin;
}
buf[len] = '\0'; buf[len] = '\0';
char*devname = strdup(buf); devname = strdup(buf);
applog(LOG_DEBUG, "ModMiner identified as: %s", devname); applog(LOG_DEBUG, "ModMiner identified as: %s", devname);
if (1 != write(fd, "\x02", 1)) // Get FPGA count // FPGA count
bailout(LOG_DEBUG, "ModMiner detect: write failed on %s (get FPGA count)", devpath); if (1 != write(fd, "\x02", 1)) {
applog(LOG_ERR, "ModMiner detect: FPGA count request failed on %s (%d)", devpath, errno);
goto shin;
}
len = read(fd, buf, 1); len = read(fd, buf, 1);
if (len < 1)
bailout(LOG_ERR, "ModMiner detect: timeout waiting for FPGA count from %s", devpath); if (len < 1) {
if (!buf[0]) applog(LOG_ERR, "ModMiner detect: timeout waiting for FPGA count from %s (%d)", devpath, errno);
bailout(LOG_ERR, "ModMiner detect: zero FPGAs reported on %s", devpath); goto shin;
applog(LOG_DEBUG, "ModMiner %s has %u FPGAs", devname, buf[0]); }
serial_close(fd); serial_close(fd);
#else
fd = select_open(devpath);
if (fd < 0) {
applog(LOG_ERR, "ModMiner detect: failed to open %s", devpath);
return false;
}
// Don't care if they fail
select_write(fd, (char *)NOOP, sizeof(NOOP)-1);
// Will clear up to a max of sizeof(buf)-1 chars
select_read(fd, buf, sizeof(buf)-1);
// Version
if (select_write(fd, "\x01", 1) < 1) {
applog(LOG_ERR, "ModMiner detect: version request failed on %s (%d)", devpath, errno);
goto shin;
}
if ((len = select_read(fd, buf, sizeof(buf)-1)) < 1) {
applog(LOG_ERR, "ModMiner detect: no version reply on %s (%d)", devpath, errno);
goto shin;
}
buf[len] = '\0';
devname = strdup(buf);
applog(LOG_DEBUG, "ModMiner identified as: %s", devname);
// FPGA count
if (select_write(fd, "\x02", 1) < 1) {
applog(LOG_ERR, "ModMiner detect: FPGA count request failed on %s (%d)", devpath, errno);
goto shin;
}
if ((len = select_read(fd, buf, 1)) < 1) {
applog(LOG_ERR, "ModMiner detect: no FPGA count reply on %s (%d)", devpath, errno);
goto shin;
}
select_close(fd);
#endif
// TODO: check if it supports 2 byte temperatures and if not
// add a flag and set it use 1 byte and code to use the flag
if (buf[0] == 0) {
applog(LOG_ERR, "ModMiner detect: zero FPGA count from %s", devpath);
goto shin;
}
if (buf[0] < 1 || buf[0] > 4) {
applog(LOG_ERR, "ModMiner detect: invalid FPGA count (%u) from %s", buf[0], devpath);
goto shin;
}
applog(LOG_DEBUG, "ModMiner %s has %u FPGAs", devname, buf[0]);
struct cgpu_info *modminer; struct cgpu_info *modminer;
modminer = calloc(1, sizeof(*modminer)); modminer = calloc(1, sizeof(*modminer));
@ -85,24 +178,28 @@ modminer_detect_one(const char *devpath)
modminer->deven = DEV_ENABLED; modminer->deven = DEV_ENABLED;
modminer->threads = buf[0]; modminer->threads = buf[0];
modminer->name = devname; modminer->name = devname;
modminer->cutofftemp = 85;
return add_cgpu(modminer); return add_cgpu(modminer);
}
#undef bailout shin:
#ifdef WIN32
serial_close(fd);
#else
select_close(fd);
#endif
return false;
}
static int static int modminer_detect_auto()
modminer_detect_auto()
{ {
return return
serial_autodetect_udev (modminer_detect_one, "BTCFPGA*ModMiner") ?: serial_autodetect_udev (modminer_detect_one, "*ModMiner*") ?:
serial_autodetect_devserial(modminer_detect_one, "BTCFPGA_ModMiner") ?: serial_autodetect_devserial(modminer_detect_one, "BTCFPGA_ModMiner") ?:
0; 0;
} }
static void static void modminer_detect()
modminer_detect()
{ {
serial_detect_auto(&modminer_api, modminer_detect_one, modminer_detect_auto); serial_detect_auto(&modminer_api, modminer_detect_one, modminer_detect_auto);
} }
@ -138,12 +235,11 @@ select(fd+1, &fds, NULL, NULL, NULL); \
bailout2(LOG_ERR, "%s %u: Wrong " eng " programming %s", modminer->api->name, modminer->device_id, modminer->device_path); \ bailout2(LOG_ERR, "%s %u: Wrong " eng " programming %s", modminer->api->name, modminer->device_id, modminer->device_path); \
} while(0) } while(0)
static bool static bool modminer_fpga_upload_bitstream(struct cgpu_info *modminer)
modminer_fpga_upload_bitstream(struct cgpu_info*modminer)
{ {
fd_set fds; fd_set fds;
char buf[0x100]; char buf[0x100];
unsigned char *ubuf = (unsigned char*)buf; unsigned char *ubuf = (unsigned char *)buf;
unsigned long len; unsigned long len;
char *p; char *p;
const char *fwfile = BITSTREAM_FILENAME; const char *fwfile = BITSTREAM_FILENAME;
@ -215,10 +311,9 @@ modminer_fpga_upload_bitstream(struct cgpu_info*modminer)
return true; return true;
} }
static bool static bool modminer_device_prepare(struct cgpu_info *modminer)
modminer_device_prepare(struct cgpu_info *modminer)
{ {
int fd = serial_open(modminer->device_path, 0, /*FIXME=-1*/3000, true); int fd = serial_open(modminer->device_path, 0, 10, true);
if (unlikely(-1 == fd)) if (unlikely(-1 == fd))
bailout(LOG_ERR, "%s %u: Failed to open %s", modminer->api->name, modminer->device_id, modminer->device_path); bailout(LOG_ERR, "%s %u: Failed to open %s", modminer->api->name, modminer->device_id, modminer->device_path);
@ -234,12 +329,12 @@ modminer_device_prepare(struct cgpu_info *modminer)
#undef bailout #undef bailout
static bool static bool modminer_fpga_prepare(struct thr_info *thr)
modminer_fpga_prepare(struct thr_info *thr)
{ {
struct cgpu_info *modminer = thr->cgpu; struct cgpu_info *modminer = thr->cgpu;
// Don't need to lock the mutex here, since prepare runs from the main thread before the miner threads start // Don't need to lock the mutex here,
// since prepare runs from the main thread before the miner threads start
if (modminer->device_fd == -1 && !modminer_device_prepare(modminer)) if (modminer->device_fd == -1 && !modminer_device_prepare(modminer))
return false; return false;
@ -247,43 +342,86 @@ modminer_fpga_prepare(struct thr_info *thr)
state = thr->cgpu_data = calloc(1, sizeof(struct modminer_fpga_state)); state = thr->cgpu_data = calloc(1, sizeof(struct modminer_fpga_state));
state->next_work_cmd[0] = '\x08'; // Send Job state->next_work_cmd[0] = '\x08'; // Send Job
state->next_work_cmd[1] = thr->device_thread; // FPGA id state->next_work_cmd[1] = thr->device_thread; // FPGA id
state->shares_to_good = MODMINER_EARLY_UP;
return true; return true;
} }
static bool /*
modminer_reduce_clock(struct thr_info*thr, bool needlock) * Clocking rules:
* If device exceeds cutoff temp - shut down - and decrease the clock by
* MODMINER_OVERHEAT_CLOCK for when it restarts
*
* When to clock down:
* If device overheats
* or
* If device gets MODMINER_HW_ERROR_PERCENT errors since last clock up or down
* if clock is <= default it requires 2 HW to do this test
* if clock is > default it only requires 1 HW to do this test
*
* When to clock up:
* If device gets shares_to_good good shares in a row
*
* N.B. clock must always be a multiple of 2
*/
static bool modminer_delta_clock(struct thr_info *thr, bool needlock, int delta, bool temp)
{ {
struct cgpu_info*modminer = thr->cgpu; struct cgpu_info *modminer = thr->cgpu;
struct modminer_fpga_state *state = thr->cgpu_data; struct modminer_fpga_state *state = thr->cgpu_data;
char fpgaid = thr->device_thread; char fpgaid = thr->device_thread;
int fd = modminer->device_fd; int fd = modminer->device_fd;
unsigned char cmd[6], buf[1]; unsigned char cmd[6], buf[1];
struct timeval now;
gettimeofday(&now, NULL);
// Only do once if multiple shares per work or multiple reasons
// Since the temperature down clock test is first in the code this is OK
if (tdiff(&now, &(state->last_changed)) < 0.5)
return false;
if (state->clock <= 100) // Update before possibly aborting to avoid repeating unnecessarily
memcpy(&(state->last_changed), &now, sizeof(struct timeval));
state->shares = 0;
state->shares_last_hw = 0;
state->hw_errors = 0;
// If drop requested due to temperature, clock drop is always allowed
if (!temp && delta < 0 && state->clock <= MODMINER_MIN_CLOCK)
return false;
if (delta > 0 && state->clock >= MODMINER_MAX_CLOCK)
return false; return false;
if (delta < 0) {
if ((state->shares_to_good * 2) < MODMINER_TRY_UP)
state->shares_to_good *= 2;
else
state->shares_to_good = MODMINER_TRY_UP;
}
state->clock += delta;
cmd[0] = '\x06'; // set clock speed cmd[0] = '\x06'; // set clock speed
cmd[1] = fpgaid; cmd[1] = fpgaid;
cmd[2] = state->clock -= 2; cmd[2] = state->clock;
cmd[3] = cmd[4] = cmd[5] = '\0'; cmd[3] = cmd[4] = cmd[5] = '\0';
if (needlock) if (needlock)
mutex_lock(&modminer->device_mutex); mutex_lock(&modminer->device_mutex);
if (6 != write(fd, cmd, 6)) if (6 != write(fd, cmd, 6))
bailout2(LOG_ERR, "%s %u.%u: Error writing (set clock speed)", modminer->api->name, modminer->device_id, fpgaid); bailout2(LOG_ERR, "%s%u.%u: Error writing (set clock speed)", modminer->api->name, modminer->device_id, fpgaid);
if (serial_read(fd, &buf, 1) != 1) if (serial_read(fd, &buf, 1) != 1)
bailout2(LOG_ERR, "%s %u.%u: Error reading (set clock speed)", modminer->api->name, modminer->device_id, fpgaid); bailout2(LOG_ERR, "%s%u.%u: Error reading (set clock speed)", modminer->api->name, modminer->device_id, fpgaid);
if (needlock) if (needlock)
mutex_unlock(&modminer->device_mutex); mutex_unlock(&modminer->device_mutex);
applog(LOG_WARNING, "%s %u.%u: Setting clock speed to %u", modminer->api->name, modminer->device_id, fpgaid, state->clock); applog(LOG_WARNING, "%s%u.%u: Set clock speed %sto %u", modminer->api->name, modminer->device_id, fpgaid, (delta < 0) ? "down " : (delta > 0 ? "up " : ""), state->clock);
return true; return true;
} }
static bool static bool modminer_fpga_init(struct thr_info *thr)
modminer_fpga_init(struct thr_info *thr)
{ {
struct cgpu_info *modminer = thr->cgpu; struct cgpu_info *modminer = thr->cgpu;
struct modminer_fpga_state *state = thr->cgpu_data; struct modminer_fpga_state *state = thr->cgpu_data;
@ -303,20 +441,20 @@ modminer_fpga_init(struct thr_info *thr)
cmd[0] = '\x04'; // Read USER code (bitstream id) cmd[0] = '\x04'; // Read USER code (bitstream id)
cmd[1] = fpgaid; cmd[1] = fpgaid;
if (write(fd, cmd, 2) != 2) if (write(fd, cmd, 2) != 2)
bailout2(LOG_ERR, "%s %u.%u: Error writing (read USER code)", modminer->api->name, modminer->device_id, fpgaid); bailout2(LOG_ERR, "%s%u.%u: Error writing (read USER code)", modminer->api->name, modminer->device_id, fpgaid);
if (serial_read(fd, buf, 4) != 4) if (serial_read(fd, buf, 4) != 4)
bailout2(LOG_ERR, "%s %u.%u: Error reading (read USER code)", modminer->api->name, modminer->device_id, fpgaid); bailout2(LOG_ERR, "%s%u.%u: Error reading (read USER code)", modminer->api->name, modminer->device_id, fpgaid);
if (memcmp(buf, BISTREAM_USER_ID, 4)) { if (memcmp(buf, BISTREAM_USER_ID, 4)) {
applog(LOG_ERR, "%s %u.%u: FPGA not programmed", modminer->api->name, modminer->device_id, fpgaid); applog(LOG_ERR, "%s%u.%u: FPGA not programmed", modminer->api->name, modminer->device_id, fpgaid);
if (!modminer_fpga_upload_bitstream(modminer)) if (!modminer_fpga_upload_bitstream(modminer))
return false; return false;
} }
else else
applog(LOG_DEBUG, "%s %u.%u: FPGA is already programmed :)", modminer->api->name, modminer->device_id, fpgaid); applog(LOG_DEBUG, "%s%u.%u: FPGA is already programmed :)", modminer->api->name, modminer->device_id, fpgaid);
state->clock = 212; // Will be reduced to 210 by modminer_reduce_clock state->clock = MODMINER_DEF_CLOCK;
modminer_reduce_clock(thr, false); modminer_delta_clock(thr, false, MODMINER_CLOCK_SET, false);
mutex_unlock(&modminer->device_mutex); mutex_unlock(&modminer->device_mutex);
@ -325,8 +463,7 @@ modminer_fpga_init(struct thr_info *thr)
return true; return true;
} }
static void static void get_modminer_statline_before(char *buf, struct cgpu_info *modminer)
get_modminer_statline_before(char *buf, struct cgpu_info *modminer)
{ {
char info[18] = " | "; char info[18] = " | ";
int tc = modminer->threads; int tc = modminer->threads;
@ -337,16 +474,16 @@ get_modminer_statline_before(char *buf, struct cgpu_info *modminer)
tc = 4; tc = 4;
for (i = tc - 1; i >= 0; --i) { for (i = tc - 1; i >= 0; --i) {
struct thr_info*thr = modminer->thr[i]; struct thr_info *thr = modminer->thr[i];
struct modminer_fpga_state *state = thr->cgpu_data; struct modminer_fpga_state *state = thr->cgpu_data;
unsigned char temp = state->temp; float temp = state->temp;
info[i*3+2] = '/'; info[i*3+2] = '/';
if (temp) { if (temp) {
havetemp = true; havetemp = true;
if (temp > 9) if (temp > 9)
info[i*3+0] = 0x30 + (temp / 10); info[i*3+0] = 0x30 + (temp / 10);
info[i*3+1] = 0x30 + (temp % 10); info[i*3+1] = 0x30 + ((int)temp % 10);
} }
} }
if (havetemp) { if (havetemp) {
@ -358,8 +495,7 @@ get_modminer_statline_before(char *buf, struct cgpu_info *modminer)
strcat(buf, " | "); strcat(buf, " | ");
} }
static bool static bool modminer_prepare_next_work(struct modminer_fpga_state *state, struct work *work)
modminer_prepare_next_work(struct modminer_fpga_state*state, struct work*work)
{ {
char *midstate = state->next_work_cmd + 2; char *midstate = state->next_work_cmd + 2;
char *taildata = midstate + 32; char *taildata = midstate + 32;
@ -370,11 +506,10 @@ modminer_prepare_next_work(struct modminer_fpga_state*state, struct work*work)
return true; return true;
} }
static bool static bool modminer_start_work(struct thr_info *thr)
modminer_start_work(struct thr_info*thr)
{ {
fd_set fds; fd_set fds;
struct cgpu_info*modminer = thr->cgpu; struct cgpu_info *modminer = thr->cgpu;
struct modminer_fpga_state *state = thr->cgpu_data; struct modminer_fpga_state *state = thr->cgpu_data;
char fpgaid = thr->device_thread; char fpgaid = thr->device_thread;
SOCKETTYPE fd = modminer->device_fd; SOCKETTYPE fd = modminer->device_fd;
@ -383,7 +518,7 @@ fd_set fds;
mutex_lock(&modminer->device_mutex); mutex_lock(&modminer->device_mutex);
if (46 != write(fd, state->next_work_cmd, 46)) if (46 != write(fd, state->next_work_cmd, 46))
bailout2(LOG_ERR, "%s %u.%u: Error writing (start work)", modminer->api->name, modminer->device_id, fpgaid); bailout2(LOG_ERR, "%s%u.%u: Error writing (start work)", modminer->api->name, modminer->device_id, fpgaid);
gettimeofday(&state->tv_workstart, NULL); gettimeofday(&state->tv_workstart, NULL);
state->hashes = 0; state->hashes = 0;
status_read("start work"); status_read("start work");
@ -394,42 +529,48 @@ fd_set fds;
#define work_restart(thr) thr->work_restart #define work_restart(thr) thr->work_restart
static uint64_t static uint64_t modminer_process_results(struct thr_info *thr)
modminer_process_results(struct thr_info*thr)
{ {
struct cgpu_info*modminer = thr->cgpu; struct cgpu_info *modminer = thr->cgpu;
struct modminer_fpga_state *state = thr->cgpu_data; struct modminer_fpga_state *state = thr->cgpu_data;
char fpgaid = thr->device_thread; char fpgaid = thr->device_thread;
int fd = modminer->device_fd; int fd = modminer->device_fd;
struct work *work = &state->running_work; struct work *work = &state->running_work;
char cmd[2], temperature; char cmd[2], temperature[2];
uint32_t nonce; uint32_t nonce;
long iter; long iter;
int curr_hw_errors; uint32_t curr_hw_errors;
cmd[0] = '\x0a';
// \x0a is 1 byte temperature
// \x0d is 2 byte temperature
cmd[0] = '\x0d';
cmd[1] = fpgaid; cmd[1] = fpgaid;
mutex_lock(&modminer->device_mutex); mutex_lock(&modminer->device_mutex);
if (2 == write(fd, cmd, 2) && read(fd, &temperature, 1) == 1) if (2 == write(fd, cmd, 2) && read(fd, &temperature, 2) == 2)
{ {
state->temp = temperature; // Only accurate to 2 and a bit places
state->temp = roundf((temperature[1] * 256.0 + temperature[0]) / 0.128) / 1000.0;
if (!fpgaid) if (!fpgaid)
modminer->temp = (float)temperature; modminer->temp = state->temp;
if (temperature > modminer->cutofftemp - 2) {
if (temperature > modminer->cutofftemp) {
applog(LOG_WARNING, "%s %u.%u: Hit thermal cutoff limit, disabling device!", modminer->api->name, modminer->device_id, fpgaid);
modminer->deven = DEV_RECOVER;
if (state->temp >= MODMINER_OVERHEAT_TEMP) {
if (state->temp >= MODMINER_CUTOFF_TEMP) {
applog(LOG_WARNING, "%s%u.%u: Hit thermal cutoff limit (%f) at %f, disabling device!", modminer->api->name, modminer->device_id, fpgaid, MODMINER_CUTOFF_TEMP, state->temp);
modminer_delta_clock(thr, true, MODMINER_OVERHEAT_CLOCK, true);
modminer->deven = DEV_RECOVER;
modminer->device_last_not_well = time(NULL); modminer->device_last_not_well = time(NULL);
modminer->device_not_well_reason = REASON_DEV_THERMAL_CUTOFF; modminer->device_not_well_reason = REASON_DEV_THERMAL_CUTOFF;
++modminer->dev_thermal_cutoff_count; modminer->dev_thermal_cutoff_count++;
} else { } else {
time_t now = time(NULL); applog(LOG_WARNING, "%s%u.%u Overheat limit (%f) reached %f", modminer->api->name, modminer->device_id, fpgaid, MODMINER_OVERHEAT_TEMP, state->temp);
if (state->last_cutoff_reduced != now) { modminer_delta_clock(thr, true, MODMINER_CLOCK_DOWN, true);
state->last_cutoff_reduced = now;
modminer_reduce_clock(thr, false); modminer->device_last_not_well = time(NULL);
} modminer->device_not_well_reason = REASON_DEV_OVER_HEAT;
modminer->dev_over_heat_count++;
} }
} }
} }
@ -438,24 +579,33 @@ modminer_process_results(struct thr_info*thr)
iter = 200; iter = 200;
while (1) { while (1) {
if (write(fd, cmd, 2) != 2) if (write(fd, cmd, 2) != 2)
bailout2(LOG_ERR, "%s %u.%u: Error reading (get nonce)", modminer->api->name, modminer->device_id, fpgaid); bailout2(LOG_ERR, "%s%u.%u: Error reading (get nonce)", modminer->api->name, modminer->device_id, fpgaid);
serial_read(fd, &nonce, 4); serial_read(fd, &nonce, 4);
mutex_unlock(&modminer->device_mutex); mutex_unlock(&modminer->device_mutex);
if (memcmp(&nonce, "\xff\xff\xff\xff", 4)) { if (memcmp(&nonce, "\xff\xff\xff\xff", 4)) {
state->shares++;
state->no_nonce_counter = 0; state->no_nonce_counter = 0;
curr_hw_errors = modminer->hw_errors; curr_hw_errors = state->hw_errors;
submit_nonce(thr, work, nonce); submit_nonce(thr, work, nonce);
if (modminer->hw_errors > curr_hw_errors) { if (state->hw_errors > curr_hw_errors) {
if (modminer->hw_errors * 100 > 1000 + state->good_share_counter) state->shares_last_hw = state->shares;
// Only reduce clocks if hardware errors are more than ~1% of results if (state->clock > MODMINER_DEF_CLOCK || state->hw_errors > 1) {
modminer_reduce_clock(thr, true); float pct = (state->hw_errors * 100.0 / (state->shares ? : 1.0));
if (pct >= MODMINER_HW_ERROR_PERCENT)
modminer_delta_clock(thr, true, MODMINER_CLOCK_DOWN, false);
}
} else {
// If we've reached the required good shares in a row then clock up
if ((state->shares - state->shares_last_hw) >= state->shares_to_good)
modminer_delta_clock(thr, true, MODMINER_CLOCK_UP, false);
} }
} } else if (++state->no_nonce_counter > 18000) {
else // TODO: NFI what this is - but will be gone
if (++state->no_nonce_counter > 18000) { // when the threading rewrite is done
state->no_nonce_counter = 0; state->no_nonce_counter = 0;
modminer_reduce_clock(thr, true); modminer_delta_clock(thr, true, MODMINER_CLOCK_DOWN, false);
} }
if (work_restart(thr)) if (work_restart(thr))
break; break;
usleep(10000); usleep(10000);
@ -480,8 +630,7 @@ modminer_process_results(struct thr_info*thr)
return hashes; return hashes;
} }
static int64_t static int64_t modminer_scanhash(struct thr_info *thr, struct work *work, int64_t __maybe_unused max_nonce)
modminer_scanhash(struct thr_info*thr, struct work*work, int64_t __maybe_unused max_nonce)
{ {
struct modminer_fpga_state *state = thr->cgpu_data; struct modminer_fpga_state *state = thr->cgpu_data;
int64_t hashes = 0; int64_t hashes = 0;
@ -508,8 +657,14 @@ modminer_scanhash(struct thr_info*thr, struct work*work, int64_t __maybe_unused
return hashes; return hashes;
} }
static void static void modminer_hw_error(struct thr_info *thr)
modminer_fpga_shutdown(struct thr_info *thr) {
struct modminer_fpga_state *state = thr->cgpu_data;
state->hw_errors++;
}
static void modminer_fpga_shutdown(struct thr_info *thr)
{ {
free(thr->cgpu_data); free(thr->cgpu_data);
} }
@ -522,5 +677,6 @@ struct device_api modminer_api = {
.thread_prepare = modminer_fpga_prepare, .thread_prepare = modminer_fpga_prepare,
.thread_init = modminer_fpga_init, .thread_init = modminer_fpga_init,
.scanhash = modminer_scanhash, .scanhash = modminer_scanhash,
.hw_error = modminer_hw_error,
.thread_shutdown = modminer_fpga_shutdown, .thread_shutdown = modminer_fpga_shutdown,
}; };

93
fpgautils.c

@ -477,3 +477,96 @@ FILE *open_bitstream(const char *dname, const char *filename)
return NULL; return NULL;
} }
#ifndef WIN32
static bool _select_wait_read(int fd, struct timeval *timeout)
{
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
if (select(fd+1, &rfds, NULL, NULL, timeout) > 0)
return true;
else
return false;
}
// Default timeout 100ms - only for device initialisation
const struct timeval tv_timeout_default = { 0, 100000 };
// Default inter character timeout = 1ms - only for device initialisation
const struct timeval tv_inter_char_default = { 0, 1000 };
// Device initialisation function - NOT for work processing
size_t _select_read(int fd, char *buf, size_t bufsiz, struct timeval *timeout, struct timeval *char_timeout, int finished)
{
struct timeval tv_time, tv_char;
ssize_t siz, red = 0;
char got;
// timeout is the maximum time to wait for the first character
tv_time.tv_sec = timeout->tv_sec;
tv_time.tv_usec = timeout->tv_usec;
if (!_select_wait_read(fd, &tv_time))
return 0;
while (4242) {
if ((siz = read(fd, buf, 1)) < 0)
return red;
got = *buf;
buf += siz;
red += siz;
bufsiz -= siz;
if (bufsiz < 1 || (finished >= 0 && got == finished))
return red;
// char_timeout is the maximum time to wait for each subsequent character
// this is OK for initialisation, but bad for work processing
// work processing MUST have a fixed size so this doesn't come into play
tv_char.tv_sec = char_timeout->tv_sec;
tv_char.tv_usec = char_timeout->tv_usec;
if (!_select_wait_read(fd, &tv_char))
return red;
}
return red;
}
// Device initialisation function - NOT for work processing
size_t _select_write(int fd, char *buf, size_t siz, struct timeval *timeout)
{
struct timeval tv_time, tv_now, tv_finish;
fd_set rfds;
ssize_t wrote = 0, ret;
gettimeofday(&tv_now, NULL);
timeradd(&tv_now, timeout, &tv_finish);
// timeout is the maximum time to spend trying to write
tv_time.tv_sec = timeout->tv_sec;
tv_time.tv_usec = timeout->tv_usec;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
while (siz > 0 && (tv_now.tv_sec < tv_finish.tv_sec || (tv_now.tv_sec == tv_finish.tv_sec && tv_now.tv_usec < tv_finish.tv_usec)) && select(fd+1, NULL, &rfds, NULL, &tv_time) > 0) {
if ((ret = write(fd, buf, 1)) > 0) {
buf++;
wrote++;
siz--;
}
else if (ret < 0)
return wrote;
gettimeofday(&tv_now, NULL);
}
return wrote;
}
#endif // ! WIN32

43
fpgautils.h

@ -36,4 +36,47 @@ extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char *eol);
extern FILE *open_bitstream(const char *dname, const char *filename); extern FILE *open_bitstream(const char *dname, const char *filename);
#ifndef WIN32
extern const struct timeval tv_timeout_default;
extern const struct timeval tv_inter_char_default;
extern size_t _select_read(int fd, char *buf, size_t bufsiz, struct timeval *timeout, struct timeval *char_timeout, int finished);
extern size_t _select_write(int fd, char *buf, size_t siz, struct timeval *timeout);
#define select_open(devpath) \
serial_open(devpath, 0, 0, false)
#define select_open_purge(devpath, purge)\
serial_open(devpath, 0, 0, purge)
#define select_write(fd, buf, siz) \
_select_write(fd, buf, siz, (struct timeval *)(&tv_timeout_default))
#define select_write_full _select_write
#define select_read(fd, buf, bufsiz) \
_select_read(fd, buf, bufsiz, (struct timeval *)(&tv_timeout_default), \
(struct timeval *)(&tv_inter_char_default), -1)
#define select_read_til(fd, buf, bufsiz, eol) \
_select_read(fd, buf, bufsiz, (struct timeval *)(&tv_timeout_default), \
(struct timeval *)(&tv_inter_char_default), eol)
#define select_read_wait(fd, buf, bufsiz, timeout) \
_select_read(fd, buf, bufsiz, timeout, \
(struct timeval *)(&tv_inter_char_default), -1)
#define select_read_wait_til(fd, buf, bufsiz, timeout, eol) \
_select_read(fd, buf, bufsiz, timeout, \
(struct timeval *)(&tv_inter_char_default), eol)
#define select_read_wait_both(fd, buf, bufsiz, timeout, char_timeout) \
_select_read(fd, buf, bufsiz, timeout, char_timeout, -1)
#define select_read_full _select_read
#define select_close(fd) close(fd)
#endif // ! WIN32
#endif #endif

12
miner.h

@ -267,6 +267,7 @@ struct device_api {
void (*free_work)(struct thr_info*, struct work*); void (*free_work)(struct thr_info*, struct work*);
bool (*prepare_work)(struct thr_info*, struct work*); bool (*prepare_work)(struct thr_info*, struct work*);
int64_t (*scanhash)(struct thr_info*, struct work*, int64_t); int64_t (*scanhash)(struct thr_info*, struct work*, int64_t);
void (*hw_error)(struct thr_info*);
void (*thread_shutdown)(struct thr_info*); void (*thread_shutdown)(struct thr_info*);
void (*thread_enable)(struct thr_info*); void (*thread_enable)(struct thr_info*);
}; };
@ -950,11 +951,14 @@ struct modminer_fpga_state {
char next_work_cmd[46]; char next_work_cmd[46];
unsigned char clock; unsigned char clock;
int no_nonce_counter; float temp;
int good_share_counter;
time_t last_cutoff_reduced;
unsigned char temp; uint32_t shares;
uint32_t shares_last_hw;
uint32_t hw_errors;
uint32_t shares_to_good;
struct timeval last_changed;
uint32_t no_nonce_counter;
}; };
#endif #endif

Loading…
Cancel
Save