Skip to content

Add support for latest ZTEX firmware. #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: testing
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 41 additions & 17 deletions modules/theseven/ztex/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def __init__(self, proxy, serial, takeover, firmware):
self.takeover = takeover
self.firmware = firmware
self.handle = None
self.timeout = 1000 # ms, same value as used in ZTEX SDK
permissionproblem = False
deviceinuse = False
for bus in usb.busses():
Expand Down Expand Up @@ -76,7 +77,7 @@ def __init__(self, proxy, serial, takeover, firmware):
raise Exception("Can not open the specified device, possibly due to insufficient permissions")
raise Exception("Can not open the specified device")

descriptor = array("B", self.handle.controlMsg(0xc0, 0x22, 40, 0, 0, 100))
descriptor = array("B", self.handle.controlMsg(0xc0, 0x22, 40, timeout=self.timeout))
if len(descriptor) != 40: raise Exception("Bad ZTEX descriptor length: %d" % len(descriptor))
size, version, magic = struct.unpack("<2BI", descriptor[:6])
product = struct.unpack("4B", descriptor[6:10])
Expand All @@ -88,29 +89,47 @@ def __init__(self, proxy, serial, takeover, firmware):
if version != 1: raise Exception("Bad ZTEX descriptor version: %d" % version)
if magic != struct.unpack("<I", b"ZTEX")[0]: raise Exception("Bad ZTEX descriptor magic: %08X" % magic)
if product[0] != 10: raise Exception("Firmware vendor is not ZTEX: %d.%d.%d.%d" % product)
if product[2:] != (1, 1): raise Exception("Device is not running a bitcoin miner firmware: %02X %02X %02X %02X" % product)
if product[2:] not in ((1, 1), (1, 2)): raise Exception("Unsupported device or not running bitcoin miner firmware: %02X %02X %02X %02X" % product)
if ifversion != 1: raise Exception("Bad ZTEX interface version: %d" % ifversion)
if not (ifcaps[0] & 2): raise Exception("Firmware doesn't support FPGA capability")
self.hs_supported = ifcaps[0] & 32
self.proxy.log("MCU firmware: %d.%d.%d.%d, version %d, serial number %s, high speed programming%s supported\n" % (product + (fwversion, sn, "" if self.hs_supported else " NOT")), 400, "B")

descriptor = array("B", self.handle.controlMsg(0xc0, 0x82, 64, 0, 0, 100))
descriptor = array("B", self.handle.controlMsg(0xc0, 0x82, 64, timeout=self.timeout))
if len(descriptor) != 64: raise Exception("Bad BTCMiner descriptor length: %d" % len(descriptor))
version, numnonces, offset, basefreq, defaultmultiplier, maxmultiplier, hashesperclock = struct.unpack("<BBHHBBH", descriptor[:10])
firmware = struct.unpack("54s", descriptor[10:])[0].split(b"\0", 1)[0].decode("ascii")
if version != 4: raise Exception("Bad BTCMiner descriptor version: %d, firmware outdated?" % version)
version, numnonces, offset, basefreq, defaultmultiplier, maxmultiplier, hashesperclock, extra = struct.unpack("<BBHHBBHB", descriptor[:11])
if version < 4: raise Exception("Bad BTCMiner descriptor version: %d, firmware outdated?" % version)
if version > 5: raise Exception("Bad BTCMiner descriptor version: %d, firmware too new?" % version)
firmware_start = 11 if version > 4 else 10
firmware_end = firmware_start
while descriptor[firmware_end] != 0 and firmware_end < len(descriptor):
firmware_end += 1
firmware = ''.join([ chr(c) for c in descriptor[firmware_start:firmware_end] ])
self.num_nonces = numnonces + 1
self.nonce_offset = offset - 10000
self.base_frequency = basefreq * 10000
self.default_multiplier = min(defaultmultiplier, maxmultiplier)
self.maximum_multiplier = maxmultiplier
self.hashes_per_clock = hashesperclock / 128.
self.extra_solutions = extra if version > 4 else 0
self.firmware_name = firmware

self.num_fpgas = 1
if version > 4 and ifcaps[0] & 128:
try:
num_fpgas, sel_fpga, parallel_config = struct.unpack("<BBB", array("B", self.handle.controlMsg(0xc0, 0x50, 3, timeout=self.timeout)))
except:
pass
else:
self.num_fpgas += num_fpgas
if self.num_fpgas > 1:
self.proxy.log("MPBM does not yet fully support multi-FPGA ZTEX boards. Your %s has %d FPGAs, only #%d will be used.\n" % (self.firmware_name, self.num_fpgas, sel_fpga), 400, "B")

