class OpenADCInterface_FTDI(Parameterized, Plugin): _name = "FTDI (SASEBO-W/SAKURA-G)" def __init__(self, oadcInstance): self.serialNumber = '' self._serialnumbers = [''] self.params = Parameter(name=self.getName(), type='group') self.params.addChildren([ { 'name': 'Refresh Device List', 'type': 'action', 'action': self.serialRefresh }, { 'name': 'Device Serial Number', 'key': 'snum', 'type': 'list', 'values': [''], 'get': self.getSerialNumber, 'set': self.setSelectedDevice }, ]) self.ser = None if (openadc_qt is None) or (ft is None): raise ImportError("Needed imports for FTDI missing") else: self.scope = oadcInstance def getSerialNumber(self): return self.serialNumber @setupSetParam("Device Serial Number") def setSelectedDevice(self, snum): self.serialNumber = snum def con(self): if self.ser is None: try: self.dev = ft.openEx(str(self.serialNumber), ft.ftd2xx.OPEN_BY_SERIAL_NUMBER) self.dev.setBitMode(0x00, 0x40) self.dev.setTimeouts(500, 500) self.dev.setLatencyTimer(2) self.ser = self except ft.ftd2xx.DeviceError, e: self.ser = None raise IOError("Could not open %s: %s" % (self.serialNumber, e)) try: self.scope.con(self.ser) logging.info('OpenADC Found, Connecting') except IOError as e: exctype, value = sys.exc_info()[:2] raise IOError("OpenADC Error: %s" % (str(exctype) + str(value)) + " - " + e.message)
class OpenADCInterface_FTDI(Parameterized, Plugin): _name = "FTDI (SASEBO-W/SAKURA-G)" def __init__(self, oadcInstance): self.serialNumber = '' self._serialnumbers = [''] self.params = Parameter(name=self.getName(), type='group') self.params.addChildren([ {'name':'Refresh Device List', 'type':'action', 'action':self.serialRefresh}, {'name':'Device Serial Number', 'key':'snum', 'type':'list', 'values':[''], 'get':self.getSerialNumber, 'set':self.setSelectedDevice}, ]) self.ser = None if (openadc_qt is None) or (ft is None): raise ImportError("Needed imports for FTDI missing") else: self.scope = oadcInstance def getSerialNumber(self): return self.serialNumber @setupSetParam("Device Serial Number") def setSelectedDevice(self, snum): self.serialNumber = snum def __del__(self): if self.ser != None: self.ser.close() def con(self): if self.ser == None: try: self.dev = ft.openEx(str(self.serialNumber), ft.ftd2xx.OPEN_BY_SERIAL_NUMBER) self.dev.setBitMode(0x00, 0x40) self.dev.setTimeouts(500, 500) self.dev.setLatencyTimer(2) self.ser = self except ft.ftd2xx.DeviceError, e: self.ser = None raise IOError("Could not open %s: %s" % (self.serialNumber, e)) try: self.scope.con(self.ser) print("OpenADC Found, Connecting") except IOError,e: exctype, value = sys.exc_info()[:2] raise IOError("OpenADC Error: %s"%(str(exctype) + str(value)) + " - " + e.message)
class AttackSettings(Parameterized): def __init__(self): self._attack = None self.valid_attacks = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.analyzer.attacks", True, True) self.params = Parameter(name="Attack Settings", type="group") self.params.addChildren([ {'name': 'Attack', 'type':'list', 'values':self.valid_attacks, 'get':self.getAttack, 'set':self.setAttack} ]) @setupSetParam("Attack") def setAttack(self, atk): self._attack = atk if self._attack is not None: self.params.append(self._attack.params) def getAttack(self): return self._attack
class PreprocessingSettings(Parameterized): _num_modules = 4 def __init__(self, api): self._api = api self.valid_preprocessingModules = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.analyzer.preprocessing", False, False) self.params = Parameter(name="Preprocessing Settings", type='group') self._moduleParams = [ Parameter(name='self.ppmod[%d]' % (i), type='group') for i in range(self._num_modules) ] self._initModules() self.params.addChildren([ {'name':'Selected Modules', 'type':'group', 'children':[ {'name':'self.ppmod[%d]' % (step), 'type':'list', 'values':self.valid_preprocessingModules, 'get':partial(self.getModule, step), 'set':partial(self.setModule, step)} for step in range(0, len(self._modules)) ]}, ]) for m in self._moduleParams: self.params.append(m) def _initModules(self): self._modules = [None]*self._num_modules for i in range(self._num_modules): mod = PassThrough(name="Preprocessing Module ppmod[%d]" % (i)) self.setModule(i, mod) def getModule(self, num): return self._modules[num].__class__ # TODO: calling this from the command line causes a cosmetic bug @setupSetParam("") def setModule(self, num, module): """Insert the preprocessing module selected from the GUI into the list of active modules. This ensures that the options for that module are then displayed in the GUI, along with writing the auto-generated script. """ if module is None: raise ValueError("Received None as module in setModule()" % module) if num == 0: trace_source = self._api.project().traceManager() else: trace_source = self._modules[num-1] if self._modules[num] is not None: self._modules[num].deregister() self._moduleParams[num].clearChildren() if inspect.isclass(module): module = module(name="Preprocessing Module #%d" % (num+1)) self._modules[num] = module self._modules[num].setTraceSource(trace_source) self._moduleParams[num].append(self._modules[num].getParams()) if (num+1) < len(self._modules) and self._modules[num+1] is not None: self._modules[num+1].setTraceSource(module) def __getitem__(self, idx): return self._modules[idx] def __setitem__(self, idx, module): self.setModule(idx, module) logging.warning("Selected Modules parameter list didn't update during setModule call in PreprocessingSettings") def __str__(self): return str(self._modules) def __repr__(self): return str(self)
class ChipWhispererSAD(Parameterized): """ Communicates and drives with the Sum of Absolute Differences (SAD) Module inside the ChipWhisperer System. You need to configure the trigger module as active & set the trigger polarity to "high" for this to work. """ _name = 'SAD Trigger Module' STATUS_RUNNING_MASK = 1 << 3 STATUS_RESET_MASK = 1 << 0 STATUS_START_MASK = 1 << 1 def __init__(self, oa): self.oldlow = None self.oldhigh = None self.oa = oa self.sadref = [0] try: # Update SAD calculation when data changes ResultsBase.registeredObjects["Trace Output Plot"].dataChanged.connect(self.dataChanged) outwid = ResultsBase.registeredObjects["Trace Output Plot"] rangewidget = {'name':'Point Range', 'key':'pointrng', 'type':'rangegraph', 'limits':(0, 0), 'value':(0, 0), 'default':(0, 0), 'graphwidget':outwid, 'action':self.updateSADTraceRef, 'fixedsize':128} except KeyError: rangewidget = {'name':'Point Range', 'key':'pointrng', 'type':'range', 'limits':(0, 0), 'value':(0, 0), 'default':(0, 0), 'action':self.updateSADTraceRef, 'fixedsize':128} self.params = Parameter(name=self.getName(), type='group') self.params.addChildren([ # {'name':'Open SAD Viewer', 'type':'action'}, {'name':'SAD Ref From Captured', 'key':'sad', 'type':'group', 'children':[ rangewidget, {'name':'Set SAD Reference from Current Trace', 'key':'docopyfromcapture', 'type':'action', 'action':self.copyFromCaptureTrace}, {'name':'SAD Reference vs. Cursor', 'key':'sadrefcur', 'type':'int', 'value':0, 'limits':(-1, 100E6), 'readonly':True}, ]}, {'name':'SAD Threshold', 'type':'int', 'limits':(0, 100000), 'default':0, 'set':self.setThreshold, 'get':self.getThreshold} ]) def dataChanged(self, data, offset): """ Called when data in the trace window has changed. Used to update the limits for the point selection dialog. """ low = offset up = offset + len(data) - 1 if self.oldlow != low or self.oldup != up: self.oldlow = low self.oldup = up self.findParam(['sad', 'pointrng']).setLimits((low, up)) self.findParam(['sad', 'pointrng']).setValue((low, min(up, low + 128))) self.updateSADTraceRef() def getCaptueTraceRef(self): """ Get the reference data for SAD algorithm from the api trace window """ try: waveformWidget = ResultsBase.registeredObjects["Trace Output Plot"] except KeyError: print "SAD Trigger: Not running in GUI mode, no data source" return [0.0]*128 pstart = self.findParam(['sad', 'pointrng']).getValue()[0] - waveformWidget.lastStartOffset pend = self.findParam(['sad', 'pointrng']).getValue()[1] - waveformWidget.lastStartOffset data = waveformWidget.lastTraceData[pstart:pend] data = np.array(data) data = (data + 0.5) * 1024 return data def copyFromCaptureTrace(self, _=None): """ Send reference data to hardware from the trace window """ data = self.getCaptueTraceRef() if len(data) != 128: print "WARNING: Reference IS NOT 128 samples long, got %d"%len(data) self.sadref = data.copy() self.setRefWaveform(data) def updateSADTraceRef(self, ignored=None): """ Update the calculated SAD value parameter """ data = self.getCaptueTraceRef() diff = data - self.sadref diff = sum(abs(diff)) self.findParam(['sad','sadrefcur']).setValue(diff, ignoreReadonly=True) def reset(self): """ Reset the SAD hardware block. The ADC clock must be running! """ data = self.oa.sendMessage(CODE_READ, sadcfgaddr, maxResp=4) data[0] = 0x01 self.oa.sendMessage(CODE_WRITE, sadcfgaddr, data) if self.checkStatus(): raise IOError("SAD Reset in progress, but SAD reports still running. Is ADC Clock stopped?") data[0] = 0x00 self.oa.sendMessage(CODE_WRITE, sadcfgaddr, data) def start(self): """ Start the SAD algorithm, which causes the reference data to be loaded from the FIFO """ data = self.oa.sendMessage(CODE_READ, sadcfgaddr, maxResp=4) data[0] = 0x02 self.oa.sendMessage(CODE_WRITE, sadcfgaddr, data, Validate=False) data[0] = 0x00 self.oa.sendMessage(CODE_WRITE, sadcfgaddr, data, Validate=False) def checkStatus(self): """ Check if the SAD module is running & outputting valid data """ data = self.oa.sendMessage(CODE_READ, sadcfgaddr, maxResp=4) if not (data[0] & self.STATUS_RUNNING_MASK): return False else: return True def getThreshold(self): """ Get the threshold. When the SAD output falls below this threshold the system triggers """ data = self.oa.sendMessage(CODE_READ, sadcfgaddr, maxResp=4) threshold = data[1] threshold |= data[2] << 8 threshold |= data[3] << 16 return threshold @setupSetParam("SAD Threshold") def setThreshold(self, threshold): """ Set the threshold. When the SAD output falls below this threshold the system triggers """ data = self.oa.sendMessage(CODE_READ, sadcfgaddr, maxResp=4) data[1] = threshold & 0xff data[2] = (threshold >> 8) & 0xff data[3] = (threshold >> 16) & 0xff self.oa.sendMessage(CODE_WRITE, sadcfgaddr, data, Validate=False) if self.checkStatus() == False: raise IOError("SAD Threshold set, but SAD compare not running. No valid trigger will be present. Did you load a reference waveform?") def setRefWaveform(self, dataRef): """ Download a reference waveform. Resets the SAD module & starts it again after loading the new data. ADC Clock must be running! """ dataRefInt = [int(i) for i in dataRef] self.reset() # print dataRefInt dataRefInt = dataRefInt[::-1] wavedata = [] for d in dataRefInt: wavedata.append((d >> 8) & 0xff) wavedata.append(d & 0xff) self.oa.sendMessage(CODE_WRITE, saddataaddr, wavedata, Validate=False) self.start()
class ChipWhispererGlitch(Parameterized): """ Drives the Glitch Module inside the ChipWhisperer Capture Hardware Rev2, or can be used to drive this FPGA module inserted into other systems. """ CLKSOURCE0_BIT = 0b00000000 CLKSOURCE1_BIT = 0b00000001 CLKSOURCE_MASK = 0b00000011 _name = 'Glitch Module' def __init__(self, cwtype, scope, oa): # Setup FPGA partial configuration dataZ self.prCon = pr.PartialReconfigConnection() self.prEnabled = False self.oa = oa # Glitch width/offset # Note that these are ints scaled by 256/100 self._width = 26 self._offset = 26 # These ranges are updated during __init__: see below self._min_width = 0 self._max_width = 100 self._min_offset = 0 self._max_offset = 100 # Single-shot arm timing self._ssarm = 2 self.params = Parameter(name=self.getName(), type='group').register() self.params.addChildren([ { 'name': 'Clock Source', 'type': 'list', 'values': { 'Target IO-IN': self.CLKSOURCE0_BIT, 'CLKGEN': self.CLKSOURCE1_BIT }, 'set': self.setGlitchClkSource, 'get': self.glitchClkSource }, { 'name': 'Glitch Width (as % of period)', 'key': 'width', 'type': 'float', 'limits': (self._min_width, self._max_width), 'step': 0.39062, 'readonly': True, 'set': self.setGlitchWidth, 'get': self.getGlitchWidth }, { 'name': 'Glitch Width (fine adjust)', 'key': 'widthfine', 'type': 'int', 'limits': (-255, 255), 'set': self.setGlitchWidthFine, 'get': self.getGlitchWidthFine }, { 'name': 'Glitch Offset (as % of period)', 'key': 'offset', 'type': 'float', 'limits': (self._min_offset, self._max_offset), 'step': 0.39062, 'readonly': True, 'set': self.setGlitchOffset, 'get': self.getGlitchOffset }, { 'name': 'Glitch Offset (fine adjust)', 'key': 'offsetfine', 'type': 'int', 'limits': (-255, 255), 'set': self.setGlitchOffsetFine, 'get': self.getGlitchOffsetFine }, { 'name': 'Glitch Trigger', 'type': 'list', 'values': { 'Ext Trigger:Continous': 1, 'Manual': 0, 'Continuous': 2, 'Ext Trigger:Single-Shot': 3 }, 'set': self.setGlitchTrigger, 'get': self.glitchTrigger }, { 'name': 'Single-Shot Arm', 'type': 'list', 'key': 'ssarm', 'values': { 'Before Scope Arm': 1, 'After Scope Arm': 2 }, 'set': self.setArmTiming, 'get': self.getArmTiming }, { 'name': 'Ext Trigger Offset', 'type': 'int', 'range': (0, 50000000), 'set': self.setTriggerOffset, 'get': self.triggerOffset }, { 'name': 'Repeat', 'type': 'int', 'limits': (1, 255), 'set': self.setNumGlitches, 'get': self.numGlitches }, { 'name': 'Manual Trigger / Single-Shot Arm', 'type': 'action', 'action': self.glitchManual }, { 'name': 'Output Mode', 'type': 'list', 'values': { 'Clock XORd': 0, 'Clock ORd': 1, 'Glitch Only': 2, 'Clock Only': 3, 'Enable Only': 4 }, 'set': self.setGlitchType, 'get': self.glitchType }, { 'name': 'Read Status', 'type': 'action', 'action': self.checkLocked }, { 'name': 'Reset DCM', 'type': 'action', 'action': self.actionResetDCMs }, ]) # Check if we've got partial reconfiguration stuff for this scope try: if cwtype == "cwrev2" or cwtype == "cwcrev2": settingprefix = "cwcrev2" partialbasename = "s6lx25" self.glitchPR = pr.PartialReconfigDataMulti() elif cwtype == "cwlite": settingprefix = "cwlite" partialbasename = "cwlite" self.glitchPR = pr.PartialReconfigDataMulti() elif cwtype == "cw1200": settingprefix = "cw1200" partialbasename = "cw1200" self.glitchPR = pr.PartialReconfigDataMulti() else: raise ValueError("Invalid ChipWhisperer Mode: %s" % cwtype) if scope.getFWConfig().loader._release_mode != "debug": if scope.getFWConfig().loader._release_mode == "builtin": filelike = scope.getFWConfig().loader._bsBuiltinData zfile = zipfile.ZipFile(filelike) elif scope.getFWConfig().loader._release_mode == "zipfile": fileloc = scope.getFWConfig().loader._bsZipLoc if zipfile.is_zipfile(fileloc): zfile = zipfile.ZipFile(fileloc, "r") else: logging.warning( 'Partial Reconfiguration DISABLED: no zip-file for FPGA' ) zfile = None else: logging.warning( 'Partial Reconfiguration DISABLED: no PR data for FPGA' ) zfile = None raise ValueError("Unknown FPGA mode: %s" % scope.getFWConfig().loader._release_mode) if zfile: self.glitchPR.load( zfile.open("%s-glitchwidth.p" % partialbasename)) self.glitchPR.load( zfile.open("%s-glitchoffset.p" % partialbasename)) self.prEnabled = True else: self.prEnabled = False else: logging.warning( 'Partial Reconfiguration DISABLED: Debug bitstream mode') self.prEnabled = False except IOError as e: logging.error(str(e)) self.prEnabled = False except ValueError as e: logging.error(str(e)) self.prEnabled = False except OSError as e: # Also catches WindowsError logging.error(str(e)) self.prEnabled = False if self.prEnabled: # Enable glitch width, check what we've got access to self.findParam('width').setReadonly(False) self._min_width = self.glitchPR.limitList[0][0] / 2.55 self._max_width = self.glitchPR.limitList[0][1] / 2.55 self.findParam('width').setLimits( (self._min_width, self._max_width)) self.findParam('offset').setReadonly(False) self._min_offset = self.glitchPR.limitList[1][0] / 2.55 self._max_offset = self.glitchPR.limitList[1][1] / 2.55 self.findParam('offset').setLimits( (self._min_offset, self._max_offset)) self.setOpenADC(oa) self.glitchSettings = GlitchSettings(self) def setOpenADC(self, oa): self.oa = None if self.prEnabled: self.prCon.con(oa) # Check this is actually working if self.prCon.isPresent() == False: self.prEnabled = False logging.warning( 'Partial Reconfiguration block not detected, PR disabled') return # Reset FPGA back to defaults in case previous bitstreams loaded self.updatePartialReconfig() self.oa = oa try: self.params.refreshAllParameters() except TypeError: return def updatePartialReconfig(self, _=None): """ Reads the values set via the GUI & updates the hardware settings for partial reconfiguration. Checks that PR is enabled with self.prEnabled. """ if not self.prEnabled: return if self._width == 0: logging.warning( 'Partial reconfiguration for width = 0 may not work') if self._offset == 0: logging.warning( 'Partial reconfiguration for offset = 0 may not work') bs = self.glitchPR.getPartialBitstream([self._width, self._offset]) if self.prEnabled: self.prCon.program(bs) if self.oa is not None: self.resetDCMs(keepPhase=True) # print "Partial: %d %d" % (widthint, offsetint) self.updateGlitchReadBack() def updateGlitchReadBack(self, test=False): """Updates the readback register in the FPGA with glitch information, used for LCD update on CW1200 hardware.""" #Don't write if PR disable by accident if self.oa is None: return width = self.getGlitchWidth() offset = self.getGlitchOffset() cmd = bytearray(8) #Integer downloads cmd[0] = self._offset & 0xff cmd[1] = (self._offset >> 8) & 0xff cmd[2] = self._width & 0xff cmd[3] = (self._width >> 8) & 0xff #Floating point info cmd[4] = int(offset) & 0xff cmd[5] = int(("%f" % offset).split(".")[1][0:2]) & 0xff cmd[6] = int(width) & 0xff cmd[7] = int(("%f" % width).split(".")[1][0:2]) & 0xff self.oa.sendMessage(CODE_WRITE, glitchreadbackaddr, cmd, Validate=False) @setupSetParam("Glitch Width (as % of period)") def setGlitchWidth(self, width): if width > self._max_width: width = self._max_width if width < self._min_width: width = self._min_width self._width = int(round((width / 100.) * 256.)) self.updatePartialReconfig() def getGlitchWidth(self): return self._width * 100. / 256. @setupSetParam("Glitch Offset (as % of period)") def setGlitchOffset(self, offset): if offset > self._max_offset: offset = self._max_offset if offset < self._min_offset: offset = self._min_offset self._offset = int(round((offset / 100.) * 256.)) self.updatePartialReconfig() def getGlitchOffset(self): return self._offset * 100. / 256. @setupSetParam("Ext Trigger Offset") def setTriggerOffset(self, offset): offset = int(offset) """Set offset between trigger event and glitch in clock cycles""" cmd = bytearray(4) cmd[0] = ((offset >> 0) & 0xFF) cmd[1] = ((offset >> 8) & 0xFF) cmd[2] = ((offset >> 16) & 0xFF) cmd[3] = ((offset >> 24) & 0xFF) self.oa.sendMessage(CODE_WRITE, glitchoffsetaddr, cmd) def triggerOffset(self): """Get offset between trigger event and glitch in clock cycles""" cmd = self.oa.sendMessage(CODE_READ, glitchoffsetaddr, maxResp=4) offset = cmd[0] offset |= cmd[1] << 8 offset |= cmd[2] << 16 offset |= cmd[3] << 24 return offset @setupSetParam("Glitch Offset (fine adjust)") def setGlitchOffsetFine(self, fine): """Set the fine glitch offset adjust, range -255 to 255""" current = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) if current is None or len(current) < 8: logging.warning('Glitch Module not present?') return LSB = fine & 0x00FF MSB = (fine & 0x0100) >> 8 current[0] = LSB #7..0 current[1] = (current[1] & ~0x01) | MSB #15..8 #Start adjust current[2] = current[2] | 0x04 # 23..16 #assign clockglitch_settings_read[37] = phase1_done_reg; #assign clockglitch_settings_read[38] = phase2_done_reg; self.oa.sendMessage(CODE_WRITE, glitchaddr, current, Validate=False) def getGlitchWidthFine(self): return self.getDCMStatus()[1] @setupSetParam("Glitch Width (fine adjust)") def setGlitchWidthFine(self, fine): """Set the fine glitch width adjust, range -255 to 255""" current = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) if current is None or len(current) < 8: logging.warning('Glitch Module not present?') return LSB = fine & 0x00FF MSB = (fine & 0x0100) >> 8 current[1] = (current[1] & 0x01) | ((LSB & 0x7F) << 1) current[2] = (current[2] & ~0x03) | ((LSB >> 7) | (MSB << 1)) #Start adjust current[2] = current[2] | 0x04 # 23..16 #assign clockglitch_settings_read[37] = phase1_done_reg; # assign clockglitch_settings_read[38] = phase2_done_reg; self.oa.sendMessage(CODE_WRITE, glitchaddr, current, Validate=False) def getGlitchOffsetFine(self): return self.getDCMStatus()[0] def getDCMStatus(self): current = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) glitch_offset_fine_loaded = current[2] >> 3 glitch_offset_fine_loaded |= (current[3] & 0x0F) << 5 glitch_offset_fine_loaded = SIGNEXT(glitch_offset_fine_loaded, 9) glitch_width_fine_loaded = (current[3] & 0xF0) >> 4 glitch_width_fine_loaded |= (current[4] & 0x1F) << 4 glitch_width_fine_loaded = SIGNEXT(glitch_width_fine_loaded, 9) dcm1Lock = False dcm2Lock = False if current[4] & 0x80: dcm1Lock = True if current[5] & 0x01: dcm2Lock = True return (glitch_offset_fine_loaded, glitch_width_fine_loaded, dcm1Lock, dcm2Lock) def actionResetDCMs(self, _=None): """Action for parameter class""" self.resetDCMs() def resetDCMs(self, keepPhase=False): """Reset the DCMs for the Glitch width & Glitch offset. Required after doing a PR operation""" reset = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) reset[5] |= (1 << 1) self.oa.sendMessage(CODE_WRITE, glitchaddr, reset, Validate=False) reset[5] &= ~(1 << 1) self.oa.sendMessage(CODE_WRITE, glitchaddr, reset, Validate=False) # Reload any special phase offset if keepPhase: self.setGlitchWidthFine(self.findParam('widthfine').getValue()) self.setGlitchOffsetFine(self.findParam('offsetfine').getValue()) else: self.findParam('widthfine').setValue(0) self.findParam('offsetfine').setValue(0) def checkLocked(self, _=None): """Check if the DCMs are locked and print results """ stat = self.getDCMStatus() logging.info('DCM1: Phase %d, Locked %r' % (stat[0], stat[2])) logging.info('DCM2: Phase %d, Locked %r' % (stat[1], stat[3])) @setupSetParam("Repeat") def setNumGlitches(self, num): """Set number of glitches to occur after a trigger""" num = int(num) resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) if resp is None or len(resp) < 8: logging.warning('Glitch Module not present?') return if num < 1: num = 1 resp[6] = num - 1 self.oa.sendMessage(CODE_WRITE, glitchaddr, resp, Validate=False) def numGlitches(self): """Get number of glitches to occur after a trigger""" resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) return resp[6] + 1 @setupSetParam("Glitch Trigger") def setGlitchTrigger(self, trigger): """Set glitch trigger type (manual, continous, adc-trigger)""" resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) resp[5] = (resp[5] & ~(0x0C)) | (trigger << 2) self.oa.sendMessage(CODE_WRITE, glitchaddr, resp, Validate=False) def glitchTrigger(self): """Get glitch trigger type""" resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) return (resp[5] & 0x0C) >> 2 @setupSetParam("Output Mode") def setGlitchType(self, t): """Set glitch output type (ORd with clock, XORd with clock, clock only, glitch only)""" resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) resp[5] = (resp[5] & ~(0x70)) | (t << 4) self.oa.sendMessage(CODE_WRITE, glitchaddr, resp, Validate=False) def glitchType(self): resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) return (resp[5] & 0x70) >> 4 def glitchManual(self, _=None): """ Cause a single glitch event to occur. Depending on setting of numGlitches() this may mean multiple glitches in a row """ resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) resp[5] = resp[5] | (1 << 7) self.oa.sendMessage(CODE_WRITE, glitchaddr, resp, Validate=False) resp[5] = resp[5] & ~(1 << 7) self.oa.sendMessage(CODE_WRITE, glitchaddr, resp, Validate=False) def glitchArm(self): """If trigger is set to single-shot mode, this must be called before the selected trigger occurs""" self.glitchManual() @setupSetParam("Clock Source") def setGlitchClkSource(self, source): """Set the source of the glitched clock, either the HS1-In or the CLKGEN Module""" resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) resp[7] = (resp[7] & ~self.CLKSOURCE_MASK) | source self.oa.sendMessage(CODE_WRITE, glitchaddr, resp, Validate=False) def glitchClkSource(self): resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) return (resp[7] & self.CLKSOURCE_MASK) def getArmTiming(self): return self._ssarm @setupSetParam("Single-Shot Arm") def setArmTiming(self, val): self._ssarm = val def armPreScope(self): """Called before scope trigger is armed""" if self.getArmTiming() == 1: self.glitchArm() def armPostScope(self): """Called after scope trigger is armed""" if self.getArmTiming() == 2: self.glitchArm()
class Project(Parameterized): """Class describing an open ChipWhisperer project. Basic capture usage:: import chipwhisperer as cw proj = cw.create_project("project") trace = cw.Trace(trace_data, plaintext, ciphertext, key) proj.traces.append(trace) proj.save() Basic analyzer usage:: import chipwhisperer as cw import chipwhisperer.analyzer as cwa proj = cw.open_project("project") attack = cwa.cpa(proj) #run attack Use a trace_manager when analyzing traces, since that allows analyzer to work with multiple trace segments. * :attr:`project.location <.Project.location>` * :attr:`project.waves <.Project.waves>` * :attr:`project.textins <.Project.textins>` * :attr:`project.textouts <.Project.textouts>` * :attr:`project.keys <.Project.keys>` * :meth:`project.get_filename <.Project.get_filename>` * :meth:`project.trace_manager <.Project.trace_manager>` * :meth:`project.save <.Project.save>` * :meth:`project.export <.Project.export>` """ untitledFileName = os.path.normpath( os.path.join(Settings().value("project-home-dir"), "tmp", "default.cwp")) def __init__(self, prog_name="ChipWhisperer", prog_ver=""): self.valid_traces = None self._trace_format = None self.params = Parameter(name="Project Settings", type="group") self.params.addChildren([ { 'name': 'Trace Format', 'type': 'list', 'values': self.valid_traces, 'get': self.get_trace_format, 'set': self.set_trace_format }, ]) #self.findParam("Trace Format").setValue(TraceContainerNative(project=self), addToList=True) self._trace_format = TraceContainerNative(project=self) #self.traceParam = Parameter(name="Trace Settings", type='group', addLoadSave=True).register() #self.params.getChild('Trace Format').stealDynamicParameters(self.traceParam) self.sigFilenameChanged = util.Signal() self.sigStatusChanged = util.Signal() self.dirty = util.Observable(True) self.settingsDict = { 'Project Name': "Untitled", 'Project File Version': "1.00", 'Project Author': "Unknown" } self.datadirectory = "" self.config = ConfigObjProj(callback=self.configObjChanged) self._traceManager = TraceManager().register() self._traceManager.dirty.connect(self.__dirtyCallback) self.setFilename(Project.untitledFileName) self.setProgramName(prog_name) self.setProgramVersion(prog_ver) self._segments = Segments(self) self._traces = Traces(self) self._keys = IndividualIterable(self._traceManager.get_known_key, self._traceManager.num_traces) self._textins = IndividualIterable(self._traceManager.get_textin, self._traceManager.num_traces) self._textouts = IndividualIterable(self._traceManager.get_textout, self._traceManager.num_traces) self._waves = IndividualIterable(self._traceManager.get_trace, self._traceManager.num_traces) if __debug__: logging.debug('Created: ' + str(self)) def append_segment(self, segment): """ Adds a new trace segment to the project Args: segment (TraceContainer): Trace segment to add to project """ self._traceManager.appendSegment(copy.copy(segment)) appendSegment = append_segment def new_segment(self): """ Returns the __class__() of the trace container used to store traces in the project You probably want get_new_trace_segment() Returns: __class()__ of the current trace container """ return self._trace_format.__class__() newSegment = new_segment def get_trace_format(self): """ Gets the TraceContainer used to store traces Returns: The trace container used in the project """ return self._trace_format getTraceFormat = get_trace_format def get_new_trace_segment(self): """Return a new TraceContainer object for the specified format. Once you're done working with this segment, you can readd it to the project with append_segment() Returns: a new TraceContainer object """ tmp = copy.copy(self._trace_format) tmp.clear() starttime = datetime.now() prefix = starttime.strftime('%Y.%m.%d-%H.%M.%S') + "_" tmp.config.setConfigFilename( os.path.join(self.datadirectory, "traces", "config_" + prefix + ".cfg")) tmp.config.setAttr("prefix", prefix) tmp.config.setAttr("date", starttime.strftime('%Y-%m-%d %H:%M:%S')) return tmp getNewTraceSegment = get_new_trace_segment @setupSetParam("Trace Format") def set_trace_format(self, trace_format): self._trace_format = trace_format setTraceFormat = util.camel_case_deprecated(set_trace_format) def __dirtyCallback(self): self.dirty.setValue(self._traceManager.dirty.value() or self.dirty.value()) def configObjChanged(self, key): self.dirty.setValue(True) def isUntitled(self): return self.filename == Project.untitledFileName def trace_manager(self): """ Gets the trace manager for the project Returns: The trace manager for the project """ return self._traceManager traceManager = util.camel_case_deprecated(trace_manager) def setProgramName(self, name): self.settingsDict['Program Name'] = name def setProgramVersion(self, ver): self.settingsDict['Program Version'] = ver def setAuthor(self, author): self.settingsDict['Project Author'] = author def setProjectName(self, name): self.settingsDict['Project Name'] = name def setFileVersion(self, ver): self.settingsDict['Project File Version'] = ver def get_filename(self): """Gets the filename associated with the project. Returns: Filename of the project config file ending with ".cwp". """ return self.filename getFilename = util.camel_case_deprecated(get_filename) def setFilename(self, f): self.filename = f self.config.filename = f self.datadirectory = os.path.splitext(self.filename)[0] + "_data" self.createDataDirectory() self.sigStatusChanged.emit() def createDataDirectory(self): # Check if data-directory exists? if not os.path.isdir(self.datadirectory): os.makedirs(self.datadirectory) # Make trace storage directory too if not os.path.isdir(os.path.join(self.datadirectory, 'traces')): os.mkdir(os.path.join(self.datadirectory, 'traces')) # Make analysis storage directory if not os.path.isdir(os.path.join(self.datadirectory, 'analysis')): os.mkdir(os.path.join(self.datadirectory, 'analysis')) # Make glitchresults storage directory if not os.path.isdir(os.path.join(self.datadirectory, 'glitchresults')): os.mkdir(os.path.join(self.datadirectory, 'glitchresults')) def load(self, f=None): if f is not None: self.setFilename(os.path.abspath(os.path.expanduser(f))) if not os.path.isfile(self.filename): raise IOError("File " + self.filename + " does not exist or is not a file") self.config = ConfigObjProj(infile=self.filename, callback=self.configObjChanged) self._traceManager.loadProject(self.filename) self.dirty.setValue(False) def getDataFilepath(self, filename, subdirectory='analysis'): datadir = os.path.join(self.datadirectory, subdirectory) fname = os.path.join(datadir, filename) relfname = os.path.relpath(fname, os.path.split(self.config.filename)[0]) fname = os.path.normpath(fname) relfname = os.path.normpath(relfname) return {"abs": fname, "rel": relfname} def convertDataFilepathAbs(self, relativepath): return os.path.join(os.path.split(self.filename)[0], relativepath) def checkDataConfig(self, config, requiredSettings): """Check a configuration section for various settings. Don't use.""" requiredSettings = util.convert_to_str(requiredSettings) config = util.convert_to_str(config) return set(requiredSettings.items()).issubset(set(config.items())) def getDataConfig(self, sectionName="Aux Data", subsectionName=None, requiredSettings=None): """ Don't use. Get all configuration sections of data type given in __init__() call, and also matching the given sectionName. e.g. if dataName='Aux Data' and sectionName='Frequency', this would return a list of all sections of the type 'Aux Data NNNN - Frequency'. """ sections = [] # Get all section names for sname in list(self.config.keys()): # Find if starts with 'Aux Data' if sname.startswith(sectionName): # print "Found %s" % sname # Find if module name matches if applicable if subsectionName is None or sname.endswith(subsectionName): # print "Found %s" % sname if requiredSettings is None: sections.append(self.config[sname]) else: self.checkDataConfig(self.config[sname], requiredSettings) return sections def addDataConfig(self, settings=None, sectionName="Aux Data", subsectionName=None): # Check configuration file to find incrementing number. Don't use maxNumber = 0 for sname in list(self.config.keys()): # Find if starts with 'Aux Data' if sname.startswith(sectionName): maxNumber = int(re.findall(r'\d+', sname)[0]) + 1 cfgSectionName = "%s %04d" % (sectionName, maxNumber) if subsectionName: cfgSectionName += " - %s" % subsectionName # Generate the configuration section self.config[cfgSectionName] = {} if settings is not None: for k in list(settings.keys()): self.config[cfgSectionName][k] = settings[k] return self.config[cfgSectionName] def saveAllSettings(self, fname=None, onlyVisibles=False): """ Save registered parameters to a file, so it can be loaded again latter. Don't use.""" if fname is None: fname = os.path.join(self.datadirectory, 'settings.cwset') logging.info('Saving settings to file: ' + fname) Parameter.saveRegistered(fname, onlyVisibles) def saveTraceManager(self): #Waveform list is Universal across ALL types. Don't use. if 'Trace Management' not in self.config: self.config['Trace Management'] = {} self._traceManager.save_project(self.config, self.filename) def save(self): """Saves the project. Writes the project to the disk. Before this is called your data is not saved. Filenames for traces are set in this method. """ if self.filename is None: return self.saveTraceManager() #self.config['Waveform List'] = self.config['Waveform List'] + self.waveList #Program-Specific Options pn = self.settingsDict['Program Name'] self.config[pn] = {} self.config[pn]['General Settings'] = self.settingsDict self.config.write() self.sigStatusChanged.emit() self.dirty.setValue(False) def checkDiff(self): """ Don't use. Check if there is a difference - returns True if so, and False if no changes present. Also updates widget with overview of the differences if requested with updateGUI """ self.saveTraceManager() disk = util.convert_to_str(ConfigObjProj(infile=self.filename)) ram = util.convert_to_str(self.config) def hasDiffs(self): if self.dirty.value(): return True #added, removed, changed = self.checkDiff() if (len(added) + len(removed) + len(changed)) == 0: return False return True def consolidate(self, keepOriginals=True): for indx, t in enumerate(self._traceManager.traceSegments): destinationDir = os.path.normpath( os.path.join(self.datadirectory, "traces")) config = ConfigObj(t.config.configFilename()) prefix = config['Trace Config']['prefix'] tracePath = os.path.normpath( os.path.split(t.config.configFilename())[0]) if destinationDir == tracePath: continue for traceFile in os.listdir(tracePath): if traceFile.startswith(prefix): util.copyFile( os.path.normpath(os.path.join(tracePath, traceFile)), destinationDir, keepOriginals) util.copyFile(t.config.configFilename(), destinationDir, keepOriginals) t.config.setConfigFilename( os.path.normpath( os.path.join(destinationDir, os.path.split(t.config.configFilename())[1]))) self.sigStatusChanged.emit() def __del__(self): if __debug__: logging.debug('Deleted: ' + str(self)) @property def location(self): """The directory in which the project is located. Example:: print(project.location) '/path/to/the/directory/containing/this/project' :Getter: (str) Returns the file path of the projects parent directory. .. versionadded:: 5.1 Added **location** attribute to project. """ return os.path.dirname(os.path.abspath(self.get_filename())) def export(self, file_path, file_type='zip'): """Export a chipwhisperer project. Saves project before exporting. Supported export types: * zip (default) Returns: (str) Path to the exported file. .. versionadded:: 5.1 Add **export** method to active project. """ self.save() _, cwp_file = os.path.split(self.get_filename()) name, ext = os.path.splitext(cwp_file) data_folder = os.path.join(self.location, '_'.join([name, 'data'])) file_paths = list() file_paths.append(os.path.join(self.location, cwp_file)) for root, directories, files in os.walk(data_folder): for filename in files: file_paths.append(os.path.join(root, filename)) if file_type == 'zip': file_path = os.path.abspath(file_path) with zipfile.ZipFile(file_path, 'w') as zip: common_path = os.path.commonpath(file_paths) for file in file_paths: relative_path = os.path.relpath(file, common_path) zip.write(file, arcname=relative_path) export_file_path = os.path.abspath(zip.filename) else: raise ValueError('{} not supported'.format(file_type)) return export_file_path def close(self, save=True): """Closes the project cleanly. Saves by default. Then closes all claimed files. Args: save (bool): Saves the project before closing. """ if save: self.save() for seg in self.segments: seg.unloadAllTraces() def remove(self, i_am_sure=False): """Remove a project from disk. Args: i_am_sure (bool): Are you sure you want to remove the project? """ if not i_am_sure: raise RuntimeWarning( 'Project not removed... i_am_sure not set to True.') self.close(save=False) try: shutil.rmtree(self.datadirectory) except FileNotFoundError: pass _, cwp_file = os.path.split(self.get_filename()) try: os.remove(os.path.join(self.location, cwp_file)) except FileNotFoundError: pass @property def traces(self): """The interface to all traces contained in the project. Instance of :class:`.Traces`. """ return self._traces @property def segments(self): """The interface to all segments contained in the project. Instance of :class:`.Segments`. """ return self._segments @property def keys(self): """Iterable for working with only the known keys. Each item in the iterable is a byte array. Supports iterating, indexing, and slicing:: for key in my_project.keys: # do something """ return self._keys @property def textins(self): """Iterable for working with only the text in. Each item in the iterable is a byte array. Supports iterating, indexing, and slicing:: for textin in my_project.textins: # do something """ return self._textins @property def textouts(self): """Iterable for working with only the text out. Each item in the iterable is a byte array. Supports iterating, indexing, and slicing:: for textout in my_project.textouts: # do something """ return self._textouts @property def waves(self): """Iterable for working with only the trace data. Each item in the iterable is a numpy array. Supports iterating, indexing, and slicing:: for wave in my_project.waves: # do something """ return self._waves
class CWCoreAPI(Parameterized): """ ChipWisperer API Class. Provides access to the most important parts of the tool. It has a singleton method called CWCoreAPI.getInstance() that returns a reference to the API instance. """ __name__ = "ChipWhisperer" __organization__ = "NewAE Technology Inc." __version__ = "V4.0.2" _name = 'Generic Settings' instance = None def __init__(self): logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) CWCoreAPI.instance = self self.sigNewProject = util.Signal() self.sigConnectStatus = util.Signal() self.sigAttackChanged = util.Signal() self.sigNewInputData = util.Signal() self.sigNewTextResponse = util.Signal() self.sigTraceDone = util.Signal() self.sigCampaignStart = util.Signal() self.sigCampaignDone = util.Signal() self.executingScripts = util.Observable(False) self.valid_scopes = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.capture.scopes", True, True) self.valid_targets = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.capture.targets", True, True) self.valid_traces = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.common.traces", True, True) self.valid_aux = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.capture.auxiliary", True, True) self.valid_acqPatterns = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.capture.acq_patterns", True, False) self.settings = Settings() # Initialize default values self._project = self._scope = self._target = self._traceFormat = self._acqPattern = self._attack = None self._acqPattern = self.valid_acqPatterns["Basic"] self._auxList = AuxList() self._numTraces = 50 self._numTraceSets = 1 # Storage for last key/plaintext/ciphertext self._lastKey = None self._lastTextin = None self._lastTextout = None self._lastExpected = None self.params = Parameter(name='Generic Settings', type='group', addLoadSave=True).register() self.params.addChildren([ {'name':'Scope Module', 'key':'scopeMod', 'type':'list', 'values':self.valid_scopes, 'get':self.getScope, 'set':self.setScope}, {'name':'Target Module', 'key':'targetMod', 'type':'list', 'values':self.valid_targets, 'get':self.getTarget, 'set':self.setTarget}, {'name':'Acquisition Settings', 'type':'group', 'children':[ {'name':'Number of Traces', 'type':'int', 'limits':(1, 1E9), 'get':self.getNumTraces, 'set':self.setNumTraces, 'linked':['Traces per Set']}, {'name':'Number of Sets', 'type':'int', 'limits':(1, 1E6), 'get':self.getNumTraceSets, 'set':self.setNumTraceSets, 'linked':['Traces per Set'], 'tip': 'Break acquisition into N sets, ' 'which may cause data to be saved more frequently. The default capture driver requires that NTraces/NSets is small enough to avoid running out of system memory ' 'as each segment is buffered into RAM before being written to disk.'}, {'name':'Traces per Set', 'type':'int', 'readonly':True, 'get':self.tracesPerSet}, {'name':'Key/Text Pattern', 'type':'list', 'values':self.valid_acqPatterns, 'get':self.getAcqPattern, 'set':self.setAcqPattern}, ]}, ]) self.scopeParam = Parameter(name="Scope Settings", type='group', addLoadSave=True).register() self.params.getChild('Scope Module').stealDynamicParameters(self.scopeParam) self.targetParam = Parameter(name="Target Settings", type='group', addLoadSave=True).register() self.params.getChild('Target Module').stealDynamicParameters(self.targetParam) # Aux settings self.auxParam = self._auxList.getParams().register() # Note: Project settings are set up in setProject() self.newProject() def updateLastKeyText(self, key, textin, textout, exp): """Callback for acq controller signal - update key/textin/textout """ self._lastKey = key self._lastTextin = textin self._lastTextout = textout self._lastExpected = exp self.sigNewTextResponse.emit(key, textin, textout, exp) def getResults(self, name): """Return the requested result widget. It should be registered.""" return ResultsBase.registeredObjects[name] def getScope(self): """Return the current scope module object.""" return self._scope @setupSetParam("Scope Module") def setScope(self, driver): """Set the current scope module object.""" if self.getScope(): # Don't do anything if we're not changing scopes if self.getScope() is driver: return else: self.getScope().dis() self._scope = driver if self.getScope(): self.getScope().connectStatus.connect(self.sigConnectStatus.emit) self.scopeParam.append(self.getScope().params) try: ResultsBase.registeredObjects["Trace Output Plot"].setTraceSource( TraceSource.registeredObjects[next(reversed(TraceSource.registeredObjects))]) except KeyError: pass if self.getScope().getStatus(): self.getScope().connectStatus.emit() def getTarget(self): """Return the current target module object.""" return self._target @setupSetParam("Target Module") def setTarget(self, driver): """Set the current target module object.""" if self.getTarget(): # Don't do anything if we're not changing targets if self.getTarget() is driver: return self.getTarget().dis() self._target = driver if self.getTarget(): self.targetParam.append(self.getTarget().params) self.getTarget().newInputData.connect(self.sigNewInputData.emit) self.getTarget().connectStatus.connect(self.sigConnectStatus.emit) if self.getTarget().getStatus(): self.getTarget().connectStatus.emit() def getAuxList(self): return self._auxList def setAuxList(self, new_list): self._auxList = new_list def getAuxFunctions(self, only_enabled): """TODO: doc """ return self._auxList.getDict(only_enabled) def getAcqPattern(self): """Return the selected acquisition pattern.""" return self._acqPattern def getAttack(self): return self._attack @setupSetParam(["Attack Settings", "Attack"]) def setAttack(self, atk): self._attack = atk #if self._attack is not None: # self.attackParam.append(self._attack.params) @setupSetParam(["Acquisition Settings", "Key/Text Pattern"]) def setAcqPattern(self, pat): """Set the current acquisition pattern.""" self._acqPattern = pat if self._acqPattern is not None: self._acqPattern.getParams().remove() self.getParams().append(self._acqPattern.getParams()) def getNewTrace(self, format): """Return a new trace object for the specified format.""" if format is None: raise Warning("No trace format selected.") tmp = copy.copy(format) tmp.clear() starttime = datetime.now() prefix = starttime.strftime('%Y.%m.%d-%H.%M.%S') + "_" tmp.config.setConfigFilename(CWCoreAPI.getInstance().project().datadirectory + "traces/config_" + prefix + ".cfg") tmp.config.setAttr("prefix", prefix) tmp.config.setAttr("date", starttime.strftime('%Y-%m-%d %H:%M:%S')) return tmp def getTraceFormat(self): """Return the selected trace format.""" if self._project is not None: return self._project.getTraceFormat() else: return None def setTraceFormat(self, format): """Set the current trace format for acquisition.""" if self._project is not None: self._project.setTraceFormat(format) def project(self): """Return the current opened project""" return self._project def setProject(self, proj): """Set the current opened project""" self._project = proj self.params.append(proj.getParams()) self.sigNewProject.emit() def newProject(self): """Create a new project""" self.setProject(ProjectFormat(self.__name__, self.__version__)) def openProject(self, fname): """Open project file""" self.newProject() self.project().load(fname) try: ResultsBase.registeredObjects["Trace Output Plot"].setTraceSource(TraceSource.registeredObjects["Trace Management"]) except KeyError: pass def saveProject(self, fname=None): """Save the current opened project to file""" if fname is not None: self.project().setFilename(fname) self.project().save() def connectScope(self): """Connect to the selected scope""" try: if self.getScope(): self.getScope().con() try: # Sets the Plot Widget input (if it exists) to the last added TraceSource ResultsBase.registeredObjects["Trace Output Plot"].setTraceSource( TraceSource.registeredObjects[next(reversed(TraceSource.registeredObjects))]) except KeyError: pass except Warning: sys.excepthook(*sys.exc_info()) return False return True def disconnectScope(self): """Disconnect the current scope""" self.getScope().dis() def connectTarget(self): """Connect to the selected target""" try: if self.getTarget(): self.getTarget().con(scope=self.getScope()) except Warning: sys.excepthook(*sys.exc_info()) return False return True def disconnectTarget(self): """Disconnect the current target""" self.getTarget().dis() def doConDis(self): """DEPRECATED: It is here just for compatibility reasons""" logging.warning('Method doConDis() is deprecated... use connect() or disconnect() instead') return self.connect() def connect(self): """Connect both: scope and target""" return self.connectScope() and self.connectTarget() def disconnect(self): """Disconnect both: scope and target""" self.disconnectScope() self.disconnectTarget() def getNumTraces(self): """Return the total number or traces for acquisition purposes""" return self._numTraces @setupSetParam(["Acquisition Settings", "Number of Traces"]) def setNumTraces(self, n): """Set the total number or traces for acquisition purposes""" self._numTraces = n def getNumTraceSets(self): """Return the number of sets/segments""" return self._numTraceSets @setupSetParam(["Acquisition Settings", "Number of Sets"]) def setNumTraceSets(self, s): """Set the number of sets/segments""" self._numTraceSets = s def tracesPerSet(self): """Return the number of traces in each set/segment""" return int(self._numTraces / self._numTraceSets) def capture1(self): """Capture one trace""" try: aux_dict = self.getAuxFunctions(True) ac = AcquisitionController(self.getScope(), self.getTarget(), writer=None, aux=aux_dict, keyTextPattern=self.getAcqPattern()) ac.sigNewTextResponse.connect(self.updateLastKeyText) if self.getTarget(): self.getTarget().init() return ac.doSingleReading() except Warning: sys.excepthook(*sys.exc_info()) return False def captureM(self, progressBar=None, scope=None, target=None, project=None, aux_list=None, ktp=None, N=1, seg_size=None): """Capture multiple traces and save its result""" if not progressBar: progressBar = ProgressBarText() if seg_size is None: seg_size = 1000 trace_mgr = project.traceManager() if project is not None else None trace_fmt = project.getTraceFormat() if project is not None else None aux_dict = aux_list.getDict(True) if aux_list is not None else None segments = int(math.ceil(N / float(seg_size))) with progressBar: progressBar.setStatusMask("Current Segment = %d Current Trace = %d", (0,0)) progressBar.setMaximum(N) waveBuffer = None tcnt = 0 for i in range(0, segments): if progressBar.wasAborted(): break this_seg_size = min(seg_size, N - i*seg_size) if trace_fmt is not None: currentTrace = self.getNewTrace(trace_fmt) # Load trace writer information prefix = currentTrace.config.attr("prefix")[:-1] currentTrace.config.setAttr("targetHW", target.getName() if target is not None else "None") currentTrace.config.setAttr("targetSW", os.path.split(Programmer.lastFlashedFile)[1]) currentTrace.config.setAttr("scopeName", scope.getName() if scope is not None else "None") notes_str = "AckPattern: " + str(ktp) + "; " notes_str += "Aux: " if aux_dict is not None: for t in aux_dict.keys(): notes_str += "%s" % t + ", ".join([str(item) for item in aux_dict[t] if item]) currentTrace.config.setAttr("notes", notes_str) currentTrace.setTraceHint(this_seg_size) if waveBuffer is not None: currentTrace.setTraceBuffer(waveBuffer) else: currentTrace = None prefix = datetime.now().strftime('%Y.%m.%d-%H.%M.%S') if aux_dict is not None: for func in aux_dict['set_prefix']: func(prefix) ac = AcquisitionController(scope, target, currentTrace, aux_dict, ktp) ac.setMaxtraces(this_seg_size) ac.sigNewTextResponse.connect(self.updateLastKeyText) ac.sigTraceDone.connect(self.sigTraceDone.emit) __pb = lambda: progressBar.updateStatus(i*seg_size + ac.currentTrace + 1, (i, ac.currentTrace)) ac.sigTraceDone.connect(__pb) self.sigCampaignStart.emit(prefix) ac.doReadings(tracesDestination=trace_mgr, progressBar=progressBar) if currentTrace is not None: project.saveAllSettings(os.path.dirname(currentTrace.config.configFilename()) + "/%s_settings.cwset" % prefix, onlyVisibles=True) waveBuffer = currentTrace.traces # Re-use the wave buffer to avoid memory reallocation self.sigCampaignDone.emit() tcnt += seg_size if progressBar.wasAborted(): break if currentTrace is not None: currentTrace.unloadAllTraces() # Required in order to make the GC work properly :( trace_fmt.unloadAllTraces() return True def runScriptModule(self, mod, funcName="run"): """Execute the function in the Plugin classes of the specified module""" try: classes = pluginmanager.getPluginClassesFromModules([mod]) if len(classes) == 0: raise Warning("No UserScriptBase class found") return [self.runScriptClass(c, funcName) for c in classes] except Exception as e: sys.excepthook(Warning, "Could not execute Script Module %s: '%s:%s'" % (str(mod), "".join(traceback.format_exception_only(sys.exc_info()[0], e.message)).rstrip("\n "), str(e).rstrip("\n ") ), sys.exc_info()[2]) def runScriptClass(self, scriptClass, funcName="run"): """Execute the funcName function in the specified class.""" try: self.executingScripts.setValue(True) m = scriptClass(self) if funcName is not None: return eval('m.%s()' % funcName) except Exception as e: sys.excepthook(Warning, "Could not execute method %s in script class %s: '%s:%s'" % (funcName, scriptClass.__name__, "".join(traceback.format_exception_only(sys.exc_info()[0], e.message)).rstrip("\n "), str(e).rstrip("\n ") ), sys.exc_info()[2]) finally: self.executingScripts.setValue(False) def getParameter(self, path): """Return the value of a registered parameter""" return Parameter.getParameter(path) def setParameter(self, pathAndValue): """Set the parameter value, given its path. It should be registered in Parameter.registeredParameters""" Parameter.setParameter(pathAndValue) @staticmethod def getInstance(): """Implements the singleton pattern/anti-pattern. Returns a reference to the API instance.""" return CWCoreAPI.instance def getLastKey(self): return self._lastKey def getLastTextin(self): return self._lastTextin def getLastTextout(self): return self._lastTextout def getLastExpected(self): return self._lastExpected
class OpenADCInterface_FTDI(Parameterized): _name = "FTDI (SASEBO-W/SAKURA-G)" def __init__(self, oadcInstance): self.serialNumber = '' self._serialnumbers = [''] self.params = Parameter(name=self.getName(), type='group') self.params.addChildren([ {'name':'Refresh Device List', 'type':'action', 'action':self.serialRefresh}, {'name':'Device Serial Number', 'key':'snum', 'type':'list', 'values':[''], 'get':self.getSerialNumber, 'set':self.setSelectedDevice}, ]) self.ser = None if (openadc_qt is None) or (ft is None): raise ImportError("Needed imports for FTDI missing") else: self.scope = oadcInstance def getSerialNumber(self): return self.serialNumber @setupSetParam("Device Serial Number") def setSelectedDevice(self, snum): self.serialNumber = snum def con(self): if self.ser is None: try: self.dev = ft.openEx(str(self.serialNumber), ft.ftd2xx.OPEN_BY_SERIAL_NUMBER) self.dev.setBitMode(0x00, 0x40) self.dev.setTimeouts(500, 500) self.dev.setLatencyTimer(2) self.ser = self except ft.ftd2xx.DeviceError as e: self.ser = None raise IOError("Could not open %s: %s" % (self.serialNumber, e)) try: self.scope.con(self.ser) logging.info('OpenADC Found, Connecting') except IOError as e: exctype, value = sys.exc_info()[:2] raise IOError("OpenADC Error: %s" % (str(exctype) + str(value)) + " - " + e.message) def dis(self): self.ser = None if hasattr(self, 'dev'): self.dev.close() del self.dev def __del__(self): self.dis() def serialRefresh(self, _=None): serialnames = ft.listDevices() if serialnames == None: serialnames = [""] self.setSerialNumberLimits(serialnames) self.findParam('snum').setValue(serialnames[0]) def setSerialNumberLimits(self, newitems): for s in newitems: if s not in self._serialnumbers: self._serialnumbers.append(s) self.findParam('snum').setLimits(self._serialnumbers) def read(self, N=0, debug=False): return bytearray(self.dev.read(N)) def write(self, data, debug=False): return self.dev.write(data)
class ChipWhispererGlitch(Parameterized): """ Drives the Glitch Module inside the ChipWhisperer Capture Hardware Rev2, or can be used to drive this FPGA module inserted into other systems. """ CLKSOURCE0_BIT = 0b00000000 CLKSOURCE1_BIT = 0b00000001 CLKSOURCE_MASK = 0b00000011 _name= 'Glitch Module' def __init__(self, cwtype, scope, oa): # Setup FPGA partial configuration dataZ self.prCon = pr.PartialReconfigConnection() self.oa = oa self.params = Parameter(name=self.getName(), type='group').register() self.params.addChildren([ {'name':'Clock Source', 'type':'list', 'values':{'Target IO-IN':self.CLKSOURCE0_BIT, 'CLKGEN':self.CLKSOURCE1_BIT},'set':self.setGlitchClkSource, 'get':self.glitchClkSource}, {'name':'Glitch Width (as % of period)', 'key':'width', 'type':'float', 'limits':(0, 100), 'step':0.39062, 'readonly':True, 'value':10, 'action':self.updatePartialReconfig}, {'name':'Glitch Width (fine adjust)', 'key':'widthfine', 'type':'int', 'limits':(-255, 255), 'set':self.setGlitchWidthFine, 'get':self.getGlitchWidthFine}, {'name':'Glitch Offset (as % of period)', 'key':'offset', 'type':'float', 'limits':(0, 100), 'step':0.39062, 'readonly':True, 'value':10, 'action':self.updatePartialReconfig}, {'name':'Glitch Offset (fine adjust)', 'key':'offsetfine', 'type':'int', 'limits':(-255, 255), 'set':self.setGlitchOffsetFine, 'get':self.getGlitchOffsetFine}, {'name':'Glitch Trigger', 'type':'list', 'values':{'Ext Trigger:Continous':1, 'Manual':0, 'Continuous':2, 'Ext Trigger:Single-Shot':3}, 'set':self.setGlitchTrigger, 'get':self.glitchTrigger}, {'name':'Single-Shot Arm', 'type':'list', 'key':'ssarm', 'values':{'Before Scope Arm':1, 'After Scope Arm':2}, 'value':2}, {'name':'Ext Trigger Offset', 'type':'int', 'range':(0, 50000000), 'set':self.setTriggerOffset, 'get':self.triggerOffset}, {'name':'Repeat', 'type':'int', 'limits':(1,255), 'set':self.setNumGlitches, 'get':self.numGlitches}, {'name':'Manual Trigger / Single-Shot Arm', 'type':'action', 'action': self.glitchManual}, {'name':'Output Mode', 'type':'list', 'values':{'Clock XORd':0, 'Clock ORd':1, 'Glitch Only':2, 'Clock Only':3, 'Enable Only':4}, 'set':self.setGlitchType, 'get':self.glitchType}, {'name':'Read Status', 'type':'action', 'action':self.checkLocked}, {'name':'Reset DCM', 'type':'action', 'action':self.actionResetDCMs}, ]) # Check if we've got partial reconfiguration stuff for this scope try: if cwtype == "cwrev2" or cwtype == "cwcrev2": settingprefix = "cwcrev2" partialbasename = "s6lx25" self.glitchPR = pr.PartialReconfigDataMulti() elif cwtype == "cwlite": settingprefix = "cwlite" partialbasename = "cwlite" self.glitchPR = pr.PartialReconfigDataMulti() elif cwtype == "cw1200": settingprefix = "cw1200" partialbasename = "cw1200" self.glitchPR = pr.PartialReconfigDataMulti() else: raise ValueError("Invalid ChipWhisperer Mode: %s" % cwtype) if scope.cwFirmwareConfig.loader._release_mode != "debug": if scope.cwFirmwareConfig.loader._release_mode == "builtin": filelike = scope.cwFirmwareConfig.loader._bsBuiltinData zfile = zipfile.ZipFile(filelike) elif scope.cwFirmwareConfig.loader._release_mode == "zipfile": fileloc = scope.cwFirmwareConfig.loader._bsZipLoc if zipfile.is_zipfile(fileloc): zfile = zipfile.ZipFile(fileloc, "r") else: logging.warning('Partial Reconfiguration DISABLED: no zip-file for FPGA') zfile = None else: logging.warning('Partial Reconfiguration DISABLED: no PR data for FPGA') zfile = None raise ValueError("Unknown FPGA mode: %s"%scope.cwFirmwareConfig.loader._release_mode) if zfile: self.glitchPR.load(zfile.open("%s-glitchwidth.p" % partialbasename)) self.glitchPR.load(zfile.open("%s-glitchoffset.p" % partialbasename)) self.prEnabled = True else: self.prEnabled = False else: logging.warning('Partial Reconfiguration DISABLED: Debug bitstream mode') self.prEnabled = False except IOError as e: logging.error(str(e)) self.prEnabled = False except ValueError as e: logging.error(str(e)) self.prEnabled = False except OSError as e: # Also catches WindowsError logging.error(str(e)) self.prEnabled = False if self.prEnabled: # Enable glitch width, check what we've got access to self.findParam('width').setReadonly(False) lim = (self.glitchPR.limitList[0][0] / 2.55, self.glitchPR.limitList[0][1] / 2.55) self.findParam('width').setLimits(lim) self.findParam('offset').setReadonly(False) lim = (self.glitchPR.limitList[1][0] / 2.55, self.glitchPR.limitList[1][1] / 2.55) self.findParam('offset').setLimits(lim) self.setOpenADC(oa) def setOpenADC(self, oa): self.oa = None if self.prEnabled: self.prCon.con(oa) # Check this is actually working if self.prCon.isPresent() == False: self.prEnabled = False logging.warning('Partial Reconfiguration block not detected, PR disabled') return # Reset FPGA back to defaults in case previous bitstreams loaded self.updatePartialReconfig() self.oa = oa try: self.params.refreshAllParameters() except TypeError: return def updatePartialReconfig(self, _=None): """ Reads the values set via the GUI & updates the hardware settings for partial reconfiguration. Checks that PR is enabled with self.prEnabled. """ width = float(self.findParam('width').getValue()) offset = float(self.findParam('offset').getValue()) widthint = round((width / 100) * 256) offsetint = round((offset / 100) * 256) if widthint == 0: logging.warning('Partial reconfiguration for width = 0 may not work') if offsetint == 0: logging.warning('Partial reconfiguration for width = 0 may not work') bs = self.glitchPR.getPartialBitstream([widthint, offsetint]) if self.prEnabled: self.prCon.program(bs) if self.oa is not None: self.resetDCMs(keepPhase=True) # print "Partial: %d %d" % (widthint, offsetint) self.updateGlitchReadBack() def updateGlitchReadBack(self, test=False): """Updates the readback register in the FPGA with glitch information, used for LCD update on CW1200 hardware.""" width = float(self.findParam('width').getValue()) offset = float(self.findParam('offset').getValue()) #Don't write if PR disable by accident if self.oa is None: return widthint = int(round((width / 100) * 256)) offsetint = int(round((offset / 100) * 256)) cmd = bytearray(8) #Integer downloads cmd[0] = offsetint & 0xff cmd[1] = (offsetint >> 8) & 0xff cmd[2] = widthint & 0xff cmd[3] = (widthint >> 8) & 0xff #Floating point info cmd[4] = int(offset) & 0xff cmd[5] = int(("%f"%offset).split(".")[1][0:2]) & 0xff cmd[6] = int(width) & 0xff cmd[7] = int(("%f"%width).split(".")[1][0:2]) & 0xff self.oa.sendMessage(CODE_WRITE, glitchreadbackaddr, cmd, Validate=False) @setupSetParam("Ext Trigger Offset") def setTriggerOffset(self, offset): """Set offset between trigger event and glitch in clock cycles""" cmd = bytearray(4) cmd[0] = ((offset >> 0) & 0xFF) cmd[1] = ((offset >> 8) & 0xFF) cmd[2] = ((offset >> 16) & 0xFF) cmd[3] = ((offset >> 24) & 0xFF) self.oa.sendMessage(CODE_WRITE, glitchoffsetaddr, cmd) def triggerOffset(self): """Get offset between trigger event and glitch in clock cycles""" cmd = self.oa.sendMessage(CODE_READ, glitchoffsetaddr, maxResp=4) offset = cmd[0] offset |= cmd[1] << 8 offset |= cmd[2] << 16 offset |= cmd[3] << 24 return offset @setupSetParam("Glitch Offset (fine adjust)") def setGlitchOffsetFine(self, fine): """Set the fine glitch offset adjust, range -255 to 255""" current = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) if current is None or len(current) < 8: logging.warning('Glitch Module not present?') return LSB = fine & 0x00FF MSB = (fine & 0x0100) >> 8 current[0] = LSB #7..0 current[1] = (current[1] & ~0x01) | MSB #15..8 #Start adjust current[2] = current[2] | 0x04 # 23..16 #assign clockglitch_settings_read[37] = phase1_done_reg; #assign clockglitch_settings_read[38] = phase2_done_reg; self.oa.sendMessage(CODE_WRITE, glitchaddr, current, Validate=False) def getGlitchWidthFine(self): return self.getDCMStatus()[0] @setupSetParam("Glitch Width (fine adjust)") def setGlitchWidthFine(self, fine): """Set the fine glitch width adjust, range -255 to 255""" current = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) if current is None or len(current) < 8: logging.warning('Glitch Module not present?') return LSB = fine & 0x00FF MSB = (fine & 0x0100) >> 8 current[1] = (current[1] & 0x01) | ((LSB & 0x7F) << 1) current[2] = (current[2] & ~0x03) | ((LSB >> 7) | (MSB << 1)) #Start adjust current[2] = current[2] | 0x04 # 23..16 #assign clockglitch_settings_read[37] = phase1_done_reg; # assign clockglitch_settings_read[38] = phase2_done_reg; self.oa.sendMessage(CODE_WRITE, glitchaddr, current, Validate=False) def getGlitchOffsetFine(self): return self.getDCMStatus()[1] def getDCMStatus(self): current = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) phase1 = current[2] >> 3 phase1 |= (current[3] & 0x0F) << 5 phase1 = SIGNEXT(phase1, 9) phase2 = (current[3] & 0xF0) >> 4 phase2 |= (current[4] & 0x1F) << 4 phase2 = SIGNEXT(phase2, 9) dcm1Lock = False dcm2Lock = False if current[4] & 0x80: dcm1Lock = True if current[5] & 0x01: dcm2Lock = True return (phase1, phase2, dcm1Lock, dcm2Lock) def actionResetDCMs(self, _=None): """Action for parameter class""" self.resetDCMs() def resetDCMs(self, keepPhase=False): """Reset the DCMs for the Glitch width & Glitch offset. Required after doing a PR operation""" reset = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) reset[5] |= (1<<1) self.oa.sendMessage(CODE_WRITE, glitchaddr, reset, Validate=False) reset[5] &= ~(1<<1) self.oa.sendMessage(CODE_WRITE, glitchaddr, reset, Validate=False) # Reload any special phase offset if keepPhase: self.setGlitchWidthFine(self.findParam('widthfine').getValue()) self.setGlitchOffsetFine(self.findParam('offsetfine').getValue()) else: self.findParam('widthfine').setValue(0) self.findParam('offsetfine').setValue(0) def checkLocked(self, _=None): """Check if the DCMs are locked and print results """ stat = self.getDCMStatus() logging.info('DCM1: Phase %d, Locked %r' % (stat[0], stat[2])) logging.info('DCM2: Phase %d, Locked %r' % (stat[1], stat[3])) @setupSetParam("Repeat") def setNumGlitches(self, num): """Set number of glitches to occur after a trigger""" resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) if resp is None or len(resp) < 8: logging.warning('Glitch Module not present?') return if num < 1: num = 1 resp[6] = num-1 self.oa.sendMessage(CODE_WRITE, glitchaddr, resp, Validate=False) def numGlitches(self): """Get number of glitches to occur after a trigger""" resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) return resp[6]+1 @setupSetParam("Glitch Trigger") def setGlitchTrigger(self, trigger): """Set glitch trigger type (manual, continous, adc-trigger)""" resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) resp[5] = (resp[5] & ~(0x0C)) | (trigger << 2) self.oa.sendMessage(CODE_WRITE, glitchaddr, resp, Validate=False) def glitchTrigger(self): """Get glitch trigger type""" resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) return (resp[5] & 0x0C) >> 2 @setupSetParam("Output Mode") def setGlitchType(self, t): """Set glitch output type (ORd with clock, XORd with clock, clock only, glitch only)""" resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) resp[5] = (resp[5] & ~(0x70)) | (t << 4) self.oa.sendMessage(CODE_WRITE, glitchaddr, resp, Validate=False) def glitchType(self): resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) return (resp[5] & 0x70) >> 4 def glitchManual(self, _=None): """ Cause a single glitch event to occur. Depending on setting of numGlitches() this may mean multiple glitches in a row """ resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) resp[5] = resp[5] | (1 << 7) self.oa.sendMessage(CODE_WRITE, glitchaddr, resp, Validate=False) resp[5] = resp[5] & ~(1 << 7) self.oa.sendMessage(CODE_WRITE, glitchaddr, resp, Validate=False) def glitchArm(self): """If trigger is set to single-shot mode, this must be called before the selected trigger occurs""" self.glitchManual() @setupSetParam("Clock Source") def setGlitchClkSource(self, source): """Set the source of the glitched clock, either the HS1-In or the CLKGEN Module""" resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) resp[7] = (resp[7] & ~self.CLKSOURCE_MASK) | source self.oa.sendMessage(CODE_WRITE, glitchaddr, resp, Validate=False) def glitchClkSource(self): resp = self.oa.sendMessage(CODE_READ, glitchaddr, Validate=False, maxResp=8) return (resp[7] & self.CLKSOURCE_MASK) def armPreScope(self): """Called before scope trigger is armed""" if self.findParam('ssarm').getValue() == 1: self.glitchArm() def armPostScope(self): """Called after scope trigger is armed""" if self.findParam('ssarm').getValue() == 2: self.glitchArm()
class HWInformation(Parameterized): _name = 'HW Information' def __init__(self, oaiface): self.oa = oaiface self.oa.hwInfo = self self.sysFreq = 0 self.params = Parameter(name=self.getName(), type='group') self.params.addChildren([ {'name': 'Version', 'type': 'str', 'get':self.versions, 'readonly':True}, {'name': 'Synth Date', 'type': 'str', 'get':self.synthDate, 'readonly':True}, {'name': 'System Freq', 'type': 'int', 'siPrefix':True, 'suffix': 'Hz', 'get':self.sysFrequency, 'readonly':True}, {'name': 'Max Samples', 'type': 'int', 'get':self.maxSamples, 'readonly':True} ]) self.vers = None def versions(self): result = self.oa.sendMessage(CODE_READ, ADDR_VERSIONS, maxResp=6) regver = result[0] & 0xff hwtype = result[1] >> 3 hwver = result[1] & 0x07 hwList = ["Default/Unknown", "LX9 MicroBoard", "SASEBO-W", "ChipWhisperer Rev2 LX25", "Reserved?", "ZedBoard", "Papilio Pro", "SAKURA-G", "ChipWhisperer-Lite"] try: textType = hwList[hwtype] except: textType = "Invalid/Unknown" self.vers = (regver, hwtype, textType, hwver) #TODO: Temp fix for wrong HW reporting if hwtype == 1: self.sysFreq = 40E6 return self.vers def synthDate(self): return "unknown" def maxSamples(self): return self.oa.hwMaxSamples def sysFrequency(self, force=False): if (self.sysFreq > 0) & (force == False): return self.sysFreq '''Return the system clock frequency in specific firmware version''' freq = 0x00000000 temp = self.oa.sendMessage(CODE_READ, ADDR_SYSFREQ, maxResp=4) freq = freq | (temp[0] << 0) freq = freq | (temp[1] << 8) freq = freq | (temp[2] << 16) freq = freq | (temp[3] << 24) self.sysFreq = long(freq) return self.sysFreq def __del__(self): self.oa.hwInfo = None
class OpenADCInterface_Serial(Parameterized): _name = "Serial Port (LX9)" def __init__(self, oadcInstance): self.portName = '' self.ser = None self.params = Parameter(name=self.getName(), type='group') self.params.addChildren([ { 'name': 'Refresh List', 'type': 'action', 'action': self.serialRefresh }, { 'name': 'Selected Port', 'type': 'list', 'values': [''], 'get': self.getPortName, 'set': self.setPortName }, ]) self.scope = oadcInstance def getPortName(self): return self.portName @setupSetParam("Selected Port") def setPortName(self, snum): self.portName = snum def con(self): if self.ser is None: self.ser = serial.Serial() self.ser.port = self.portName self.ser.baudrate = 512000 self.ser.timeout = 2 # 2 second timeout attempts = 4 while attempts > 0: try: self.ser.open() attempts = 0 except serial.SerialException as e: attempts -= 1 self.ser = None if attempts == 0: raise IOError("Could not open %s" % self.ser.name) try: self.scope.con(self.ser) logging.info('OpenADC Found, Connecting') except IOError as e: exctype, value = sys.exc_info()[:2] raise IOError("OpenADC Error (Serial Port): %s" % (str(exctype) + str(value))) def dis(self): if self.ser is not None: self.ser.close() self.ser = None def __del__(self): if self.ser is not None: self.ser.close() def serialRefresh(self, _=None): serialnames = scan.scan() if serialnames is None or len(serialnames) == 0: serialnames = [" "] p = self.params.getChild('Selected Port') p.setLimits(serialnames) p.setValue(serialnames[0])
class ClockSettings(Parameterized): _name = 'Clock Setup' readMask = [0x1f, 0xff, 0xff, 0xfd] def __init__(self, oaiface, hwinfo=None): self.oa = oaiface self._hwinfo = hwinfo self.params = Parameter(name=self.getName(), type='group') self.params.addChildren([ {'name':'Refresh Status', 'type':'action', 'linked':[('ADC Clock', 'DCM Locked'), ('ADC Clock', 'ADC Freq'), ('CLKGEN Settings', 'DCM Locked'), 'Freq Counter'], 'help':'%namehdr%' + 'Update if the Digital Clock Manager (DCM) are "locked" and their operating frequency.'}, {'name':'Reset DCMs', 'type':'action', 'action':self.resetDcms, 'linked':[('CLKGEN Settings', 'Multiply'), ('CLKGEN Settings', 'Divide')], 'help':'%namehdr%' + 'When the input frequency to the DCM blocks changes, it can cause them to become "unlocked". When they are "unlocked" they are NOT ' + 'generating a reliable output frequency. You must press the "Reset" button to cause them to re-lock. This is currently not automatically ' + 'done as during regular operation they shouldn\'t become unlocked.\n\nHowever every time you change the DCM block source, it will cause ' + 'the blocks to lose lock.'}, {'name': 'ADC Clock', 'type':'group', 'children': [ {'name': 'Source', 'type':'list', 'values':{"EXTCLK Direct":("extclk", 4, "clkgen"), "EXTCLK x4 via DCM":("dcm", 4, "extclk"), "EXTCLK x1 via DCM":("dcm", 1, "extclk"), "CLKGEN x4 via DCM":("dcm", 4, "clkgen"), "CLKGEN x1 via DCM":("dcm", 1, "clkgen")}, 'set':self.setAdcSource, 'get':self.adcSource, 'help':'%namehdr%' + 'The ADC sample clock is generated from this source. Options are either an external input (which input set elsewhere) or an internal clock generator. Details of each option:\n\n' + '=================== ====================== =================== ===============\n' + ' Name Description Input Freq Range Fine Phase Adj.\n' + '=================== ====================== =================== ===============\n' + ' EXCLK Direct Connects sample clock 1-105 MHz NO\n' + ' external pin directly.\n' + ' EXTCLK xN via DCM Takes external pin, 5-105 MHz (x1) YES\n\n' + ' multiplies frequency 5-26.25 MHz (x4) \n\n' + ' xN and feeds to ADC. \n' + ' CLKGEN xN via DCM Multiples CLKGEN by 5-105 MHz (x1) YES\n\n' + ' xN and feeds to ADC. 5-26.25 MHz (x4) \n\n' + '=================== ====================== =================== ===============\n'}, {'name': 'Phase Adjust', 'type':'int', 'limits':(-255, 255), 'set':self.setPhase, 'get':self.phase, 'help':'%namehdr%' + 'Makes small amount of adjustment to sampling point compared to the clock source. This can be used to improve the stability ' + 'of the measurement. Total phase adjustment range is < 5nS regardless of input frequency.'}, {'name': 'ADC Freq', 'type': 'int', 'siPrefix':True, 'suffix': 'Hz', 'readonly':True, 'get':self.adcFrequency}, {'name': 'DCM Locked', 'type':'bool', 'get':self.dcmADCLocked, 'readonly':True}, {'name':'Reset ADC DCM', 'type':'action', 'action':lambda _ : self.resetDcms(True, False), 'linked':['Phase Adjust']}, ]}, {'name': 'Freq Counter', 'type': 'str', 'readonly':True, 'get':lambda: str(self.extFrequency()) + " Hz"}, {'name': 'Freq Counter Src', 'type':'list', 'values':{'EXTCLK Input':0, 'CLKGEN Output':1}, 'set':self.setFreqSrc, 'get':self.freqSrc}, {'name': 'CLKGEN Settings', 'type':'group', 'children': [ {'name':'Input Source', 'type':'list', 'values':["system", "extclk"], 'set':self.setClkgenSrc, 'get':self.clkgenSrc}, {'name':'Multiply', 'type':'int', 'limits':(2, 256), "default":2, 'set':self.setClkgenMul, 'get':self.clkgenMul, 'linked':['Current Frequency']}, {'name':'Divide', 'type':'int', 'limits':(1, 256), 'set':self.setClkgenDiv, 'get':self.clkgenDiv, 'linked':['Current Frequency']}, {'name':'Desired Frequency', 'type':'float', 'limits':(3.3E6, 200E6), 'default':0, 'step':1E6, 'siPrefix':True, 'suffix':'Hz', 'set':self.autoMulDiv, 'get':self.getClkgen, 'linked':['Multiply', 'Divide']}, {'name':'Current Frequency', 'type':'str', 'default':0, 'readonly':True, 'get':lambda: str(self.getClkgen()) + " Hz"}, {'name':'DCM Locked', 'type':'bool', 'default':False, 'get':self.clkgenLocked, 'readonly':True}, {'name':'Reset CLKGEN DCM', 'type':'action', 'action':lambda _ : self.resetDcms(False, True), 'linked':['Multiply', 'Divide']}, ]} ]) self.params.refreshAllParameters() @setupSetParam("Freq Counter Src") def setFreqSrc(self, src): result = self.oa.sendMessage(CODE_READ, ADDR_ADVCLK, maxResp=4) result[3] = result[3] & ~(0x08) result[3] |= src << 3 #print "%x"%result[3] self.oa.sendMessage(CODE_WRITE, ADDR_ADVCLK, result, readMask=self.readMask) def freqSrc(self): if self.oa is None: return 0 result = self.oa.sendMessage(CODE_READ, ADDR_ADVCLK, maxResp=4) return ((result[3] & 0x08) >> 3) def getClkgen(self): return (self._hwinfo.sysFrequency() * self.clkgenMul()) / self.clkgenDiv() @setupSetParam(['CLKGEN Settings', 'Desired Frequency']) def autoMulDiv(self, freq): inpfreq = self._hwinfo.sysFrequency() sets = self.calculateClkGenMulDiv(freq, inpfreq) self.setClkgenMul(sets[0]) self.setClkgenDiv(sets[1]) self.resetDcms(False, True) def calculateClkGenMulDiv(self, freq, inpfreq=30E6): """Calculate Multiply & Divide settings based on input frequency""" #Max setting for divide is 60 (see datasheet) #Multiply is 2-256 lowerror = 1E99 best = (0, 0) # From datasheet, if input freq is < 52MHz limit max divide if inpfreq < 52E6: maxdiv = int(inpfreq / 0.5E6) else: maxdiv = 256 for mul in range(2, 257): for div in range(1, maxdiv): err = abs(freq - ((inpfreq * mul) / div)) if err < lowerror: lowerror = err best = (mul, div) return best @setupSetParam(['CLKGEN Settings', 'Multiply']) def setClkgenMul(self, mul): if mul < 2: mul = 2 self._setClkgenMul(mul) def _setClkgenMul(self, mul): result = self.oa.sendMessage(CODE_READ, ADDR_ADVCLK, maxResp=4) mul -= 1 result[1] = mul result[3] |= 0x01 self.oa.sendMessage(CODE_WRITE, ADDR_ADVCLK, result, readMask=self.readMask) result[3] &= ~(0x01) self.oa.sendMessage(CODE_WRITE, ADDR_ADVCLK, result, readMask=self.readMask) def clkgenMul(self): timeout = 2 while timeout > 0: result = self.oa.sendMessage(CODE_READ, ADDR_ADVCLK, maxResp=4) val = result[1] if val==0: val = 1 # Fix incorrect initialization on FPGA self._setClkgenMul(2) val += 1 if (result[3] & 0x02): return val self.clkgenLoad() timeout -= 1 # raise IOError("clkgen never loaded value?") return 0 @setupSetParam(['CLKGEN Settings', 'Divide']) def setClkgenDiv(self, div): if div < 1: div = 1 result = self.oa.sendMessage(CODE_READ, ADDR_ADVCLK, maxResp=4) div -= 1 result[2] = div result[3] |= 0x01 self.oa.sendMessage(CODE_WRITE, ADDR_ADVCLK, result, readMask=self.readMask) result[3] &= ~(0x01) self.oa.sendMessage(CODE_WRITE, ADDR_ADVCLK, result, readMask=self.readMask) def clkgenLoad(self): result = self.oa.sendMessage(CODE_READ, ADDR_ADVCLK, maxResp=4) result[3] |= 0x01 self.oa.sendMessage(CODE_WRITE, ADDR_ADVCLK, result, readMask=self.readMask) result[3] &= ~(0x01) self.oa.sendMessage(CODE_WRITE, ADDR_ADVCLK, result, readMask=self.readMask) def clkgenDiv(self): if self.oa is None: return 2 timeout = 2 while timeout > 0: result = self.oa.sendMessage(CODE_READ, ADDR_ADVCLK, maxResp=4) val = result[2] val += 1 if (result[3] & 0x02): # Done loading value yet return val self.clkgenLoad() timeout -= 1 raise Warning("CLKGEN Failed to load divider value. Most likely clock input to CLKGEN is stopped, check CLKGEN" " source settings.") def adcSource(self): if self.oa is None: return ("dcm", 1, "extclk") result = self.oa.sendMessage(CODE_READ, ADDR_ADVCLK, maxResp=4) result[0] = result[0] & 0x07 if result[0] & 0x04: dcminput = "extclk" else: dcminput = "clkgen" if result[0] & 0x02: dcmout = 1 else: dcmout = 4 if result[0] & 0x01: source = "extclk" else: source = "dcm" return (source, dcmout, dcminput) @setupSetParam(['ADC Clock', 'Source']) def setAdcSource(self, source="dcm", dcmout=4, dcminput="clkgen"): #Deal with being passed tuple with all 3 arguments if isinstance(source, (list, tuple)): dcminput = source[2] dcmout = source[1] source=source[0] result = self.oa.sendMessage(CODE_READ, ADDR_ADVCLK, maxResp=4) result[0] = result[0] & ~0x07 if dcminput == "clkgen": pass elif dcminput == "extclk": result[0] = result[0] | 0x04 else: raise ValueError("dcminput must be 'clkgen' or 'extclk'") if dcmout == 4: pass elif dcmout == 1: result[0] = result[0] | 0x02 else: raise ValueError("dcmout must be 1 or 4") if source == "dcm": pass elif source == "extclk": result[0] = result[0] | 0x01 else: raise ValueError("source must be 'dcm' or 'extclk'") self.oa.sendMessage(CODE_WRITE, ADDR_ADVCLK, result) @setupSetParam(['CLKGEN Settings', 'Input Source']) def setClkgenSrc(self, source="system"): result = self.oa.sendMessage(CODE_READ, ADDR_ADVCLK, maxResp=4) result[0] = result[0] & ~0x08 if source == "system": pass elif source == "extclk": result[0] = result[0] | 0x08 else: raise ValueError("source must be 'system' or 'extclk'") self.oa.sendMessage(CODE_WRITE, ADDR_ADVCLK, result, readMask=self.readMask) def clkgenSrc(self): if self.oa is not None and self.oa.sendMessage(CODE_READ, ADDR_ADVCLK, maxResp=4)[0] & 0x08: return "extclk" else: return "system" @setupSetParam(['ADC Clock', 'Phase Adjust']) def setPhase(self, phase): '''Set the phase adjust, range -255 to 255''' LSB = phase & 0x00FF MSB = (phase & 0x0100) >> 8 cmd = bytearray(2) cmd[0] = LSB cmd[1] = MSB | 0x02 self.oa.sendMessage(CODE_WRITE, ADDR_PHASE, cmd, False) def phase(self): if self.oa is None: return 0 result = self.oa.sendMessage(CODE_READ, ADDR_PHASE, maxResp=2) if (result[1] & 0x02): LSB = result[0] MSB = result[1] & 0x01 phase = LSB | (MSB << 8) #Sign Extend phase = SIGNEXT(phase, 9) return phase else: print("No phase shift loaded") return 0 def dcmADCLocked(self): result = self.DCMStatus() return result[0] def clkgenLocked(self): result = self.DCMStatus() return result[1] def DCMStatus(self): if self.oa is None: return (False, False) result = self.oa.sendMessage(CODE_READ, ADDR_ADVCLK, maxResp=4) if (result[0] & 0x80) == 0: print("ERROR: ADVCLK register not present. Version mismatch") return (False, False) if (result[0] & 0x40) == 0: dcmADCLocked = False else: dcmADCLocked = True if (result[0] & 0x20) == 0: dcmCLKGENLocked = False else: dcmCLKGENLocked = True #if (result[3] & 0x02): # print "CLKGEN Programming Done" return (dcmADCLocked, dcmCLKGENLocked) def resetDcms(self, resetMain=True, resetClkgen=True): result = self.oa.sendMessage(CODE_READ, ADDR_ADVCLK, maxResp=4) #Set reset high on requested blocks only if resetMain: result[0] = result[0] | 0x10 #NB: High-Level system will call 'get' to re-read ADC phase if resetClkgen: result[3] = result[3] | 0x04 self.oa.sendMessage(CODE_WRITE, ADDR_ADVCLK, result, Validate=False) #Set reset low result[0] = result[0] & ~(0x10) result[3] = result[3] & ~(0x04) self.oa.sendMessage(CODE_WRITE, ADDR_ADVCLK, result, Validate=False) #Load clkgen if required if resetClkgen: self.clkgenLoad() def extFrequency(self): """Return frequency of clock measured on EXTCLOCK pin in Hz""" if self.oa is None: return 0 freq = 0x00000000 #Get sample frequency samplefreq = float(self.oa.hwInfo.sysFrequency()) / float(pow(2,23)) temp = self.oa.sendMessage(CODE_READ, ADDR_FREQ, maxResp=4) freq = freq | (temp[0] << 0) freq = freq | (temp[1] << 8) freq = freq | (temp[2] << 16) freq = freq | (temp[3] << 24) measured = freq * samplefreq return long(measured) def adcFrequency(self): """Return the external frequency measured on 'CLOCK' pin. Returned value is in Hz""" if self.oa is None: return 0 freq = 0x00000000 #Get sample frequency samplefreq = float(self.oa.hwInfo.sysFrequency()) / float(pow(2,23)) temp = self.oa.sendMessage(CODE_READ, ADDR_ADCFREQ, maxResp=4) freq = freq | (temp[0] << 0) freq = freq | (temp[1] << 8) freq = freq | (temp[2] << 16) freq = freq | (temp[3] << 24) measured = freq * samplefreq return long(measured)
class TriggerSettings(Parameterized): _name = 'Trigger Setup' def __init__(self, oaiface): self.oa = oaiface self.maxsamples = 0 self.presamples_desired = 0 self.presamples_actual = 0 self.presampleTempMargin = 24 self._timeout = 2 self.params = Parameter(name=self.getName(), type='group') self.params.addChildren([ {'name': 'Refresh Status', 'type':'action', 'linked':['Digital Pin State'], 'visible':False, 'help':'%namehdr%'+ 'Refreshes the "Digital Pin State" status.'}, {'name': 'Source', 'type': 'list', 'values':["digital", "analog"], 'set':self.setSource, 'get':self.source, 'help':'%namehdr%'+ 'Selects if trigger system is based on digital signal (including internally generated), or an ADC level. Currently ' + 'only the digital trigger system is supported.'}, {'name': 'Digital Pin State', 'type':'bool', 'readonly':True, 'get':self.extTriggerPin, 'help':'%namehdr%'+ 'Gives the status of the digital signal being used as the trigger signal, either high or low.'}, {'name': 'Mode', 'type':'list', 'values':["rising edge", "falling edge", "low", "high"], 'default':"low", 'set':self.setMode, 'get':self.mode, 'help':'%namehdr%'+ 'When using a digital system, sets the trigger mode:\n\n' ' =============== ==============================\n' + ' Mode Description\n' + ' =============== ==============================\n' + ' Rising Edge Trigger on rising edge only.\n' + ' Falling Edge Trigger on falling edge only.\n' + ' Low Trigger when line is "low".\n' + ' High Trigger when line is "high".\n' + ' =============== ==============================\n\n' + 'Note the "Trigger Mode" should be set to "Rising Edge" if using either the "SAD Trigger" or "IO Trigger" modes.' }, {'name': 'Timeout (secs)', 'type':'float', 'step':1, 'limits':(0, 1E99), 'set':self.setTimeout, 'get':self.timeout, 'help':'%namehdr%'+ 'If no trigger occurs in this many seconds, force the trigger.'}, {'name': 'Offset', 'type':'int', 'limits':(0, 4294967294), 'set':self.setOffset, 'get':self.offset, 'help':'%namehdr%'+ 'Delays this many samples after the trigger event before recording samples. Based on the ADC clock cycles. ' + 'If using a 4x mode for example, an offset of "1000" would mean we skip 250 cycles of the target device.'}, {'name': 'Pre-Trigger Samples', 'type':'int', 'limits':(0, 1000000), 'set':self.setPresamples, 'get':self.presamples, 'help':'%namehdr%'+ 'Record a certain number of samples before the main samples are captured. If "offset" is set to 0, this means ' + 'recording samples BEFORE the trigger event.'}, {'name': 'Total Samples', 'type':'int', 'limits':(0, 1000000), 'set':self.setMaxSamples, 'get':self.maxSamples, 'help':'%namehdr%'+ 'Total number of samples to record. Note the api system has an upper limit, and may have a practical lower limit (i.e.,' + ' if this value is set too low the system may not api samples. Suggest to always set > 256 samples.'}, ]) @setupSetParam("Total Samples") def setMaxSamples(self, samples): self.maxsamples = samples self.oa.setMaxSamples(samples) def maxSamples(self, cached=False): if self.oa is None: return 0 if cached: return self.maxsamples else: return self.oa.maxSamples() @setupSetParam("Timeout (secs)") def setTimeout(self, timeout): self._timeout = timeout if self.oa: self.oa.setTimeout(timeout) def timeout(self): return self._timeout @setupSetParam("Offset") def setOffset(self, offset): cmd = bytearray(4) cmd[0] = ((offset >> 0) & 0xFF) cmd[1] = ((offset >> 8) & 0xFF) cmd[2] = ((offset >> 16) & 0xFF) cmd[3] = ((offset >> 24) & 0xFF) self.oa.sendMessage(CODE_WRITE, ADDR_OFFSET, cmd) def offset(self): if self.oa is None: return 0 cmd = self.oa.sendMessage(CODE_READ, ADDR_OFFSET, maxResp=4) offset = cmd[0] offset |= cmd[1] << 8 offset |= cmd[2] << 16 offset |= cmd[3] << 24 return offset @setupSetParam("Pre-Trigger Samples") def setPresamples(self, samples): #enforce samples is multiple of 3 samplesact = int(samples / 3) #Account for shitty hardware design if samplesact > 0: samplesact = samplesact + self.presampleTempMargin cmd = bytearray(4) cmd[0] = ((samplesact >> 0) & 0xFF) cmd[1] = ((samplesact >> 8) & 0xFF) cmd[2] = ((samplesact >> 16) & 0xFF) cmd[3] = ((samplesact >> 24) & 0xFF) self.oa.sendMessage(CODE_WRITE, ADDR_PRESAMPLES, cmd) self.presamples_actual = samplesact*3 self.presamples_desired = samples #print "Requested presamples: %d, actual: %d"%(samples, self.presamples_actual) self.oa.presamples_desired = samples return self.presamples_actual def presamples(self, cached=False): """If cached returns DESIRED presamples""" if self.oa is None: return 0 if cached: return self.presamples_desired samples = 0x00000000 temp = self.oa.sendMessage(CODE_READ, ADDR_PRESAMPLES, maxResp=4) samples = samples | (temp[0] << 0) samples = samples | (temp[1] << 8) samples = samples | (temp[2] << 16) samples = samples | (temp[3] << 24) self.presamples_actual = samples*3 return samples*3 @setupSetParam("Source") def setSource(self, src): return def source(self): return "digital" @setupSetParam("Mode") def setMode(self, mode): """ Input to trigger module options: 'rising edge', 'falling edge', 'high', 'low' """ if mode == 'rising edge': trigmode = SETTINGS_TRIG_HIGH | SETTINGS_WAIT_YES elif mode == 'falling edge': trigmode = SETTINGS_TRIG_LOW | SETTINGS_WAIT_YES elif mode == 'high': trigmode = SETTINGS_TRIG_HIGH | SETTINGS_WAIT_NO elif mode == 'low': trigmode = SETTINGS_TRIG_LOW | SETTINGS_WAIT_NO else: raise ValueError, "%s invalid trigger mode"%mode cur = self.oa.settings() & ~(SETTINGS_TRIG_HIGH | SETTINGS_WAIT_YES) self.oa.setSettings(cur | trigmode) def mode(self): if self.oa is None: return 'low' sets = self.oa.settings() case = sets & (SETTINGS_TRIG_HIGH | SETTINGS_WAIT_YES) if case == SETTINGS_TRIG_HIGH | SETTINGS_WAIT_YES: mode = "rising edge" elif case == SETTINGS_TRIG_LOW | SETTINGS_WAIT_YES: mode = "falling edge" elif case == SETTINGS_TRIG_HIGH | SETTINGS_WAIT_NO: mode = "high" else: mode = "low" return mode def extTriggerPin(self): if (self.oa is not None) and (self.oa.getStatus() & STATUS_EXT_MASK): return True else: return False
class GainSettings(Parameterized): _name = 'Gain Setting' def __init__(self, oaiface): self.oa = oaiface self.gainlow_cached = False self.gain_cached = 0 self.params = Parameter(name=self.getName(), type='group') self.params.addChildren([ {'name': 'Mode', 'type': 'list', 'values': {"high", "low"}, 'default': 'low', 'set':self.setMode, 'get':self.mode, 'help': '%namehdr%'+ 'Sets the AD8331 Low Noise Amplifier into to "High" or "Low" gain mode. Low mode ranges from ' + '-4.5dB to +43.5dB, and High mode ranges from +7.5dB to +55.5dB. Better performance is found ' + 'using the "High" gain mode typically.'}, {'name': 'Setting', 'type': 'int', 'limits': (0, 78), 'default': 0, 'set':self.setGain, 'get':self.gain, 'linked':['Result'], 'help':'%namehdr%'+ 'Sets the AD8331 gain value. This is a unitless number which ranges from 0 (minimum) to 78 (maximum).' + ' The resulting gain in dB is given in the "calculated" output.'}, {'name': 'Result', 'type': 'float', 'suffix':'dB', 'readonly':True, 'get':self.gainDB, 'help':'%namehdr%'+ 'Gives the gain the AD8331 should have, based on the "High/Low" setting and the "gain setting".'}, ]) @setupSetParam("Mode") def setMode(self, gainmode): '''Set the gain Mode''' if gainmode == "high": self.oa.setSettings(self.oa.settings() | SETTINGS_GAIN_HIGH) self.gainlow_cached = False elif gainmode == "low": self.oa.setSettings(self.oa.settings() & ~SETTINGS_GAIN_HIGH) self.gainlow_cached = True else: raise ValueError, "Invalid Gain Mode, only 'low' or 'high' allowed" def mode(self): return "low" #TODO: Read it from hardware! @setupSetParam("Setting") def setGain(self, gain): '''Set the Gain range 0-78''' if (gain < 0) | (gain > 78): raise ValueError, "Invalid Gain, range 0-78 Only" self.gain_cached = gain cmd = bytearray(1) cmd[0] = gain self.oa.sendMessage(CODE_WRITE, ADDR_GAIN, cmd) def gain(self, cached=False): if cached == False: self.gain_cached = self.oa.sendMessage(CODE_READ, ADDR_GAIN)[0] return self.gain_cached def gainDB(self): #GAIN (dB) = 50 (dB/V) * VGAIN - 6.5 dB, (HILO = LO) # GAIN (dB) = 50 (dB/V) * VGAIN + 5.5 dB, (HILO = HI) gainV = (float(self.gain_cached) / 256.0) * 3.3 if self.gainlow_cached: gaindb = 50.0 * gainV - 6.5 else: gaindb = 50.0 * gainV + 5.5 return gaindb
class ProjectFormat(Parameterized): """Class describing an open ChipWhisperer project. """ untitledFileName = os.path.normpath( os.path.join(Settings().value("project-home-dir"), "tmp/default.cwp")) def __init__(self, prog_name="ChipWhisperer", prog_ver=""): self.valid_traces = pluginmanager.getPluginsInDictFromPackage( "chipwhisperer.common.traces", True, True) self._trace_format = None self.params = Parameter(name="Project Settings", type="group") self.params.addChildren([ { 'name': 'Trace Format', 'type': 'list', 'values': self.valid_traces, 'get': self.getTraceFormat, 'set': self.setTraceFormat }, ]) self.findParam("Trace Format").setValue(TraceContainerNative(), addToList=True) #self.traceParam = Parameter(name="Trace Settings", type='group', addLoadSave=True).register() #self.params.getChild('Trace Format').stealDynamicParameters(self.traceParam) self.sigFilenameChanged = util.Signal() self.sigStatusChanged = util.Signal() self.dirty = util.Observable(True) self.settingsDict = { 'Project Name': "Untitled", 'Project File Version': "1.00", 'Project Author': "Unknown" } self.datadirectory = "" self.config = ConfigObjProj(callback=self.configObjChanged) self._traceManager = TraceManager().register() self._traceManager.dirty.connect(self.__dirtyCallback) self.setFilename(ProjectFormat.untitledFileName) self.setProgramName(prog_name) self.setProgramVersion(prog_ver) if __debug__: logging.debug('Created: ' + str(self)) def getTraceFormat(self): return self._trace_format @setupSetParam("Trace Format") def setTraceFormat(self, trace_format): self._trace_format = trace_format def __dirtyCallback(self): self.dirty.setValue(self._traceManager.dirty.value() or self.dirty.value()) def configObjChanged(self, key): self.dirty.setValue(True) def isUntitled(self): return self.filename == ProjectFormat.untitledFileName def traceManager(self): return self._traceManager def setProgramName(self, name): self.settingsDict['Program Name'] = name def setProgramVersion(self, ver): self.settingsDict['Program Version'] = ver def setAuthor(self, author): self.settingsDict['Project Author'] = author def setProjectName(self, name): self.settingsDict['Project Name'] = name def setFileVersion(self, ver): self.settingsDict['Project File Version'] = ver def addWave(self, configfile): return def getFilename(self): return self.filename def setFilename(self, f): self.filename = f self.config.filename = f self.datadirectory = os.path.splitext(self.filename)[0] + "_data/" self.createDataDirectory() self.sigStatusChanged.emit() def createDataDirectory(self): # Check if data-directory exists? if not os.path.isdir(self.datadirectory): os.makedirs(self.datadirectory) # Make trace storage directory too if not os.path.isdir(os.path.join(self.datadirectory, 'traces')): os.mkdir(os.path.join(self.datadirectory, 'traces')) # Make analysis storage directory if not os.path.isdir(os.path.join(self.datadirectory, 'analysis')): os.mkdir(os.path.join(self.datadirectory, 'analysis')) # Make glitchresults storage directory if not os.path.isdir(os.path.join(self.datadirectory, 'glitchresults')): os.mkdir(os.path.join(self.datadirectory, 'glitchresults')) def load(self, f=None): if f is not None: self.setFilename(f) self.config = ConfigObjProj(infile=self.filename, callback=self.configObjChanged) self._traceManager.loadProject(self.filename) self.dirty.setValue(False) def getDataFilepath(self, filename, subdirectory='analysis'): datadir = os.path.join(self.datadirectory, subdirectory) fname = os.path.join(datadir, filename) relfname = os.path.relpath(fname, os.path.split(self.config.filename)[0]) fname = os.path.normpath(fname) relfname = os.path.normpath(relfname) return {"abs": fname, "rel": relfname} def convertDataFilepathAbs(self, relativepath): return os.path.join(os.path.split(self.filename)[0], relativepath) def checkDataConfig(self, config, requiredSettings): """Check a configuration section for various settings""" requiredSettings = util.convert_to_str(requiredSettings) config = util.convert_to_str(config) return set(requiredSettings.items()).issubset(set(config.items())) def getDataConfig(self, sectionName="Aux Data", subsectionName=None, requiredSettings=None): """ Get all configuration sections of data type given in __init__() call, and also matching the given sectionName. e.g. if dataName='Aux Data' and sectionName='Frequency', this would return a list of all sections of the type 'Aux Data NNNN - Frequency'. """ sections = [] # Get all section names for sname in self.config.keys(): # Find if starts with 'Aux Data' if sname.startswith(sectionName): # print "Found %s" % sname # Find if module name matches if applicable if subsectionName is None or sname.endswith(subsectionName): # print "Found %s" % sname if requiredSettings is None: sections.append(self.config[sname]) else: self.checkDataConfig(self.config[sname], requiredSettings) return sections def addDataConfig(self, settings=None, sectionName="Aux Data", subsectionName=None): # Check configuration file to find incrementing number maxNumber = 0 for sname in self.config.keys(): # Find if starts with 'Aux Data' if sname.startswith(sectionName): maxNumber = int(re.findall(r'\d+', sname)[0]) + 1 cfgSectionName = "%s %04d" % (sectionName, maxNumber) if subsectionName: cfgSectionName += " - %s" % subsectionName # Generate the configuration section self.config[cfgSectionName] = {} if settings is not None: for k in settings.keys(): self.config[cfgSectionName][k] = settings[k] return self.config[cfgSectionName] def saveAllSettings(self, fname=None, onlyVisibles=False): """ Save registered parameters to a file, so it can be loaded again latter.""" if fname is None: fname = os.path.join(self.datadirectory, 'settings.cwset') logging.info('Saving settings to file: ' + fname) Parameter.saveRegistered(fname, onlyVisibles) def saveTraceManager(self): #Waveform list is Universal across ALL types if 'Trace Management' not in self.config: self.config['Trace Management'] = {} self._traceManager.saveProject(self.config, self.filename) def save(self): if self.filename is None: return self.saveTraceManager() #self.config['Waveform List'] = self.config['Waveform List'] + self.waveList #Program-Specific Options pn = self.settingsDict['Program Name'] self.config[pn] = {} self.config[pn]['General Settings'] = self.settingsDict self.config.write() self.sigStatusChanged.emit() self.dirty.setValue(False) def checkDiff(self): """ Check if there is a difference - returns True if so, and False if no changes present. Also updates widget with overview of the differences if requested with updateGUI """ self.saveTraceManager() disk = util.convert_to_str(ConfigObjProj(infile=self.filename)) ram = util.convert_to_str(self.config) diff = DictDiffer(ram, disk) added = diff.added() removed = diff.removed() changed = diff.changed( ) #TODO: bug when comparing projects with template sections. It is returning changes when there is not. return added, removed, changed def hasDiffs(self): if self.dirty.value(): return True added, removed, changed = self.checkDiff() if (len(added) + len(removed) + len(changed)) == 0: return False return True def consolidate(self, keepOriginals=True): for indx, t in enumerate(self._traceManager.traceSegments): destinationDir = os.path.normpath(self.datadirectory + "traces/") config = ConfigObj(t.config.configFilename()) prefix = config['Trace Config']['prefix'] tracePath = os.path.normpath( os.path.split(t.config.configFilename())[0]) if destinationDir == tracePath: continue for traceFile in os.listdir(tracePath): if traceFile.startswith(prefix): util.copyFile( os.path.normpath(tracePath + "/" + traceFile), destinationDir, keepOriginals) util.copyFile(t.config.configFilename(), destinationDir, keepOriginals) t.config.setConfigFilename( os.path.normpath(destinationDir + "/" + os.path.split(t.config.configFilename())[1])) self.sigStatusChanged.emit() def __del__(self): if __debug__: logging.debug('Deleted: ' + str(self))
class ChipWhispererSAD(Parameterized): """ Communicates and drives with the Sum of Absolute Differences (SAD) Module inside the ChipWhisperer System. You need to configure the trigger module as active & set the trigger polarity to "high" for this to work. """ _name = 'SAD Trigger Module' STATUS_RUNNING_MASK = 1 << 3 STATUS_RESET_MASK = 1 << 0 STATUS_START_MASK = 1 << 1 def __init__(self, oa): self.oldlow = None self.oldhigh = None self.oa = oa self.sadref = [0] try: # Update SAD calculation when data changes ResultsBase.registeredObjects[ "Trace Output Plot"].dataChanged.connect(self.dataChanged) outwid = ResultsBase.registeredObjects["Trace Output Plot"] rangewidget = { 'name': 'Point Range', 'key': 'pointrng', 'type': 'rangegraph', 'limits': (0, 0), 'value': (0, 0), 'default': (0, 0), 'graphwidget': outwid, 'action': self.updateSADTraceRef, 'fixedsize': 128 } except KeyError: rangewidget = { 'name': 'Point Range', 'key': 'pointrng', 'type': 'range', 'limits': (0, 0), 'value': (0, 0), 'default': (0, 0), 'action': self.updateSADTraceRef, 'fixedsize': 128 } self.params = Parameter(name=self.getName(), type='group') self.params.addChildren([ # {'name':'Open SAD Viewer', 'type':'action'}, { 'name': 'SAD Ref From Captured', 'key': 'sad', 'type': 'group', 'children': [ rangewidget, { 'name': 'Set SAD Reference from Current Trace', 'key': 'docopyfromcapture', 'type': 'action', 'action': self.copyFromCaptureTrace }, { 'name': 'SAD Reference Trace', 'key': 'sadref', 'type': 'str', 'value': '', 'visible': False, 'action': self.setTrace }, { 'name': 'SAD Reference vs. Cursor', 'key': 'sadrefcur', 'type': 'int', 'value': 0, 'limits': (-1, 100E6), 'readonly': True }, ] }, { 'name': 'SAD Threshold', 'type': 'int', 'limits': (0, 100000), 'default': 0, 'set': self.setThreshold, 'get': self.getThreshold } ]) def dataChanged(self, data, offset): """Called when data in the trace window has changed. Used to update the limits for the point selection dialog.""" low = offset up = offset + len(data) - 1 if self.oldlow != low or self.oldup != up: self.oldlow = low self.oldup = up self.findParam(['sad', 'pointrng']).setLimits((low, up)) self.findParam(['sad', 'pointrng']).setValue( (low, min(up, low + 128))) self.updateSADTraceRef() def getCaptueTraceRef(self): """ Get the reference data for SAD algorithm from the api trace window """ try: waveformWidget = ResultsBase.registeredObjects["Trace Output Plot"] except KeyError: logging.warning( 'SAD Trigger: Trace Output Plot not running, no data source') return [0.0] * 128 pstart = self.findParam( ['sad', 'pointrng']).getValue()[0] - waveformWidget.lastStartOffset pend = self.findParam( ['sad', 'pointrng']).getValue()[1] - waveformWidget.lastStartOffset data = waveformWidget.lastTraceData[pstart:pend] data = np.array(data) data = (data + 0.5) * 1024 return data def packTrace(self, data): # Pack a reference trace into a struct # Returns a base64 string if (len(data) != 128): logging.error( "SAD trigger: expected length 128 reference; got %d" % len(data)) return None packed = base64.b64encode(struct.pack("f" * 128, *data)) return packed def unpackTrace(self, savedData): # Unpack a base64-encoded trace try: data = struct.unpack("f" * 128, base64.b64decode(savedData)) except struct.error: logging.error("SAD trigger: could not unpack saved trace") return None return np.array(data) def setTrace(self, param=None): # Update the trace with a new base64-encoded value # Unpack the trace packedData = param.getValue() data = self.unpackTrace(packedData) # Use the unpacked trace as the new waveform self.sadref = data.copy() self.setRefWaveform(data) def copyFromCaptureTrace(self, _=None): """ Send reference data to hardware from the trace window """ ds_param = Parameter.findParameter( ['OpenADC', 'Trigger Setup', 'Downsample Factor']) if ds_param is not None and ds_param.getValue() != 1: logging.warning( "OpenADC downsampling is enabled - SAD trigger will not work") data = self.getCaptueTraceRef() if len(data) != 128: logging.warning('Reference IS NOT 128 samples long, got %d' % len(data)) self.findParam(['sad', 'sadref']).setValue(self.packTrace(data)) def updateSADTraceRef(self, ignored=None): """ Update the calculated SAD value parameter """ data = self.getCaptueTraceRef() diff = data - self.sadref diff = sum(abs(diff)) self.findParam(['sad', 'sadrefcur']).setValue(diff, ignoreReadonly=True) def reset(self): """ Reset the SAD hardware block. The ADC clock must be running! """ data = self.oa.sendMessage(CODE_READ, sadcfgaddr, maxResp=4) data[0] = 0x01 self.oa.sendMessage(CODE_WRITE, sadcfgaddr, data) if self.checkStatus(): raise IOError( "SAD Reset in progress, but SAD reports still running. Is ADC Clock stopped?" ) data[0] = 0x00 self.oa.sendMessage(CODE_WRITE, sadcfgaddr, data) def start(self): """ Start the SAD algorithm, which causes the reference data to be loaded from the FIFO """ data = self.oa.sendMessage(CODE_READ, sadcfgaddr, maxResp=4) data[0] = 0x02 self.oa.sendMessage(CODE_WRITE, sadcfgaddr, data, Validate=False) data[0] = 0x00 self.oa.sendMessage(CODE_WRITE, sadcfgaddr, data, Validate=False) def checkStatus(self): """ Check if the SAD module is running & outputting valid data """ data = self.oa.sendMessage(CODE_READ, sadcfgaddr, maxResp=4) if not (data[0] & self.STATUS_RUNNING_MASK): return False else: return True def getThreshold(self): """ Get the threshold. When the SAD output falls below this threshold the system triggers """ data = self.oa.sendMessage(CODE_READ, sadcfgaddr, maxResp=4) threshold = data[1] threshold |= data[2] << 8 threshold |= data[3] << 16 return threshold @setupSetParam("SAD Threshold") def setThreshold(self, threshold): """ Set the threshold. When the SAD output falls below this threshold the system triggers """ data = self.oa.sendMessage(CODE_READ, sadcfgaddr, maxResp=4) data[1] = threshold & 0xff data[2] = (threshold >> 8) & 0xff data[3] = (threshold >> 16) & 0xff self.oa.sendMessage(CODE_WRITE, sadcfgaddr, data, Validate=False) if self.checkStatus() == False: raise IOError( "SAD Threshold set, but SAD compare not running. No valid trigger will be present. Did you load a reference waveform?" ) def setRefWaveform(self, dataRef): """ Download a reference waveform. Resets the SAD module & starts it again after loading the new data. ADC Clock must be running! """ dataRefInt = [int(i) for i in dataRef] self.reset() # print dataRefInt dataRefInt = dataRefInt[::-1] wavedata = [] for d in dataRefInt: wavedata.append((d >> 8) & 0xff) wavedata.append(d & 0xff) self.oa.sendMessage(CODE_WRITE, saddataaddr, wavedata, Validate=False) self.start()
class CWCoreAPI(Parameterized): """ ChipWisperer API Class. Provides access to the most important parts of the tool. It has a singleton method called CWCoreAPI.getInstance() that returns a reference to the API instance. """ __name__ = "ChipWhisperer" __organization__ = "NewAE Technology Inc." __version__ = "V3.1.9" _name = 'Generic Settings' instance = None def __init__(self): CWCoreAPI.instance = self self.sigNewProject = util.Signal() self.sigNewScopeData = util.Signal() self.sigConnectStatus = util.Signal() self.sigAttackChanged = util.Signal() self.sigNewInputData = util.Signal() self.sigNewTextResponse = util.Signal() self.sigTraceDone = util.Signal() self.sigCampaignStart = util.Signal() self.sigCampaignDone = util.Signal() self.sigTracesChanged = util.Signal() self.executingScripts = util.Observable(False) self.valid_scopes = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.capture.scopes", True, True) self.valid_targets = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.capture.targets", True, True) self.valid_traces = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.common.traces", True, True) self.valid_aux = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.capture.auxiliary", True, True) self.valid_acqPatterns = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.capture.acq_patterns", True, False) self.valid_attacks = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.analyzer.attacks", True, False) self.valid_preprocessingModules = pluginmanager.getPluginsInDictFromPackage("chipwhisperer.analyzer.preprocessing", False, True) self.settings = Settings() # Initialize default values self._project = self._scope = self._target = self._attack = self._traceFormat = self._acqPattern = None self._attack = self.valid_attacks.get("CPA", None) self._acqPattern = self.valid_acqPatterns["Basic"] self._auxList = [None] # TODO: implement it as a list in the whole class self._numTraces = 50 self._numTraceSets = 1 self.params = Parameter(name='Generic Settings', type='group', addLoadSave=True).register() self.params.addChildren([ {'name':'Scope Module', 'key':'scopeMod', 'type':'list', 'values':self.valid_scopes, 'get':self.getScope, 'set':self.setScope}, {'name':'Target Module', 'key':'targetMod', 'type':'list', 'values':self.valid_targets, 'get':self.getTarget, 'set':self.setTarget}, {'name':'Trace Format', 'type':'list', 'values':self.valid_traces, 'get':self.getTraceFormat, 'set':self.setTraceFormat}, {'name':'Auxiliary Module', 'type':'list', 'values':self.valid_aux, 'get':self.getAuxModule, 'set':self.setAux}, {'name':'Acquisition Settings', 'type':'group', 'children':[ {'name':'Number of Traces', 'type':'int', 'limits':(1, 1E9), 'get':self.getNumTraces, 'set':self.setNumTraces, 'linked':['Traces per Set']}, {'name':'Number of Sets', 'type':'int', 'limits':(1, 1E6), 'get':self.getNumTraceSets, 'set':self.setNumTraceSets, 'linked':['Traces per Set'], 'tip': 'Break acquisition into N sets, ' 'which may cause data to be saved more frequently. The default capture driver requires that NTraces/NSets is small enough to avoid running out of system memory ' 'as each segment is buffered into RAM before being written to disk.'}, {'name':'Traces per Set', 'type':'int', 'readonly':True, 'get':self.tracesPerSet}, {'name':'Key/Text Pattern', 'type':'list', 'values':self.valid_acqPatterns, 'get':self.getAcqPattern, 'set':self.setAcqPattern}, ]}, ]) self.scopeParam = Parameter(name="Scope Settings", type='group', addLoadSave=True).register() self.params.getChild('Scope Module').stealDynamicParameters(self.scopeParam) self.targetParam = Parameter(name="Target Settings", type='group', addLoadSave=True).register() self.params.getChild('Target Module').stealDynamicParameters(self.targetParam) self.traceParam = Parameter(name="Trace Settings", type='group', addLoadSave=True).register() self.params.getChild('Trace Format').stealDynamicParameters(self.traceParam) self.auxParam = Parameter(name="Aux Settings", type='group', addLoadSave=True).register() self.params.getChild('Auxiliary Module').stealDynamicParameters(self.auxParam) # self.attackParam = Parameter(name="Attack Settings", type='group') # self.params.getChild('Attack Module').getDynamicParameters(self.attackParam) self.newProject() def getResults(self, name): """Return the requested result widget. It should be registered.""" return ResultsBase.registeredObjects[name] def getScope(self): """Return the current scope module object.""" return self._scope @setupSetParam("Scope Module") def setScope(self, driver): """Set the current scope module object.""" if self.getScope(): self.getScope().dis() self._scope = driver if self.getScope(): self.getScope().dataUpdated.connect(self.sigNewScopeData.emit) self.getScope().connectStatus.connect(self.sigConnectStatus.emit) def getTarget(self): """Return the current target module object.""" return self._target @setupSetParam("Target Module") def setTarget(self, driver): """Set the current target module object.""" if self.getTarget(): self.getTarget().dis() self._target = driver if self.getTarget(): self.getTarget().newInputData.connect(self.sigNewInputData.emit) self.getTarget().connectStatus.connect(self.sigConnectStatus.emit) def getAuxModule(self): """Return a list with the auxiliary modules.""" return self._auxList[0] def getAuxList(self): """Return a list with the auxiliary modules.""" return self._auxList @setupSetParam("Auxiliary Module") def setAux(self, aux): """Set the first aux module. Will be updated to support more modules.""" self._auxList = [aux] def getAcqPattern(self): """Return the selected acquisition pattern.""" return self._acqPattern @setupSetParam("Key/Text Pattern") def setAcqPattern(self, pat): """Set the current acquisition pattern.""" self._acqPattern = pat if self._acqPattern is not None: self._acqPattern.getParams().remove() self.getParams().append(self._acqPattern.getParams()) def getNewTrace(self, format): """Return a new trace object for the specified format.""" if format is None: raise Warning("No trace format selected.") tmp = copy.copy(format) tmp.clear() starttime = datetime.now() prefix = starttime.strftime('%Y.%m.%d-%H.%M.%S') + "_" tmp.config.setConfigFilename(CWCoreAPI.getInstance().project().datadirectory + "traces/config_" + prefix + ".cfg") tmp.config.setAttr("prefix", prefix) tmp.config.setAttr("date", starttime.strftime('%Y-%m-%d %H:%M:%S')) return tmp def getTraceFormat(self): """Return the selected trace format.""" return self._traceFormat @setupSetParam("Trace Format") def setTraceFormat(self, format): """Set the current trace format for acquisition.""" self._traceFormat = format def getAttack(self): """Return the current attack module. NOT BEING USED AT THE MOMENT""" return self._attack def setAttack(self, attack): """Set the current attack module. NOT BEING USED AT THE MOMENT""" self._attack = attack if self.getAttack(): self.getAttack().setTraceLimits(self.project().traceManager().numTraces(), self.project().traceManager().numPoints()) self.sigAttackChanged.emit() def project(self): """Return the current opened project""" return self._project def setProject(self, proj): """Set the current opened project""" self._project = proj self.sigNewProject.emit() def newProject(self): """Create a new project""" self.setProject(ProjectFormat()) self.project().setProgramName(self.__name__) self.project().setProgramVersion(self.__version__) self.project().traceManager().sigTracesChanged.connect(self.sigTracesChanged.emit) def openProject(self, fname): """Open project file""" self.newProject() self.project().load(fname) try: ResultsBase.registeredObjects["Trace Output Plot"].setTraceSource(TraceSource.registeredObjects["Trace Management"]) except KeyError: pass def saveProject(self, fname=None): """Save the current opened project to file""" if fname is not None: self.project().setFilename(fname) self.project().save() def connectScope(self): """Connect to the selected scope""" try: if self.getScope(): self.getScope().con() try: # Sets the Plot Widget input (if it exists) to the last added TraceSource ResultsBase.registeredObjects["Trace Output Plot"].setTraceSource( TraceSource.registeredObjects[next(reversed(TraceSource.registeredObjects))]) except KeyError: pass except Warning: sys.excepthook(*sys.exc_info()) return False return True def disconnectScope(self): """Disconnect the current scope""" self.getScope().dis() def connectTarget(self): """Connect to the selected target""" try: if self.getTarget(): self.getTarget().con(scope=self.getScope()) except Warning: sys.excepthook(*sys.exc_info()) return False return True def disconnectTarget(self): """Disconnect the current target""" self.getTarget().dis() def doConDis(self): """DEPRECATED: It is here just for compatibility reasons""" print "Method doConDis() is deprecated... use connect() or disconnect() instead" return self.connect() def connect(self): """Connect both: scope and target""" return self.connectScope() and self.connectTarget() def disconnect(self): """Disconnect both: scope and target""" self.disconnectScope() self.disconnectTarget() def getNumTraces(self): """Return the total number or traces for acquisition purposes""" return self._numTraces @setupSetParam("Number of Traces") def setNumTraces(self, n): """Set the total number or traces for acquisition purposes""" self._numTraces = n def getNumTraceSets(self): """Return the number of sets/segments""" return self._numTraceSets @setupSetParam("Number of Sets") def setNumTraceSets(self, s): """Set the number of sets/segments""" self._numTraceSets = s def tracesPerSet(self): """Return the number of traces in each set/segment""" return int(self._numTraces / self._numTraceSets) def capture1(self): """Capture one trace""" try: ac = AcquisitionController(self.getScope(), self.getTarget(), writer=None, auxList=self._auxList, keyTextPattern=self.getAcqPattern()) ac.sigNewTextResponse.connect(self.sigNewTextResponse.emit) return ac.doSingleReading() except Warning: sys.excepthook(*sys.exc_info()) return False def captureM(self, progressBar = None): """Capture multiple traces and save its result""" if not progressBar: progressBar = ProgressBarText() with progressBar: progressBar.setStatusMask("Current Segment = %d Current Trace = %d", (0,0)) progressBar.setMaximum(self._numTraces) waveBuffer = None tcnt = 0 setSize = self.tracesPerSet() for i in range(0, self._numTraceSets): if progressBar.wasAborted(): break if self.getTraceFormat() is not None: currentTrace = self.getNewTrace(self.getTraceFormat()) # Load trace writer information prefix = currentTrace.config.attr("prefix")[:-1] currentTrace.config.setAttr("targetHW", self.getTarget().getName() if self.getTarget() is not None else "None") currentTrace.config.setAttr("targetSW", os.path.split(Programmer.lastFlashedFile)[1]) currentTrace.config.setAttr("scopeName", self.getScope().getName() if self.getScope() is not None else "None") currentTrace.config.setAttr("scopeSampleRate", 0) currentTrace.config.setAttr("notes", "AckPattern: " + str(self.getAcqPattern()) + "; Aux: " + ', '.join(item.getName() for item in self._auxList if item)) currentTrace.setTraceHint(setSize) if waveBuffer is not None: currentTrace.setTraceBuffer(waveBuffer) else: currentTrace = None prefix = datetime.now().strftime('%Y.%m.%d-%H.%M.%S') for aux in self._auxList: if aux: aux.setPrefix(prefix) ac = AcquisitionController(self.getScope(), self.getTarget(), currentTrace, self._auxList, self.getAcqPattern()) ac.setMaxtraces(setSize) ac.sigNewTextResponse.connect(self.sigNewTextResponse.emit) ac.sigTraceDone.connect(self.sigTraceDone.emit) __pb = lambda: progressBar.updateStatus(i*setSize + ac.currentTrace + 1, (i, ac.currentTrace)) ac.sigTraceDone.connect(__pb) self.sigCampaignStart.emit(prefix) ac.doReadings(tracesDestination=self.project().traceManager(), progressBar=progressBar) self.sigCampaignDone.emit() self.project().saveAllSettings(os.path.dirname(currentTrace.config.configFilename()) + "/%s_settings.cwset" % prefix, onlyVisibles=True) tcnt += setSize if currentTrace is not None: waveBuffer = currentTrace.traces # Re-use the wave buffer to avoid memory reallocation if progressBar.wasAborted(): break if currentTrace is not None: currentTrace.unloadAllTraces() # Required in order to make the GC work properly :( self._traceFormat.unloadAllTraces() def runScriptModule(self, mod, funcName="run"): """Execute the function in the Plugin classes of the specified module""" try: classes = pluginmanager.getPluginClassesFromModules([mod]) if len(classes) == 0: raise Warning("No UserScriptBase class found") for c in classes: self.runScriptClass(c, funcName) except Exception as e: sys.excepthook(Warning, "Could not execute Script Module %s: '%s'" % (str(mod), "".join(traceback.format_exception_only(sys.exc_info()[0], e.message)).rstrip("\n ") ), sys.exc_info()[2]) def runScriptClass(self, scriptClass, funcName="run"): """Execute the funcName function in the specified class.""" try: self.executingScripts.setValue(True) m = scriptClass(self) if funcName is not None: eval('m.%s()' % funcName) except Exception as e: sys.excepthook(Warning, "Could not execute method %s in script class %s: '%s'" % (funcName, scriptClass.__name__, "".join(traceback.format_exception_only(sys.exc_info()[0], e.message)).rstrip("\n ") ), sys.exc_info()[2]) finally: self.executingScripts.setValue(False) def getParameter(self, path): """Return the value of a registered parameter""" return Parameter.getParameter(path) def setParameter(self, pathAndValue): """Set the parameter value, given its path. It should be registered in Parameter.registeredParameters""" Parameter.setParameter(pathAndValue) @staticmethod def getInstance(): """Implements the singleton pattern/anti-pattern. Returns a reference to the API instance.""" return CWCoreAPI.instance
class AttackKeeloqParameters(Parameterized, AutoScript): _name= 'Attack Settings' def __init__(self, hasHardwareModel=True, hasMultipleRuns=True): self.hasHardwareModel = hasHardwareModel self.hasMultipleRuns = hasMultipleRuns self.maxSubKeys = 32 AutoScript.__init__(self) self.useAbs = True #TODO: Where to get this from? self.numsubkeys = 16 self.allPointsSame = True self.startPoint = [0]*self.numsubkeys self.endPoint = [0]*self.numsubkeys self.traceMax = 0 self.traceLimitsChanged = util.Signal() self.setupTraceParam() self.setupPointsParam() if self.hasHardwareModel: self.getParams().addChildren([ {'name':'Hardware Model', 'type':'group', 'children':[ {'name':'Crypto Algorithm', 'key':'hw_algo', 'type':'list', 'values':{'Keeloq':models_keeloq}, 'value':models_keeloq, 'action':self.updateScript}, {'name':'Leakage Model', 'key':'hw_leak', 'type':'list', 'values':models_keeloq.leakagemodels, 'value':"LEAK_HW_CIPHERTEXT_BIT", 'action':self.updateScript}, ]}, {'name':'Take Absolute', 'type':'bool', 'get':self.getAbsoluteMode, 'set':self.setAbsoluteMode}, # #TODO: Should be called from the AES module to figure out # of bytes # {'name':'Attacked Bytes', 'type':'group', 'children': self.getByteList()}, ]) #self.params.append(self.pointsParams) #self.params.append(self.traceParams) self.updateBytesVisible() def updateScript(self, _=None): pass def getAbsoluteMode(self): return self.useAbs @setupSetParam("Take Absolute") def setAbsoluteMode(self, mode): self.useAbs = mode def getByteList(self): init = [dict(name='Byte %d' % bnum, type='bool', key='bnumenabled%d' % bnum, value=True, bytenum=bnum, action=self.updateScriptBytesEnabled) for bnum in range(0, self.maxSubKeys)] init.insert(0,{'name':'All On', 'type':'action', 'action':self.allBytesOn}) init.insert(0,{'name':'All Off', 'type':'action', 'action':self.allBytesOff}) return init def updateScriptBytesEnabled(self, _=None): blist = [] for i,t in enumerate(self.bytesParameters()): if i < self.numsubkeys: if t.getValue() == True: blist.append(t.opts['bytenum']) self.addFunction("init", "setTargetBytes", str(blist)) def updateBytesVisible(self): for i,t in enumerate(self.bytesParameters()): if i < self.numsubkeys: t.show() else: t.hide() self.updateScriptBytesEnabled() def allBytesOn(self, _=None): for t in self.bytesParameters(): t.setValue(True) def allBytesOff(self, _=None): for t in self.bytesParameters(): t.setValue(False) def bytesParameters(self): blist = [] for i in range(0, 64): p = self.findParam(['Attacked Bytes','bnumenabled%d' % i]) if p: blist.append(p) return blist ############ Trace-Specific def setupTraceParam(self): #--- find or create group (attack can pre-create the group to influence parameter order) try: self.traceParams = self.findParam('tracesetup') except KeyError: self.traceParams = Parameter(self, name='Trace Setup', key='tracesetup', type='group') self.params.append(self.traceParams) #--- update children self.traceParams.clearChildren() self.traceParams.addChildren([ {'name':'Starting Trace', 'key':'strace', 'type':'int', 'value':0, 'action':self.updateGenericScript}, {'name':'Traces per Attack', 'key':'atraces', 'type':'int', 'limits':(1, 1E6), 'value':1, 'action':self.updateGenericScript}, {'name':'Attack Runs', 'key':'runs', 'type':'int', 'limits':(1, 1E6), 'value':1, 'action':self.updateGenericScript}, {'name':'Reporting Interval', 'key':'reportinterval', 'type':'int', 'value':10, 'action':self.updateGenericScript}, ]) # TODO: # if self.hasMultipleRuns: # else: # if not self.hasMultipleRuns: # self.traceParams.addChildren([ # {'name':'First Trace', 'key':'trace_first', 'type':'int', 'value':0, 'action':self.updateGenericScript}, # {'name':'Last Trace', 'key':'trace_last', 'type':'int', 'value':-1, 'action':self.updateGenericScript}, # ]) self.addFunction("init", "setTraceStart", "0") self.addFunction("init", "setTracesPerAttack", "1") self.addFunction("init", "setIterations", "1") self.addFunction("init", "setReportingInterval", "10") def updateGenericScript(self, _=None): runs = self.traceParams.getChild('runs') atraces = self.traceParams.getChild('atraces') strace = self.traceParams.getChild('strace') ri = self.traceParams.getChild('reportinterval') #print "runs = %d\natraces= %d\nstrace = %d\n"%(runs.value(), atraces.value(), strace.value()) if (runs.getValue() * atraces.getValue() + strace.getValue()) > (self.traceMax) or atraces.getValue()<=0: solv = (self.traceMax - strace.getValue()) / runs.getValue() solv = int(solv) atraces.setValue(1, blockAction = True) atraces.setLimits((1, solv)) atraces.setValue(solv, blockAction = True) else: atraces.setLimits((1, self.traceMax)) pointrng = (self.pointsParams.getChild('startpoint').getValue(), self.pointsParams.getChild('endpoint').getValue()) self.addFunction("init", "setTraceStart", "%d" % strace.getValue()) self.addFunction("init", "setTracesPerAttack", "%d" % atraces.getValue()) self.addFunction("init", "setIterations", "%d" % runs.getValue()) self.addFunction("init", "setReportingInterval", "%d" % ri.getValue()) self.addFunction("init", "setPointRange", "(%d,%d)" % (pointrng[0], pointrng[1])) ############# Points-Specific def setupPointsParam(self): #--- find or create group (attack can pre-create the group to influence parameter order) try: self.pointsParams = self.findParam('pointsetup') except KeyError: self.pointsParams = Parameter(self, name='Point Setup', key='pointsetup', type='group') self.params.append(self.pointsParams) #--- update children self.pointsParams.clearChildren() self.pointsParams.addChildren(self.getPointList()) def getPointList(self): # init = [{'name':'Point Range', 'key':'pointrng', 'type':'rangegraph', 'value':(0,0), 'limits':(self.startPoint[0], self.endPoint[0]), 'default':(0, 0), 'set':self.updateGenericScript, 'graphwidget':ResultsBase.registeredObjects["Trace Output Plot"]}, init = [{'name':'Starting Point', 'key':'startpoint', 'type':'int', 'value':self.startPoint[0], 'limits':(self.startPoint[0], self.endPoint[0]), 'action':self.updateGenericScript}, {'name':'Ending Point', 'key':'endpoint', 'type':'int', 'value':self.endPoint[0], 'limits':(self.startPoint[0], self.endPoint[0]), 'action':self.updateGenericScript}, ] # # #NOT ACTUALLY SUPPORTED # init.insert(0,{'name':'Points Same across Subkeys', 'type':'bool', 'value':self.allPointsSame, 'set':self.setAllPointsSame, 'readonly':True}) return init # def updatePointRange(self, bnum): # (startparam, endparam) = self.findPointParam(self.pointsParams, bnum) # # if (startparam is None) & (bnum is not None): # #We don't have per-byte difference actually, just get regular # (startparam, endparam) = self.findPointParam(self.pointsParams) # # val = (startparam.value(), endparam.value()) # return val # def copyPointsFromOutput(self, bnum=None): # if self.MainWindow is not None: # xran = self.MainWindow.results.graphoutput.xRange() # self.setPointRange(xran[0],xran[1], bnum) # def copyPointsFromTrace(self, bnum=None): # if self.MainWindow is not None: # xran = self.MainWindow.waveformDock.widget().xRange() # self.setPointRange(xran[0],xran[1], bnum) def setTraceLimits(self, traces, points): self.setGenericPointRange(0, points, setlimits=True) self.traceMax = traces self.addFunction("init", "setPointRange", "(%d,%d)" % (0, points)) strace = self.traceParams.getChild('strace') self.traceParams.getChild('runs').setValue(1) atrace = self.traceParams.getChild('atraces') strace.setLimits((0, self.traceMax-1)) atrace.setValue(1, blockAction=True) atrace.setLimits((1, traces)) atrace.setValue(traces, blockAction=True) self.traceLimitsChanged.emit(traces, points) def setGenericPointRange(self, start, end, bnum=None, setlimits=False): start = int(start) end = int(end)-1 startparam = self.pointsParams.getChild('startpoint') endparam = self.pointsParams.getChild('endpoint') if startparam: if setlimits: startparam.setLimits((start, end)) startparam.setDefault(start) self.startPointLimits = (start, end) start = enforceLimits(start, self.startPointLimits) startparam.setValue(start) if endparam: if setlimits: endparam.setLimits((start, end)) endparam.setDefault(end) self.endPointLimits = (start, end) end = enforceLimits(end, self.endPointLimits) endparam.setValue(end) if bnum is None: self.startPoint[:] = [start] * len(self.startPoint) self.endPoint[:] = [end] * len(self.endPoint) else: self.startPoint[bnum] = start self.endPoint[bnum] = end
class OpenADCInterface_Serial(Parameterized, Plugin): _name = "Serial Port (LX9)" def __init__(self, parentParam, oadcInstance): self.portName = '' self.ser = None self.params = Parameter(name=self.getName(), type='group') self.params.addChildren([ {'name':'Refresh List', 'type':'action', 'action':lambda _: self.serialRefresh()}, {'name':'Selected Port', 'type':'list', 'values':[''], 'get':self.getPortName, 'set':self.setPortName}, ]) if (openadc_qt is None) or (serial is None): raise ImportError("Needed imports for serial missing") else: self.scope = oadcInstance def getPortName(self): return self.portName @setupSetParam("Selected Port") def setPortName(self, snum): self.portName = snum def __del__(self): if self.ser != None: self.ser.close() def con(self): if self.ser == None: self.ser = serial.Serial() self.ser.port = self.portName self.ser.baudrate = 512000 self.ser.timeout = 2 # 2 second timeout attempts = 4 while attempts > 0: try: self.ser.open() attempts = 0 except serial.SerialException as e: attempts = attempts - 1 self.ser = None if attempts == 0: raise IOError("Could not open %s" % self.ser.name) try: self.scope.con(self.ser) print("OpenADC Found, Connecting") except IOError as e: exctype, value = sys.exc_info()[:2] raise IOError("OpenADC Error (Serial Port): %s" % (str(exctype) + str(value))) def dis(self): if self.ser != None: self.ser.close() self.ser = None def serialRefresh(self): serialnames = scan.scan() if serialnames == None or len(serialnames) == 0: serialnames = [" "] p = self.params.getChild('Selected Port') p.setLimits(serialnames) p.setValue(serialnames[0]) def getTextName(self): try: return self.ser.name except: return "None?"
class TraceContainer(Parameterized, Plugin): """ TraceContainer holds traces for the system to operate on. This can include both reading in traces for analysis, and writing traces to disk. This class is normally used as a base class for some specific format. For example the 'TraceFormatNative' class adds functions for reading/storing data in the 'native' ChipWhisperer format. """ _name = "Trace Configuration" def __init__(self, parentParam=None, configfile=None): self.configfile = configfile self.fmt = None self.params = Parameter(name=self._name, type='group').register() self.params.addChildren([ {'name':'Config File', 'key':'cfgfile', 'type':'str', 'readonly':True, 'value':''}, {'name':'Format', 'key':'format', 'type':'str', 'readonly':True, 'value':''}, ]) self.clear() def clear(self): self.config = _cfgfile.TraceContainerConfig(configfile=self.configfile) self.textins = [] self.textouts = [] self.keylist = [] self.knownkey = None self.dirty = False self.tracedtype = np.double self.traces = None self.tracehint = 1 self.pointhint = 0 self._numTraces = 0 self._isloaded = False def setDirty(self, dirty): self.dirty = dirty def updateConfigData(self): return def numPoints(self): try: return self.traces.shape[1] except: return 0 def setTraceBuffer(self, tracebuffer): """Reuse a trace buffer allocated elsewhere""" self.traces = tracebuffer def setTraceHint(self, traces): self.tracehint = traces def setPointHint(self, points): self.pointhint = points def numTraces(self): cfint = int(self.config.attr("numTraces")) if cfint != self._numTraces: self._numTraces = max(cfint, self._numTraces) return self._numTraces def addTrace(self, trace, textin, textout, key, dtype=np.double): self.addWave(trace, dtype) self.addTextin(textin) self.addTextout(textout) self.addKey(key) def writeDataToConfig(self): self.config.setAttr("numTraces", self._numTraces) self.config.setAttr("numPoints", self.numPoints()) def addWave(self, trace, dtype=None): try: if self.traces is None: if dtype is None: dtype = np.double self.tracedtype = dtype self.traces = np.zeros((self.tracehint, len(trace)), dtype=dtype) self.traces[self._numTraces][:] = trace else: # Check can fit this if self.traces.shape[0] <= self._numTraces: if self._numTraces >= self.tracehint: # Tracehint wrong - increase by 25 self.tracehint += 25 # Do a resize now to allocate more memory self.traces.resize((self.tracehint, self.traces.shape[1])) #Validate traces fit - if too short warn & pad (prevents aborting long captures) pad = self.traces.shape[1] - len(trace) if pad > 0: print "WARNING: Trace too short - length = %d"%len(trace) print " Padding with %d zero points"%pad print " ***** This MAY SUGGEST DATA CORRUPTION *****" trace.extend([0]*pad) self.traces[self._numTraces][:] = trace except MemoryError: raise MemoryError("Failed to allocate/resize array for %d x %d, if you have sufficient memory it may be fragmented. Use smaller segments and retry." % (self.tracehint, self.traces.shape[1])) self._numTraces += 1 self.setDirty(True) self.writeDataToConfig() def setKnownKey(self, key): self.knownkey = key def addKey(self, key): self.keylist.append(key) def addTextin(self, data): self.textins.append(data) def addTextout(self, data): self.textouts.append(data) def getTrace(self, n): data = self.traces[n] #Following line will normalize all traces relative to each #other by mean & standard deviation #data = (data - np.mean(data)) / np.std(data) return data def getTextin(self, n): return self.textins[n] def getTextout(self, n): return self.textouts[n] def getKnownKey(self, n=0): if hasattr(self, 'keylist'): if self.keylist is not None: return self.keylist[n] return self.knownkey def getAuxDataConfig(self, newmodule): """ Get auxilary data section in config file, searches based on both 'modname' and 'uniquedict'. Checks file itself & NOT the local database, since the auxilary data is not loaded into database. """ # Get all section names for sname in self.config.config.keys(): # Find if starts with 'Aux Data' if sname.startswith("Aux Data"): # print "Found %s" % sname # Find if module name matches if sname.endswith(newmodule["sectionName"]): # print "Found %s" % sname # Finally confirm unique dictionary values for k in newmodule["values"].keys(): try: if newmodule["values"][k]["definesunique"]: try: if str(self.config.config[sname][k]) != str(newmodule["values"][k]["value"]): return None except KeyError: return None except KeyError: pass return self.config.config[sname] return None def addAuxDataConfig(self, newmodule): """Add a new module to the config file, place in aux data""" # newmodule is a dict of data # sectionName will be prepended with "Aux Data N - ", where NNNN # is a sequential number. The original section name will be called # .originalSectionName, which can be used when searching the system. # the NNNN will be also written as an integer to the .auxNumber member newdict = copy.deepcopy(newmodule) #Check dictionary maxNumber = 0 for ad in self.config.attrList: if hasattr(ad, "auxNumber"): maxNumber = max(maxNumber, ad.auxNumber + 1) #Check configuration file for sname in self.config.config.keys(): # Find if starts with 'Aux Data' if sname.startswith("Aux Data"): maxNumber = max(int(re.findall(r'\d+', sname)[0]) + 1, maxNumber) newdict["auxNumber"] = maxNumber newdict["originalSectionName"] = newdict["sectionName"] newdict["sectionName"] = "Aux Data %04d - " % maxNumber + newdict["sectionName"] self.config.attrList.append(newdict) self.config.syncFile(sectionname=newdict["sectionName"]) return newdict def prepareDisk(self): """Placeholder called after creating a new file setup, but before actually writing traces to it""" #if self.traces is None: # self.traces = np.zeros((self.tracehint, self.pointhint)) def loadAllConfig(self): """Placeholder for loading configuration data ONLY. May not be required.""" pass def loadAllTraces(self, directory=None, prefix=""): """Placeholder for load command. May not actually read everything into memory depending on format.""" self._isloaded = True raise AttributeError("%s doesn't have this method implemented"%self.__class__.__name__) def unloadAllTraces(self): """Placeholder for unload command. If loadAllTraces unloaded into memory, this may drop from memory. May not be implemented.""" self._isloaded = False def saveAuxData(self, extraname, data): """Placeholder for command to save auxiliary data into some location which follows traces (e.g. same database/folder with same prefix.)""" raise AttributeError("%s doesn't have this method implemented" % self.__class__.__name__) def saveAllTraces(self, directory, prefix=""): """Placeholder for save command.""" raise AttributeError("%s doesn't have this method implemented"%self.__class__.__name__) def copyTo(self, srcTraces=None, srcFormat=None): """Placeholder for copy/import command. Different from load as copies data INTO this classes format, possibly from another format""" raise AttributeError("%s doesn't have this method implemented"%self.__class__.__name__) def closeAll(self, clearTrace=True, clearText=True, clearKeys=True): """Writer is done, can close/save any files.""" #raise AttributeError("%s doesn't have this method implemented"%self.__class__.__name__) pass def validateSettings(self): """Check settings, log any messages to special setup window""" return [] def isLoaded(self): """Returns true if you can use getTrace, getTextin, etc methods""" return self._isloaded