mirror of
https://github.com/TinyTapeout/tt-support-tools.git
synced 2026-11-03 04:36:09 +01:00
feat(fpga): add support for the new FabricFox devkit
* TT FPGA tool, prelim import * config.ini manager, prelim * bitstream uploads and FPGA config.ini management Contributed by @psychogenic, with minor edits by @htfab
This commit is contained in:
Executable
+239
@@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
|
||||
TT config.ini manager
|
||||
|
||||
# global defaults
|
||||
python tt_db_config.py --project tt_um_factory_test --mode ASIC_MANUAL_INPUTS
|
||||
|
||||
|
||||
# project specific
|
||||
python tt_db_config.py --name tt_um_factory_test project --clockrate 100
|
||||
|
||||
"""
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from configupdater import ConfigUpdater
|
||||
|
||||
DefaultPort = "/dev/ttyACM0"
|
||||
CommandProject = "project"
|
||||
CommandDefault = "default"
|
||||
|
||||
|
||||
def getParser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="TT Demoboard Configuration Tool",
|
||||
# formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
|
||||
# Global options (available to all subcommands)
|
||||
|
||||
parser.add_argument(
|
||||
"--port",
|
||||
help=f"port for connection to DB [{DefaultPort}]",
|
||||
required=False,
|
||||
default=DefaultPort,
|
||||
)
|
||||
|
||||
parser.add_argument("--name", help="project/section name", required=False)
|
||||
|
||||
parser.add_argument(
|
||||
"--debug",
|
||||
metavar="LEVEL",
|
||||
help="debug logging",
|
||||
action="store_const",
|
||||
dest="loglevel",
|
||||
const=logging.DEBUG,
|
||||
default=logging.INFO,
|
||||
)
|
||||
|
||||
# Create subparsers
|
||||
subparsers = parser.add_subparsers(
|
||||
dest="command",
|
||||
required=True, # make subcommand mandatory
|
||||
help="Available commands",
|
||||
)
|
||||
|
||||
defaults = subparsers.add_parser(CommandDefault, help="Default settings")
|
||||
|
||||
defaults.add_argument(
|
||||
"--project",
|
||||
metavar="PROJNAME",
|
||||
type=str,
|
||||
help="Set the default project on boot",
|
||||
)
|
||||
|
||||
defaults.add_argument(
|
||||
"--mode",
|
||||
metavar="I/O MODE",
|
||||
type=str,
|
||||
choices=["SAFE", "ASIC_RP_CONTROL", "ASIC_MANUAL_INPUTS"],
|
||||
)
|
||||
|
||||
project = subparsers.add_parser(CommandProject, help="Project overrides")
|
||||
|
||||
project.add_argument(
|
||||
"--clockrate",
|
||||
metavar="RATE",
|
||||
type=int,
|
||||
help="Clock rate in Hz",
|
||||
)
|
||||
project.add_argument(
|
||||
"--set-default",
|
||||
action="store_true",
|
||||
help="set this project as the default on startup",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
class ConfigFile:
|
||||
|
||||
def __init__(self, filepath: str):
|
||||
self.filepath = filepath
|
||||
|
||||
self.updater = ConfigUpdater()
|
||||
self.updater.read(filepath)
|
||||
return
|
||||
|
||||
def save(self):
|
||||
with open(self.filepath, "w") as f:
|
||||
self.updater.write(f)
|
||||
|
||||
def set_default_project(self, name: str):
|
||||
self.updater["DEFAULT"]["project"] = name
|
||||
|
||||
def set_default_mode(self, mode: str):
|
||||
self.updater["DEFAULT"]["mode"] = mode
|
||||
|
||||
def set_project_clockrate(self, name: str, rate: int):
|
||||
if name not in self.updater:
|
||||
self.updater.add_section(name)
|
||||
|
||||
self.updater[name]["clock_frequency"] = str(rate)
|
||||
|
||||
|
||||
class TTDBConfig:
|
||||
|
||||
def __init__(self, port: str = DefaultPort):
|
||||
self.port = port
|
||||
|
||||
@classmethod
|
||||
def get_temp_file(cls, prefix="ttconf-", suffix=".tmp", dir=None, mode="wb"):
|
||||
# path is tmp.name
|
||||
tmp = tempfile.NamedTemporaryFile(
|
||||
mode=mode, # text mode + read/write
|
||||
delete=False, # ← keep file after closing
|
||||
suffix=suffix, # optional but recommended
|
||||
prefix=prefix,
|
||||
dir=dir, # None = system temp dir
|
||||
)
|
||||
return tmp
|
||||
|
||||
@classmethod
|
||||
def get_temp_filename(cls, prefix="ttconf-", suffix=".tmp", dir=None):
|
||||
tmp = cls.get_temp_file(prefix, suffix, dir)
|
||||
tmp.close()
|
||||
return tmp.name
|
||||
|
||||
def reset(self):
|
||||
commands = ["connect", self.port, "+", "reset"]
|
||||
subprocess.run(["mpremote", *commands], check=False)
|
||||
|
||||
def deploy(self, files: list, destination_dir: str, do_reset: bool = True):
|
||||
commands = ["connect", self.port]
|
||||
|
||||
if isinstance(files, str):
|
||||
files = [files]
|
||||
|
||||
for f in files:
|
||||
bname = os.path.basename(f)
|
||||
commands += ["+", "cp", f, f":{destination_dir}/{bname}"]
|
||||
|
||||
if do_reset:
|
||||
commands += ["+", "reset"]
|
||||
|
||||
subprocess.run(["mpremote", *commands], check=True)
|
||||
|
||||
def write_file(
|
||||
self, local_filepath: str, remote_filepath: str, do_reset: bool = False
|
||||
):
|
||||
commands = [
|
||||
"connect",
|
||||
self.port,
|
||||
"+",
|
||||
"cp",
|
||||
local_filepath,
|
||||
f":{remote_filepath}",
|
||||
]
|
||||
|
||||
if do_reset:
|
||||
commands += ["+", "reset"]
|
||||
|
||||
subprocess.run(["mpremote", *commands], check=False)
|
||||
|
||||
def fetch_safe(self, remote_filepath: str):
|
||||
tmp_filepath = self.get_temp_filename()
|
||||
|
||||
commands = ["connect", self.port]
|
||||
commands += ["+", "cp", f":{remote_filepath}", tmp_filepath]
|
||||
|
||||
subprocess.run(["mpremote", *commands], check=True)
|
||||
return tmp_filepath
|
||||
|
||||
def fetch(self, files: list, destination_dir: str = "."):
|
||||
commands = ["connect", self.port]
|
||||
|
||||
if isinstance(files, str):
|
||||
files = [files]
|
||||
|
||||
for f in files:
|
||||
bname = os.path.basename(f)
|
||||
commands += ["+", "cp", f":{f}", f"{destination_dir}/{bname}"]
|
||||
|
||||
subprocess.run(["mpremote", *commands], check=True)
|
||||
|
||||
def get_config_ini(self) -> ConfigFile:
|
||||
tmpfile = self.fetch_safe("/config.ini")
|
||||
return ConfigFile(tmpfile)
|
||||
|
||||
def upload_config_ini(self, local_filepath: str, do_reset: bool = True):
|
||||
self.write_file(local_filepath, "/config.ini", do_reset)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
parser = getParser()
|
||||
args = parser.parse_args()
|
||||
ttconf = TTDBConfig()
|
||||
|
||||
cnf = None
|
||||
if args.command == CommandDefault:
|
||||
cnf = ttconf.get_config_ini()
|
||||
if args.project:
|
||||
cnf.set_default_project(args.project)
|
||||
if args.mode:
|
||||
cnf.set_default_mode(args.mode)
|
||||
|
||||
elif args.command == CommandProject:
|
||||
if not args.name:
|
||||
parser.print_help()
|
||||
print("MUST specify NAME of project this applies to using --name")
|
||||
sys.exit(1)
|
||||
|
||||
cnf = ttconf.get_config_ini()
|
||||
if args.clockrate:
|
||||
cnf.set_project_clockrate(args.name, args.clockrate)
|
||||
|
||||
if args.set_default:
|
||||
cnf.set_default_project(args.name)
|
||||
|
||||
if cnf is not None:
|
||||
cnf.save()
|
||||
ttconf.upload_config_ini(cnf.filepath, do_reset=False)
|
||||
ttconf.reset()
|
||||
Reference in New Issue
Block a user