defaultspeed = self.base_frequency * self.default_multiplier * self.hashes_per_clock / 1000000
maxspeed = self.base_frequency * self.maximum_multiplier * self.hashes_per_clock / 1000000
self.proxy.log("FPGA firmware: %s, default speed: %f MH/s, maximum speed: %f MH/s\n" % (self.firmware_name, defaultspeed, maxspeed), 400, "B")

unconfigured, checksum, bytestransferred, initb, result, bitswap = struct.unpack("<BBIBBB", array("B", self.handle.controlMsg(0xc0, 0x30, 9, 0, 0, 100)))
unconfigured, checksum, bytestransferred, initb, result, bitswap = struct.unpack("<BBIBBB", array("B", self.handle.controlMsg(0xc0, 0x30, 9, timeout=self.timeout)))
if unconfigured:
self.proxy.log("Programming FPGA with firmware %s...\n" % self.firmware_name, 300, "B")
firmwarepath = "%s/%s.bit" % (self.firmware, self.firmware_name)
Expand All @@ -122,36 +141,41 @@ def __init__(self, proxy, serial, takeover, firmware):
sig1 = bitstream.find(b"\xaa\x99\x55\x66")
sig2 = bitstream.find(b"\x55\x99\xaa\x66")
if sig2 < 0 or (sig1 >= 0 and sig1 < sig2): raise Exception("Signature not found in bitstream, wrong bit order?")
self.handle.controlMsg(0x40, 0x31, b"", 0, 0, 100)
self.handle.controlMsg(0x40, 0x31, b"", timeout=self.timeout)
if self.hs_supported:
ep, interface = struct.unpack("<BB", array("B", self.handle.controlMsg(0xc0, 0x33, 2, 0, 0, 100)))
self.handle.controlMsg(0x40, 0x34, b"", 0, 0, 100)
ep, interface = struct.unpack("<BB", array("B", self.handle.controlMsg(0xc0, 0x33, 2, timeout=self.timeout)))
self.handle.controlMsg(0x40, 0x34, b"", timeout=self.timeout)
pos = 0
while pos < len(bitstream): pos += self.handle.bulkWrite(ep, bitstream[pos : pos + 65536], 500)
self.handle.controlMsg(0x40, 0x35, b"", 0, 0, 100)
self.handle.controlMsg(0x40, 0x35, b"", timeout=self.timeout)
else:
pos = 0
while pos < len(bitstream): pos += self.handle.controlMsg(0x40, 0x32, bitstream[pos : pos + 2048], 0, 0, 500)
unconfigured, checksum, bytestransferred, initb, result, bitswap = struct.unpack("<BBIBBB", array("B", self.handle.controlMsg(0xc0, 0x30, 9, 0, 0, 100)))
unconfigured, checksum, bytestransferred, initb, result, bitswap = struct.unpack("<BBIBBB", array("B", self.handle.controlMsg(0xc0, 0x30, 9, timeout=self.timeout)))
if unconfigured: raise Exception("FPGA configuration failed: FPGA did not assert DONE")


def set_multiplier(self, multiplier):
with self.lock:
self.handle.controlMsg(0x40, 0x83, b"", multiplier, 0, 100)
self.handle.controlMsg(0x40, 0x83, b"", multiplier, timeout=self.timeout)


def send_job(self, data):
with self.lock:
self.handle.controlMsg(0x40, 0x80, data, 0, 0, 100)
self.handle.controlMsg(0x40, 0x80, data, timeout=self.timeout)


def read_nonces(self):
bs = 12 + self.extra_solutions * 4
with self.lock:
data = array("B", self.handle.controlMsg(0xc0, 0x81, 12 * self.num_nonces, 0, 0, 100))
data = array("B", self.handle.controlMsg(0xc0, 0x81, bs * self.num_nonces, timeout=self.timeout))
nonces = []
for i in range(self.num_nonces):
values = struct.unpack("<III", data[12 * i : 12 * (i + 1)])
values = struct.unpack("<III", data[bs * i : bs * i + 12])
nonces.append((values[0] - self.nonce_offset, values[1] - self.nonce_offset, values[2]))
for j in range(self.extra_solutions):
extra_offset = bs * i + 12 + j * 4
extra = struct.unpack("<I", data[extra_offset : extra_offset + 4])
nonces.append((extra[0] - self.nonce_offset, values[1] - self.nonce_offset, values[2]))
return nonces