Exemplo n.º 1
0
class CW305(TargetTemplate):
    """CW305 target object.

    This class contains the public API for the CW305 hardware.
    To connect to the CW305, the easiest method is::

        import chipwhisperer as cw
        scope = cw.scope()

        # scope can also be None here, unlike with the default SimpleSerial
        target = cw.target(scope,
                targets.CW305, bsfile=<valid FPGA bitstream file>)

    As of CW5.3, you can also specify the following::

        # can also be '35t'
        target = cw.target(scope, fpga_id='100t')

    To automatically program with the example AES bitstream

    If you're using the reference designs, typical configuration
    requires you to set the FPGA VCC-INT voltage and enable and 
    set the clock via the PLL. You'll probably also want to
    disable the USB clock during encryption to reduce noise::

        target.vccint_set(1.0) #set VCC-INT to 1V
        
        target.pll.pll_enable_set(True) #Enable PLL chip
        target.pll.pll_outenable_set(False, 0) # Disable unused PLL0
        target.pll.pll_outenable_set(True, 1)  # Enable PLL 
        target.pll.pll_outenable_set(False, 2) # Disable unused PLL2

        # optional, but reduces power trace noise
        target.clkusbautooff = True
        target.clksleeptime = 1 # 1ms typically good for sleep


    Don't forget to clock the ChipWhisperer ADC off the FPGA instead
    of the internal clock::

        scope.clock.adc_src = "extclk_x4"
        scope.clock.reset_adc() # make sure the DCM is locked

    Note that connecting to the CW305 includes programming the CW305 FPGA,
    if it isn't already.

    For more help about CW305 settings, try help() on this CW305 submodule:

       * target.pll
    """

    _name = "ChipWhisperer CW305 (Artix-7)"
    BATCHRUN_START = 0x1
    BATCHRUN_RANDOM_KEY = 0x2
    BATCHRUN_RANDOM_PT = 0x4

    def __init__(self):
        TargetTemplate.__init__(self)
        self._naeusb = NAEUSB()
        self.pll = PLLCDCE906(self._naeusb, ref_freq=12.0E6)
        self.fpga = FPGA(self._naeusb)

        self.hw = None
        self.oa = None

        self._woffset = 0x400
        self._woffset_sam3U = 0x000

        self._clksleeptime = 1
        self._clkusbautooff = True
        self.last_key = bytearray([0] * 16)

    def _getNAEUSB(self):
        return self._naeusb

    def fpga_write(self, addr, data):
        """Write to an address on the FPGA

        Args:
            addr (int): Address to write to
            data (list): Data to write to addr

        Raises:
            IOError: User attempted to write to a read-only location
        """
        if addr < self._woffset:
            raise IOError("Write to read-only location: 0x%04x" % addr)

        return self._naeusb.cmdWriteMem(addr, data)

    def fpga_read(self, addr, readlen):
        """Read from an address on the FPGA

        Args:
            addr (int): Address to read from
            readlen (int): Length of data to read

        Returns:
            Requested data as a list
        """
        if addr > self._woffset:
            logging.info(
                'Read from write address, confirm this is not an error')

        data = self._naeusb.cmdReadMem(addr, readlen)
        return data

    def usb_clk_setenabled(self, status):
        """ Turn on or off the Data Clock to the FPGA """
        if status:
            self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG, CW305_USB.SYSCFG_CLKON)
        else:
            self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG,
                                  CW305_USB.SYSCFG_CLKOFF)

    def usb_trigger_toggle(self, _=None):
        """ Toggle the trigger line high then low """
        self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG, CW305_USB.SYSCFG_TOGGLE)

    def vccint_set(self, vccint=1.0):
        """ Set the VCC-INT for the FPGA """

        # print "vccint = " + str(vccint)

        if (vccint < 0.6) or (vccint > 1.15):
            raise ValueError("VCC-Int out of range 0.6V-1.1V")

        # Convert to mV
        vccint = int(vccint * 1000)
        vccsetting = [vccint & 0xff, (vccint >> 8) & 0xff, 0]

        # calculate checksum
        vccsetting[2] = vccsetting[0] ^ vccsetting[1] ^ CW305_USB.VCCINT_XORKEY

        self._naeusb.sendCtrl(CW305_USB.REQ_VCCINT, 0, vccsetting)

        resp = self._naeusb.readCtrl(CW305_USB.REQ_VCCINT, dlen=3)
        if resp[0] != 2:
            raise IOError("VCC-INT Write Error, response = %d" % resp[0])

    def vccint_get(self):
        """ Get the last set value for VCC-INT """

        resp = self._naeusb.readCtrl(CW305_USB.REQ_VCCINT, dlen=3)
        return float(resp[1] | (resp[2] << 8)) / 1000.0

    def _con(self, scope=None, bsfile=None, force=False, fpga_id=None):
        """Connect to CW305 board, and download bitstream.

        If the target has already been programmed it skips reprogramming
        unless forced.

        Args:
            scope (ScopeTemplate): An instance of a scope object.
            bsfile (path): The path to the bitstream file to program the FPGA with.
            force (bool): Whether or not to force reprogramming.
            fpga_id (string): '100t', '35t', or None. If bsfile is None and fpga_id specified,
                              program with AES firmware for fpga_id
        """

        from datetime import datetime
        self._naeusb.con(idProduct=[0xC305])
        if not fpga_id is None:
            if fpga_id not in ('100t', '35t'):
                raise ValueError(f"Invalid fpga {fpga_id}")
        self._fpga_id = fpga_id
        if self.fpga.isFPGAProgrammed() == False or force:
            if bsfile is None:
                if not fpga_id is None:
                    from chipwhisperer.hardware.firmware.cw305 import getsome
                    bsdata = getsome(f"AES_{fpga_id}.bit")
                    starttime = datetime.now()
                    status = self.fpga.FPGAProgram(bsdata,
                                                   exceptOnDoneFailure=False)
                    stoptime = datetime.now()
                    if status:
                        logging.info('FPGA Config OK, time: %s' %
                                     str(stoptime - starttime))
                    else:
                        logging.warning(
                            'FPGA Done pin failed to go high, check bitstream is for target device.'
                        )
                else:
                    print("No FPGA Bitstream file specified.")
            elif not os.path.isfile(bsfile):
                print(("FPGA Bitstream not configured or '%s' not a file." %
                       str(bsfile)))
            else:
                starttime = datetime.now()
                status = self.fpga.FPGAProgram(open(bsfile, "rb"),
                                               exceptOnDoneFailure=False)
                stoptime = datetime.now()
                if status:
                    logging.info('FPGA Config OK, time: %s' %
                                 str(stoptime - starttime))
                else:
                    logging.warning(
                        'FPGA Done pin failed to go high, check bitstream is for target device.'
                    )
        self.usb_clk_setenabled(True)
        self.fpga_write(0x100 + self._woffset, [0])
        self.pll.cdce906init()

    def _dis(self):
        if self._naeusb:
            self._naeusb.close()

    def checkEncryptionKey(self, key):
        """Validate encryption key"""
        return key

    def loadEncryptionKey(self, key):
        """Write encryption key to FPGA"""
        self.key = key
        key = key[::-1]
        self.fpga_write(0x100 + self._woffset, key)

    def loadInput(self, inputtext):
        """Write input to FPGA"""
        self.input = inputtext
        text = inputtext[::-1]
        self.fpga_write(0x200 + self._woffset, text)

    def is_done(self):
        """Check if FPGA is done"""
        result = self.fpga_read(0x50, 1)[0]

        if result == 0x00:
            return False
        else:
            # Clear trigger
            self.fpga_write(0x40 + self._woffset, [0])
            # LED Off
            self.fpga_write(0x10 + self._woffset, [0])
            return True

    isDone = camel_case_deprecated(is_done)

    def readOutput(self):
        """"Read output from FPGA"""
        data = self.fpga_read(0x200, 16)
        data = data[::-1]
        #self.newInputData.emit(util.list2hexstr(data))
        return data

    @property
    def latest_fw(self):
        cw_type = self._getCWType()
        if cw_type == "cwlite":
            from chipwhisperer.hardware.firmware.cwlite import fwver
        elif cw_type == "cw1200":
            from chipwhisperer.hardware.firmware.cw1200 import fwver

        ret = OrderedDict()
        return {"major": fwver[0], "minor": fwver[1]}

    @property
    def fw_version(self):
        a = self._naeusb.readFwVersion()
        return {"major": a[0], "minor": a[1], "debug": a[2]}

    @property
    def clkusbautooff(self):
        """ If set, the USB clock is automatically disabled on capture.

        The USB clock is re-enabled after self.clksleeptime milliseconds.

        Reads/Writes to the FPGA will not be possible until after
        the USB clock is reenabled, meaning :code:`usb_trigger_toggle()`
        must be used to trigger the FPGA to perform an encryption.

        :Getter: Gets whether to turn off the USB clock on capture

        :Setter: Sets whether to turn off the USB clock on capture
        """
        return self._clkusbautooff

    @clkusbautooff.setter
    def clkusbautooff(self, state):
        self._clkusbautooff = state

    @property
    def clksleeptime(self):
        """ Time (in milliseconds) that the USB clock is disabled for upon
        capture, if self.clkusbautooff is set.
        """
        return self._clksleeptime

    @clksleeptime.setter
    def clksleeptime(self, value):
        self._clksleeptime = value

    def go(self):
        """Disable USB clock (if requested), perform encryption, re-enable clock"""
        if self.clkusbautooff:
            self.usb_clk_setenabled(False)

        #LED On
        self.fpga_write(0x10 + self._woffset, [0x01])

        time.sleep(0.001)
        self.usb_trigger_toggle()
        # self.FPGAWrite(0x100, [1])
        # self.FPGAWrite(0x100, [0])

        if self.clkusbautooff:
            time.sleep(self.clksleeptime / 1000.0)
            self.usb_clk_setenabled(True)

    def simpleserial_read(self, cmd, pay_len, end='\n', timeout=250, ack=True):
        """Read data from target

        Mimics simpleserial protocol of serial based targets

        Args:
            cmd (str): Command to ues. Only accepts 'r' for now.
            pay_len: Unused
            end: Unused
            timeout: Unused
            ack: Unused

        Returns: Value from Crypto output register

        .. versionadded:: 5.1
            Added simpleserial_read to CW305
        """
        if cmd == "r":
            return self.readOutput()
        else:
            raise ValueError("Unknown command {}".format(cmd))

    def simpleserial_write(self, cmd, data, end=None):
        """Write data to target.

        Mimics simpleserial protocol of serial based targets.

        Args:
            cmd (str): Command to use. Target supports 'p' (write plaintext),
                and 'k' (write key).
            data (bytearray): Data to write to target
            end: Unused

        Raises:
            ValueError: Unknown command

        .. versionadded:: 5.1
            Added simpleserial_write to CW305
        """
        if cmd == 'p':
            self.loadInput(data)
            self.go()
        elif cmd == 'k':
            self.loadEncryptionKey(data)
        else:
            raise ValueError("Unknown command {}".format(cmd))

    def set_key(self, key, ack=False, timeout=250):
        """Checks if key is different from the last one sent. If so, send it.

        Args:
            key (bytearray):  key to send
            ack: Unused
            timeout: Unused

        .. versionadded:: 5.1
            Added set_key to CW305
        """
        if self.last_key != key:
            self.last_key = key
            self.simpleserial_write('k', key)

    def batchRun(self,
                 batchsize=1024,
                 random_key=True,
                 random_pt=True,
                 seed=None):
        """
            Run multiple encryptions on random data

            Args:
                batchsize (int): The number of encryption to run (default 1024).
                random_key (bool): True if the key is random (default False).
                random_pt (bool): True if the plaintext are random (default True).
                seed (int): random int32 for the PRG.
        """
        if seed is None:
            seed = random.randint(0, 2**32)

        data = []
        data.extend(
            packuint32(1 | (random_key << 1) | (random_pt << 2)
                       | (batchsize << 16)))
        data.extend(packuint32(seed))
        self.sam3u_write(0, data)

        # generate the inputs
        if random_key:
            key = [[0 for x in range(16)] for y in range(batchsize)]
        else:
            key = None

        if random_pt:
            pt = [[0 for x in range(16)] for y in range(batchsize)]
        else:
            pt = None

        for b in range(batchsize):
            if random_key:
                for j in range(16):
                    key[b][15 - j] = seed >> 24
                    seed += ((seed * seed) & 0xffffffff) | 0x5
                    seed &= 0xffffffff
            if random_pt:
                for j in range(16):
                    pt[b][15 - j] = seed >> 24
                    seed += ((seed * seed) & 0xffffffff) | 0x5
                    seed &= 0xffffffff
        return key, pt

    def sam3u_write(self, addr, data):
        """Write to an address on the FPGA

        Args:
            addr (int): Address to write to
            data (list): Data to write to addr

        Raises:
            IOError: User attempted to write to a read-only location
        """
        if addr < self._woffset_sam3U:
            raise IOError("Write to read-only location: 0x%04x" % addr)
        if len(data) > (256 + addr):
            raise IOError("Write will overflow at location: 0x%04x" % (256))

        return self._naeusb.cmdWriteSam3U(addr, data)

    @fw_ver_required(0, 30)
    def spi_mode(self, enable=True, timeout=200, bsfile=None):
        """Enter programming mode for the onboard SPI chip
        
        Reprograms the FPGA with the appropriate bitstream and 
        returns an object with which to program the CW305 SPI chip
        (see documentation on the returned object for more info)

        Args:
            enable (bool): Enable the SPI interface before returning it. Defaults to True
            timeout (int): USB timeout in ms. Defaults to 200.
            bsfile (string): If not None, program with a bitstream pointed to by bsfile.
                             If None, program with SPI passthrough bitstream for the chip
                             specified during connection (or cw.target()) 

        Returns:
            A FPGASPI object which can be used to erase/program/verify/read the SPI
            chip on the CW305.
        """
        from datetime import datetime
        if self._fpga_id is None and bsfile is None:
            logging.warning(
                "CW305 requires passthrough bitstream to program SPI chip, but file/chip not specified"
            )
        else:
            bsdata = None
            if self._fpga_id:
                from chipwhisperer.hardware.firmware.cw305 import getsome
                bsdata = getsome(f"SPI_flash_{self._fpga_id}.bit")
            else:
                bsdata = open(bsfile, "rb")
            starttime = datetime.now()
            status = self.fpga.FPGAProgram(bsdata, exceptOnDoneFailure=False)
            stoptime = datetime.now()
            if status:
                logging.info('FPGA Config OK, time: %s' %
                             str(stoptime - starttime))
            else:
                logging.warning(
                    'FPGA Done pin failed to go high, check bitstream is for target device.'
                )

        spi = FPGASPI(self._naeusb, timeout)
        spi.enable_interface(enable)
        return spi
