#!/usr/bin/env python """ # TOP2049 Open Source programming suite # # Qt-based graphical user interface # # Copyright (c) 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 libtoprammer.toprammer_main import * from libtoprammer.util import * from PyQt4.QtCore import * from PyQt4.QtGui import * import sys import time import cgi import ConfigParser EVENT_HWTHREAD = QEvent.User + 0 def stringRemoveChars(string, chars): ret = [] for c in string: if c not in chars: ret.append(c) return "".join(ret) def htmlEscape(plaintext): return cgi.escape(plaintext) def getIconPath(name): return pkg_resources.resource_filename("libtoprammer", "icons/" + name + ".png") def getIcon(name): return QIcon(getIconPath(name)) def hex2bin(hexdata): assert(len(hexdata) % 2 == 0) bindata = [] for i in range(0, len(hexdata), 2): bindata.append(chr(int(hexdata[i:i+2], 16))) return "".join(bindata) def bin2hex(bindata): if not bindata: return "" hexdata = [] for c in bindata: hexdata.append("%02X" % ord(c)) return "".join(hexdata) def bin2ascii(bindata): if not bindata: return "" asciidata = [] for c in bindata: if ord(c) >= 32 and ord(c) <= 126: asciidata.append(c) else: asciidata.append(".") return "".join(asciidata) class ZifWidget(QGroupBox): def __init__(self, unitest, nrZifPins): QGroupBox.__init__(self, "ZIF socket", unitest) self.unitest = unitest self.setLayout(QGridLayout()) self.nrPins = nrZifPins assert(self.nrPins % 2 == 0) self.blockedPins = [] self.ignoreOutChange = False label = QLabel(self) label.setFrameStyle(QFrame.Panel | QFrame.Sunken) label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.layout().addWidget(label, 0, 2, self.nrPins // 2, 1) self.pins = [ None ] * self.nrPins self.pinOuten = [ None ] * self.nrPins for i in range(0, self.nrPins // 2): left = i + 1 right = self.nrPins - i self.pins[left - 1] = QCheckBox(str(left), self) self.connect(self.pins[left - 1], SIGNAL("stateChanged(int)"), self.__outChanged) self.pinOuten[left - 1] = QCheckBox("out", self) self.connect(self.pinOuten[left - 1], SIGNAL("stateChanged(int)"), self.__outEnChanged) self.layout().addWidget(self.pinOuten[left - 1], left - 1, 0) self.layout().addWidget(self.pins[left - 1], left - 1, 1) self.pins[right - 1] = QCheckBox(str(right), self) self.connect(self.pins[right - 1], SIGNAL("stateChanged(int)"), self.__outChanged) self.pinOuten[right - 1] = QCheckBox("out", self) self.connect(self.pinOuten[right - 1], SIGNAL("stateChanged(int)"), self.__outEnChanged) self.layout().addWidget(self.pins[right - 1], self.nrPins - right, 3) self.layout().addWidget(self.pinOuten[right - 1], self.nrPins - right, 4) self.__outEnChanged() self.__outChanged() def readInputs(self): try: inputMask = self.unitest.queryTop("top.getChip().getInputs()") except (TOPException), e: QMessageBox.critical(self, "TOP communication failed", "Failed to fetch input states:\n" +\ str(e)) return False for i in range(0, self.nrPins): if self.pinOuten[i].checkState() != Qt.Checked and\ i + 1 not in self.blockedPins: state = Qt.Unchecked if inputMask & (1 << i): state = Qt.Checked self.ignoreOutChange = True self.pins[i].setCheckState(state) self.ignoreOutChange = False return True def __updateInOutStates(self): for i in range(0, self.nrPins): if i + 1 in self.blockedPins: self.pins[i].setEnabled(False) self.pins[i].setCheckState(Qt.Unchecked) self.pinOuten[i].setEnabled(False) self.pinOuten[i].setCheckState(Qt.Unchecked) else: self.pinOuten[i].setEnabled(True) if self.pinOuten[i].checkState() == Qt.Checked: self.pins[i].setEnabled(True) else: self.pins[i].setEnabled(False) def __outEnChanged(self): self.__updateInOutStates() outEnMask = self.getOutEnMask() try: self.unitest.queryTop("top.getChip().setOutputEnableMask(...)", (outEnMask, )) except (TOPException), e: QMessageBox.critical(self, "TOP communication failed", "Failed to set output-enable states:\n" +\ str(e)) return self.readInputs() def __outChanged(self): if self.ignoreOutChange: return outMask = self.getOutMask() try: self.unitest.queryTop("top.getChip().setOutputs(...)", (outMask, )) except (TOPException), e: QMessageBox.critical(self, "TOP communication failed", "Failed to set output states:\n" +\ str(e)) return self.readInputs() def setBlockedPins(self, blockedPins): self.blockedPins = blockedPins self.__updateInOutStates() self.readInputs() def getOutEnMask(self): outEnMask = 0 for i in range(0, self.nrPins): if self.pinOuten[i].checkState() == Qt.Checked: outEnMask |= (1 << i) return outEnMask def setOutEnMask(self, mask): for i in range(0, self.nrPins): if mask & (1 << i): self.pinOuten[i].setCheckState(Qt.Checked) else: self.pinOuten[i].setCheckState(Qt.Unchecked) mask &= ~(1 << i) if mask: raise TOPException("ZIF out-en mask has too many bits set") def getOutMask(self): outMask = 0 for i in range(0, self.nrPins): if self.pins[i].checkState() == Qt.Checked: outMask |= (1 << i) return outMask def setOutMask(self, mask): for i in range(0, self.nrPins): if mask & (1 << i): self.pins[i].setCheckState(Qt.Checked) else: self.pins[i].setCheckState(Qt.Unchecked) mask &= ~(1 << i) if mask: raise TOPException("ZIF out mask has too many bits set") class UnitestDialog(QDialog): def __init__(self, mainWindow): QDialog.__init__(self, mainWindow) self.setWindowTitle("Universal chip tester") self.mainWindow = mainWindow self.setLayout(QGridLayout()) self.inputPollBlocked = 0 # Initialize the unitest chip (failed, returnValue) = self.mainWindow.runOperationSync( HwThread.TASK_INITCHIP, "unitest") if failed: raise TOPException("Failed to load 'unitest' chip: %s" % str(returnValue)) # Query the hardware layer for common parameters self.param_topType = self.queryTop("top.getProgrammerType()") self.param_gndLayouts = self.queryTop("top.gnd.supportedLayouts()") self.param_nrZifPins = self.queryTop("top.gnd.getNrOfPins()") self.param_vccxLayouts = self.queryTop("top.vccx.supportedLayouts()") self.param_minVccxVolt = self.queryTop("top.vccx.minVoltage()") self.param_maxVccxVolt = self.queryTop("top.vccx.maxVoltage()") self.param_vppLayouts = self.queryTop("top.vpp.supportedLayouts()") self.param_minVppVolt = self.queryTop("top.vpp.minVoltage()") self.param_maxVppVolt = self.queryTop("top.vpp.maxVoltage()") self.param_oscFreq = self.queryTop("top.getChip().getOscFreq()") assert(self.param_nrZifPins % 2 == 0) self.param_vppLayouts.sort(key=lambda (layId, layMask): layMask) self.menuBar = QMenuBar(self) self.menuBar.addAction("&Load settings...", self.loadSettings) self.menuBar.addAction("&Save settings...", self.saveSettings) self.layout().addWidget(self.menuBar, 0, 0, 1, 3) self.zifWidget = ZifWidget(self, self.param_nrZifPins) self.layout().addWidget(self.zifWidget, 1, 0, 10, 1) group = QGroupBox("GND layout", self) group.setLayout(QGridLayout()) self.gndLayout = QComboBox(self) self.gndLayout.addItem("Not connected", QVariant(0)) for (layId, layMask) in self.param_gndLayouts: if not layMask: continue descr = "GND on pin " for i in range(0, self.param_nrZifPins): if layMask & (1 << i): descr += str(i + 1) + " " self.gndLayout.addItem(descr, QVariant(layId)) group.layout().addWidget(self.gndLayout, 0, 0) self.layout().addWidget(group, 1, 1) group = QGroupBox("VCCX layout", self) group.setLayout(QGridLayout()) self.vccxVoltage = QDoubleSpinBox(self) self.vccxVoltage.setSuffix(" V") self.vccxVoltage.setMinimum(self.param_minVccxVolt) self.vccxVoltage.setMaximum(self.param_maxVccxVolt) self.vccxVoltage.setSingleStep(0.1) group.layout().addWidget(self.vccxVoltage, 0, 0) self.vccxLayout = QComboBox(self) self.vccxLayout.addItem("Not connected", QVariant(0)) for (layId, layMask) in self.param_vccxLayouts: if not layMask: continue descr = "VCCX on pin " for i in range(0, self.param_nrZifPins): if layMask & (1 << i): descr += str(i + 1) + " " self.vccxLayout.addItem(descr, QVariant(layId)) group.layout().addWidget(self.vccxLayout, 1, 0) self.layout().addWidget(group, 2, 1) group = QGroupBox("Input polling", self) group.setLayout(QGridLayout()) self.inputPollEn = QCheckBox("Input polling enabled", self) self.inputPollEn.setCheckState(Qt.Checked) group.layout().addWidget(self.inputPollEn, 0, 0) self.inputPollInterval = QDoubleSpinBox(self) self.inputPollInterval.setPrefix("Interval ") self.inputPollInterval.setSuffix(" seconds") self.inputPollInterval.setMinimum(0.25) self.inputPollInterval.setSingleStep(0.25) self.inputPollInterval.setValue(1.0) group.layout().addWidget(self.inputPollInterval, 1, 0) self.layout().addWidget(group, 3, 1) group = QGroupBox("Oscillator", self) group.setLayout(QGridLayout()) self.oscPin = QComboBox(self) self.oscPin.addItem("Disabled", QVariant(0)) for i in range(0, self.zifWidget.nrPins): self.oscPin.addItem("On pin %d" % (i + 1), QVariant(1 << i)) group.layout().addWidget(self.oscPin, 0, 0) self.oscDiv = QSpinBox(self) self.oscDiv.setPrefix("Divider ") self.oscDiv.setSingleStep(1) self.oscDiv.setMinimum(1) self.oscDiv.setMaximum(self.param_oscFreq) self.oscDiv.setValue(self.param_oscFreq // 1000) group.layout().addWidget(self.oscDiv, 1, 0) self.oscFreq = QLabel(self) group.layout().addWidget(self.oscFreq, 2, 0) self.layout().addWidget(group, 4, 1) group = QGroupBox("VPP layout", self) group.setLayout(QGridLayout()) self.vppVoltage = QDoubleSpinBox(self) self.vppVoltage.setSuffix(" V") self.vppVoltage.setMinimum(self.param_minVppVolt) self.vppVoltage.setMaximum(self.param_maxVppVolt) self.vppVoltage.setSingleStep(0.1) group.layout().addWidget(self.vppVoltage, 0, 0, 1, 2) self.vppLayouts = {} xOffset = 0 yOffset = 0 for (layId, layMask) in self.param_vppLayouts: if not layMask: continue for i in range(0, self.param_nrZifPins): if layMask & (1 << i): descr = str(i + 1) + " " self.vppLayouts[layId] = QCheckBox(descr, self) self.connect(self.vppLayouts[layId], SIGNAL("stateChanged(int)"), self.vppLayoutChanged) group.layout().addWidget(self.vppLayouts[layId], yOffset + 1, xOffset) yOffset += 1 if yOffset == len(self.param_vppLayouts) // 2: yOffset = 0 xOffset += 1 self.layout().addWidget(group, 1, 2, 8, 1) self.inputPollTimer = QTimer() self.inputPollTimer.setSingleShot(True) self.connect(self.inputPollTimer, SIGNAL("timeout()"), self.doInputPollTimer) self.connect(self.inputPollEn, SIGNAL("stateChanged(int)"), self.inputPollChanged) self.connect(self.inputPollInterval, SIGNAL("valueChanged(double)"), self.inputPollChanged) self.connect(self.gndLayout, SIGNAL("currentIndexChanged(int)"), self.gndLayoutChanged) self.connect(self.vccxVoltage, SIGNAL("valueChanged(double)"), self.vccxLayoutChanged) self.connect(self.vccxLayout, SIGNAL("currentIndexChanged(int)"), self.vccxLayoutChanged) self.connect(self.vppVoltage, SIGNAL("valueChanged(double)"), self.vppLayoutChanged) self.connect(self.oscPin, SIGNAL("currentIndexChanged(int)"), self.oscChanged) self.connect(self.oscDiv, SIGNAL("valueChanged(int)"), self.oscChanged) self.gndLayoutChanged() self.vccxLayoutChanged() self.inputPollChanged() self.oscChanged() def __loadFile(self, filename): try: p = ConfigParser.SafeConfigParser() p.read((filename,)) sect = "TOPRAMMER-UNITEST-SETTINGS" if not p.has_section(sect): raise TOPException("Invalid file format") ver = p.getint(sect, "fileVersion") verExpected = 1 if ver != verExpected: raise TOPException("Unsupported file version (got %d, expected %d)" %\ (ver, verExpected)) pType = p.get(sect, "programmerType") if pType.upper() != self.param_topType.upper(): raise TOPException("Programmer type mismatch (file = %s, connected = %s)" %\ (pType, self.param_topType)) layout = p.getint(sect, "gndLayout") idx = self.gndLayout.findData(QVariant(layout)) if idx < 0: raise TOPException("Invalid GND layout") self.gndLayout.setCurrentIndex(idx) layout = p.getint(sect, "vccxLayout") idx = self.vccxLayout.findData(QVariant(layout)) if idx < 0: raise TOPException("Invalid VCCX layout") self.vccxLayout.setCurrentIndex(idx) voltage = p.getfloat(sect, "vccxVoltage") self.vccxVoltage.setValue(voltage) layouts = p.get(sect, "vppLayout").split(",") for layId in self.vppLayouts.keys(): if layId in layouts: self.vppLayouts[layId].setCheckState(Qt.Checked) else: self.vppLayouts[layId].setCheckState(Qt.Unchecked) voltage = p.getfloat(sect, "vppVoltage") self.vppVoltage.setValue(voltage) interval = p.getfloat(sect, "inputPollInterval") self.inputPollInterval.setValue(interval) enabled = p.getboolean(sect, "inputPollEnabled") if enabled: self.inputPollEn.setCheckState(Qt.Checked) else: self.inputPollEn.setCheckState(Qt.Unchecked) div = p.getint(sect, "oscillatorDiv") self.oscDiv.setValue(div) mask = int(p.get(sect, "oscillatorMask"), 16) idx = self.oscPin.findData(QVariant(mask)) if idx < 0: raise TOPException("Invalid oscillator mask") self.oscPin.setCurrentIndex(idx) mask = int(p.get(sect, "zifOutEnMask"), 16) self.zifWidget.setOutEnMask(mask) mask = int(p.get(sect, "zifOutMask"), 16) self.zifWidget.setOutMask(mask) except (ConfigParser.Error, TOPException, ValueError), e: QMessageBox.critical(self, "Failed to load settings", "Failed to load settings: %s" % str(e)) def loadSettings(self): fn = str(QFileDialog.getOpenFileName(self, "Load settings", "", "Toprammer-unitest settings (*.tus);;" "All files (*)")) if not fn: return self.__loadFile(fn) def __saveFile(self, filename): try: fd = file(filename, "w+b") fd.write("[TOPRAMMER-UNITEST-SETTINGS]\r\n") fd.write("fileVersion=%d\r\n" % 1) fd.write("programmerType=%s\r\n" % self.param_topType) idx = self.gndLayout.currentIndex() fd.write("gndLayout=%d\r\n" % self.gndLayout.itemData(idx).toPyObject()) idx = self.vccxLayout.currentIndex() fd.write("vccxLayout=%d\r\n" % self.vccxLayout.itemData(idx).toPyObject()) fd.write("vccxVoltage=%f\r\n" % self.vccxVoltage.value()) vppLayouts = "" for layId in self.vppLayouts.keys(): if self.vppLayouts[layId].checkState() == Qt.Checked: if vppLayouts: vppLayouts += "," vppLayouts += str(layId) if not vppLayouts: vppLayouts = "0" fd.write("vppLayout=%s\r\n" % vppLayouts) fd.write("vppVoltage=%f\r\n" % self.vppVoltage.value()) fd.write("inputPollEnabled=%d\r\n" % int(self.inputPollEn.checkState() == Qt.Checked)) fd.write("inputPollInterval=%f\r\n" % self.inputPollInterval.value()) idx = self.oscPin.currentIndex() fd.write("oscillatorMask=%X\r\n" % self.oscPin.itemData(idx).toPyObject()) fd.write("oscillatorDiv=%d\r\n" % self.oscDiv.value()) fd.write("zifOutEnMask=%X\r\n" % self.zifWidget.getOutEnMask()) fd.write("zifOutMask=%X\r\n" % self.zifWidget.getOutMask()) except (IOError), e: QMessageBox.critical(self, "Failed to save settings", "Failed to write settings to file: %s" % str(e)) def saveSettings(self): fn = str(QFileDialog.getSaveFileName(self, "Save settings", "", "Toprammer-unitest settings (*.tus)")) if not fn: return if not fn.endswith(".tus"): fn += ".tus" self.__saveFile(fn) def closeEvent(self, e): self.inputPollTimer.stop() def queryTop(self, funcname, parameters=[]): self.blockInputPoll() # Avoid recursion taskParams = [ funcname, ] taskParams.extend(parameters) (failed, returnValue) = self.mainWindow.runOperationSync( HwThread.TASK_GENERICTOPCALL, taskParams) self.unblockInputPoll() if failed: raise TOPException("Failed to query TOP %s" % funcname) return returnValue def shutdown(self): self.inputPollTimer.stop() def updateZifCheckboxes(self): blockedPins = [] # GND idx = self.gndLayout.currentIndex() lay = self.gndLayout.itemData(idx).toPyObject() blockedPins.extend(self.queryTop("top.gnd.ID2pinlist(...)", (lay, ))) # VCCX idx = self.vccxLayout.currentIndex() lay = self.vccxLayout.itemData(idx).toPyObject() blockedPins.extend(self.queryTop("top.vccx.ID2pinlist(...)", (lay, ))) # VPP for key in self.vppLayouts.keys(): if self.vppLayouts[key].checkState() == Qt.Checked: blockedPins.extend(self.queryTop("top.vpp.ID2pinlist(...)", (key, ))) # OSC idx = self.oscPin.currentIndex() mask = self.oscPin.itemData(idx).toPyObject() for i in range(0, self.zifWidget.nrPins): if mask & (1 << i): blockedPins.append(i + 1) self.zifWidget.setBlockedPins(blockedPins) def gndLayoutChanged(self, unused=None): idx = self.gndLayout.currentIndex() selLayout = self.gndLayout.itemData(idx).toPyObject() try: self.queryTop("top.getChip().setGND(...)", (selLayout, )) except (TOPException), e: QMessageBox.critical(self, "TOP communication failed", "Failed to set GND layout:\n" +\ str(e)) return self.updateZifCheckboxes() def vccxLayoutChanged(self, unused=None): selVoltage = self.vccxVoltage.value() idx = self.vccxLayout.currentIndex() selLayout = self.vccxLayout.itemData(idx).toPyObject() try: self.queryTop("top.getChip().setVCCX(...)", (selVoltage, selLayout)) except (TOPException), e: QMessageBox.critical(self, "TOP communication failed", "Failed to set VCCX layout:\n" +\ str(e)) return self.updateZifCheckboxes() def vppLayoutChanged(self, unused=None): selVoltage = self.vppVoltage.value() selLayouts = [] for key in self.vppLayouts.keys(): if self.vppLayouts[key].checkState() == Qt.Checked: selLayouts.append(key) try: self.queryTop("top.getChip().setVPP(...)", (selVoltage, selLayouts)) except (TOPException), e: QMessageBox.critical(self, "TOP communication failed", "Failed to set VPP layout:\n" +\ str(e)) return self.updateZifCheckboxes() def blockInputPoll(self): self.inputPollBlocked += 1 def unblockInputPoll(self): self.inputPollBlocked -= 1 def doInputPollTimer(self): if self.inputPollBlocked: # Blocked. Reschedule. self.inputPollTimer.start(1) return self.mainWindow.blockInputEvents() result = self.zifWidget.readInputs() self.mainWindow.unblockInputEvents() if not result: # Whoops, error. Disable input polling self.inputPollEn.setCheckState(Qt.Unchecked) return False # Reschedule inter = int(self.inputPollInterval.value() * 1000) self.inputPollTimer.start(inter) return True def inputPollChanged(self, unused=None): if self.inputPollEn.checkState() == Qt.Checked: self.inputPollInterval.setEnabled(True) if self.doInputPollTimer(): inter = int(self.inputPollInterval.value() * 1000) self.inputPollTimer.start(inter) else: self.inputPollInterval.setEnabled(False) self.inputPollTimer.stop() def oscChanged(self, unused=None): try: div = self.oscDiv.value() self.queryTop("top.getChip().setOscDivider(...)", (div, )) idx = self.oscPin.currentIndex() mask = self.oscPin.itemData(idx).toPyObject() self.queryTop("top.getChip().setOscMask(...)", (mask, )) except (TOPException), e: QMessageBox.critical(self, "TOP communication failed", "Failed configure oscillator:\n" +\ str(e)) return self.oscFreq.setText("%.03f Hz" % (float(self.param_oscFreq) / div)) self.updateZifCheckboxes() class GuiUserInterface(AbstractUserInterface): # Global progress PROGRESSMETER_USER_GLOBAL = AbstractUserInterface.PROGRESSMETER_USER + 0 def __init__(self, hwThread): self.hwThread = hwThread def progressMeterInit(self, meterId, message, nrSteps): self.hwThread.appendMessage("progrInit", (meterId, message, nrSteps)) def progressMeterFinish(self, meterId): self.hwThread.appendMessage("progrFinish", meterId) def progressMeter(self, meterId, step): self.hwThread.appendMessage("progress", (meterId, step)) def __consoleMessage(self, message, newline=True): if newline: message += "\n" self.hwThread.appendMessage("console", message) def warningMessage(self, message, newline=True): self.__consoleMessage(message, newline) def infoMessage(self, message, newline=True): self.__consoleMessage(message, newline) def debugMessage(self, message, newline=True): self.__consoleMessage(message, newline) class HwThread(QThread): TASK_SHUTDOWN = 0 TASK_INITCHIP = 1 TASK_READALL = 2 TASK_GENERICTOPCALL = 3 class CancelException(Exception): def __init__(self): Exception.__init__(self, "Operation cancelled") def __init__(self, mainWindow): QThread.__init__(self, mainWindow) self.mainWindow = mainWindow self.killRequest = False self.messageQueue = [] self.top = None self.task = None self.taskParameter = None self.cancel = False self.cancellationBlocked = 0 self.waitCondition = QWaitCondition() self.mutex = QMutex() self.setTopParameters() self.start() def killThread(self): if self.isRunning(): self.mutex.lock() self.task = self.TASK_SHUTDOWN self.killRequest = True self.waitCondition.wakeAll() self.mutex.unlock() self.wait() def setTopParameters(self, busDev=None, verbose=2, forceLevel=0, usebroken=False, forceBitfileUpload=True): assert(self.top is None and self.task is None) self.param_busDev = busDev self.param_verbose = verbose self.param_forceLevel = forceLevel self.param_usebroken = usebroken self.param_forceBitfileUpload = forceBitfileUpload def triggerTask(self, task, taskParameter=None): self.mutex.lock() self.cancel = False self.task = task self.taskParameter = taskParameter self.waitCondition.wakeAll() self.mutex.unlock() return True def run(self): self.mutex.lock() while True: if not self.killRequest and self.task is None: self.waitCondition.wait(self.mutex) self.mutex.unlock() self.__taskWorker() self.mutex.lock() if self.killRequest and self.task is None: break self.mutex.unlock() def cancelTask(self): self.mutex.lock() self.cancel = True self.mutex.unlock() def __blockCancellation(self): self.cancellationBlocked += 1 def __unblockCancellation(self): self.cancellationBlocked -= 1 def __cancellationPoint(self): # Mutex must be locked if not self.cancel: return if self.cancellationBlocked: return self.cancel = False self.mutex.unlock() raise HwThread.CancelException() # Caught in __taskWorker def __doCancelTask(self): # Make sure the device is in a consistent state. self.__blockCancellation() if self.top: print "Operation cancelled. Resetting chip." self.top.resetChip() self.__unblockCancellation() def appendMessage(self, message, data, nocancel=False): # Append a message to the message queue. # This is a cancellation point! self.mutex.lock() if not nocancel: self.__cancellationPoint() self.messageQueue.append( (message, data) ) self.mutex.unlock() self.__notifyMainWindow() def __taskWorker(self): failed = True try: self.cancellationBlocked = 0 result = self.__runTask(self.task) failed = False except (TOPException), e: result = e except (HwThread.CancelException), e: self.__doCancelTask() result = e except (Exception), e: result = e self.appendMessage("finished", (self.task, failed, result), nocancel=True) self.task = None self.taskParameter = None def __runTask(self, task): retval = None if task == self.TASK_SHUTDOWN or task == self.TASK_INITCHIP: if self.top: self.__blockCancellation() self.top.shutdownChip() self.top.shutdownProgrammer() self.top = None self.__unblockCancellation() if task == self.TASK_SHUTDOWN: return None if not self.top: # Initialize Hardware access self.__blockCancellation() self.top = TOP(busDev = self.param_busDev, verbose = self.param_verbose, forceLevel = self.param_forceLevel, usebroken = self.param_usebroken, forceBitfileUpload = self.param_forceBitfileUpload, userInterface = GuiUserInterface(self)) self.__unblockCancellation() if task == self.TASK_INITCHIP: self.__blockCancellation() self.top.initializeChip(self.taskParameter) chip = self.top.getChip() asciiArtLayout = None layoutGen = chip.getLayoutGenerator() if layoutGen: asciiArtLayout = layoutGen.zifLayoutAsciiArt() chipSupportFlags = Chip.getSupportFlags(chip) self.__unblockCancellation() retval = (chipSupportFlags, asciiArtLayout) elif task == self.TASK_READALL: self.__globalProgressInit(6) self.top.checkChip() sigImage = None progmemImage = None eepromImage = None fuseImage = None lockbitsImage = None supportFlags = Chip.getSupportFlags(self.top.getChip()) if supportFlags & Chip.SUPPORT_SIGREAD: sigImage = self.top.readSignature() self.__globalProgress(1) if supportFlags & Chip.SUPPORT_PROGMEMREAD: progmemImage = self.top.readProgmem() self.__globalProgress(2) if supportFlags & Chip.SUPPORT_EEPROMREAD: eepromImage = self.top.readEEPROM() self.__globalProgress(3) if supportFlags & Chip.SUPPORT_FUSEREAD: fuseImage = self.top.readFuse() self.__globalProgress(4) if supportFlags & Chip.SUPPORT_LOCKREAD: lockbitsImage = self.top.readLockbits() self.__globalProgress(5) retval = (sigImage, progmemImage, eepromImage, fuseImage, lockbitsImage) elif task == self.TASK_GENERICTOPCALL: funcname = str(self.taskParameter[0]).strip() if funcname.endswith(")"): # strip (...) suffix funcname = funcname[:funcname.rfind("(")] if funcname.startswith("top."): funcname = funcname[4:] params = self.taskParameter[1:] paramList = [] for i in range(0, len(params)): paramList.append("params[%d]" % i) retval = eval("self.top.%s(%s)" % (funcname, ", ".join(paramList))) else: raise TOPException("INTERNAL ERROR: HwThread: unknown task") return retval def __globalProgressInit(self, nrSteps): self.appendMessage("progrInit", (GuiUserInterface.PROGRESSMETER_USER_GLOBAL, None, nrSteps)) def __globalProgress(self, step): self.appendMessage("progress", (GuiUserInterface.PROGRESSMETER_USER_GLOBAL, step)) def __notifyMainWindow(self): QApplication.postEvent(self.mainWindow, QEvent(EVENT_HWTHREAD)) def handleMessageQueue(self): self.mutex.lock() for (message, data) in self.messageQueue: if message == "finished": self.mainWindow.hardwareTaskFinished( task=data[0], failed=data[1], returnValue=data[2]) elif message == "console": self.mainWindow.console.showMessage(data) elif message == "progrInit": (meterId, message, nrSteps) = data self.mainWindow.console.progressMeterInit(meterId, nrSteps) elif message == "progrFinish": meterId = data self.mainWindow.console.progressMeter(meterId, -1) elif message == "progress": (meterId, step) = data self.mainWindow.console.progressMeter(meterId, step) else: assert(0) self.messageQueue = [] self.mutex.unlock() class Console(QDockWidget): STAT_OK = 0 STAT_ERROR = 1 STAT_PROGRESS = 2 def __init__(self, mainWindow): QDockWidget.__init__(self, mainWindow) self.statusUpdateBlocked = 0 self.setFeatures(self.DockWidgetMovable | self.DockWidgetFloatable) self.setWidget(QWidget(self)) self.widget().show() self.widget().setLayout(QGridLayout(self.widget())) self.consoleMsgs = [] self.consoleText = QTextEdit(self) self.consoleText.setReadOnly(True) self.widget().layout().addWidget(self.consoleText, 0, 0, 1, 3) self.statusLabel = QLabel(self) self.widget().layout().addWidget(self.statusLabel, 1, 0, 2, 1) self.chipaccessProgress = QProgressBar(self) self.widget().layout().addWidget(self.chipaccessProgress, 1, 1) self.globalProgress = QProgressBar(self) self.widget().layout().addWidget(self.globalProgress, 2, 1) self.cancelButton = QPushButton("Cancel", self) self.cancelButton.setEnabled(False) self.widget().layout().addWidget(self.cancelButton, 1, 2, 2, 1) self.setStatus(self.STAT_OK) self.idToProgressBar = { GuiUserInterface.PROGRESSMETER_CHIPACCESS : self.chipaccessProgress, GuiUserInterface.PROGRESSMETER_USER_GLOBAL : self.globalProgress, } self.connect(self.cancelButton, SIGNAL("released()"), mainWindow.cancelHardwareTask) def blockStatusUpdate(self): self.statusUpdateBlocked += 1 def unblockStatusUpdate(self): self.statusUpdateBlocked -= 1 def setTaskRunning(self, running, success=True): if self.statusUpdateBlocked: return self.cancelButton.setEnabled(running) if running: self.setStatus(Console.STAT_PROGRESS) # Set progress meters to "busy" for meterId in self.idToProgressBar.keys(): self.progressMeterInit(meterId, 0) else: if success: self.setStatus(Console.STAT_OK) else: self.setStatus(Console.STAT_ERROR) # Reset progress meters to 0% for meterId in self.idToProgressBar.keys(): self.progressMeterInit(meterId, 2) def setStatus(self, status): if self.statusUpdateBlocked: return if status == self.STAT_OK: path = getIconPath("ok") elif status == self.STAT_ERROR: path = getIconPath("error") elif status == self.STAT_PROGRESS: path = getIconPath("progress") else: assert(0) self.statusLabel.setPixmap(QPixmap(path)) def __commitText(self): limit = 100 if len(self.consoleMsgs) > limit: self.consoleMsgs.pop(0) assert(len(self.consoleMsgs) == limit) html = "" + "".join(self.consoleMsgs) + "" self.consoleText.setHtml(html) # Scroll to end scroll = self.consoleText.verticalScrollBar() scroll.setTracking(True) scroll.setSliderPosition(scroll.maximum()) scroll.setValue(scroll.maximum()) def showMessage(self, message, bold=False): message = str(message) if not message: return newline = False if message.endswith("\n"): newline = True message = message[:-1] message = htmlEscape(message) if bold: message = "" + message + "" if newline: message += "
" lastmsg = None if self.consoleMsgs: lastmsg = self.consoleMsgs[-1] if lastmsg and not lastmsg.endswith("
"): self.consoleMsgs[-1] = self.consoleMsgs[-1] + message else: self.consoleMsgs.append(message) self.__commitText() def progressMeterInit(self, meterId, nrSteps): if self.statusUpdateBlocked: return progress = self.idToProgressBar[meterId] progress.setMinimum(0) progress.setMaximum(max(0, nrSteps - 1)) progress.setValue(progress.maximum()) progress.setValue(0) def progressMeter(self, meterId, step): if self.statusUpdateBlocked: return progress = self.idToProgressBar[meterId] if progress.maximum() == 0: progress.setMaximum(1) progress.setValue(1) else: progress.setValue(step) class BufferWidget(QWidget): def __init__(self, mainWindow, name): QWidget.__init__(self, mainWindow) self.mainWindow = mainWindow self.name = name self.hide() self.setLayout(QGridLayout(self)) self.setAvailable(False) self.setReadOnly() def getName(self): return self.name def setAvailable(self, available): self.available = available def isAvailable(self): return self.available def setReadOnly(self, readOnly=True): self.readOnly = readOnly def isReadOnly(self): return self.readOnly def getRawData(self): return None def setRawData(self, data): return False def clear(self): pass class InfoBufferWidget(BufferWidget): def __init__(self, mainWindow, name): BufferWidget.__init__(self, mainWindow, name) self.zifLayout = QLabel(self) font = self.zifLayout.font() font.setFixedPitch(True) font.setFamily("monospace") self.zifLayout.setFont(font) self.layout().addWidget(self.zifLayout, 0, 0, 3, 1) l = QLabel("Chip name:", self) self.layout().addWidget(l, 0, 1) self.chipName = QLabel(self) font = self.chipName.font() font.setBold(True) self.chipName.setFont(font) self.layout().addWidget(self.chipName, 0, 2) l = QLabel("Chip signature bytes:", self) self.layout().addWidget(l, 1, 1) self.chipSig = QLabel(self) font = self.chipSig.font() font.setBold(True) self.chipSig.setFont(font) self.layout().addWidget(self.chipSig, 1, 2) self.layout().setColumnStretch(3, 99) self.layout().setRowStretch(2, 99) self.clear() def clear(self): self.setChipName("") self.setChipSignature("") self.setChipLayout(None) def setChipName(self, name): self.chipName.setText(name) def setChipSignature(self, bindata): self.chipSig.setText(bin2hex(bindata)) def setChipLayout(self, asciiArtLayout): if asciiArtLayout: self.zifLayout.setText(asciiArtLayout) self.zifLayout.setFrameShape(QFrame.StyledPanel) else: self.zifLayout.clear() self.zifLayout.setFrameShape(QFrame.NoFrame) class ImageBufferWidget(BufferWidget): def __init__(self, mainWindow, name): BufferWidget.__init__(self, mainWindow, name) self.browser = QPlainTextEdit(self) self.browser.setReadOnly(True) fmt = self.browser.currentCharFormat() fmt.setFontFamily("monospace") fmt.setFontFixedPitch(True) self.browser.setCurrentCharFormat(fmt) self.layout().addWidget(self.browser, 0, 0) self.image = None def loadImage(self, image): self.image = image if not self.image: self.browser.setPlainText("" % self.getName()) return text = [] asc = [] for i in range(0, len(image)): if i % 16 == 0: if i != 0: text.append(" |%s|\n" % "".join(asc)) asc = [] text.append("[%08X]: " % i) if i % 2 == 0: text.append(" ") text.append(bin2hex(image[i])) asc.append(bin2ascii(image[i])) if asc: if len(image) % 16: nrLeft = 16 - (len(image) % 16) text.append(" " * nrLeft) text.append(" " * (nrLeft / 2)) text.append(" |%s|" % "".join(asc)) self.browser.setPlainText("".join(text)) def getRawData(self): return self.image def setRawData(self, data): if self.isReadOnly(): return False self.loadImage(data) return True def clear(self): self.loadImage(None) class BitBufferWidget(BufferWidget): def __init__(self, mainWindow, name, bitDescriptionsAttr): BufferWidget.__init__(self, mainWindow, name) self.bitDescriptionsAttr = bitDescriptionsAttr self.bitsAreaScroll = QScrollArea(self) self.layout().addWidget(self.bitsAreaScroll, 0, 0) self.bitsAreaScroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.bitsAreaScroll.setWidgetResizable(False) self.bitsArea = None self.image = None self.checkboxes = [] def __getBitDescription(self, bitNr): if not self.mainWindow.currentChipDescription: return "" bitDescList = getattr(self.mainWindow.currentChipDescription, self.bitDescriptionsAttr, None) if not bitDescList: return "" bitDesc = filter(lambda x: x.bitNr == bitNr, bitDescList) if not bitDesc: return "" return bitDesc[0].description def loadImage(self, image): self.image = image for checkbox in self.checkboxes: checkbox.deleteLater() self.checkboxes = [] if self.bitsArea: self.bitsArea.deleteLater() self.bitsArea = None if not self.image: return self.bitsArea = QWidget(self) self.bitsArea.setLayout(QGridLayout(self.bitsArea)) self.bitsArea.layout().addWidget(QLabel("Currently set %s:" % self.getName(), self.bitsArea), 0, 0) self.hexView = QLabel(self.bitsArea) font = self.hexView.font() font.setBold(True) self.hexView.setFont(font) self.bitsArea.layout().addWidget(self.hexView, 0, 1) for i in range(0, len(image) * 8): desc = self.__getBitDescription(i) if desc: desc = " (%s)" % desc checkbox = QCheckBox("bit %d%s" % (i, desc), self.bitsArea) if ord(image[i // 8]) & (1 << (i % 8)): checkbox.setCheckState(Qt.Checked) self.bitsArea.layout().addWidget(checkbox, 1 + i, 0, 1, 2) self.connect(checkbox, SIGNAL("stateChanged(int)"), self.__bitStateChanged) self.checkboxes.append(checkbox) self.__updateHexView() def __updateHexView(self): self.hexView.setText(bin2hex(self.image)) self.bitsAreaScroll.setWidget(self.bitsArea) def __bitStateChanged(self, unused): self.image = "" tmp = 0 i = 0 for checkbox in self.checkboxes: if checkbox.checkState() == Qt.Checked: tmp |= (1 << i) i += 1 if i == 8: self.image += chr(tmp) i = 0 tmp = 0 assert(i == 0) self.__updateHexView() def getRawData(self): return self.image def setRawData(self, data): if self.isReadOnly(): return False if len(data) > 32: # Don't want huge files return False self.loadImage(data) return True def clear(self): self.loadImage(None) class BufferTabWidget(QTabWidget): def __init__(self, mainWindow): QTabWidget.__init__(self, mainWindow) self.mainWindow = mainWindow self.infoBuffer = InfoBufferWidget(mainWindow, "chip info") self.progmemBuffer = ImageBufferWidget(mainWindow, "program memory") self.eepromBuffer = ImageBufferWidget(mainWindow, "(E)EPROM memory") self.fuseBuffer = BitBufferWidget(mainWindow, "fuse bits", "fuseDesc") self.lockBuffer = BitBufferWidget(mainWindow, "lock bits", "lockbitDesc") self.setTabPosition(self.South) self.__addBufferTab(self.infoBuffer) self.removeAll() def __addBufferTab(self, bufferWidget, readOnly=True): self.addTab(bufferWidget, bufferWidget.getName()) bufferWidget.setReadOnly(readOnly) bufferWidget.setAvailable(True) def __removeBufferTab(self, bufferWidget): self.removeTab(self.indexOf(bufferWidget)) bufferWidget.clear() bufferWidget.setAvailable(False) def __removeAll(self): self.__removeBufferTab(self.progmemBuffer) self.__removeBufferTab(self.eepromBuffer) self.__removeBufferTab(self.fuseBuffer) self.__removeBufferTab(self.lockBuffer) def removeAll(self): self.__removeAll() def setupBuffers(self, chipSupportFlags): prevSelect = self.getCurrentBuffer() self.__removeAll() if chipSupportFlags & (Chip.SUPPORT_PROGMEMREAD | Chip.SUPPORT_PROGMEMWRITE): self.__addBufferTab(self.progmemBuffer, not bool(chipSupportFlags & Chip.SUPPORT_PROGMEMWRITE)) if chipSupportFlags & (Chip.SUPPORT_EEPROMREAD | Chip.SUPPORT_EEPROMWRITE): self.__addBufferTab(self.eepromBuffer, not bool(chipSupportFlags & Chip.SUPPORT_EEPROMWRITE)) if chipSupportFlags & (Chip.SUPPORT_FUSEREAD | Chip.SUPPORT_FUSEWRITE): self.__addBufferTab(self.fuseBuffer, not bool(chipSupportFlags & Chip.SUPPORT_FUSEWRITE)) if chipSupportFlags & (Chip.SUPPORT_LOCKREAD | Chip.SUPPORT_LOCKWRITE): self.__addBufferTab(self.lockBuffer, not bool(chipSupportFlags & Chip.SUPPORT_LOCKWRITE)) if prevSelect and prevSelect.isAvailable(): self.setCurrentIndex(self.indexOf(prevSelect)) def loadBuffers(self, progmemImage, eepromImage, fuseImage, lockbitsImage): if progmemImage is not None: self.progmemBuffer.loadImage(progmemImage) if eepromImage is not None: self.eepromBuffer.loadImage(eepromImage) if fuseImage is not None: self.fuseBuffer.loadImage(fuseImage) if lockbitsImage is not None: self.lockBuffer.loadImage(lockbitsImage) def verifyBuffers(self, progmemImage, eepromImage, fuseImage): fail = False for (image, bufferWidget) in ((progmemImage, self.progmemBuffer), (eepromImage, self.eepromBuffer), (fuseImage, self.fuseBuffer)): if image is None: continue if bufferWidget.getRawData() != image: self.mainWindow.console.showMessage( "Chip verify of %s FAILED!\n" % bufferWidget.getName(), bold=True) fail = True if fail: self.mainWindow.console.setStatus(Console.STAT_ERROR) else: self.mainWindow.console.showMessage("Chip verify succeed\n", bold=True) self.mainWindow.console.setStatus(Console.STAT_OK) def getCurrentBuffer(self): return self.currentWidget() class ChipSelectDialog(QDialog): def __init__(self, parent): QDialog.__init__(self, parent) self.setLayout(QGridLayout(self)) self.chipList = QListWidget(self) for chipDescription in registeredChips: if chipDescription.broken: continue if chipDescription.internal: continue item = QListWidgetItem(chipDescription.description, self.chipList) item.setData(Qt.UserRole, chipDescription) self.chipList.sortItems() self.layout().addWidget(self.chipList, 0, 0, 1, 2) self.okButton = QPushButton("&Ok", self) self.layout().addWidget(self.okButton, 1, 0) self.cancelButton = QPushButton("&Cancel", self) self.layout().addWidget(self.cancelButton, 1, 1) self.selectionChanged() self.connect(self.okButton, SIGNAL("released()"), self.accept) self.connect(self.cancelButton, SIGNAL("released()"), self.reject) self.connect(self.chipList, SIGNAL("itemSelectionChanged()"), self.selectionChanged) self.connect(self.chipList, SIGNAL("itemDoubleClicked(QListWidgetItem)"), self.accept) def selectionChanged(self): item = self.chipList.currentItem() self.okButton.setEnabled(bool(item)) def getSelectedChip(self): item = self.chipList.currentItem() if not item: return None chipDescription = item.data(Qt.UserRole).toPyObject() return chipDescription class StatusBar(QStatusBar): def __init__(self, mainWindow): QStatusBar.__init__(self, mainWindow) class TopToolBar(QToolBar): def __init__(self, mainWindow): QToolBar.__init__(self, "Toolbar", mainWindow) self.addAction(getIcon("open"), "Load buffer", mainWindow.loadBuffer) self.addAction(getIcon("save"), "Save buffer", mainWindow.saveBuffer) self.addAction(getIcon("chip"), "Select chip", mainWindow.selectChip) self.addSeparator() self.addAction(getIcon("up"), "Read chip", mainWindow.readChip) self.addSeparator() self.addAction(getIcon("erase"), "Erase", mainWindow.eraseChip) self.addAction(getIcon("memory"), "Write progmem", mainWindow.writeChipProgmem) self.addAction(getIcon("memory"), "Write EPROM", mainWindow.writeChipEeprom) self.addAction(getIcon("memory"), "Write fuses", mainWindow.writeChipFuses) self.addAction(getIcon("lock"), "Write lockbits", mainWindow.writeChipLockbits) self.addAction(getIcon("verify"), "Verify", mainWindow.verifyChip) self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) class AutorunWidget(QDockWidget): def __init__(self, mainWindow): QDockWidget.__init__(self, "Autorun", mainWindow) self.mainWindow = mainWindow self.running = False self.currentAction = -1 self.setFeatures(self.DockWidgetMovable | self.DockWidgetFloatable) self.setup(0) def __addCheckBox(self, row, condition, text, checked): if condition: checkbox = QCheckBox(text, self.widget()) if checked: checkbox.setCheckState(Qt.Checked) self.widget().layout().addWidget(checkbox, row, 0) row += 1 return (checkbox, row) return (None, row) def setup(self, chipSupportFlags): self.setWidget(QWidget(self)) self.widget().show() self.widget().setLayout(QGridLayout(self.widget())) row = 0 (self.eraseCheckBox, row) = self.__addCheckBox(row, chipSupportFlags & Chip.SUPPORT_ERASE, "Erase", True) (self.progmemCheckBox, row) = self.__addCheckBox(row, chipSupportFlags & Chip.SUPPORT_PROGMEMWRITE, "Write program memory", True) (self.eepromCheckBox, row) = self.__addCheckBox(row, chipSupportFlags & Chip.SUPPORT_EEPROMWRITE, "Write (E)EPROM", True) (self.fusesCheckBox, row) = self.__addCheckBox(row, chipSupportFlags & Chip.SUPPORT_FUSEWRITE, "Write fuses", True) (self.verifyCheckBox, row) = self.__addCheckBox(row, chipSupportFlags != 0, "Verify", True) (self.lockbitsCheckBox, row) = self.__addCheckBox(row, chipSupportFlags & Chip.SUPPORT_LOCKWRITE, "Write lock bits", False) self.runButton = None if chipSupportFlags != 0: self.runButton = QPushButton("Run", self.widget()) self.runButton.setIconSize(QSize(32, 32)) self.runButton.setIcon(getIcon("run")) self.widget().layout().addWidget(self.runButton, row, 0) row += 1 self.connect(self.runButton, SIGNAL("released()"), self.runNextChip) self.widget().layout().setRowStretch(row, 99) def isRunning(self): return self.running def abortRun(self, withMessage=False): if withMessage: QMessageBox.critical(self, "Aborting autorun", "One action failed. Aborting autorun.") self.running = False self.currentAction = -1; self.mainWindow.guiUpdateEnable() def runNextChip(self): self.running = True self.currentAction = 0 res = QMessageBox.information(self, "Autorun - Please insert chip", "Please insert a new %s chip into the ZIF socket " "and press Ok to perform the operations..." %\ self.mainWindow.currentChipDescription.description, QMessageBox.Ok | QMessageBox.Cancel, QMessageBox.Ok) if res != QMessageBox.Ok: self.abortRun() return self.runNextAction() def runNextAction(self): assert(self.currentAction >= 0) action = 0 if self.eraseCheckBox: if self.currentAction == action: if self.__runAction(self.mainWindow.eraseChip, self.eraseCheckBox, "Erase"): return action += 1 if self.progmemCheckBox: if self.currentAction == action: if self.__runAction(self.mainWindow.writeChipProgmem, self.progmemCheckBox, "Write program memory"): return action += 1 if self.eepromCheckBox: if self.currentAction == action: if self.__runAction(self.mainWindow.writeChipEeprom, self.eepromCheckBox, "Write (E)EPROM"): return action += 1 if self.fusesCheckBox: if self.currentAction == action: if self.__runAction(self.mainWindow.writeChipFuses, self.fusesCheckBox, "Write fuses"): return action += 1 if self.verifyCheckBox: if self.currentAction == action: if self.__runAction(self.mainWindow.verifyChip, self.verifyCheckBox, "Verify"): return action += 1 if self.lockbitsCheckBox: if self.currentAction == action: if self.__runAction(self.mainWindow.writeChipLockbits, self.lockbitsCheckBox, "Write lock bits"): return action += 1 self.runNextChip() def __runAction(self, function, checkbox, actionName): self.currentAction += 1 if checkbox.checkState() == Qt.Checked: self.mainWindow.console.showMessage( "Running action: %s\n" % actionName, bold=True) if not function(): self.abortRun(withMessage=True) return True return False class MainWindow(QMainWindow): OP_NONE = 0 OP_SHUTDOWN = 1 OP_INITCHIP = 2 OP_READALL = 3 OP_ERASE = 4 OP_WRITEPROG = 5 OP_WRITEEPROM = 6 OP_WRITEFUSE = 7 OP_WRITELOCK = 8 OP_VERIFY = 9 OP_RAWCOMMAND = 10 OP_SYNCHRONOUS = 11 def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowTitle("TOPrammer v%s - Open Source programming suite" % VERSION) self.guiDisable = 0 self.inputEventsBlocked = 0 self.chipSupportFlags = 0 # Chip.SUPPORT_... for the loaded chip self.currentChipDescription = None self.previousRawCommand = "" self.setStatusBar(StatusBar(self)) self.topToolBar = TopToolBar(self) self.addToolBar(Qt.TopToolBarArea, self.topToolBar) self.setMenuBar(QMenuBar(self)) menu = QMenu("&File", self) menu.addAction("&Load buffer...", self.loadBuffer) menu.addAction("&Save buffer...", self.saveBuffer) menu.addSeparator() menu.addAction("&Exit", self.close) self.menuBar().addMenu(menu) menu = QMenu("&Run", self) menu.addAction("&Select chip", self.selectChip) menu.addSeparator() menu.addAction("&Read chip", self.readChip) menu.addAction("&Erase", self.eraseChip) menu.addAction("Write &program memory", self.writeChipProgmem) menu.addAction("Write (E)EP&ROM", self.writeChipEeprom) menu.addAction("Write &fuses", self.writeChipFuses) menu.addAction("Write &lock bits", self.writeChipLockbits) menu.addAction("&Verify chip", self.verifyChip) self.menuBar().addMenu(menu) menu = QMenu("&Programmer", self) menu.addAction("&Universal chip tester...", self.startUnitest) menu.addSeparator() menu.addAction("&Send raw command...", self.sendRawCommand) self.menuBar().addMenu(menu) menu = QMenu("&Help", self) menu.addAction("&About", self.showAbout) self.menuBar().addMenu(menu) self.bufferTab = BufferTabWidget(self) self.setCentralWidget(self.bufferTab) self.console = Console(self) self.addDockWidget(Qt.BottomDockWidgetArea, self.console) self.autorun = AutorunWidget(self) self.addDockWidget(Qt.LeftDockWidgetArea, self.autorun) self.hwThread = HwThread(self) self.taskRunning = False self.taskRetval = None self.operation = self.OP_NONE def blockInputEvents(self): self.inputEventsBlocked += 1 def unblockInputEvents(self): self.inputEventsBlocked -= 1 def event(self, e): if e.type() == EVENT_HWTHREAD: self.hwThread.handleMessageQueue() e.accept() return True return QMainWindow.event(self, e) def closeEvent(self, e): if self.taskRunning: QMessageBox.critical(self, "Task running", "A hardware access task is running. Wait for it " "to finish before closing the application.") e.ignore() return self.operation = self.OP_SHUTDOWN self.hwThread.killThread() e.accept() def showAbout(self): QMessageBox.information(self, "About TOPrammer", "Copyright (c) Michael Buesch ") def startUnitest(self): self.bufferTab.setupBuffers(0) self.bufferTab.infoBuffer.clear() self.autorun.setup(0) self.guiDisable += 1 self.console.blockStatusUpdate() try: dlg = UnitestDialog(self) dlg.exec_() except (TOPException), e: self.console.showMessage("Failed to start Unitest: %s\n" % str(e), bold=True) self.console.unblockStatusUpdate() self.guiDisable -= 1 self.guiUpdateEnable() self.runOperation(self.OP_SHUTDOWN, HwThread.TASK_SHUTDOWN) def sendRawCommand(self): (string, ok) = QInputDialog.getText(self, "Send raw command to programmer", "Enter raw command to send, in " +\ "hex format (AABB1122...).\n" +\ "Warning: The programmer will malfunction on invalid commands.", QLineEdit.Normal, self.previousRawCommand) if not ok: return string = str(string).strip().upper() string = stringRemoveChars(string, " \t\r\n") if stringRemoveChars(string, "0123456789ABCDEF"): QMessageBox.critical(self, "Invalid characters", "Invalid characters in raw command string.\n" +\ "Only hex characters 0-9,a-f allowed.") return if len(string) % 2 != 0 or len(string) // 2 > 64: QMessageBox.critical(self, "Invalid length", "Invalid command length. Length must be even " +\ "and smaller or equal to 64 bytes.") return self.previousRawCommand = string command = hex2bin(string) self.runOperation(self.OP_RAWCOMMAND, HwThread.TASK_GENERICTOPCALL, ("top.runCommandSync(...)", command)) def loadBuffer(self): bufWidget = self.bufferTab.getCurrentBuffer() if not bufWidget: return if bufWidget.isReadOnly(): QMessageBox.critical(self, "Buffer is read only", "Cannot load data into the %s buffer.\n" "The buffer is read-only." %\ bufWidget.getName()) return selectedFilter = QString() fn = QFileDialog.getOpenFileName(self, "%s - open file" % bufWidget.getName(), QString(), "Intel hex file (*.hex *.ihex);;" "Binary file (*)", selectedFilter) if not fn: return extensions = str(selectedFilter).split("(")[1].\ split(")")[0].replace("*", "").strip().split() try: data = file(fn, "rb").read() except (IOError), e: QMessageBox.critical(self, "Failed to read file", "Failed to read %s:\n%s" %\ (str(fn), str(e.strerror))) return try: if ".hex" in extensions or ".ihex" in extensions: data = IO_ihex().toBinary(data) elif not extensions: data = IO_binary().toBinary(data) else: assert(0) except (TOPException), e: QMessageBox.critical(self, "Failed to convert data", "Failed to convert the input file data to binary\n%s" % str(e)) return if not bufWidget.setRawData(data): QMessageBox.critical(self, "Failed to load data", "Failed to load the file into the buffer") def saveBuffer(self): bufWidget = self.bufferTab.getCurrentBuffer() if not bufWidget: return data = bufWidget.getRawData() if not data: return selectedFilter = QString() fn = QFileDialog.getSaveFileName(self, "%s - save file" % bufWidget.getName(), QString(), "Intel hex file (*.hex);;" "Binary file (*)", selectedFilter) if not fn: return extensions = str(selectedFilter).split("(")[1].\ split(")")[0].replace("*", "").strip().split() if not extensions: extensions = [ "" ] if not fn.endsWith(extensions[0]): fn += extensions[0] if ".hex" in extensions or ".ihex" in extensions: data = IO_ihex().fromBinary(data) elif not extensions[0]: data = IO_binary().fromBinary(data) else: assert(0) try: file(fn, "wb").write(data) except (IOError), e: QMessageBox.critical(self, "Failed to write file", "Failed to write %s:\n%s" %\ (str(fn), str(e.strerror))) return def selectChip(self): dlg = ChipSelectDialog(self) if dlg.exec_() != QDialog.Accepted: return chipDescription = dlg.getSelectedChip() if not chipDescription: return self.currentChipDescription = chipDescription return self.runOperation(self.OP_INITCHIP, HwThread.TASK_INITCHIP, chipDescription.chipID) def readChip(self): return self.runOperation(self.OP_READALL, HwThread.TASK_READALL) def eraseChip(self): return self.runOperation(self.OP_ERASE, HwThread.TASK_GENERICTOPCALL, ("top.eraseChip()", )) def writeChipProgmem(self): bufferWidget = self.bufferTab.progmemBuffer data = bufferWidget.getRawData() if not bufferWidget.isAvailable() or not data: QMessageBox.critical(self, "No program memory", "No program memory available") return False return self.runOperation(self.OP_WRITEPROG, HwThread.TASK_GENERICTOPCALL, ("top.writeProgmem(...)", data)) def writeChipEeprom(self): bufferWidget = self.bufferTab.eepromBuffer data = bufferWidget.getRawData() if not bufferWidget.isAvailable() or not data: QMessageBox.critical(self, "No (E)EPROM memory", "No (E)EPROM memory available") return False return self.runOperation(self.OP_WRITEEPROM, HwThread.TASK_GENERICTOPCALL, ("top.writeEEPROM(...)", data)) def writeChipFuses(self): bufferWidget = self.bufferTab.fuseBuffer data = bufferWidget.getRawData() if not bufferWidget.isAvailable() or not data: QMessageBox.critical(self, "No fuse bits", "No fuse bits available") return False return self.runOperation(self.OP_WRITEFUSE, HwThread.TASK_GENERICTOPCALL, ("top.writeFuse(...)", data)) def writeChipLockbits(self): bufferWidget = self.bufferTab.lockBuffer data = bufferWidget.getRawData() if not bufferWidget.isAvailable() or not data: QMessageBox.critical(self, "No lock bits", "No lock bits available") return False return self.runOperation(self.OP_WRITELOCK, HwThread.TASK_GENERICTOPCALL, ("top.writeLockbits(...)", data)) def verifyChip(self): return self.runOperation(self.OP_VERIFY, HwThread.TASK_READALL) def guiUpdateEnable(self): enable = not self.guiDisable and not self.taskRunning and not self.autorun.isRunning() self.menuBar().setEnabled(enable) self.topToolBar.setEnabled(enable) self.autorun.setEnabled(enable) self.bufferTab.setEnabled(enable) def cancelHardwareTask(self): if self.autorun.isRunning(): self.autorun.abortRun() if self.taskRunning: self.hwThread.cancelTask() def runOperation(self, operation, hwTask, taskParameter=None): assert(not self.taskRunning) self.taskRunning = True if not self.hwThread.triggerTask(hwTask, taskParameter): self.taskRunning = False return False self.console.setTaskRunning(True) self.operation = operation self.guiUpdateEnable() return True def runOperationSync(self, hwTask, taskParameter=None): assert(not self.taskRunning) if not self.runOperation(self.OP_SYNCHRONOUS, hwTask, taskParameter): return (True, None) while self.taskRunning: evFilter = QEventLoop.AllEvents if self.inputEventsBlocked: evFilter |= QEventLoop.ExcludeUserInputEvents QApplication.processEvents(evFilter, 50) (retTask, failed, returnValue) = self.taskRetval self.taskRetval = None assert(not self.taskRunning and retTask == hwTask and\ self.operation == self.OP_NONE) return (failed, returnValue) def hardwareTaskFinished(self, task, failed, returnValue): self.taskRunning = False self.guiUpdateEnable() if self.operation == self.OP_SYNCHRONOUS: self.operation = self.OP_NONE self.console.setTaskRunning(False) self.taskRetval = (task, failed, returnValue) return if failed: self.console.showMessage("[op %d task %d failed] %s\n" %\ (self.operation, task, str(returnValue)), bold=True) self.console.setTaskRunning(False, False) self.operation = self.OP_NONE if self.autorun.isRunning(): self.autorun.abortRun(withMessage=True) return # Task succeed self.console.setTaskRunning(False) if self.operation == self.OP_INITCHIP: assert(task == HwThread.TASK_INITCHIP) (self.chipSupportFlags, asciiArtLayout) = returnValue self.bufferTab.setupBuffers(self.chipSupportFlags) self.bufferTab.infoBuffer.clear() self.bufferTab.infoBuffer.setChipName(self.currentChipDescription.description) self.bufferTab.infoBuffer.setChipLayout(asciiArtLayout) self.autorun.setup(self.chipSupportFlags) elif self.operation == self.OP_SHUTDOWN: pass # Nothing to do elif self.operation == self.OP_READALL: assert(task == HwThread.TASK_READALL) (sigImage, progmemImage, eepromImage, fuseImage, lockbitsImage) = returnValue self.bufferTab.setupBuffers(self.chipSupportFlags) self.bufferTab.loadBuffers(progmemImage, eepromImage, fuseImage, lockbitsImage) self.bufferTab.infoBuffer.setChipSignature(sigImage) elif self.operation == self.OP_ERASE: pass # Nothing to do elif self.operation == self.OP_WRITEPROG: pass # Nothing to do elif self.operation == self.OP_WRITEEPROM: pass # Nothing to do elif self.operation == self.OP_WRITEFUSE: pass # Nothing to do elif self.operation == self.OP_WRITELOCK: pass # Nothing to do elif self.operation == self.OP_VERIFY: assert(task == HwThread.TASK_READALL) (sigImage, progmemImage, eepromImage, fuseImage, lockbitsImage) = returnValue self.bufferTab.verifyBuffers(progmemImage, eepromImage, fuseImage) elif self.operation == self.OP_RAWCOMMAND: self.console.showMessage("Successfully sent raw command\n", bold=True) else: print "ERROR: No handler for op %d task %d" % (self.operation, task) self.operation = self.OP_NONE if self.autorun.isRunning(): QTimer.singleShot(0, self.autorun.runNextAction) def main(): app = QApplication(sys.argv) mainwnd = MainWindow() mainwnd.show() mainwnd.resize(int(mainwnd.width() * 1.0), int(mainwnd.height() * 1.4)) return app.exec_() if __name__ == "__main__": sys.exit(main())