Exemple #1
0
    def __init__(self, firmware_file=None, vendor_id=VID_QIHW, product_id=PID_GLASGOW):
        self.usb_context = usb1.USBContext()
        self.usb_poller = _PollerThread(self.usb_context)
        self.usb_poller.start()

        self._open_device(vendor_id, product_id)

        device_id = self.usb.getDevice().getbcdDevice()
        if device_id & 0xFF00 in (0x0000, 0xA000):
            revision = chr(ord("A") + (device_id & 0xFF) - 1)
            logger.debug("found rev%s device without firmware", revision)

            if firmware_file is None:
                raise GlasgowDeviceError("firmware is not uploaded")
            else:
                logger.debug("loading firmware from %s", firmware_file)
                with open(firmware_file, "rb") as f:
                    self._download_firmware(input_data(f, fmt="ihex"))

                # let the device re-enumerate and re-acquire it
                time.sleep(1)
                self._open_device(VID_QIHW, PID_GLASGOW)

                # still not the right firmware?
                if self.usb.getDevice().getbcdDevice() & 0xFF00 in (0x0000, 0xA000):
                    raise GlasgowDeviceError("firmware upload failed")

        # https://github.com/vpelletier/python-libusb1/issues/39
        # serial = self.usb.getDevice().getSerialNumber()
        serial = self.usb.getASCIIStringDescriptor(
            self.usb.getDevice().device_descriptor.iSerialNumber)
        logger.debug("found device with serial %s", serial)
Exemple #2
0
    def __init__(self, firmware_file=None, vendor_id=VID_QIHW, product_id=PID_GLASGOW):
        super().__init__(vendor_id, product_id)

        device_id = self.usb.getDevice().getbcdDevice()
        if device_id & 0xFF00 in (0x0000, 0xA000):
            revision = chr(ord("A") + (device_id & 0xFF) - 1)
            logger.debug("found rev%s device without firmware", revision)

            if firmware_file is None:
                raise GlasgowDeviceError("Firmware is not uploaded")
            else:
                logger.debug("loading firmware from %s", firmware_file)
                with open(firmware_file, "rb") as f:
                    self.load_ram(input_data(f, fmt="ihex"))

                # let the device re-enumerate and re-acquire it
                time.sleep(1)
                super().__init__(VID_QIHW, PID_GLASGOW)

                # still not the right firmware?
                if self.usb.getDevice().getbcdDevice() & 0xFF00 in (0x0000, 0xA000):
                    raise GlasgowDeviceError("Firmware upload failed")

        logger.debug("found device with serial %s",
                     self.usb.getDevice().getSerialNumber())

        self.claimed_ports = []
Exemple #3
0
    def __init__(self, firmware_file=None):
        super().__init__(VID_QIHW, PID_GLASGOW)
        if self._device.getDevice().getbcdDevice() == 0:
            if firmware_file is None:
                raise GlasgowDeviceError("Firmware is not uploaded")
            else:
                # TODO: log?
                with open(firmware_file, "rb") as f:
                    self.load_ram(input_data(f, fmt="ihex"))

                # let the device re-enumerate and re-acquire it
                time.sleep(1)
                super().__init__(VID_QIHW, PID_GLASGOW)

                # still not the right firmware?
                if self._device.getDevice().getbcdDevice() == 0:
                    raise GlasgowDeviceError("Firmware upload failed")
Exemple #4
0
    async def interact(self, device, args, avr_iface):
        await avr_iface.programming_enable()

        signature = await avr_iface.read_signature()
        device = devices_by_signature[signature]
        self.logger.info("device signature: %s (%s)",
            "{:02x} {:02x} {:02x}".format(*signature),
            "unknown" if device is None else device.name)

        if args.operation is not None and device is None:
            raise GlasgowAppletError("cannot operate on unknown device")

        if args.operation == "read":
            if args.fuses:
                fuses = await avr_iface.read_fuse_range(range(device.fuses_size))
                if device.fuses_size > 2:
                    self.logger.info("fuses: low %s high %s extra %s",
                                     "{:08b}".format(fuses[0]),
                                     "{:08b}".format(fuses[1]),
                                     "{:08b}".format(fuses[2]))
                elif device.fuses_size > 1:
                    self.logger.info("fuses: low %s high %s",
                                     "{:08b}".format(fuses[0]),
                                     "{:08b}".format(fuses[1]))
                else:
                    self.logger.info("fuse: %s", "{:08b}".format(fuses[0]))

            if args.lock_bits:
                lock_bits = await avr_iface.read_lock_bits()
                self.logger.info("lock bits: %s", "{:08b}".format(lock_bits))

            if args.calibration:
                calibration = \
                    await avr_iface.read_calibration_range(range(device.calibration_size))
                self.logger.info("calibration bytes: %s",
                                 " ".join(["%02x" % b for b in calibration]))

            if args.program:
                self._check_format(args.program, "program memory")
                self.logger.info("reading program memory (%d bytes)", device.program_size)
                output_data(args.program,
                    await avr_iface.read_program_memory_range(range(device.program_size)))

            if args.eeprom:
                self._check_format(args.eeprom, "EEPROM")
                self.logger.info("reading EEPROM (%d bytes)", device.eeprom_size)
                output_data(args.eeprom,
                    await avr_iface.read_eeprom_range(range(device.eeprom_size)))

        if args.operation == "write-fuses":
            if args.high and device.fuses_size < 2:
                raise GlasgowAppletError("device does not have high fuse")

            if args.low:
                self.logger.info("writing low fuse")
                await avr_iface.write_fuse(0, args.low)
                written = await avr_iface.read_fuse(0)
                if written != args.low:
                    raise GlasgowAppletError("verification of low fuse failed: %s" %
                                             "{:08b} != {:08b}".format(written, args.low))

            if args.high:
                self.logger.info("writing high fuse")
                await avr_iface.write_fuse(1, args.high)
                written = await avr_iface.read_fuse(1)
                if written != args.high:
                    raise GlasgowAppletError("verification of high fuse failed: %s" %
                                             "{:08b} != {:08b}".format(written, args.high))

        if args.operation == "write-lock":
            self.logger.info("writing lock bits")
            await avr_iface.write_lock_bits(args.bits)
            written = await avr_iface.read_lock_bits()
            if written != args.bits:
                raise GlasgowAppletError("verification of lock bits failed: %s" %
                                         "{:08b} != {:08b}".format(written, args.bits))

        if args.operation == "write-program":
            self.logger.info("erasing chip")
            await avr_iface.chip_erase()

            self._check_format(args.file, "program memory")
            data = input_data(args.file)
            self.logger.info("writing program memory (%d bytes)",
                             sum([len(chunk) for address, chunk in data]))
            for address, chunk in data:
                chunk = bytes(chunk)
                await avr_iface.write_program_memory_range(address, chunk, device.program_page)
                written = await avr_iface.read_program_memory_range(range(address, len(chunk)))
                if written != chunk:
                    raise GlasgowAppletError("verification failed at address %#06x: %s != %s" %
                                             (address, written.hex(), chunk.hex()))

        if args.operation == "write-eeprom":
            self._check_format(args.file, "EEPROM")
            data = input_data(args.file)
            self.logger.info("writing EEPROM (%d bytes)",
                             sum([len(chunk) for address, chunk in data]))
            for address, chunk in data:
                chunk = bytes(chunk)
                await avr_iface.write_eeprom_range(address, chunk, device.eeprom_page)
                written = await avr_iface.read_eeprom_range(range(address, len(chunk)))
                if written != chunk:
                    raise GlasgowAppletError("verification failed at address %#06x: %s != %s" %
                                             (address, written.hex(), chunk.hex()))

        await avr_iface.programming_disable()