Exemplo n.º 2
0
class CW305(TargetTemplate):
    """CW305 target object.

    This class contains the public API for the CW305 hardware.
    To connect to the CW305, the easiest method is::

        import chipwhisperer as cw
        scope = cw.scope()

        # scope can also be None here, unlike with the default SimpleSerial
        target = cw.target(scope,
                targets.CW305, bsfile=<valid FPGA bitstream file>)

    As of CW5.3, you can also specify the following::

        # can also be '35t'
        target = cw.target(scope, fpga_id='100t')

    To automatically program with the example AES bitstream

    If you're using the reference designs, typical configuration
    requires you to set the FPGA VCC-INT voltage and enable and 
    set the clock via the PLL. You'll probably also want to
    disable the USB clock during encryption to reduce noise::

        target.vccint_set(1.0) #set VCC-INT to 1V
        
        target.pll.pll_enable_set(True) #Enable PLL chip
        target.pll.pll_outenable_set(False, 0) # Disable unused PLL0
        target.pll.pll_outenable_set(True, 1)  # Enable PLL 
        target.pll.pll_outenable_set(False, 2) # Disable unused PLL2

        # optional, but reduces power trace noise
        target.clkusbautooff = True
        target.clksleeptime = 1 # 1ms typically good for sleep


    Don't forget to clock the ChipWhisperer ADC off the FPGA instead
    of the internal clock::

        scope.clock.adc_src = "extclk_x4"
        scope.clock.reset_adc() # make sure the DCM is locked

    Note that connecting to the CW305 includes programming the CW305 FPGA,
    if it isn't already.

    For more help about CW305 settings, try help() on this CW305 submodule:

       * target.pll
    """

    _name = "ChipWhisperer CW305 (Artix-7)"
    BATCHRUN_START = 0x1
    BATCHRUN_RANDOM_KEY = 0x2
    BATCHRUN_RANDOM_PT = 0x4

    def __init__(self):
        TargetTemplate.__init__(self)
        self._naeusb = NAEUSB()
        self.pll = PLLCDCE906(self._naeusb, ref_freq=12.0E6)
        self.fpga = FPGA(self._naeusb)

        self.hw = None
        self.oa = None

        self._woffset_sam3U = 0x000
        self.default_verilog_defines = 'cw305_defines.v'
        self.default_verilog_defines_full_path = '../../hardware/victims/cw305_artixtarget/fpga/common/' + self.default_verilog_defines
        self.registers = 12  # number of registers we expect to find
        self.bytecount_size = 7  # pBYTECNT_SIZE in Verilog

        self._clksleeptime = 1
        self._clkusbautooff = True
        self.last_key = bytearray([0] * 16)
        self.target_name = 'AES'

    def _getNAEUSB(self):
        return self._naeusb

    def slurp_defines(self, defines_files=None):
        """ Parse Verilog defines file so we can access register and bit
        definitions by name and avoid 'magic numbers'.
        Args:
            defines_files (list): list of Verilog define files to parse
        """
        self.verilog_define_matches = 0
        if type(defines_files) != list:
            logging.error(
                'defines_files must be provided as a list (even it it contains a single element)'
            )
        for i, defines_file in enumerate(defines_files):
            if type(defines_file) == io.BytesIO:
                defines = io.TextIOWrapper(defines_file)
            else:
                if not os.path.isfile(defines_file):
                    logging.error(
                        'Cannot find %s. Please specify the location of %s on your filesystem.'
                        % (defines_files, self.default_verilog_defines))
                defines = open(defines_file, 'r')
            define_regex_base = re.compile(r'`define')
            define_regex_reg = re.compile(r'`define\s+?REG_')
            define_regex_radix = re.compile(
                r'`define\s+?(\w+).+?\'([bdh])([0-9a-fA-F]+)')
            define_regex_noradix = re.compile(r'`define\s+?(\w+?)\s+?(\d+?)')
            block_offset = 0
            for define in defines:
                if define_regex_base.search(define):
                    reg = define_regex_reg.search(define)
                    match = define_regex_radix.search(define)
                    if match:
                        self.verilog_define_matches += 1
                        if match.group(2) == 'b':
                            radix = 2
                        elif match.group(2) == 'h':
                            radix = 16
                        else:
                            radix = 10
                        setattr(self, match.group(1),
                                int(match.group(3), radix) + block_offset)
                    else:
                        match = define_regex_noradix.search(define)
                        if match:
                            self.verilog_define_matches += 1
                            setattr(self, match.group(1),
                                    int(match.group(2), 10) + block_offset)
                        else:
                            logging.warning("Couldn't parse line: %s", define)
            defines.close()
        # make sure everything is cool:
        if self.verilog_define_matches != self.registers:
            logging.warning(
                "Trouble parsing Verilog defines files (%s): didn't find the right number of defines; expected %d, got %d.\n"
                % (defines_file, self.registers, self.verilog_define_matches) +
                "Ensure that the Verilog defines files above are the same that were used to build the bitfile."
            )

    def get_fpga_buildtime(self):
        """Returns date and time when FPGA bitfile was generated.
        """
        raw = self.fpga_read(self.REG_BUILDTIME, 4)
        # definitions: Xilinx XAPP1232
        day = raw[3] >> 3
        month = ((raw[3] & 0x7) << 1) + (raw[2] >> 7)
        year = ((raw[2] >> 1) & 0x3f) + 2000
        hour = ((raw[2] & 0x1) << 4) + (raw[1] >> 4)
        minute = ((raw[1] & 0xf) << 2) + (raw[0] >> 6)
        return "FPGA build time: {}/{}/{}, {}:{}".format(
            month, day, year, hour, minute)

    def fpga_write(self, addr, data):
        """Write to an address on the FPGA

        Args:
            addr (int): Address to write to
            data (list): Data to write to addr

        """
        addr = addr << self.bytecount_size
        return self._naeusb.cmdWriteMem(addr, data)

    def fpga_read(self, addr, readlen):
        """Read from an address on the FPGA

        Args:
            addr (int): Address to read from
            readlen (int): Length of data to read

        Returns:
            Requested data as a list
        """
        addr = addr << self.bytecount_size
        data = self._naeusb.cmdReadMem(addr, readlen)
        return data

    def usb_clk_setenabled(self, status):
        """ Turn on or off the Data Clock to the FPGA """
        if status:
            self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG, CW305_USB.SYSCFG_CLKON)
        else:
            self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG,
                                  CW305_USB.SYSCFG_CLKOFF)

    def usb_trigger_toggle(self, _=None):
        """ Toggle the trigger line high then low """
        self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG, CW305_USB.SYSCFG_TOGGLE)

    def vccint_set(self, vccint=1.0):
        """ Set the VCC-INT for the FPGA """

        # print "vccint = " + str(vccint)

        if (vccint < 0.6) or (vccint > 1.15):
            raise ValueError("VCC-Int out of range 0.6V-1.1V")

        # Convert to mV
        vccint = int(vccint * 1000)
        vccsetting = [vccint & 0xff, (vccint >> 8) & 0xff, 0]

        # calculate checksum
        vccsetting[2] = vccsetting[0] ^ vccsetting[1] ^ CW305_USB.VCCINT_XORKEY

        self._naeusb.sendCtrl(CW305_USB.REQ_VCCINT, 0, vccsetting)

        resp = self._naeusb.readCtrl(CW305_USB.REQ_VCCINT, dlen=3)
        if resp[0] != 2:
            raise IOError("VCC-INT Write Error, response = %d" % resp[0])

    def vccint_get(self):
        """ Get the last set value for VCC-INT """

        resp = self._naeusb.readCtrl(CW305_USB.REQ_VCCINT, dlen=3)
        return float(resp[1] | (resp[2] << 8)) / 1000.0

    def _con(self,
             scope=None,
             bsfile=None,
             force=False,
             fpga_id=None,
             defines_files=None,
             slurp=True):
        """Connect to CW305 board, and download bitstream.

        If the target has already been programmed it skips reprogramming
        unless forced.

        Args:
            scope (ScopeTemplate): An instance of a scope object.
            bsfile (path): The path to the bitstream file to program the FPGA with.
            force (bool): Whether or not to force reprogramming.
            fpga_id (string): '100t', '35t', or None. If bsfile is None and fpga_id specified,
                              program with AES firmware for fpga_id
            defines_files (list, optional): path to cw305_defines.v
            slurp (bool, optional): Whether or not to slurp the Verilog defines.
        """

        from datetime import datetime
        self._naeusb.con(idProduct=[0xC305])
        if not fpga_id is None:
            if fpga_id not in ('100t', '35t'):
                raise ValueError(f"Invalid fpga {fpga_id}")
        self._fpga_id = fpga_id
        if self.fpga.isFPGAProgrammed() == False or force:
            if bsfile is None:
                if not fpga_id is None:
                    from chipwhisperer.hardware.firmware.cw305 import getsome
                    if self.target_name == 'AES':
                        bsdata = getsome(f"AES_{fpga_id}.bit")
                    elif self.target_name == 'Cryptech ecdsa256-v1 pmul':
                        bsdata = getsome(f"ECDSA256v1_pmul_{fpga_id}.bit")
                    starttime = datetime.now()
                    status = self.fpga.FPGAProgram(bsdata,
                                                   exceptOnDoneFailure=False)
                    stoptime = datetime.now()
                    if status:
                        logging.info('FPGA Config OK, time: %s' %
                                     str(stoptime - starttime))
                    else:
                        logging.warning(
                            'FPGA Done pin failed to go high, check bitstream is for target device.'
                        )
                else:
                    print("No FPGA Bitstream file specified.")
            elif not os.path.isfile(bsfile):
                print(("FPGA Bitstream not configured or '%s' not a file." %
                       str(bsfile)))
            else:
                starttime = datetime.now()
                status = self.fpga.FPGAProgram(open(bsfile, "rb"),
                                               exceptOnDoneFailure=False)
                stoptime = datetime.now()
                if status:
                    logging.info('FPGA Config OK, time: %s' %
                                 str(stoptime - starttime))
                else:
                    logging.warning(
                        'FPGA Done pin failed to go high, check bitstream is for target device.'
                    )

        self.usb_clk_setenabled(True)
        self.pll.cdce906init()

        if defines_files is None:
            if fpga_id is None:
                verilog_defines = [self.default_verilog_defines_full_path]
            else:
                from chipwhisperer.hardware.firmware.cw305 import getsome
                verilog_defines = [getsome(self.default_verilog_defines)]
        else:
            verilog_defines = defines_files
        if slurp:
            self.slurp_defines(verilog_defines)

    def _dis(self):
        if self._naeusb:
            self._naeusb.close()

    def checkEncryptionKey(self, key):
        """Validate encryption key"""
        return key

    def loadEncryptionKey(self, key):
        """Write encryption key to FPGA"""
        self.key = key
        key = key[::-1]
        self.fpga_write(self.REG_CRYPT_KEY, key)

    def loadInput(self, inputtext):
        """Write input to FPGA"""
        self.input = inputtext
        text = inputtext[::-1]
        self.fpga_write(self.REG_CRYPT_TEXTIN, text)

    def is_done(self):
        """Check if FPGA is done"""
        result = self.fpga_read(self.REG_CRYPT_GO, 1)[0]
        if result == 0x01:
            return False
        else:
            self.fpga_write(self.REG_USER_LED, [0])
            return True

    isDone = camel_case_deprecated(is_done)

    def readOutput(self):
        """"Read output from FPGA"""
        data = self.fpga_read(self.REG_CRYPT_CIPHEROUT, 16)
        data = data[::-1]
        #self.newInputData.emit(util.list2hexstr(data))
        return data

    @property
    def latest_fw(self):
        cw_type = self._getCWType()
        if cw_type == "cwlite":
            from chipwhisperer.hardware.firmware.cwlite import fwver
        elif cw_type == "cw1200":
            from chipwhisperer.hardware.firmware.cw1200 import fwver

        ret = OrderedDict()
        return {"major": fwver[0], "minor": fwver[1]}

    @property
    def fw_version(self):
        a = self._naeusb.readFwVersion()
        return {"major": a[0], "minor": a[1], "debug": a[2]}

    @property
    def clkusbautooff(self):
        """ If set, the USB clock is automatically disabled on capture.

        The USB clock is re-enabled after self.clksleeptime milliseconds.

        Reads/Writes to the FPGA will not be possible until after
        the USB clock is reenabled, meaning :code:`usb_trigger_toggle()`
        must be used to trigger the FPGA to perform an encryption.

        :Getter: Gets whether to turn off the USB clock on capture

        :Setter: Sets whether to turn off the USB clock on capture
        """
        return self._clkusbautooff

    @clkusbautooff.setter
    def clkusbautooff(self, state):
        self._clkusbautooff = state

    @property
    def clksleeptime(self):
        """ Time (in milliseconds) that the USB clock is disabled for upon
        capture, if self.clkusbautooff is set.
        """
        return self._clksleeptime

    @clksleeptime.setter
    def clksleeptime(self, value):
        self._clksleeptime = value

    def go(self):
        """Disable USB clock (if requested), perform encryption, re-enable clock"""
        if self.clkusbautooff:
            self.usb_clk_setenabled(False)

        self.fpga_write(self.REG_USER_LED, [0x01])

        time.sleep(0.001)
        self.usb_trigger_toggle()
        # it's also possible to 'go' via register write but that won't take if
        # the USB clock was turned off:
        #self.fpga_write(self.REG_CRYPT_GO, [1])

        if self.clkusbautooff:
            time.sleep(self.clksleeptime / 1000.0)
            self.usb_clk_setenabled(True)

    def simpleserial_read(self, cmd, pay_len, end='\n', timeout=250, ack=True):
        """Read data from target

        Mimics simpleserial protocol of serial based targets

        Args:
            cmd (str): Command to ues. Only accepts 'r' for now.
            pay_len: Unused
            end: Unused
            timeout: Unused
            ack: Unused

        Returns: Value from Crypto output register

        .. versionadded:: 5.1
            Added simpleserial_read to CW305
        """
        if cmd == "r":
            return self.readOutput()
        else:
            raise ValueError("Unknown command {}".format(cmd))

    def simpleserial_write(self, cmd, data, end=None):
        """Write data to target.

        Mimics simpleserial protocol of serial based targets.

        Args:
            cmd (str): Command to use. Target supports 'p' (write plaintext),
                and 'k' (write key).
            data (bytearray): Data to write to target
            end: Unused

        Raises:
            ValueError: Unknown command

        .. versionadded:: 5.1
            Added simpleserial_write to CW305
        """
        if cmd == 'p':
            self.loadInput(data)
            self.go()
        elif cmd == 'k':
            self.loadEncryptionKey(data)
        else:
            raise ValueError("Unknown command {}".format(cmd))

    def set_key(self, key, ack=False, timeout=250):
        """Checks if key is different from the last one sent. If so, send it.

        Args:
            key (bytearray):  key to send
            ack: Unused
            timeout: Unused

        .. versionadded:: 5.1
            Added set_key to CW305
        """
        if self.last_key != key:
            self.last_key = key
            self.simpleserial_write('k', key)

    def batchRun(self,
                 batchsize=1024,
                 random_key=True,
                 random_pt=True,
                 seed=None):
        """
            Run multiple encryptions on random data

            Args:
                batchsize (int): The number of encryption to run (default 1024).
                random_key (bool): True if the key is random (default False).
                random_pt (bool): True if the plaintext are random (default True).
                seed (int): random int32 for the PRG.
        """
        if seed is None:
            seed = random.randint(0, 2**32)

        data = []
        data.extend(
            packuint32(1 | (random_key << 1) | (random_pt << 2)
                       | (batchsize << 16)))
        data.extend(packuint32(seed))
        self.sam3u_write(0, data)

        # generate the inputs
        if random_key:
            key = [[0 for x in range(16)] for y in range(batchsize)]
        else:
            key = None

        if random_pt:
            pt = [[0 for x in range(16)] for y in range(batchsize)]
        else:
            pt = None

        for b in range(batchsize):
            if random_key:
                for j in range(16):
                    key[b][15 - j] = seed >> 24
                    seed += ((seed * seed) & 0xffffffff) | 0x5
                    seed &= 0xffffffff
            if random_pt:
                for j in range(16):
                    pt[b][15 - j] = seed >> 24
                    seed += ((seed * seed) & 0xffffffff) | 0x5
                    seed &= 0xffffffff
        return key, pt

    def sam3u_write(self, addr, data):
        """Write to an address on the FPGA

        Args:
            addr (int): Address to write to
            data (list): Data to write to addr

        Raises:
            IOError: User attempted to write to a read-only location
        """
        if addr < self._woffset_sam3U:
            raise IOError("Write to read-only location: 0x%04x" % addr)
        if len(data) > (256 + addr):
            raise IOError("Write will overflow at location: 0x%04x" % (256))

        return self._naeusb.cmdWriteSam3U(addr, data)

    @fw_ver_required(0, 30)
    def spi_mode(self, enable=True, timeout=200, bsfile=None):
        """Enter programming mode for the onboard SPI chip
        
        Reprograms the FPGA with the appropriate bitstream and 
        returns an object with which to program the CW305 SPI chip
        (see documentation on the returned object for more info)

        Args:
            enable (bool): Enable the SPI interface before returning it. Defaults to True
            timeout (int): USB timeout in ms. Defaults to 200.
            bsfile (string): If not None, program with a bitstream pointed to by bsfile.
                             If None, program with SPI passthrough bitstream for the chip
                             specified during connection (or cw.target()) 

        Returns:
            A FPGASPI object which can be used to erase/program/verify/read the SPI
            chip on the CW305.
        """
        from datetime import datetime
        if self._fpga_id is None and bsfile is None:
            logging.warning(
                "CW305 requires passthrough bitstream to program SPI chip, but file/chip not specified"
            )
        else:
            bsdata = None
            if self._fpga_id:
                from chipwhisperer.hardware.firmware.cw305 import getsome
                bsdata = getsome(f"SPI_flash_{self._fpga_id}.bit")
            else:
                bsdata = open(bsfile, "rb")
            starttime = datetime.now()
            status = self.fpga.FPGAProgram(bsdata, exceptOnDoneFailure=False)
            stoptime = datetime.now()
            if status:
                logging.info('FPGA Config OK, time: %s' %
                             str(stoptime - starttime))
            else:
                logging.warning(
                    'FPGA Done pin failed to go high, check bitstream is for target device.'
                )

        spi = FPGASPI(self._naeusb, timeout)
        spi.enable_interface(enable)
        return spi

    @fw_ver_required(0, 40)
    def gpio_mode(self, timeout=200):
        """Allow arbitrary GPIO access on SAM3U
        
        Allows low-level IO access to SAM3U GPIO, and also SPI transfers.
        (see documentation on the returned object for more info)

        Args:
            timeout (int): USB timeout in ms. Defaults to 200.

        Returns:
            A FPGAIO object which can be used to access IO on the CW305.
        """
        io = FPGAIO(self._naeusb, timeout)
        return io