#!/usr/bin/env python """ # TOP2049 Open Source programming suite # # Copyright (c) 2009-2010 Michael Buesch # # 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 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ from bitfile import * from util import * import sys import getopt import time try: import usb except (ImportError), e: print "Python USB support module not found. Please install python-usb." sys.exit(1) # Import the supported chip modules from chip_atmega8dip28 import * class TOP: def __init__(self, bitfileName, busDev=None, verbose=0, noqueue=False): """bitfileName is the path to the .bit file. busDev is a tuple (BUSID, DEVID) or None.""" self.verbose = verbose self.noqueue = noqueue self.commandsBlocked = False self.commandQueue = [] self.bitfile = Bitfile() self.bitfile.parseFile(bitfileName) # Find a chip handler for the given bitfile. chipID = self.bitfile.getSrcFile().lower() if chipID.endswith(".ncd"): chipID = chipID[0:-4] self.chip = chipFind(chipID) if not self.chip: raise TOPException("Did not find an implementation for the chip %s" % chipID) self.chip.setTOP(self) # Find the device for bus in usb.busses(): if busDev and bus.dirname != "%03d" % busDev[0]: continue for dev in bus.devices: if busDev and dev.filename != "%03d" % busDev[1]: continue if self.__isTOP(dev): break if busDev: raise TOPException( "Device %03d.%03d is not a TOP device" %\ (busDev[0], busDev[1])) else: continue break else: raise TOPException("TOP programmer device not found!") self.usbbus = bus self.usbdev = dev self.bulkOut = None self.bulkIn = None # Set up the USB interface try: self.usbh = self.usbdev.open() config = self.usbdev.configurations[0] interface = config.interfaces[0][0] # Find the endpoints for ep in interface.endpoints: if not self.bulkIn and \ ep.type == usb.ENDPOINT_TYPE_BULK and \ (ep.address & usb.ENDPOINT_IN) != 0: self.bulkIn = ep if not self.bulkOut and \ ep.type == usb.ENDPOINT_TYPE_BULK and \ (ep.address & usb.ENDPOINT_IN) == 0: self.bulkOut = ep if not self.bulkIn or not self.bulkOut: raise TOPException("Did not find all USB EPs") self.usbh.setConfiguration(config) self.usbh.claimInterface(interface) self.usbh.setAltInterface(interface) self.usbh.clearHalt(self.bulkOut.address) self.usbh.clearHalt(self.bulkIn.address) except (usb.USBError), e: raise TOPException("USB error: " + str(e)) if self.noqueue: self.printInfo("WARNING: Command queuing disabled. " +\ "Hardware access will be _really_ slow.") self.__initializeHardware() def printInfo(self, message, newline=True): if self.verbose >= 1: if newline: print message else: sys.stdout.write(message) sys.stdout.flush() def printDebug(self, message, newline=True): if self.verbose >= 2: if newline: print message else: sys.stdout.write(message) sys.stdout.flush() @staticmethod def __isTOP(usbdev): ids = ( (0x2471, 0x0853), ) return (usbdev.idVendor, usbdev.idProduct) in ids def __initializeHardware(self): "Initialize the hardware" ver = self.cmdRequestVersion() self.printInfo("Detected a " + ver) self.printDebug("Initializing hardware...") self.send("\x0D") stat = self.cmdReadStatusReg32() if stat != 0x00020C69: raise TOPException("Init: Unexpected status register (a): 0x%08X" % stat) self.send("\x0A\x1B\xFF") self.cmdSetVPPVoltage(0) self.cmdFlush() self.cmdSetVPPVoltage(0) self.cmdFlush() self.cmdSetVPPVoltage(0) self.cmdFlush() self.cmdSetVPPVoltage(12) self.cmdFlush() self.send("\x0E\x20\x00\x00") self.cmdFlush() self.cmdSetVCCXVoltage(0) self.cmdFlush() self.send("\x0A\x1D\x86") self.cmdSetGNDPin(0) self.cmdLoadVPPLayout(0) self.cmdLoadVCCXLayout(0) self.cmdFlush() self.cmdSetVPPVoltage(0) self.cmdFlush() self.cmdSetVPPVoltage(12) self.cmdFlush() self.send("\x0E\x20\x00\x00") self.cmdFlush() self.send("\x0E\x25\x00\x00") stat = self.cmdReadStatusReg32() if stat != 0x0000686C: raise TOPException("Init: Unexpected status register (b): 0x%08X" % stat) self.__bitfileUpload() self.chip.initializeChip() def __bitfileUpload(self): self.printDebug("Uploading bitfile...") self.send("\x0A\x1B\x00") self.cmdFPGAInitiateConfig() stat = self.cmdReadStatusReg32() if stat != 0x00006801: raise TOPException("bit-upload: Failed to initiate " +\ "config sequence (0x%08X)" % stat) data = self.bitfile.getPayload() for i in range(0, len(data), 60): self.cmdFPGAUploadConfig(data[i : i + 60]) def readImage(self): """Reads the chip image and returns it.""" self.printDebug("Reading image from chip...") image = self.chip.readImage() self.printDebug("Done reading %d bytes." % len(image)) return image def writeImage(self, image): """Writes an image to the chip.""" self.printDebug("Writing %d bytes image to chip..." % len(image)) self.chip.writeImage(image) self.printDebug("Done writing image.") def cmdFlush(self, count=1): """Send 'count' flush requests.""" assert(count >= 1) self.send(chr(0x1B) * count) def cmdReadStatusReg(self): """Read the status register. Returns 64 bytes.""" self.send(chr(0x07)) return self.receive(64) def cmdReadStatusReg32(self): """Read a 32bit value from the status register.""" stat = self.cmdReadStatusReg() stat = ord(stat[0]) | (ord(stat[1]) << 8) | \ (ord(stat[2]) << 16) | (ord(stat[3]) << 24) return stat def cmdRequestVersion(self): """Returns the device ID and versioning string.""" self.send("\x0E\x11\x00\x00") data = self.cmdReadStatusReg() return data[0:16].strip() def cmdFPGAInitiateConfig(self): """Initiate a configuration sequence on the FPGA.""" self.send("\x0E\x21\x00\x00") def cmdFPGAUploadConfig(self, data): """Upload configuration data into the FPGA.""" assert(len(data) <= 60) cmd = "\x0E\x22\x00\x00" + data cmd += "\x00" * (64 - len(cmd)) # padding self.send(cmd) def cmdFPGAReadByte(self): """Read a byte from the FPGA data line into the status register.""" self.send("\x01") def cmdFPGAWriteByte(self, byte): """Write a byte to the FPGA data line.""" self.send("\x10" + chr(byte)) def cmdSetGNDPin(self, zifPin): """Assign GND to a ZIF socket pin. 0=none""" valid = (0, 5, 14, 15, 16, 17, 18, 19, 20, 24, 26, 27, 28, 29, 33, 34, 35) assert(zifPin in valid) if zifPin != 0: zifPin -= 4 cmd = chr(0x0E) + chr(0x16) + chr(zifPin) + chr(0) self.send(cmd) time.sleep(0.15) def cmdSetVPPVoltage(self, voltage): """Set the VPP voltage. voltage is a floating point voltage number.""" centivolt = int(voltage * 10) cmd = chr(0x0E) + chr(0x12) + chr(centivolt) + chr(0) self.send(cmd) def cmdLoadVPPLayout(self, layout): """Load the VPP configuration into the shift registers.""" cmd = chr(0x0E) + chr(0x14) + chr(layout) + chr(0) self.send(cmd) time.sleep(0.15) def cmdSetVCCXVoltage(self, voltage): """Set the VCCX voltage. voltage is a floating point voltage number.""" centivolt = int(voltage * 10) cmd = chr(0x0E) + chr(0x13) + chr(centivolt) + chr(0) self.send(cmd) def cmdLoadVCCXLayout(self, layout): """Load the VCCX configuration into the shift registers.""" cmd = chr(0x0E) + chr(0x15) + chr(layout) + chr(0) self.send(cmd) time.sleep(0.15) def __doSend(self, command): try: assert(len(command) <= 64) if self.verbose >= 3: print "Sending command:" dumpMem(command) ep = self.bulkOut.address self.usbh.bulkWrite(ep, command) except (usb.USBError), e: raise TOPException("USB bulk write error: " + str(e)) def send(self, command): """Send a raw command.""" assert(len(command) <= 64) if self.commandsBlocked: self.commandQueue.append(command) else: self.__doSend(command) def receive(self, size): """Receive 'size' bytes on the bulk-in ep.""" # If there are blocked commands in the queue, send them now. self.__flushCommands() try: ep = self.bulkIn.address data = "" for c in self.usbh.bulkRead(ep, size): data += chr(c) if self.verbose >= 3: print "Received data:" dumpMem(data) except (usb.USBError), e: raise TOPException("USB bulk read error: " + str(e)) return data def blockCommands(self): """Each succeeding command will be queued in software instead of writing it to the device. The queue will be flushed and sent to the device on a call to unblockCommands() or receive().""" if self.noqueue: return assert(not self.commandsBlocked) self.commandsBlocked = True def unblockCommands(self): """Flush and unblock the software command queue and send the queued commands to the device.""" if self.noqueue: return assert(self.commandsBlocked) self.commandsBlocked = False self.__flushCommands() def __flushCommands(self): """Flush the command queue, but don't unblock it.""" command = "" for oneCommand in self.commandQueue: assert(len(oneCommand) <= 64) if len(command) + len(oneCommand) > 64: self.__doSend(command) command = "" command += oneCommand if command: self.__doSend(command) self.commandQueue = [] def usage(): print "TOP2049 Open Source programming suite" print "" print "Usage: %s [OPTIONS]" % sys.argv[0] print "" print " -b|--bitfile Path to the *.bit file (mandatory)" print "" print "Actions:" print " -w|--write FILE Flash an image to the device" print " -r|--read FILE Read the device and store image in file" print "" print "Optional:" print " -d|--device BUS.DEV Use the programmer at BUS.DEV" print " First found programmer is used, if not given." print " -V|--verbose LEVEL Set the verbosity level. Default = 2" print " -Q|--noqueue Disable command queuing. Really slow!" def main(argv): opt_verbose = 2 opt_bitfile = None opt_device = None opt_action = None opt_file = None opt_noqueue = False try: (opts, args) = getopt.getopt(sys.argv[1:], "hb:d:w:r:V:Q", [ "help", "bitfile=", "device=", "write=", "read=", "verbose=", "noqueue", ]) for (o, v) in opts: if o in ("-h", "--help"): usage() return 0 if o in ("-b", "--bitfile"): opt_bitfile = v if o in ("-d", "--device"): try: v = v.split(".") opt_device = (int(v[0]), int(v[1])) except (IndexError, ValueError), e: print "-d|--device invalid BUS.DEV id." return 1 if o in ("-w", "--write"): opt_action = "write" opt_file = v if o in ("-r", "--read"): opt_action = "read" opt_file = v if o in ("-V", "--verbose"): opt_verbose = int(v) if o in ("-Q", "--noqueue"): opt_noqueue = True except (getopt.GetoptError, ValueError), e: usage() return 1 if not opt_bitfile: print "-b|--bitfile is mandatory!" return 1 if not opt_action: print "An action is mandatory!" return 1 try: top = TOP(opt_bitfile, opt_device, opt_verbose, opt_noqueue) if opt_action == "read": fd = file(opt_file, "w+b") image = top.readImage() fd.write(image) elif opt_action == "write": fd = file(opt_file, "rb") image = fd.read() top.writeImage(image) else: assert(0) except (TOPException, BitfileException, IOError), e: print e return 1 return 0 if __name__ == "__main__": sys.exit(main(sys.argv))