Exemple #5
0
async def _main():
    args = get_argparser().parse_args()
    create_logger(args)

    try:
        firmware_file = os.path.join(os.path.dirname(__file__), "glasgow.ihex")
        if args.action in ("build", "test", "tool"):
            pass
        elif args.action == "factory":
            device = GlasgowHardwareDevice(firmware_file, VID_CYPRESS, PID_FX2)
        else:
            device = GlasgowHardwareDevice(firmware_file)

        if args.action == "voltage":
            if args.voltage is not None:
                await device.reset_alert(args.ports)
                await device.poll_alert()  # clear any remaining alerts
                try:
                    await device.set_voltage(args.ports, args.voltage)
                except:
                    await device.set_voltage(args.ports, 0.0)
                    raise
                if args.set_alert and args.voltage != 0.0:
                    await asyncio.sleep(
                        0.050)  # let the output capacitor discharge a bit
                    await device.set_alert_tolerance(args.ports, args.voltage,
                                                     args.tolerance / 100)

            print("Port\tVio\tVlimit\tVsense\tMonitor")
            alerts = await device.poll_alert()
            for port in args.ports:
                vio = await device.get_voltage(port)
                vlimit = await device.get_voltage_limit(port)
                vsense = await device.measure_voltage(port)
                alert = await device.get_alert(port)
                notice = ""
                if port in alerts:
                    notice += " (ALERT)"
                print("{}\t{:.2}\t{:.2}\t{:.3}\t{:.2}-{:.2}\t{}".format(
                    port, vio, vlimit, vsense, alert[0], alert[1], notice))

        if args.action == "voltage-limit":
            if args.voltage is not None:
                await device.set_voltage_limit(args.ports, args.voltage)

            print("Port\tVio\tVlimit")
            for port in args.ports:
                vio = await device.get_voltage(port)
                vlimit = await device.get_voltage_limit(port)
                print("{}\t{:.2}\t{:.2}".format(port, vio, vlimit))

        if args.action == "run":
            if args.applet:
                target, applet = _applet(device.revision, args)
                device.demultiplexer = DirectDemultiplexer(device)

                await device.download_target(
                    target,
                    rebuild=args.rebuild,
                    toolchain_opts=_toolchain_opts(args))

                if args.trace:
                    logger.info("starting applet analyzer")
                    await device.write_register(target.analyzer.addr_done, 0)
                    analyzer_iface = await device.demultiplexer.claim_interface(
                        target.analyzer,
                        target.analyzer.mux_interface,
                        args=None)
                    trace_decoder = TraceDecoder(target.analyzer.event_sources)
                    vcd_writer = VCDWriter(
                        args.trace,
                        timescale="1 ns",
                        check_values=False,
                        comment='Generated by Glasgow for bitstream ID %s' %
                        target.get_bitstream_id().hex())

                async def run_analyzer():
                    if not args.trace:
                        return

                    signals = {}
                    strobes = set()
                    for field_name, field_trigger, field_width in trace_decoder.events(
                    ):
                        if field_trigger == "throttle":
                            var_type = "wire"
                            var_init = 0
                        elif field_trigger == "change":
                            var_type = "wire"
                            var_init = "x"
                        elif field_trigger == "strobe":
                            if field_width > 0:
                                var_type = "tri"
                                var_init = "z"
                            else:
                                var_type = "event"
                                var_init = ""
                        else:
                            assert False
                        signals[field_name] = vcd_writer.register_var(
                            scope="",
                            name=field_name,
                            var_type=var_type,
                            size=field_width,
                            init=var_init)
                        if field_trigger == "strobe":
                            strobes.add(field_name)

                    init = True
                    while not trace_decoder.is_done():
                        trace_decoder.process(await analyzer_iface.read())
                        for cycle, events in trace_decoder.flush():
                            if events == "overrun":
                                target.analyzer.logger.error(
                                    "FIFO overrun, shutting down")

                                for name in signals:
                                    vcd_writer.change(signals[name],
                                                      next_timestamp, "x")
                                timestamp += 1e3  # 1us
                                break

                            event_repr = " ".join("{}={}".format(n, v)
                                                  for n, v in events.items())
                            target.analyzer.logger.trace(
                                "cycle %d: %s", cycle, event_repr)

                            timestamp = 1e9 * (cycle +
                                               0) // target.sys_clk_freq
                            next_timestamp = 1e9 * (cycle +
                                                    1) // target.sys_clk_freq
                            if init:
                                init = False
                                vcd_writer._timestamp = timestamp
                            for name, value in events.items():
                                vcd_writer.change(signals[name], timestamp,
                                                  value)
                            for name, _value in events.items():
                                if name in strobes:
                                    vcd_writer.change(signals[name],
                                                      next_timestamp, "z")
                            vcd_writer.flush()

                    vcd_writer.close(timestamp)

                async def run_applet():
                    logger.info("running handler for applet %r", args.applet)
                    try:
                        iface = await applet.run(device, args)
                        await applet.interact(device, args, iface)
                    except GlasgowAppletError as e:
                        applet.logger.error(str(e))
                    finally:
                        if args.trace:
                            await device.write_register(
                                target.analyzer.addr_done, 1)

                done, pending = await asyncio.wait(
                    [run_analyzer(), run_applet()],
                    return_when=asyncio.FIRST_EXCEPTION)
                for task in done:
                    await task

                # Work around bugs in python-libusb1 that cause segfaults on interpreter shutdown.
                await device.demultiplexer.flush()

            else:
                with args.bitstream as f:
                    logger.info("downloading bitstream from %r", f.name)
                    await device.download_bitstream(f.read())

        if args.action == "tool":
            tool = GlasgowApplet.all_applets[args.applet].tool_cls()
            await tool.run(args)

        if args.action == "flash":
            logger.info("reading device configuration")
            header = await device.read_eeprom("fx2", 0,
                                              8 + 4 + GlasgowConfig.size)
            header[0] = 0xC2  # see below

            fx2_config = FX2Config.decode(header, partial=True)
            if (len(fx2_config.firmware) != 1
                    or fx2_config.firmware[0][0] != 0x4000 - GlasgowConfig.size
                    or len(fx2_config.firmware[0][1]) != GlasgowConfig.size):
                raise SystemExit(
                    "Unrecognized or corrupted configuration block")
            glasgow_config = GlasgowConfig.decode(fx2_config.firmware[0][1])

            logger.info("device has serial %s-%s", glasgow_config.revision,
                        glasgow_config.serial)
            if fx2_config.disconnect:
                logger.info("device has flashed firmware")
            else:
                logger.info("device does not have flashed firmware")
            if glasgow_config.bitstream_size:
                logger.info("device has flashed bitstream ID %s",
                            glasgow_config.bitstream_id.hex())
            else:
                logger.info("device does not have flashed bitstream")

            new_bitstream = b""
            if args.remove_bitstream:
                logger.info("removing bitstream")
                glasgow_config.bitstream_size = 0
                glasgow_config.bitstream_id = b"\x00" * 16
            elif args.bitstream:
                logger.info("using bitstream from %s", args.bitstream.name)
                with args.bitstream as f:
                    new_bitstream = f.read()
                    glasgow_config.bitstream_size = len(new_bitstream)
                    glasgow_config.bitstream_id = b"\xff" * 16
            elif args.applet:
                logger.info("building bitstream for applet %s", args.applet)
                target, applet = _applet(device.revision, args)
                new_bitstream_id = target.get_bitstream_id()
                new_bitstream = target.get_bitstream(**_toolchain_opts(args))

                # We always build and reflash the bitstream in case the one currently
                # in EEPROM is corrupted. If we only compared the ID, there would be
                # no easy way to recover from that case. There's also no point in
                # storing the bitstream hash (as opposed to Verilog hash) in the ID,
                # as building the bitstream takes much longer than flashing it.
                logger.info("built bitstream ID %s", new_bitstream_id.hex())
                glasgow_config.bitstream_size = len(new_bitstream)
                glasgow_config.bitstream_id = new_bitstream_id

            fx2_config.firmware[0] = (0x4000 - GlasgowConfig.size,
                                      glasgow_config.encode())

            if args.remove_firmware:
                logger.info("removing firmware")
                fx2_config.disconnect = False
                new_image = fx2_config.encode()
                new_image[0] = 0xC0  # see below
            else:
                logger.info(
                    "using firmware from %r",
                    args.firmware.name if args.firmware else firmware_file)
                with (args.firmware or open(firmware_file, "rb")) as f:
                    for (addr, chunk) in input_data(f, fmt="ihex"):
                        fx2_config.append(addr, chunk)
                fx2_config.disconnect = True
                new_image = fx2_config.encode()

            if new_bitstream:
                logger.info("programming bitstream")
                old_bitstream = await device.read_eeprom(
                    "ice", 0, len(new_bitstream))
                if old_bitstream != new_bitstream:
                    for (addr, chunk) in diff_data(old_bitstream,
                                                   new_bitstream):
                        await device.write_eeprom("ice", addr, chunk)

                    logger.info("verifying bitstream")
                    if await device.read_eeprom(
                            "ice", 0, len(new_bitstream)) != new_bitstream:
                        logger.critical("bitstream programming failed")
                        return 1
                else:
                    logger.info("bitstream identical")

            logger.info("programming configuration and firmware")
            old_image = await device.read_eeprom("fx2", 0, len(new_image))
            if old_image != new_image:
                for (addr, chunk) in diff_data(old_image, new_image):
                    await device.write_eeprom("fx2", addr, chunk)

                logger.info("verifying configuration and firmware")
                if await device.read_eeprom("fx2", 0,
                                            len(new_image)) != new_image:
                    logger.critical(
                        "configuration/firmware programming failed")
                    return 1
            else:
                logger.info("configuration and firmware identical")

        if args.action == "build":
            target, applet = _applet(args.rev, args)
            if args.type in ("v", "verilog"):
                logger.info("building Verilog for applet %r", args.applet)
                target.get_verilog().write(args.filename or args.applet + ".v")
            if args.type in ("bin", "bitstream"):
                logger.info("building bitstream for applet %r", args.applet)
                with open(args.filename or args.applet + ".bin", "wb") as f:
                    f.write(target.get_bitstream(**_toolchain_opts(args)))
            if args.type in ("zip", "archive"):
                logger.info("building archive for applet %r", args.applet)
                with target.get_build_tree() as tree:
                    if args.filename:
                        basename, = os.path.splitext(args.filename)
                    else:
                        basename = args.applet
                    shutil.make_archive(basename,
                                        format="zip",
                                        root_dir=tree,
                                        logger=logger)

        if args.action == "test":
            logger.info("testing applet %r", args.applet)
            applet = GlasgowApplet.all_applets[args.applet]()
            loader = unittest.TestLoader()
            stream = unittest.runner._WritelnDecorator(sys.stderr)
            result = unittest.TextTestResult(stream=stream,
                                             descriptions=True,
                                             verbosity=2)
            result.failfast = True

            def startTest(test):
                unittest.TextTestResult.startTest(result, test)
                result.stream.write("\n")

            result.startTest = startTest
            if args.tests == []:
                suite = loader.loadTestsFromTestCase(applet.test_cls)
                suite.run(result)
            else:
                for test in args.tests:
                    suite = loader.loadTestsFromName(test,
                                                     module=applet.test_cls)
                    suite.run(result)
            if not result.wasSuccessful():
                for _, traceback in result.errors + result.failures:
                    print(traceback, end="", file=sys.stderr)
                return 1

        if args.action == "factory":
            logger.info("reading device configuration")
            header = await device.read_eeprom("fx2", 0,
                                              8 + 4 + GlasgowConfig.size)
            if not re.match(rb"^\xff+$", header):
                if args.force:
                    logger.warning(
                        "device already factory-programmed, proceeding anyway")
                else:
                    logger.error("device already factory-programmed")
                    return 1

            fx2_config = FX2Config(vendor_id=VID_QIHW,
                                   product_id=PID_GLASGOW,
                                   device_id=1 + ord(args.rev) - ord('A'),
                                   i2c_400khz=True)
            glasgow_config = GlasgowConfig(args.rev, args.serial)
            fx2_config.append(0x4000 - GlasgowConfig.size,
                              glasgow_config.encode())

            image = fx2_config.encode()
            # Let FX2 hardware enumerate. This won't load the configuration block
            # into memory automatically, but the firmware has code that does that
            # if it detects a C0 load.
            image[0] = 0xC0

            logger.info("programming device configuration")
            await device.write_eeprom("fx2", 0, image)

            logger.info("verifying device configuration")
            if await device.read_eeprom("fx2", 0, len(image)) != image:
                logger.critical("factory programming failed")
                return 1

    except GlasgowDeviceError as e:
        logger.error(e)
        return 1

    except GatewareBuildError as e:
        applet.logger.error(e)
        return 1

    return 0
