2017-01-30 09:24:43 +08:00
|
|
|
#!/usr/bin/env python3
|
2016-01-05 07:50:59 +08:00
|
|
|
|
|
|
|
import argparse
|
|
|
|
import os
|
|
|
|
import subprocess
|
|
|
|
import tempfile
|
2016-07-15 21:31:27 +08:00
|
|
|
import shutil
|
2018-01-19 15:39:55 +08:00
|
|
|
import re
|
2017-11-01 17:34:10 +08:00
|
|
|
from functools import partial
|
2016-01-05 07:50:59 +08:00
|
|
|
|
2016-01-26 09:04:06 +08:00
|
|
|
from artiq import __artiq_dir__ as artiq_dir
|
2018-01-19 16:28:04 +08:00
|
|
|
from artiq.tools import verbosity_args, init_logger
|
|
|
|
from artiq.remoting import SSHClient, LocalClient
|
2016-01-05 07:50:59 +08:00
|
|
|
from artiq.frontend.bit2bin import bit2bin
|
|
|
|
|
2018-01-19 16:28:04 +08:00
|
|
|
|
2016-01-19 12:41:42 +08:00
|
|
|
def get_argparser():
|
2016-01-05 07:50:59 +08:00
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
|
|
description="ARTIQ flashing/deployment tool",
|
|
|
|
epilog="""\
|
|
|
|
Valid actions:
|
|
|
|
|
2016-03-01 03:45:41 +08:00
|
|
|
* proxy: load the flash proxy gateware bitstream
|
|
|
|
* gateware: write gateware bitstream to flash
|
2017-12-28 03:14:41 +08:00
|
|
|
* bootloader: write bootloader to flash
|
2016-01-05 07:50:59 +08:00
|
|
|
* storage: write storage image to flash
|
2017-12-31 12:08:11 +08:00
|
|
|
* firmware: write firmware to flash
|
2016-03-01 03:45:41 +08:00
|
|
|
* load: load gateware bitstream into device (volatile but fast)
|
|
|
|
* start: trigger the target to (re)load its gateware bitstream from flash
|
2016-01-05 07:50:59 +08:00
|
|
|
|
|
|
|
Prerequisites:
|
|
|
|
|
|
|
|
* Connect the board through its/a JTAG adapter.
|
|
|
|
* Have OpenOCD installed and in your $PATH.
|
|
|
|
* Have access to the JTAG adapter's devices. Udev rules from OpenOCD:
|
|
|
|
'sudo cp openocd/contrib/99-openocd.rules /etc/udev/rules.d'
|
|
|
|
and replug the device. Ensure you are member of the
|
|
|
|
plugdev group: 'sudo adduser $USER plugdev' and re-login.
|
|
|
|
""")
|
2018-01-19 15:39:55 +08:00
|
|
|
|
|
|
|
verbosity_args(parser)
|
|
|
|
|
|
|
|
parser.add_argument("-H", "--host", metavar="HOSTNAME",
|
|
|
|
type=str, default=None,
|
|
|
|
help="SSH host where the development board is located")
|
2016-01-05 07:50:59 +08:00
|
|
|
parser.add_argument("-t", "--target", default="kc705",
|
2018-01-17 09:21:19 +08:00
|
|
|
help="target board, default: %(default)s, one of: "
|
2018-01-17 09:45:47 +08:00
|
|
|
"kc705 kasli sayma_amc sayma_rtm")
|
2017-12-22 16:53:58 +08:00
|
|
|
parser.add_argument("-m", "--variant", default=None,
|
|
|
|
help="board variant")
|
2017-11-01 17:34:10 +08:00
|
|
|
parser.add_argument("--preinit-command", default=[], action="append",
|
|
|
|
help="add a pre-initialization OpenOCD command. "
|
2017-12-26 10:48:36 +08:00
|
|
|
"Useful for selecting a development board "
|
2017-11-01 17:34:10 +08:00
|
|
|
"when several are connected.")
|
2016-01-05 07:50:59 +08:00
|
|
|
parser.add_argument("-f", "--storage", help="write file to storage area")
|
|
|
|
parser.add_argument("-d", "--dir", help="look for files in this directory")
|
2017-12-31 12:08:11 +08:00
|
|
|
parser.add_argument("--srcbuild", help="look for bitstream, bootloader and firmware in this "
|
2017-12-14 10:36:03 +08:00
|
|
|
"ARTIQ source build tree")
|
2016-04-22 16:33:44 +08:00
|
|
|
parser.add_argument("action", metavar="ACTION", nargs="*",
|
2017-12-31 12:08:11 +08:00
|
|
|
default="proxy gateware bootloader firmware start".split(),
|
2016-01-05 07:50:59 +08:00
|
|
|
help="actions to perform, default: %(default)s")
|
2016-01-19 12:41:42 +08:00
|
|
|
return parser
|
|
|
|
|
|
|
|
|
2017-08-21 05:23:56 +08:00
|
|
|
def scripts_path():
|
|
|
|
p = ["share", "openocd", "scripts"]
|
|
|
|
if os.name == "nt":
|
|
|
|
p.insert(0, "Library")
|
|
|
|
p = os.path.abspath(os.path.join(
|
|
|
|
os.path.dirname(shutil.which("openocd")),
|
|
|
|
"..", *p))
|
|
|
|
return p
|
|
|
|
|
|
|
|
|
2017-10-23 21:21:51 +08:00
|
|
|
def proxy_path():
|
|
|
|
p = ["share", "bscan-spi-bitstreams"]
|
|
|
|
p = os.path.abspath(os.path.join(
|
|
|
|
os.path.dirname(shutil.which("openocd")),
|
|
|
|
"..", *p))
|
|
|
|
return p
|
|
|
|
|
|
|
|
|
2017-08-21 05:23:56 +08:00
|
|
|
class Programmer:
|
2018-01-19 15:39:55 +08:00
|
|
|
def __init__(self, client, target_file, preinit_commands):
|
|
|
|
self.client = client
|
|
|
|
if target_file:
|
|
|
|
self.target_file = self._transfer_script(target_file)
|
|
|
|
else:
|
|
|
|
self.target_file = None
|
2017-11-01 17:34:10 +08:00
|
|
|
self.preinit_commands = preinit_commands
|
2017-08-21 05:23:56 +08:00
|
|
|
self.prog = []
|
|
|
|
|
2018-01-19 15:39:55 +08:00
|
|
|
def _transfer_script(self, script):
|
|
|
|
if isinstance(self.client, LocalClient):
|
|
|
|
return script
|
|
|
|
|
|
|
|
def rewriter(content):
|
|
|
|
def repl(match):
|
2018-01-19 16:28:04 +08:00
|
|
|
return self._transfer_script(match.group(1).decode()).encode()
|
|
|
|
return re.sub(rb"\[find (.+?)\]", repl, content, re.DOTALL)
|
2018-01-19 15:39:55 +08:00
|
|
|
|
|
|
|
script = os.path.join(scripts_path(), script)
|
|
|
|
return self.client.transfer_file(script, rewriter)
|
|
|
|
|
|
|
|
def _command(self, cmd):
|
|
|
|
self.prog.append(cmd.replace("{", "{{").replace("}", "}}"))
|
|
|
|
|
2017-11-01 17:34:10 +08:00
|
|
|
def init(self):
|
2018-01-19 15:39:55 +08:00
|
|
|
for command in self.preinit_commands:
|
|
|
|
self._command(command)
|
|
|
|
self._command("init")
|
2017-11-01 17:34:10 +08:00
|
|
|
|
2017-08-21 05:23:56 +08:00
|
|
|
def load(self, bitfile):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def proxy(self, proxy_bitfile):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def flash_binary(self, flashno, address, filename):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def start(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def do(self):
|
2018-01-19 15:39:55 +08:00
|
|
|
self._command("exit")
|
|
|
|
|
|
|
|
cmdline = ["openocd"]
|
|
|
|
if isinstance(self.client, LocalClient):
|
|
|
|
cmdline += ["-s", scripts_path()]
|
2017-08-21 05:23:56 +08:00
|
|
|
if self.target_file is not None:
|
2017-08-31 12:16:52 +08:00
|
|
|
cmdline += ["-f", self.target_file]
|
2017-08-21 05:23:56 +08:00
|
|
|
cmdline += ["-c", "; ".join(self.prog)]
|
2018-01-19 15:39:55 +08:00
|
|
|
|
|
|
|
self.client.run_command(cmdline)
|
2017-08-21 05:23:56 +08:00
|
|
|
|
|
|
|
|
|
|
|
class ProgrammerJtagSpi7(Programmer):
|
2018-01-19 15:39:55 +08:00
|
|
|
def __init__(self, client, target, preinit_commands):
|
|
|
|
Programmer.__init__(self, client, os.path.join("board", target + ".cfg"),
|
2017-11-01 17:34:10 +08:00
|
|
|
preinit_commands)
|
|
|
|
self.init()
|
2017-08-21 05:23:56 +08:00
|
|
|
|
2017-11-01 21:45:07 +08:00
|
|
|
def load(self, bitfile, pld=0):
|
2018-01-19 15:39:55 +08:00
|
|
|
bitfile = self.client.transfer_file(bitfile)
|
|
|
|
self._command("pld load {} {{{}}}".format(pld, bitfile))
|
2017-08-21 05:23:56 +08:00
|
|
|
|
2017-11-01 21:45:07 +08:00
|
|
|
def proxy(self, proxy_bitfile, pld=0):
|
2018-01-19 15:39:55 +08:00
|
|
|
proxy_bitfile = self.client.transfer_file(proxy_bitfile)
|
|
|
|
self._command("jtagspi_init {} {{{}}}".format(pld, proxy_bitfile))
|
2017-08-21 05:23:56 +08:00
|
|
|
|
|
|
|
def flash_binary(self, flashno, address, filename):
|
|
|
|
# jtagspi_program supports only one flash
|
|
|
|
assert flashno == 0
|
2018-01-19 15:39:55 +08:00
|
|
|
filename = self.client.transfer_file(filename)
|
|
|
|
self._command("jtagspi_program {{{}}} 0x{:x}".format(
|
2017-08-21 05:23:56 +08:00
|
|
|
filename, address))
|
|
|
|
|
|
|
|
def start(self):
|
2018-01-19 15:39:55 +08:00
|
|
|
self._command("xc7_program xc7.tap")
|
2017-08-21 05:23:56 +08:00
|
|
|
|
|
|
|
|
|
|
|
class ProgrammerSayma(Programmer):
|
2017-12-13 21:20:16 +08:00
|
|
|
sector_size = 0x10000
|
|
|
|
|
2018-01-19 15:39:55 +08:00
|
|
|
def __init__(self, client, preinit_commands):
|
|
|
|
Programmer.__init__(self, client, None, preinit_commands)
|
|
|
|
|
2017-08-21 05:23:56 +08:00
|
|
|
self.prog += [
|
|
|
|
"interface ftdi",
|
|
|
|
"ftdi_device_desc \"Quad RS232-HS\"",
|
|
|
|
"ftdi_vid_pid 0x0403 0x6011",
|
|
|
|
"ftdi_channel 0",
|
|
|
|
# EN_USB_JTAG on ADBUS7: out, high
|
|
|
|
# nTRST on ADBUS4: out, high, but R46 is DNP
|
|
|
|
"ftdi_layout_init 0x0098 0x008b",
|
2017-11-01 20:11:18 +08:00
|
|
|
"reset_config none",
|
2017-08-21 05:23:56 +08:00
|
|
|
|
2017-11-01 20:11:18 +08:00
|
|
|
"adapter_khz 5000",
|
2017-08-21 05:23:56 +08:00
|
|
|
"transport select jtag",
|
|
|
|
|
2018-01-19 16:28:04 +08:00
|
|
|
# tap 0, pld 0
|
|
|
|
"source {}".format(self._transfer_script("cpld/xilinx-xc7.cfg")),
|
|
|
|
# tap 1, pld 1
|
2017-11-01 20:11:18 +08:00
|
|
|
"set CHIP XCKU040",
|
2018-01-19 16:28:04 +08:00
|
|
|
"source {}".format(self._transfer_script("cpld/xilinx-xcu.cfg")),
|
2017-08-21 05:23:56 +08:00
|
|
|
|
2017-12-13 21:20:16 +08:00
|
|
|
"target create xcu.proxy testee -chain-position xcu.tap",
|
2017-08-21 05:23:56 +08:00
|
|
|
"set XILINX_USER1 0x02",
|
|
|
|
"set XILINX_USER2 0x03",
|
2017-11-01 20:11:18 +08:00
|
|
|
"flash bank xcu.spi0 jtagspi 0 0 0 0 xcu.proxy $XILINX_USER1",
|
|
|
|
"flash bank xcu.spi1 jtagspi 0 0 0 0 xcu.proxy $XILINX_USER2"
|
2017-08-21 05:23:56 +08:00
|
|
|
]
|
2017-11-01 17:34:10 +08:00
|
|
|
self.init()
|
2017-08-21 05:23:56 +08:00
|
|
|
|
2017-12-31 13:23:26 +08:00
|
|
|
def load(self, bitfile, pld=1):
|
2018-01-19 15:39:55 +08:00
|
|
|
bitfile = self.client.transfer_file(bitfile)
|
|
|
|
self._command("pld load {} {{{}}}".format(pld, bitfile))
|
2017-08-21 05:23:56 +08:00
|
|
|
|
2017-12-31 13:23:26 +08:00
|
|
|
def proxy(self, proxy_bitfile, pld=1):
|
|
|
|
self.load(proxy_bitfile, pld)
|
2018-01-19 15:39:55 +08:00
|
|
|
self._command("reset halt")
|
2017-08-21 05:23:56 +08:00
|
|
|
|
|
|
|
def flash_binary(self, flashno, address, filename):
|
2017-12-13 21:20:16 +08:00
|
|
|
sector_first = address // self.sector_size
|
|
|
|
size = os.path.getsize(filename)
|
|
|
|
assert size
|
|
|
|
sector_last = sector_first + (size - 1) // self.sector_size
|
|
|
|
assert sector_last >= sector_first
|
2018-01-19 15:39:55 +08:00
|
|
|
filename = self.client.transfer_file(filename)
|
|
|
|
self._command("flash probe xcu.spi{}".format(flashno))
|
|
|
|
self._command("flash erase_sector {} {} {}".format(flashno, sector_first, sector_last))
|
|
|
|
self._command("flash write_bank {} {{{}}} 0x{:x}".format(flashno, filename, address))
|
|
|
|
self._command("flash verify_bank {} {{{}}} 0x{:x}".format(flashno, filename, address))
|
2017-08-21 05:23:56 +08:00
|
|
|
|
|
|
|
def start(self):
|
2018-01-19 15:39:55 +08:00
|
|
|
self._command("xcu_program xcu.tap")
|
2017-08-21 05:23:56 +08:00
|
|
|
|
|
|
|
|
2016-01-19 12:41:42 +08:00
|
|
|
def main():
|
2018-01-19 15:39:55 +08:00
|
|
|
args = get_argparser().parse_args()
|
|
|
|
init_logger(args)
|
2016-01-05 07:50:59 +08:00
|
|
|
|
|
|
|
config = {
|
|
|
|
"kc705": {
|
2018-01-19 15:39:55 +08:00
|
|
|
"programmer_factory": partial(ProgrammerJtagSpi7, target="kc705"),
|
2017-08-21 05:23:56 +08:00
|
|
|
"proxy_bitfile": "bscan_spi_xc7k325t.bit",
|
2017-12-22 16:53:58 +08:00
|
|
|
"variants": ["nist_clock", "nist_qc2"],
|
2017-12-28 03:14:41 +08:00
|
|
|
"gateware": (0, 0x000000),
|
|
|
|
"bootloader": (0, 0xaf0000),
|
2017-12-28 22:38:56 +08:00
|
|
|
"storage": (0, 0xb30000),
|
2017-12-31 12:08:11 +08:00
|
|
|
"firmware": (0, 0xb40000),
|
2017-08-21 05:23:56 +08:00
|
|
|
},
|
2018-01-15 20:59:11 +08:00
|
|
|
"kasli": {
|
2018-01-19 15:39:55 +08:00
|
|
|
"programmer_factory": partial(ProgrammerJtagSpi7, target="kasli"),
|
2018-01-15 20:59:11 +08:00
|
|
|
"proxy_bitfile": "bscan_spi_xc7a100t.bit",
|
|
|
|
"variants": ["opticlock"],
|
|
|
|
"gateware": (0, 0x000000),
|
|
|
|
"bootloader": (0, 0x400000),
|
|
|
|
"storage": (0, 0x440000),
|
|
|
|
"firmware": (0, 0x450000),
|
|
|
|
},
|
2018-01-17 09:45:47 +08:00
|
|
|
"sayma_amc": {
|
2017-08-21 05:23:56 +08:00
|
|
|
"programmer_factory": ProgrammerSayma,
|
2017-11-01 14:57:30 +08:00
|
|
|
"proxy_bitfile": "bscan_spi_xcku040-sayma.bit",
|
2017-12-31 12:08:11 +08:00
|
|
|
"variants": ["standalone", "master", "satellite"],
|
2017-12-28 03:14:41 +08:00
|
|
|
"gateware": (0, 0x000000),
|
|
|
|
"bootloader": (1, 0x000000),
|
2017-12-28 22:38:56 +08:00
|
|
|
"storage": (1, 0x040000),
|
2017-12-31 12:08:11 +08:00
|
|
|
"firmware": (1, 0x050000),
|
2016-01-05 07:50:59 +08:00
|
|
|
},
|
2018-01-17 09:21:19 +08:00
|
|
|
"sayma_rtm": {
|
|
|
|
"programmer_factory": ProgrammerSayma,
|
|
|
|
"proxy_bitfile": "bscan_spi_xcku040-sayma.bit",
|
|
|
|
"gateware": (1, 0x150000),
|
|
|
|
},
|
2018-01-19 15:39:55 +08:00
|
|
|
}[args.target]
|
2016-01-05 07:50:59 +08:00
|
|
|
|
2018-01-19 15:39:55 +08:00
|
|
|
variant = args.variant
|
2018-01-17 09:21:19 +08:00
|
|
|
if "variants" in config:
|
|
|
|
if variant is not None and variant not in config["variants"]:
|
|
|
|
raise SystemExit("Invalid variant for this board")
|
|
|
|
if variant is None:
|
|
|
|
variant = config["variants"][0]
|
2018-01-19 15:39:55 +08:00
|
|
|
bin_dir = args.dir
|
2017-08-21 05:23:56 +08:00
|
|
|
if bin_dir is None:
|
2018-01-17 09:21:19 +08:00
|
|
|
if variant:
|
2018-01-19 15:39:55 +08:00
|
|
|
bin_name = "{}-{}".format(args.target, variant)
|
2018-01-17 09:21:19 +08:00
|
|
|
else:
|
2018-01-19 15:39:55 +08:00
|
|
|
bin_name = args.target
|
2018-01-17 09:21:19 +08:00
|
|
|
bin_dir = os.path.join(artiq_dir, "binaries", bin_name)
|
2018-01-19 15:39:55 +08:00
|
|
|
if args.srcbuild is None and not os.path.exists(bin_dir) and args.action != ["start"]:
|
2016-04-05 16:09:41 +08:00
|
|
|
raise SystemExit("Binaries directory '{}' does not exist"
|
2017-08-21 05:23:56 +08:00
|
|
|
.format(bin_dir))
|
2016-01-05 07:50:59 +08:00
|
|
|
|
2018-01-19 15:39:55 +08:00
|
|
|
if args.host is None:
|
|
|
|
client = LocalClient()
|
|
|
|
else:
|
|
|
|
client = SSHClient(args.host)
|
|
|
|
|
|
|
|
programmer = config["programmer_factory"](client, preinit_commands=args.preinit_command)
|
2016-01-05 07:50:59 +08:00
|
|
|
|
2017-08-21 05:23:56 +08:00
|
|
|
conv = False
|
2018-01-19 15:39:55 +08:00
|
|
|
for action in args.action:
|
2016-01-05 07:50:59 +08:00
|
|
|
if action == "proxy":
|
2017-08-21 05:23:56 +08:00
|
|
|
proxy_found = False
|
2017-10-23 21:21:51 +08:00
|
|
|
for p in [bin_dir, proxy_path(), os.path.expanduser("~/.migen"),
|
2016-03-01 03:45:41 +08:00
|
|
|
"/usr/local/share/migen", "/usr/share/migen"]:
|
2017-08-21 05:23:56 +08:00
|
|
|
proxy_bitfile = os.path.join(p, config["proxy_bitfile"])
|
|
|
|
if os.access(proxy_bitfile, os.R_OK):
|
|
|
|
programmer.proxy(proxy_bitfile)
|
|
|
|
proxy_found = True
|
2016-01-05 07:50:59 +08:00
|
|
|
break
|
2017-08-21 05:23:56 +08:00
|
|
|
if not proxy_found:
|
2016-01-05 07:50:59 +08:00
|
|
|
raise SystemExit(
|
2017-08-21 05:23:56 +08:00
|
|
|
"proxy gateware bitstream {} not found".format(config["proxy_bitfile"]))
|
2016-03-01 03:45:41 +08:00
|
|
|
elif action == "gateware":
|
2018-01-19 15:39:55 +08:00
|
|
|
if args.srcbuild is None:
|
2017-12-14 10:36:03 +08:00
|
|
|
path = bin_dir
|
|
|
|
else:
|
2018-01-19 15:39:55 +08:00
|
|
|
path = os.path.join(args.srcbuild, "gateware")
|
2017-12-14 10:36:03 +08:00
|
|
|
bin = os.path.join(path, "top.bin")
|
2016-01-05 07:50:59 +08:00
|
|
|
if not os.access(bin, os.R_OK):
|
2016-05-17 03:30:16 +08:00
|
|
|
bin_handle, bin = tempfile.mkstemp()
|
2017-12-14 10:36:03 +08:00
|
|
|
bit = os.path.join(path, "top.bit")
|
2017-12-13 21:20:16 +08:00
|
|
|
with open(bit, "rb") as f, open(bin_handle, "wb") as g:
|
|
|
|
bit2bin(f, g)
|
2016-01-05 07:50:59 +08:00
|
|
|
conv = True
|
2017-08-21 05:23:56 +08:00
|
|
|
programmer.flash_binary(*config["gateware"], bin)
|
2017-12-28 03:14:41 +08:00
|
|
|
elif action == "bootloader":
|
2018-01-19 15:39:55 +08:00
|
|
|
if args.srcbuild is None:
|
2017-12-14 10:36:03 +08:00
|
|
|
path = bin_dir
|
|
|
|
else:
|
2018-01-19 15:39:55 +08:00
|
|
|
path = os.path.join(args.srcbuild, "software", "bootloader")
|
2017-12-28 21:17:49 +08:00
|
|
|
programmer.flash_binary(*config["bootloader"], os.path.join(path, "bootloader.bin"))
|
2017-12-28 03:14:41 +08:00
|
|
|
elif action == "storage":
|
2018-01-19 15:39:55 +08:00
|
|
|
programmer.flash_binary(*config["storage"], args.storage)
|
2017-12-31 12:08:11 +08:00
|
|
|
elif action == "firmware":
|
|
|
|
if variant == "satellite":
|
|
|
|
firmware_name = "satman"
|
|
|
|
else:
|
|
|
|
firmware_name = "runtime"
|
2018-01-19 15:39:55 +08:00
|
|
|
if args.srcbuild is None:
|
2017-12-14 10:36:03 +08:00
|
|
|
path = bin_dir
|
|
|
|
else:
|
2018-01-19 15:39:55 +08:00
|
|
|
path = os.path.join(args.srcbuild, "software", firmware_name)
|
2017-12-31 12:08:11 +08:00
|
|
|
programmer.flash_binary(*config["firmware"],
|
|
|
|
os.path.join(path, firmware_name + ".fbi"))
|
2016-01-05 07:50:59 +08:00
|
|
|
elif action == "load":
|
2018-01-19 15:39:55 +08:00
|
|
|
if args.srcbuild is None:
|
2017-12-14 10:36:03 +08:00
|
|
|
path = bin_dir
|
|
|
|
else:
|
2018-01-19 15:39:55 +08:00
|
|
|
path = os.path.join(args.srcbuild, "gateware")
|
2017-12-14 10:36:03 +08:00
|
|
|
programmer.load(os.path.join(path, "top.bit"))
|
2016-01-05 07:50:59 +08:00
|
|
|
elif action == "start":
|
2017-08-21 05:23:56 +08:00
|
|
|
programmer.start()
|
2016-01-05 07:50:59 +08:00
|
|
|
else:
|
|
|
|
raise ValueError("invalid action", action)
|
2017-08-21 05:23:56 +08:00
|
|
|
|
2016-01-05 07:50:59 +08:00
|
|
|
try:
|
2017-08-21 05:23:56 +08:00
|
|
|
programmer.do()
|
2016-01-05 07:50:59 +08:00
|
|
|
finally:
|
|
|
|
if conv:
|
|
|
|
os.unlink(bin)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|