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
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
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
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)