Exemple #6
0
 def builtin_firmware():
     with importlib.resources.open_text(__package__, "firmware.ihex") as f:
         return input_data(f, fmt="ihex")
Exemple #7
0
async def _main():
    args = get_argparser().parse_args()
    create_logger(args)

    device = None
    try:
        # TODO(py3.7): use importlib.resources
        firmware_filename = os.path.join(os.path.dirname(__file__),
                                         "glasgow.ihex")
        if args.action in ("build", "test", "tool"):
            pass
        elif args.action == "factory":
            device = GlasgowHardwareDevice(args.serial,
                                           firmware_filename,
                                           _factory_rev=args.factory_rev)
        else:
            device = GlasgowHardwareDevice(args.serial, firmware_filename)

        if args.action == "voltage":
            if args.voltage is not None:
                await device.reset_alert(args.ports)
                await device.poll_alert()  # clear any remaining alerts
                try:
                    await device.set_voltage(args.ports, args.voltage)
                except:
                    await device.set_voltage(args.ports, 0.0)
                    raise
                if args.set_alert and args.voltage != 0.0:
                    await asyncio.sleep(
                        0.050)  # let the output capacitor discharge a bit
                    await device.set_alert_tolerance(args.ports, args.voltage,
                                                     args.tolerance / 100)

            print("Port\tVio\tVlimit\tVsense\tMonitor")
            alerts = await device.poll_alert()
            for port in args.ports:
                vio = await device.get_voltage(port)
                vlimit = await device.get_voltage_limit(port)
                vsense = await device.measure_voltage(port)
                alert = await device.get_alert(port)
                notice = ""
                if port in alerts:
                    notice += " (ALERT)"
                print("{}\t{:.2}\t{:.2}\t{:.3}\t{:.2}-{:.2}\t{}".format(
                    port, vio, vlimit, vsense, alert[0], alert[1], notice))

        if args.action == "safe":
            await device.reset_alert("AB")
            await device.set_voltage("AB", 0.0)
            await device.poll_alert()  # clear any remaining alerts
            logger.info("all ports safe")

        if args.action == "voltage-limit":
            if args.voltage is not None:
                await device.set_voltage_limit(args.ports, args.voltage)

            print("Port\tVio\tVlimit")
            for port in args.ports:
                vio = await device.get_voltage(port)
                vlimit = await device.get_voltage_limit(port)
                print("{}\t{:.2}\t{:.2}".format(port, vio, vlimit))

        if args.action in ("run", "run-repl", "run-prebuilt"):
            target, applet = _applet(device.revision, args)
            device.demultiplexer = DirectDemultiplexer(
                device, target.multiplexer.pipe_count)
            plan = target.build_plan()

            if args.action in ("run", "run-repl"):
                await device.download_target(plan, rebuild=args.rebuild)
            if args.action == "run-prebuilt":
                bitstream_file = args.bitstream or open(
                    "{}.bin".format(args.applet), "rb")
                with bitstream_file:
                    logger.warn("downloading prebuilt bitstream from %r",
                                bitstream_file.name)
                    await device.download_bitstream(bitstream_file.read())

            do_trace = hasattr(args, "trace") and args.trace
            if do_trace:
                logger.info("starting applet analyzer")
                await device.write_register(target.analyzer.addr_done, 0)
                analyzer_iface = await device.demultiplexer.claim_interface(
                    target.analyzer, target.analyzer.mux_interface, args=None)
                trace_decoder = TraceDecoder(target.analyzer.event_sources)
                vcd_writer = VCDWriter(
                    args.trace,
                    timescale="1 ns",
                    check_values=False,
                    comment='Generated by Glasgow for bitstream ID %s' %
                    plan.bitstream_id.hex())

            async def run_analyzer():
                signals = {}
                strobes = set()
                for field_name, field_trigger, field_width in trace_decoder.events(
                ):
                    if field_trigger == "throttle":
                        var_type = "wire"
                        var_init = 0
                    elif field_trigger == "change":
                        var_type = "wire"
                        var_init = "x"
                    elif field_trigger == "strobe":
                        if field_width > 0:
                            var_type = "tri"
                            var_init = "z"
                        else:
                            var_type = "event"
                            var_init = ""
                    else:
                        assert False
                    signals[field_name] = vcd_writer.register_var(
                        scope="",
                        name=field_name,
                        var_type=var_type,
                        size=field_width,
                        init=var_init)
                    if field_trigger == "strobe":
                        strobes.add(field_name)

                init = True
                while not trace_decoder.is_done():
                    trace_decoder.process(await analyzer_iface.read())
                    for cycle, events in trace_decoder.flush():
                        if events == "overrun":
                            target.analyzer.logger.error(
                                "FIFO overrun, shutting down")

                            for name in signals:
                                vcd_writer.change(signals[name],
                                                  next_timestamp, "x")
                            timestamp += 1e3  # 1us
                            break

                        event_repr = " ".join("{}={}".format(n, v)
                                              for n, v in events.items())
                        target.analyzer.logger.trace("cycle %d: %s", cycle,
                                                     event_repr)

                        timestamp = 1e9 * (cycle + 0) // target.sys_clk_freq
                        next_timestamp = 1e9 * (cycle +
                                                1) // target.sys_clk_freq
                        if init:
                            init = False
                            vcd_writer._timestamp = timestamp
                        for name, value in events.items():
                            vcd_writer.change(signals[name], timestamp, value)
                        for name, _value in events.items():
                            if name in strobes:
                                vcd_writer.change(signals[name],
                                                  next_timestamp, "z")
                        vcd_writer.flush()

                vcd_writer.close(timestamp)

            async def run_applet():
                logger.info("running handler for applet %r", args.applet)
                if applet.preview:
                    logger.warn(
                        "applet %r is PREVIEW QUALITY and may CORRUPT DATA",
                        args.applet)
                try:
                    iface = await applet.run(device, args)
                    if args.action in ("run", "run-prebuilt"):
                        await applet.interact(device, args, iface)
                    if args.action == "run-repl":
                        if applet.has_custom_repl:
                            logger.warn(
                                "applet provides customized REPL(s); consider using `run "
                                "{} ...-repl` subcommands".format(applet.name))
                        logger.info(
                            "dropping to REPL; use 'help(iface)' to see available APIs"
                        )
                        await AsyncInteractiveConsole(locals={
                            "iface": iface
                        }).interact()
                except GlasgowAppletError as e:
                    applet.logger.error(str(e))
                except asyncio.CancelledError:
                    pass  # terminate gracefully
                finally:
                    await device.demultiplexer.flush()

            async def wait_for_sigint():
                await wait_for_signal(signal.SIGINT)
                logger.debug("Ctrl+C pressed, terminating")

            if do_trace:
                analyzer_task = asyncio.ensure_future(run_analyzer())

            applet_task = asyncio.ensure_future(run_applet())
            sigint_task = asyncio.ensure_future(wait_for_sigint())

            tasks = [applet_task, sigint_task]
            done, pending = await asyncio.wait(
                tasks, return_when=asyncio.FIRST_COMPLETED)
            for task in pending:
                task.cancel()
            for task in tasks:
                try:
                    await task
                except asyncio.CancelledError:
                    pass

            if do_trace:
                await device.write_register(target.analyzer.addr_done, 1)
                await analyzer_task

            await device.demultiplexer.cancel()

        if args.action == "tool":
            tool = GlasgowApplet.all_applets[args.applet].tool_cls()
            try:
                await tool.run(args)
            except GlasgowAppletError as e:
                tool.logger.error(e)
                raise SystemExit()

        if args.action == "flash":
            logger.info("reading device configuration")
            header = await device.read_eeprom("fx2", 0,
                                              8 + 4 + GlasgowConfig.size)
            header[0] = 0xC2  # see below

            fx2_config = FX2Config.decode(header, partial=True)
            if (len(fx2_config.firmware) != 1
                    or fx2_config.firmware[0][0] != 0x4000 - GlasgowConfig.size
                    or len(fx2_config.firmware[0][1]) != GlasgowConfig.size):
                raise SystemExit(
                    "Unrecognized or corrupted configuration block")
            glasgow_config = GlasgowConfig.decode(fx2_config.firmware[0][1])

            logger.info("device has serial %s-%s", glasgow_config.revision,
                        glasgow_config.serial)
            if fx2_config.disconnect:
                logger.info("device has flashed firmware")
            else:
                logger.info("device does not have flashed firmware")
            if glasgow_config.bitstream_size:
                logger.info("device has flashed bitstream ID %s",
                            glasgow_config.bitstream_id.hex())
            else:
                logger.info("device does not have flashed bitstream")

            new_bitstream = b""
            if args.remove_bitstream:
                logger.info("removing bitstream")
                glasgow_config.bitstream_size = 0
                glasgow_config.bitstream_id = b"\x00" * 16
            elif args.bitstream:
                logger.info("using bitstream from %s", args.bitstream.name)
                with args.bitstream as f:
                    new_bitstream = f.read()
                    glasgow_config.bitstream_size = len(new_bitstream)
                    glasgow_config.bitstream_id = b"\xff" * 16
            elif args.applet:
                logger.info("building bitstream for applet %s", args.applet)
                target, applet = _applet(device.revision, args)
                plan = target.build_plan()
                new_bitstream_id = plan.bitstream_id
                new_bitstream = plan.execute()

                # We always build and reflash the bitstream in case the one currently
                # in EEPROM is corrupted. If we only compared the ID, there would be
                # no easy way to recover from that case. There's also no point in
                # storing the bitstream hash (as opposed to Verilog hash) in the ID,
                # as building the bitstream takes much longer than flashing it.
                logger.info("built bitstream ID %s", new_bitstream_id.hex())
                glasgow_config.bitstream_size = len(new_bitstream)
                glasgow_config.bitstream_id = new_bitstream_id

            fx2_config.firmware[0] = (0x4000 - GlasgowConfig.size,
                                      glasgow_config.encode())

            if args.remove_firmware:
                logger.info("removing firmware")
                fx2_config.disconnect = False
                new_image = fx2_config.encode()
                new_image[0] = 0xC0  # see below
            else:
                logger.info(
                    "using firmware from %r",
                    args.firmware.name if args.firmware else firmware_filename)
                with (args.firmware or open(firmware_filename, "rb")) as f:
                    for (addr, chunk) in input_data(f, fmt="ihex"):
                        fx2_config.append(addr, chunk)
                fx2_config.disconnect = True
                new_image = fx2_config.encode()

            if new_bitstream:
                logger.info("programming bitstream")
                old_bitstream = await device.read_eeprom(
                    "ice", 0, len(new_bitstream))
                if old_bitstream != new_bitstream:
                    for (addr, chunk) in diff_data(old_bitstream,
                                                   new_bitstream):
                        await device.write_eeprom("ice", addr, chunk)

                    logger.info("verifying bitstream")
                    if await device.read_eeprom(
                            "ice", 0, len(new_bitstream)) != new_bitstream:
                        logger.critical("bitstream programming failed")
                        return 1
                else:
                    logger.info("bitstream identical")

            logger.info("programming configuration and firmware")
            old_image = await device.read_eeprom("fx2", 0, len(new_image))
            if old_image != new_image:
                for (addr, chunk) in diff_data(old_image, new_image):
                    await device.write_eeprom("fx2", addr, chunk)

                logger.info("verifying configuration and firmware")
                if await device.read_eeprom("fx2", 0,
                                            len(new_image)) != new_image:
                    logger.critical(
                        "configuration/firmware programming failed")
                    return 1
            else:
                logger.info("configuration and firmware identical")

        if args.action == "build":
            target, applet = _applet(args.rev, args)
            plan = target.build_plan()
            if args.type in ("il", "rtlil"):
                logger.info("building RTLIL for applet %r", args.applet)
                with open(args.filename or args.applet + ".il", "wt") as f:
                    f.write(plan.rtlil)
            if args.type in ("bin", "bitstream"):
                logger.info("building bitstream for applet %r", args.applet)
                with open(args.filename or args.applet + ".bin", "wb") as f:
                    f.write(plan.execute())
            if args.type in ("zip", "archive"):
                logger.info("building archive for applet %r", args.applet)
                plan.archive(args.filename or args.applet + ".zip")

        if args.action == "test":
            logger.info("testing applet %r", args.applet)
            applet = GlasgowApplet.all_applets[args.applet]()
            loader = unittest.TestLoader()
            stream = unittest.runner._WritelnDecorator(sys.stderr)
            result = unittest.TextTestResult(stream=stream,
                                             descriptions=True,
                                             verbosity=2)
            result.failfast = True

            def startTest(test):
                unittest.TextTestResult.startTest(result, test)
                result.stream.write("\n")

            result.startTest = startTest
            if args.tests == []:
                suite = loader.loadTestsFromTestCase(applet.test_cls)
                suite.run(result)
            else:
                for test in args.tests:
                    suite = loader.loadTestsFromName(test,
                                                     module=applet.test_cls)
                    suite.run(result)
            if not result.wasSuccessful():
                for _, traceback in result.errors + result.failures:
                    print(traceback, end="", file=sys.stderr)
                return 1

        if args.action == "factory":
            logger.info("reading device configuration")
            header = await device.read_eeprom("fx2", 0,
                                              8 + 4 + GlasgowConfig.size)
            if not re.match(rb"^\xff+$", header):
                if args.force:
                    logger.warning(
                        "device already factory-programmed, proceeding anyway")
                else:
                    logger.error("device already factory-programmed")
                    return 1

            fx2_config = FX2Config(vendor_id=VID_QIHW,
                                   product_id=PID_GLASGOW,
                                   device_id=GlasgowConfig.encode_revision(
                                       args.rev),
                                   i2c_400khz=True)
            glasgow_config = GlasgowConfig(args.factory_rev,
                                           args.factory_serial)
            fx2_config.append(0x4000 - GlasgowConfig.size,
                              glasgow_config.encode())

            image = fx2_config.encode()
            # Let FX2 hardware enumerate. This won't load the configuration block
            # into memory automatically, but the firmware has code that does that
            # if it detects a C0 load.
            image[0] = 0xC0

            logger.info("programming device configuration")
            await device.write_eeprom("fx2", 0, image)

            logger.info("verifying device configuration")
            if await device.read_eeprom("fx2", 0, len(image)) != image:
                logger.critical("factory programming failed")
                return 1

    except GlasgowDeviceError as e:
        logger.error(e)
        return 1

    except GatewareBuildError as e:
        applet.logger.error(e)
        return 1

    finally:
        if device is not None:
            device.close()

    return 0
