blob: 123d3cb51a75e71d1bdeb8ce07979d352e45cbea [file] [log] [blame]
#!/usr/bin/env python3
import requests
import argparse
import os
import glob
import yaml
import logging
import sys
import csv
import re
def load_yaml(yaml_file):
with open(yaml_file, "r") as stream:
return (yaml.safe_load(stream))
def write_user_config(module_name, sources):
filename = 'user_config.tcl'
with open(os.path.join('src', filename), 'w') as fh:
fh.write("set ::env(DESIGN_NAME) {}\n".format(module_name))
fh.write('set ::env(VERILOG_FILES) "\\\n')
for line, source in enumerate(sources):
fh.write(" $::env(DESIGN_DIR)/" + source)
if line != len(sources) - 1:
fh.write(' \\\n')
fh.write('"\n')
def get_project_source(yaml):
# wokwi_id must be an int or 0
try:
wokwi_id = int(yaml['project']['wokwi_id'])
except ValueError:
logging.error("wokwi id must be an integer")
exit(1)
# it's a wokwi project
if wokwi_id != 0:
url = "https://wokwi.com/api/projects/{}/verilog".format(wokwi_id)
logging.info("trying to download {}".format(url))
r = requests.get(url)
if r.status_code != 200:
logging.warning("couldn't download {}".format(url))
exit(1)
filename = "user_module_{}.v".format(wokwi_id)
with open(os.path.join('src', filename), 'wb') as fh:
fh.write(r.content)
# also fetch the wokwi diagram
url = "https://wokwi.com/api/projects/{}/diagram.json".format(wokwi_id)
logging.info("trying to download {}".format(url))
r = requests.get(url)
if r.status_code != 200:
logging.warning("couldn't download {}".format(url))
exit(1)
with open(os.path.join('src', "wokwi_diagram.json"), 'wb') as fh:
fh.write(r.content)
return [filename, 'cells.v']
# else it's HDL, so check source files
else:
if 'source_files' not in yaml['project']:
logging.error("source files must be provided if wokwi_id is set to 0")
exit(1)
source_files = yaml['project']['source_files']
if source_files is None:
logging.error("must be more than 1 source file")
exit(1)
if len(source_files) == 0:
logging.error("must be more than 1 source file")
exit(1)
if 'top_module' not in yaml['project']:
logging.error("must provide a top module name")
exit(1)
return source_files
# documentation
def check_docs(yaml):
for key in ['author', 'title', 'description', 'how_it_works', 'how_to_test', 'language']:
if key not in yaml['documentation']:
logging.error("missing key {} in documentation".format(key))
exit(1)
if yaml['documentation'][key] == "":
logging.error("missing value for {} in documentation".format(key))
exit(1)
# if provided, check discord handle is valid
if len(yaml['documentation']['discord']):
parts = yaml['documentation']['discord'].split('#')
if len(parts) != 2 or len(parts[0]) == 0 or not re.match('^[0-9]{4}$', parts[1]):
logging.error(f'Invalid format for discord username')
exit(1)
def get_top_module(yaml):
wokwi_id = int(yaml['project']['wokwi_id'])
if wokwi_id != 0:
return "user_module_{}".format(wokwi_id)
else:
return yaml['project']['top_module']
def get_stats():
with open('runs/wokwi/reports/metrics.csv') as f:
report = list(csv.DictReader(f))[0]
print('# Routing stats')
print()
print('| Utilisation | Wire length (um) |')
print('|-------------|------------------|')
print('| {} | {} |'.format(report['OpenDP_Util'], report['wire_length']))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="TT setup")
parser.add_argument('--check-docs', help="check the documentation part of the yaml", action="store_const", const=True)
parser.add_argument('--get-stats', help="print some stats from the run", action="store_const", const=True)
parser.add_argument('--create-user-config', help="create the user_config.tcl file with top module and source files", action="store_const", const=True)
parser.add_argument('--debug', help="debug logging", action="store_const", dest="loglevel", const=logging.DEBUG, default=logging.INFO)
parser.add_argument('--yaml', help="yaml file to load", default='info.yaml')
args = parser.parse_args()
# setup log
log_format = logging.Formatter('%(asctime)s - %(module)-10s - %(levelname)-8s - %(message)s')
# configure the client logging
log = logging.getLogger('')
# has to be set to debug as is the root logger
log.setLevel(args.loglevel)
# create console handler and set level to info
ch = logging.StreamHandler(sys.stdout)
# create formatter for console
ch.setFormatter(log_format)
log.addHandler(ch)
if args.get_stats:
get_stats()
elif args.check_docs:
logging.info("checking docs")
config = load_yaml(args.yaml)
check_docs(config)
elif args.create_user_config:
logging.info("creating include file")
config = load_yaml(args.yaml)
source_files = get_project_source(config)
top_module = get_top_module(config)
assert top_module != 'top'
write_user_config(top_module, source_files)