Beispiel #1
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
Beispiel #2
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
Beispiel #3
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
Beispiel #4
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)