Exemple #8
0
    async def interact(self, device, args, nrf24lx1_iface):
        page_size = 512
        if args.device == "LE1":
            memory_map  = _nrf24le1_map
            buffer_size = 512
        elif args.device == "LU1p32k":
            memory_map  = _nrf24lu1p_32k_map
            buffer_size = 256
        elif args.device == "LU1p16k":
            memory_map  = _nrf24lu1p_16k_map
            buffer_size = 256
        else:
            assert False

        try:
            await nrf24lx1_iface.reset_program()

            async def check_info_page(address):
                old_status = await nrf24lx1_iface.read_status()
                try:
                    await nrf24lx1_iface.write_status(FSR_BIT_INFEN)
                    fuse, = await nrf24lx1_iface.read(address, 1)
                    return fuse != 0xff
                finally:
                    await nrf24lx1_iface.write_status(old_status)

            async def check_read_protected():
                if await check_info_page(0x23):
                    raise ProgramNRF24Lx1Error("MCU is read protected; run `erase --info-page`")

            if args.operation == "read":
                await check_read_protected()

                chunks = []
                for memory_area in memory_map:
                    self.logger.info("reading %s memory", memory_area.name)
                    if memory_area.spi_addr & 0x10000:
                        await nrf24lx1_iface.write_status(FSR_BIT_INFEN)
                    else:
                        await nrf24lx1_iface.write_status(0)
                    area_data = await nrf24lx1_iface.read(memory_area.spi_addr & 0xffff,
                                                          memory_area.size)
                    chunks.append((memory_area.mem_addr, area_data))
                output_data(args.file, chunks, fmt="ihex")

            if args.operation == "program":
                await check_read_protected()

                area_index   = 0
                memory_area  = memory_map[area_index]
                erased_pages = set()
                for chunk_mem_addr, chunk_data in sorted(input_data(args.file, fmt="ihex"),
                                                         key=lambda c: c[0]):
                    if len(chunk_data) == 0:
                        continue
                    if chunk_mem_addr < memory_area.mem_addr:
                        raise ProgramNRF24Lx1Error("data outside of memory map at {:#06x}"
                                                 .format(chunk_mem_addr))
                    while chunk_mem_addr >= memory_area.mem_addr + memory_area.size:
                        area_index += 1
                        if area_index >= len(memory_area):
                            raise ProgramNRF24Lx1Error("data outside of memory map at {:#06x}"
                                                     .format(chunk_mem_addr))
                        memory_area = memory_map[area_index]
                    if chunk_mem_addr + len(chunk_data) > memory_area.mem_addr + memory_area.size:
                        raise ProgramNRF24Lx1Error("data outside of memory map at {:#06x}"
                                                 .format(memory_area.mem_addr + memory_area.size))
                    if memory_area.spi_addr & 0x10000 and not args.info_page:
                        self.logger.warn("data provided for info page, but info page programming "
                                         "is not enabled")
                        continue

                    chunk_spi_addr = (chunk_mem_addr
                                      - memory_area.mem_addr
                                      + memory_area.spi_addr) & 0xffff
                    if memory_area.spi_addr & 0x10000:
                        level = logging.WARN
                        await nrf24lx1_iface.write_status(FSR_BIT_INFEN)
                    else:
                        level = logging.INFO
                        await nrf24lx1_iface.write_status(0)

                    overwrite_pages = set(range(
                        (chunk_spi_addr // page_size),
                        (chunk_spi_addr + len(chunk_data) + page_size - 1) // page_size))
                    need_erase_pages = overwrite_pages - erased_pages
                    if need_erase_pages:
                        for page in need_erase_pages:
                            page_addr = (memory_area.spi_addr & 0x10000) | (page * page_size)
                            self.logger.log(level, "erasing %s memory at %#06x+%#06x",
                                            memory_area.name, page_addr, page_size)
                            await nrf24lx1_iface.write_enable()
                            await nrf24lx1_iface.erase_page(page)
                            await nrf24lx1_iface.wait_status()
                        erased_pages.update(need_erase_pages)

                    self.logger.log(level, "programming %s memory at %#06x+%#06x",
                                    memory_area.name, chunk_mem_addr, len(chunk_data))
                    while len(chunk_data) > 0:
                        await nrf24lx1_iface.write_enable()
                        await nrf24lx1_iface.program(chunk_spi_addr, chunk_data[:buffer_size])
                        await nrf24lx1_iface.wait_status()
                        chunk_data  = chunk_data[buffer_size:]
                        chunk_spi_addr += buffer_size

            if args.operation == "erase":
                if args.info_page:
                    await nrf24lx1_iface.write_status(FSR_BIT_INFEN)
                    info_page = await nrf24lx1_iface.read(0x0000, 0x0100)
                    self.logger.warn("backing up info page to %s", args.info_page)
                    if os.path.isfile(args.info_page):
                        raise ProgramNRF24Lx1Error("info page backup file already exists")
                    with open(args.info_page, "wb") as f:
                        output_data(f, [(0x10000, info_page)])
                    self.logger.warn("erasing code and data memory, and info page")
                else:
                    await check_read_protected()
                    await nrf24lx1_iface.write_status(0)
                    self.logger.info("erasing code and data memory")
                try:
                    await nrf24lx1_iface.write_enable()
                    await nrf24lx1_iface.erase_all()
                    await nrf24lx1_iface.wait_status()
                    if args.info_page:
                        self.logger.info("restoring info page DSYS area")
                        await nrf24lx1_iface.write_enable()
                        await nrf24lx1_iface.program(0, info_page[:32]) # DSYS only
                        await nrf24lx1_iface.wait_status()
                except:
                    if args.info_page:
                        self.logger.error("IMPORTANT: programming failed; restore DSYS manually "
                                          "using `program --info-page %s`",
                                          args.info_page)
                    raise

            if args.operation == "protect-read":
                if await check_info_page(0x23):
                    raise ProgramNRF24Lx1Error("memory read protection is already enabled")

                self.logger.warn("protecting code and data memory from reads")
                await nrf24lx1_iface.write_enable()
                await nrf24lx1_iface.disable_read()
                await nrf24lx1_iface.wait_status()

            if args.operation == "enable-debug":
                if await check_info_page(0x24):
                    raise ProgramNRF24Lx1Error("hardware debugging features already enabled")

                self.logger.info("enabling hardware debugging features")
                await nrf24lx1_iface.write_enable()
                await nrf24lx1_iface.enable_debug()
                await nrf24lx1_iface.wait_status()

        finally:
            await nrf24lx1_iface.reset_application()
Exemple #9
0
async def _main():
    args = get_argparser().parse_args()

    root_logger = logging.getLogger()
    root_logger.setLevel(logging.INFO + args.quiet * 10 - args.verbose * 10)
    handler = logging.StreamHandler()
    formatter_args = {"fmt": "[{levelname:>8s}] {name:s}: {message:s}", "style": "{"}
    if sys.stderr.isatty() and sys.platform != 'win32':
        handler.setFormatter(ANSIColorFormatter(**formatter_args))
    else:
        handler.setFormatter(logging.Formatter(**formatter_args))
    root_logger.addHandler(handler)

    try:
        firmware_file = os.path.join(os.path.dirname(__file__), "glasgow.ihex")
        if args.action in ("build", "test"):
            pass
        elif args.action == "factory":
            device = GlasgowHardwareDevice(firmware_file, VID_CYPRESS, PID_FX2)
        else:
            device = GlasgowHardwareDevice(firmware_file)

        if args.action == "voltage":
            if args.voltage is not None:
                await device.reset_alert(args.ports)
                await device.poll_alert() # clear any remaining alerts
                try:
                    await device.set_voltage(args.ports, args.voltage)
                except:
                    await device.set_voltage(args.ports, 0.0)
                    raise
                if args.set_alert and args.voltage != 0.0:
                    await asyncio.sleep(0.050) # let the output capacitor discharge a bit
                    await device.set_alert_tolerance(args.ports, args.voltage,
                                                     args.tolerance / 100)

            print("Port\tVio\tVlimit\tVsense\tMonitor")
            alerts = await device.poll_alert()
            for port in args.ports:
                vio    = await device.get_voltage(port)
                vlimit = await device.get_voltage_limit(port)
                vsense = await device.measure_voltage(port)
                alert  = await device.get_alert(port)
                notice = ""
                if port in alerts:
                    notice += " (ALERT)"
                print("{}\t{:.2}\t{:.2}\t{:.3}\t{:.2}-{:.2}\t{}"
                      .format(port, vio, vlimit, vsense, alert[0], alert[1], notice))

        if args.action == "voltage-limit":
            if args.voltage is not None:
                await device.set_voltage_limit(args.ports, args.voltage)

            print("Port\tVio\tVlimit")
            for port in args.ports:
                vio    = await device.get_voltage(port)
                vlimit = await device.get_voltage_limit(port)
                print("{}\t{:.2}\t{:.2}"
                      .format(port, vio, vlimit))

        if args.action == "run":
            if args.applet:
                target, applet = _applet(args)
                device.demultiplexer = DirectDemultiplexer(device)

                bitstream_id = target.get_bitstream_id()
                if await device.bitstream_id() == bitstream_id and not args.force:
                    logger.info("device already has bitstream ID %s", bitstream_id.hex())
                else:
                    logger.info("building bitstream ID %s for applet %r",
                                bitstream_id.hex(), args.applet)
                    await device.download_bitstream(target.get_bitstream(debug=True), bitstream_id)

                logger.info("running handler for applet %r", args.applet)
                try:
                    iface = await applet.run(device, args)
                    await applet.interact(device, args, iface)
                except GlasgowAppletError as e:
                    applet.logger.error(str(e))

                # Work around bugs in python-libusb1 that cause segfaults on interpreter shutdown.
                await device.demultiplexer.flush()

            else:
                with args.bitstream as f:
                    logger.info("downloading bitstream from %r", f.name)
                    await device.download_bitstream(f.read())

        if args.action == "flash":
            logger.info("reading device configuration")
            header = await device.read_eeprom("fx2", 0, 8 + 4 + GlasgowConfig.size)
            header[0] = 0xC2 # see below

            fx2_config = FX2Config.decode(header, partial=True)
            if (len(fx2_config.firmware) != 1 or
                    fx2_config.firmware[0][0] != 0x4000 - GlasgowConfig.size or
                    len(fx2_config.firmware[0][1]) != GlasgowConfig.size):
                raise SystemExit("Unrecognized or corrupted configuration block")
            glasgow_config = GlasgowConfig.decode(fx2_config.firmware[0][1])

            logger.info("device has serial %s-%s",
                        glasgow_config.revision, glasgow_config.serial)
            if fx2_config.disconnect:
                logger.info("device has flashed firmware")
            else:
                logger.info("device does not have flashed firmware")
            if glasgow_config.bitstream_size:
                logger.info("device has flashed bitstream ID %s",
                            glasgow_config.bitstream_id.hex())
            else:
                logger.info("device does not have flashed bitstream")

            new_bitstream = b""
            if args.remove_bitstream:
                logger.info("removing bitstream")
                glasgow_config.bitstream_size = 0
                glasgow_config.bitstream_id   = b"\x00"*16
            elif args.bitstream:
                logger.info("using bitstream from %s", args.bitstream.name)
                with args.bitstream as f:
                    new_bitstream = f.read()
                    glasgow_config.bitstream_size = len(new_bitstream)
                    glasgow_config.bitstream_id   = b"\xff"*16
            elif args.applet:
                logger.info("building bitstream for applet %s", args.applet)
                target, applet = _applet(args)
                new_bitstream_id = target.get_bitstream_id()
                new_bitstream = target.get_bitstream(debug=True)

                # We always build and reflash the bitstream in case the one currently
                # in EEPROM is corrupted. If we only compared the ID, there would be
                # no easy way to recover from that case. There's also no point in
                # storing the bitstream hash (as opposed to Verilog hash) in the ID,
                # as building the bitstream takes much longer than flashing it.
                logger.info("built bitstream ID %s", new_bitstream_id.hex())
                glasgow_config.bitstream_size = len(new_bitstream)
                glasgow_config.bitstream_id   = new_bitstream_id

            fx2_config.firmware[0] = (0x4000 - GlasgowConfig.size, glasgow_config.encode())

            if args.remove_firmware:
                logger.info("removing firmware")
                fx2_config.disconnect = False
                new_image = fx2_config.encode()
                new_image[0] = 0xC0 # see below
            else:
                logger.info("using firmware from %r",
                            args.firmware.name if args.firmware else firmware_file)
                with (args.firmware or open(firmware_file, "rb")) as f:
                    for (addr, chunk) in input_data(f, fmt="ihex"):
                        fx2_config.append(addr, chunk)
                fx2_config.disconnect = True
                new_image = fx2_config.encode()

            if new_bitstream:
                logger.info("programming bitstream")
                old_bitstream = await device.read_eeprom("ice", 0, len(new_bitstream))
                if old_bitstream != new_bitstream:
                    for (addr, chunk) in diff_data(old_bitstream, new_bitstream):
                        await device.write_eeprom("ice", addr, chunk)

                    logger.info("verifying bitstream")
                    if await device.read_eeprom("ice", 0, len(new_bitstream)) != new_bitstream:
                        logger.critical("bitstream programming failed")
                        return 1
                else:
                    logger.info("bitstream identical")

            logger.info("programming configuration and firmware")
            old_image = await device.read_eeprom("fx2", 0, len(new_image))
            if old_image != new_image:
                for (addr, chunk) in diff_data(old_image, new_image):
                    await device.write_eeprom("fx2", addr, chunk)

                logger.info("verifying configuration and firmware")
                if await device.read_eeprom("fx2", 0, len(new_image)) != new_image:
                    logger.critical("configuration/firmware programming failed")
                    return 1
            else:
                logger.info("configuration and firmware identical")

        if args.action == "build":
            target, applet = _applet(args)
            logger.info("building bitstream for applet %r", args.applet)
            if args.type in ("v", "verilog"):
                target.get_verilog().write(args.filename or args.applet + ".v")
            if args.type in ("bin", "bitstream"):
                with open(args.filename or args.applet + ".bin", "wb") as f:
                    f.write(target.get_bitstream(debug=True))
            if args.type in ("zip", "archive"):
                with target.get_build_tree() as tree:
                    if args.filename:
                        basename, = os.path.splitext(args.filename)
                    else:
                        basename = args.applet
                    shutil.make_archive(basename, format="zip", root_dir=tree, logger=logger)

        if args.action == "test":
            logger.info("testing applet %r", args.applet)
            applet = GlasgowApplet.all_applets[args.applet]()
            loader = unittest.TestLoader()
            stream = unittest.runner._WritelnDecorator(sys.stderr)
            result = unittest.TextTestResult(stream=stream, descriptions=True, verbosity=2)
            result.failfast = True
            def startTest(test):
                unittest.TextTestResult.startTest(result, test)
                result.stream.write("\n")
            result.startTest = startTest
            if args.tests == []:
                suite = loader.loadTestsFromTestCase(applet.test_cls)
                suite.run(result)
            else:
                for test in args.tests:
                    suite = loader.loadTestsFromName(test, module=applet.test_cls)
                    suite.run(result)
            if not result.wasSuccessful():
                for _, traceback in result.errors + result.failures:
                    print(traceback, end="", file=sys.stderr)
                return 1

        if args.action == "internal-test":
            if args.mode == "toggle-io":
                await device.download_bitstream(TestToggleIO().get_bitstream(debug=True))
                await device.set_voltage("AB", 3.3)

            if args.mode == "mirror-i2c":
                await device.download_bitstream(TestMirrorI2C().get_bitstream(debug=True))
                await device.set_voltage("A", 3.3)

            if args.mode == "shift-out":
                await device.download_bitstream(TestShiftOut(is_async=args.is_async)
                                                .get_bitstream(debug=True))
                await device.set_voltage("A", 3.3)

            if args.mode == "gen-seq":
                await device.download_bitstream(TestGenSeq().get_bitstream(debug=True))

            if args.mode == "pll":
                await device.download_bitstream(TestPLL().get_bitstream(debug=True))

            if args.mode == "registers":
                await device.download_bitstream(TestRegisters().get_bitstream(debug=True))

        if args.action == "factory":
            logger.info("reading device configuration")
            header = await device.read_eeprom("fx2", 0, 8 + 4 + GlasgowConfig.size)
            if not re.match(rb"^\xff+$", header):
                logger.error("device already factory-programmed")
                return 1

            fx2_config = FX2Config(vendor_id=VID_QIHW, product_id=PID_GLASGOW,
                                   device_id=1 + ord(args.revision) - ord('A'),
                                   i2c_400khz=True)
            glasgow_config = GlasgowConfig(args.revision, args.serial)
            fx2_config.append(0x4000 - GlasgowConfig.size, glasgow_config.encode())

            image = fx2_config.encode()
            # Let FX2 hardware enumerate. This won't load the configuration block
            # into memory automatically, but the firmware has code that does that
            # if it detects a C0 load.
            image[0] = 0xC0

            logger.info("programming device configuration")
            await device.write_eeprom("fx2", 0, image)

            logger.info("verifying device configuration")
            if await device.read_eeprom("fx2", 0, len(image)) != image:
                logger.critical("factory programming failed")
                return 1

    except GlasgowDeviceError as e:
        logger.error(e)
        return 1

    return 0
Exemple #10
0
    def __init__(self,
                 serial=None,
                 firmware_filename=None,
                 *,
                 _factory_rev=None):
        usb_context = usb1.USBContext()

        firmware = None
        handles = {}
        discover = True
        while discover:
            discover = False

            for device in usb_context.getDeviceIterator():
                vendor_id = device.getVendorID()
                product_id = device.getProductID()
                device_id = device.getbcdDevice()
                if _factory_rev is None:
                    if (vendor_id, product_id) != (VID_QIHW, PID_GLASGOW):
                        continue
                    revision = GlasgowConfig.decode_revision(device_id & 0xFF)
                else:
                    if (vendor_id, product_id) != (VID_CYPRESS, PID_FX2):
                        continue
                    revision = _factory_rev

                if device_id & 0xFF00 in (0x0000, 0xA000):
                    if firmware_filename is None:
                        logger.warn(
                            "found device without firmware, but no firmware is provided"
                        )
                        continue
                    elif firmware is None:
                        logger.debug("loading firmware from %s",
                                     firmware_filename)
                        with open(firmware_filename, "rb") as f:
                            firmware = input_data(f, fmt="ihex")

                    logger.debug("loading firmware to rev%s device", revision)
                    handle = device.open()
                    handle.controlWrite(usb1.REQUEST_TYPE_VENDOR, REQ_RAM,
                                        REG_CPUCS, 0, [1])
                    for address, data in firmware:
                        while len(data) > 0:
                            handle.controlWrite(usb1.REQUEST_TYPE_VENDOR,
                                                REQ_RAM, address, 0,
                                                data[:4096])
                            data = data[4096:]
                            address += 4096
                    handle.controlWrite(usb1.REQUEST_TYPE_VENDOR, REQ_RAM,
                                        REG_CPUCS, 0, [0])
                    handle.close()

                    # And rediscover the device after it reenumerates.
                    discover = True
                else:
                    handle = device.open()
                    device_serial = handle.getASCIIStringDescriptor(
                        device.getSerialNumberDescriptor())
                    if device_serial in handles:
                        continue

                    logger.debug("found rev%s device with serial %s", revision,
                                 device_serial)
                    handles[device_serial] = (revision, handle)

            if discover:
                # Give every device we loaded firmware onto a bit of time to reenumerate.
                time.sleep(1.0)

        if len(handles) == 0:
            raise GlasgowDeviceError("device not found")
        if serial is None:
            if len(handles) > 1:
                raise GlasgowDeviceError(
                    "found {} devices (serial numbers {}), but a serial "
                    "number is not specified".format(len(handles), ", ".join(
                        handles.keys())))
        else:
            if serial not in handles:
                raise GlasgowDeviceError(
                    "device with serial number {} not found".format(serial))

        self.usb_context = usb_context
        self.usb_poller = _PollerThread(self.usb_context)
        self.usb_poller.start()
        if serial is None:
            self.revision, self.usb_handle = next(iter(handles.values()))
        else:
            self.revision, self.usb_handle = handles[serial]
        try:
            self.usb_handle.setAutoDetachKernelDriver(True)
        except usb1.USBErrorNotSupported:
            pass
Exemple #11
0
def main():
    args = get_argparser().parse_args()

    root_logger = logging.getLogger()
    root_logger.setLevel(logging.INFO + args.quiet * 10 - args.verbose * 10)
    handler = logging.StreamHandler()
    handler.setFormatter(logging.Formatter("[%(levelname)5s] %(name)s: %(message)s"))
    root_logger.addHandler(handler)

    try:
        firmware_file = os.path.join(os.path.dirname(__file__), "glasgow.ihex")
        if args.action in ("build",):
            pass
        elif args.action == "factory":
            device = GlasgowDevice(firmware_file, VID_CYPRESS, PID_FX2)
        else:
            device = GlasgowDevice(firmware_file)

        if args.action == "voltage":
            if args.voltage is not None:
                device.reset_alert(args.ports)
                device.poll_alert() # clear any remaining alerts
                try:
                    device.set_voltage(args.ports, args.voltage)
                except:
                    device.set_voltage(args.ports, 0.0)
                    raise
                if args.set_alert and args.voltage != 0.0:
                    time.sleep(0.050) # let the output capacitor discharge a bit
                    device.set_alert_tolerance(args.ports, args.voltage, args.tolerance / 100)

            print("Port\tVio\tVsense\tRange")
            alerts = device.poll_alert()
            for port in args.ports:
                vio = device.get_voltage(port)
                vsense = device.measure_voltage(port)
                alert = device.get_alert(port)
                if port in alerts:
                    notice = " (ALERT)"
                else:
                    notice = ""
                print("{}\t{:.2}\t{:.3}\t{:.2}-{:.2}{}"
                      .format(port, vio, vsense, alert[0], alert[1], notice))

        if args.action == "run":
            if args.applet:
                applet, target = _applet(args)

                bitstream_id = target.get_bitstream_id()
                if device.bitstream_id() == bitstream_id and not args.force:
                    logger.info("device already has bitstream ID %s", bitstream_id.hex())
                else:
                    logger.info("building bitstream ID %s for applet %s",
                                bitstream_id.hex(), args.applet)
                    device.download_bitstream(target.get_bitstream(debug=True), bitstream_id)

                logger.info("running handler for applet %s", args.applet)
                applet.run(device, args)

            else:
                with args.bitstream as f:
                    logger.info("downloading bitstream from %s", f.name)
                    device.download_bitstream(f.read())

        if args.action == "flash":
            logger.info("reading device configuration")
            header = device.read_eeprom("fx2", 0, 8 + 4 + GlasgowConfig.size)
            header[0] = 0xC2 # see below

            fx2_config = FX2Config.decode(header, partial=True)
            if (len(fx2_config.firmware) != 1 or
                    fx2_config.firmware[0][0] != 0x4000 - GlasgowConfig.size or
                    len(fx2_config.firmware[0][1]) != GlasgowConfig.size):
                raise SystemExit("Unrecognized or corrupted configuration block")
            glasgow_config = GlasgowConfig.decode(fx2_config.firmware[0][1])

            logger.info("device has serial %s-%s",
                        glasgow_config.revision, glasgow_config.serial)
            if fx2_config.disconnect:
                logger.info("device has flashed firmware")
            else:
                logger.info("device does not have flashed firmware")
            if glasgow_config.bitstream_size:
                logger.info("device has flashed bitstream ID %s",
                            glasgow_config.bitstream_id.hex())
            else:
                logger.info("device does not have flashed bitstream")

            new_bitstream = b""
            if args.remove_bitstream:
                logger.info("removing bitstream")
                glasgow_config.bitstream_size = 0
                glasgow_config.bitstream_id   = b"\x00"*16
            elif args.bitstream:
                logger.info("using bitstream from %s", args.bitstream.name)
                with args.bitstream as f:
                    new_bitstream = f.read()
                    glasgow_config.bitstream_size = len(new_bitstream)
                    glasgow_config.bitstream_id   = b"\xff"*16
            elif args.applet:
                logger.info("building bitstream for applet %s", args.applet)
                applet, target = _applet(args)
                new_bitstream_id = target.get_bitstream_id()
                new_bitstream = target.get_bitstream()

                # We always build and reflash the bitstream in case the one currently
                # in EEPROM is corrupted. If we only compared the ID, there would be
                # no easy way to recover from that case. There's also no point in
                # storing the bitstream hash (as opposed to Verilog hash) in the ID,
                # as building the bitstream takes much longer than flashing it.
                logger.info("built bitstream ID %s", new_bitstream_id.hex())
                glasgow_config.bitstream_size = len(new_bitstream)
                glasgow_config.bitstream_id   = new_bitstream_id

            fx2_config.firmware[0] = (0x4000 - GlasgowConfig.size, glasgow_config.encode())

            if args.remove_firmware:
                logger.info("removing firmware")
                fx2_config.disconnect = False
                new_image = fx2_config.encode()
                new_image[0] = 0xC0 # see below
            else:
                logger.info("using firmware from %s",
                            args.firmware.name if args.firmware else firmware_file)
                with (args.firmware or open(firmware_file, "rb")) as f:
                    for (addr, chunk) in input_data(f, fmt="ihex"):
                        fx2_config.append(addr, chunk)
                fx2_config.disconnect = True
                new_image = fx2_config.encode()

            if new_bitstream:
                logger.info("programming bitstream")
                old_bitstream = device.read_eeprom("ice", 0, len(new_bitstream))
                if old_bitstream != new_bitstream:
                    for (addr, chunk) in diff_data(old_bitstream, new_bitstream):
                        device.write_eeprom("ice", addr, chunk)

                    logger.info("verifying bitstream")
                    if device.read_eeprom("ice", 0, len(new_bitstream)) != new_bitstream:
                        raise SystemExit("Bitstream programming failed")
                else:
                    logger.info("bitstream identical")

            logger.info("programming configuration and firmware")
            old_image = device.read_eeprom("fx2", 0, len(new_image))
            if old_image != new_image:
                for (addr, chunk) in diff_data(old_image, new_image):
                    device.write_eeprom("fx2", addr, chunk)

                logger.info("verifying configuration and firmware")
                if device.read_eeprom("fx2", 0, len(new_image)) != new_image:
                    raise SystemExit("Configuration/firmware programming failed")
            else:
                logger.info("configuration and firmware identical")

        if args.action == "build":
            applet, target = _applet(args)
            logger.info("building bitstream for applet %s", args.applet)
            if args.type in ("v", "verilog"):
                target.get_verilog().write(args.filename or args.applet + ".v")
            if args.type in ("bin", "bitstream"):
                with open(args.filename or args.applet + ".bin", "wb") as f:
                    f.write(target.get_bitstream(debug=True))

        if args.action == "test":
            if args.mode == "toggle-io":
                device.download_bitstream(TestToggleIO().get_bitstream(debug=True))
                device.set_voltage("AB", 3.3)

            if args.mode == "mirror-i2c":
                device.download_bitstream(TestMirrorI2C().get_bitstream(debug=True))
                device.set_voltage("A", 3.3)

            if args.mode == "shift-out":
                device.download_bitstream(TestShiftOut(async=args.async)
                                          .get_bitstream(debug=True))
                device.set_voltage("A", 3.3)

            if args.mode == "gen-seq":
                device.download_bitstream(TestGenSeq().get_bitstream(debug=True))

            if args.mode == "pll":
                device.download_bitstream(TestPLL().get_bitstream(debug=True))

        if args.action == "factory":
            logger.info("reading device configuration")
            header = device.read_eeprom("fx2", 0, 8 + 4 + GlasgowConfig.size)
            if not re.match(rb"^\xff+$", header):
                raise SystemExit("Device already factory-programmed")

            fx2_config = FX2Config(vendor_id=VID_QIHW, product_id=PID_GLASGOW,
                                   device_id=1 + ord(args.revision) - ord('A'),
                                   i2c_400khz=True)
            glasgow_config = GlasgowConfig(args.revision, args.serial)
            fx2_config.append(0x4000 - GlasgowConfig.size, glasgow_config.encode())

            image = fx2_config.encode()
            # Let FX2 hardware enumerate. This won't load the configuration block
            # into memory automatically, but the firmware has code that does that
            # if it detects a C0 load.
            image[0] = 0xC0

            logger.info("programming device configuration")
            device.write_eeprom("fx2", 0, image)

            logger.info("verifying device configuration")
            if device.read_eeprom("fx2", 0, len(image)) != image:
                raise SystemExit("Factory programming failed")

    except (ValueError, FX2DeviceError) as e:
        raise SystemExit(e)