Beispiel #1
0
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)
Beispiel #2
0
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)
Beispiel #3
0
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
Beispiel #4
0
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()
Beispiel #6
0
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()
Beispiel #7
0
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
Beispiel #8
0
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
Beispiel #9
0
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)
Beispiel #10
0
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
Beispiel #12
0
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
Beispiel #16
0
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()
Beispiel #18
0
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
Beispiel #19
0
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?"
Beispiel #21
0
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