wip configure for tt02
diff --git a/configure.py b/configure.py
index a85740c..563c1b6 100755
--- a/configure.py
+++ b/configure.py
@@ -3,239 +3,202 @@
import yaml
from typing import List
from urllib.parse import urlparse
-import argparse, requests, base64, zipfile, io, logging, pickle, shutil, sys, os, collections, subprocess
+import argparse, requests, base64, io, logging, pickle, shutil, sys, os, collections, subprocess
+from git_utils import fetch_file_from_git, install_artifacts
+import git
+
# pipe handling
from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE, SIG_DFL)
tmp_dir = '/tmp/tt'
+project_dir = 'projects'
DEFAULT_NUM_PROJECTS = 473
class Projects():
- def __init__(self, update_cache=False, update_single=None, test=False):
- self.default_project = 0
- self.test = test
- if update_cache:
- logging.info("updating cache, this could take a while")
- self.update_cache(update_single)
+ def __init__(self, args):
+ self.args = args
+ self.project_urls = self.get_project_urls()
+ assert len(self.project_urls) == DEFAULT_NUM_PROJECTS
+ logging.info(f"loaded {len(self.project_urls)} projects")
+
+ self.projects = []
+ for index, git_url in enumerate(self.project_urls):
+ if git_url == self.get_filler_project():
+ self.projects.append(Project(index, git_url, fill=True))
+ else:
+ self.projects.append(Project(index, git_url, fill=False))
+
+ if args.clone_single is not None:
+ project = self.projects[args.clone_single]
+ logging.info(f"cloning {project}")
+ project.clone()
+
+ if args.clone_all:
+ first_fill = False
+ for project in self.projects:
+ if not first_fill:
+ logging.info(f"cloning {project}")
+ project.clone()
+ first_fill = project.fill
+
+ if args.fetch_gds:
+ first_fill = False
+ for project in self.projects:
+ if not first_fill:
+ logging.info(f"installing gds for {project}")
+ project.fetch_gds()
+ first_fill = project.fill
+
+ # projects should now be installed
+ first_fill = False
+ logging.info(f"loading project yaml")
+ for project in self.projects:
+ if not first_fill:
+ project.load_yaml()
+ first_fill = project.fill
+
+ logging.info(f"copying files to caravel")
+ first_fill = False
+ for project in self.projects:
+ if not first_fill:
+ project.copy_files_to_caravel()
+ first_fill = project.fill
+
+ def get_filler_project(self):
+ if self.args.test:
+ from project_urls_test import filler_project_url
else:
- # load projects from cache
- try:
- self.wokwi_ids = pickle.load(open(self.get_projects_db(), 'rb'))
- logging.info("loaded {} projects".format(len(self.wokwi_ids)))
- except FileNotFoundError:
- logging.error("project cache {} not found, use --update-cache to build it".format(self.get_projects_db()))
-
- # all ids must be unique
- assert len(set(self.wokwi_ids)) == len(self.wokwi_ids)
-
- def update_cache(self, update_single=None):
- self.wokwi_ids = []
- for url in self.get_project_urls():
- if update_single is not None:
- if url != update_single:
- continue
- wokwi_id = self.install_artifacts(url)
- if wokwi_id in self.wokwi_ids:
- logging.error("wokwi id already exists!")
- exit(1)
- self.wokwi_ids.append(wokwi_id)
-
- # cache it
- with open(self.get_projects_db(), 'wb') as fh:
- pickle.dump(self.wokwi_ids, fh)
-
- def get_projects_db(self):
- if self.test:
- return "projects_test.pkl"
- else:
- return "projects.pkl"
-
- # filling the space with default projects is handled by returning the default on exception
- def get_macro_instance(self, id):
- try:
- return "user_module_{}_{}".format(self.wokwi_ids[id], id)
- except IndexError:
- return "user_module_{}_{}".format(self.wokwi_ids[self.default_project], id)
-
- def get_scanchain_instance(self, id):
- try:
- return "scanchain_{}".format(id)
- except IndexError:
- return "scanchain_{}".format(id)
-
- def get_wokwi_id(self, id):
- try:
- return self.wokwi_ids[id]
- except IndexError:
- return self.wokwi_ids[self.default_project]
-
- def get_macro_gds_name(self, id):
- try:
- return "user_module_{}.gds".format(self.wokwi_ids[id])
- except IndexError:
- return "user_module_{}.gds".format(self.wokwi_ids[self.default_project])
-
- def get_macro_lef_name(self, id):
- try:
- return "user_module_{}.lef".format(self.wokwi_ids[id])
- except IndexError:
- return "user_module_{}.lef".format(self.wokwi_ids[self.default_project])
-
- def get_macro_name(self, id):
- try:
- return "user_module_{}".format(self.wokwi_ids[id])
- except IndexError:
- return "user_module_{}".format(self.wokwi_ids[self.default_project])
-
- def get_verilog_include(self, id):
- try:
- return '`include "user_module_{}.v"\n'.format(self.wokwi_ids[id])
- except IndexError:
- return ''
-
- def get_gl_verilog_names(self, id):
- try:
- return ['user_module_{}.v'.format(self.wokwi_ids[id])]
- except IndexError:
- return []
-
- def get_verilog_names(self, id):
- try:
- return ["user_module_{}.v".format(self.wokwi_ids[id])]
- except IndexError:
- return []
-
- def get_wokwi_ids(self):
- return self.wokwi_ids
-
- def get_giturl(self, id):
- try:
- return self.get_project_urls()[id]
- except IndexError:
- return self.get_project_urls()[self.default_project]
-
- @classmethod
- def build_wokwi_url(Project, wokwi_id):
- return "https://wokwi.com/projects/{}".format(wokwi_id)
+ from project_urls import filler_project_url
+ return filler_project_url
def get_project_urls(self):
- if self.test:
+ if self.args.test:
from project_urls_test import project_urls, filler_project_url
else:
from project_urls import project_urls, filler_project_url
- return [filler_project_url] + project_urls
+ filler_projects = DEFAULT_NUM_PROJECTS - len(project_urls)
+ return project_urls + filler_projects * [filler_project_url]
def check_dupes(self):
- project_urls = self.get_project_urls()
+ from project_urls import project_urls
duplicates = [item for item, count in collections.Counter(project_urls).items() if count > 1]
if duplicates:
logging.error("duplicate projects: {}".format(duplicates))
exit(1)
- @classmethod
- def load_yaml(project, yaml_file):
- with open(yaml_file, "r") as stream:
- return (yaml.safe_load(stream))
+class Project():
- @classmethod
- def get_wokwi_id_from_yaml(project, yaml):
- # wokwi_id must be an int or 0
+ def __init__(self, index, git_url, fill):
+ self.git_url = git_url
+ self.index = index
+ self.fill = fill
+ self.local_dir = os.path.join(os.path.join(project_dir, str(self.index)))
+
+ def load_yaml(self):
+ with open(os.path.join(self.local_dir, 'info.yaml')) as fh:
+ self.yaml = yaml.safe_load(fh)
+ self.wokwi_id = self.yaml['project']['wokwi_id']
+ if self.wokwi_id == 0:
+ # HDL project
+ # HDL project
+ self.top_module = self.yaml['project']['top_module']
+ self.src_files = self.yaml['project']['source_files']
+
+ def clone(self):
try:
- wokwi_id = int(yaml['project']['wokwi_id'])
- return wokwi_id
- except ValueError:
- logging.error("wokwi id must be an integer")
- exit(1)
+ git.Repo.clone_from(self.git_url, self.local_dir)
+ except git.exc.GitCommandError as e:
+ if 'already exists' in str(e):
+ logging.info("already exists")
- # the latest artifact isn't necessarily the one related to the latest commit, as github
- # could have taken longer to process an older commit than a newer one.
- # so iterate through commits and return the artifact that matches
- @classmethod
- def get_most_recent_action_url(project, commits, artifacts):
- release_sha_to_download_url = {artifact['workflow_run']['head_sha']: artifact['archive_download_url'] for artifact in artifacts}
- for commit in commits:
- if commit['sha'] in release_sha_to_download_url:
- return release_sha_to_download_url[commit['sha']]
+ def update(self):
+ # do a pull
+ pass
- @classmethod
- def split_git_url(Project, url):
- res = urlparse(url)
- try:
- _, user_name, repo = res.path.split('/')
- except ValueError:
- logging.error("couldn't split repo from {}".format(url))
- exit(1)
- repo = repo.replace('.git', '')
- return user_name, repo
+ def __str__(self):
+ return f"{self.index} {self.git_url}"
- # download the artifact for each project to get the gds & lef
- def install_artifacts(self, url):
- logging.debug(url)
- user_name, repo = Projects.split_git_url(url)
+ def fetch_gds(self):
+ install_artifacts(self.git_url, self.local_dir)
- # authenticate for rate limiting
- auth_string = os.environ['GH_USERNAME'] + ':' + os.environ['GH_TOKEN']
- encoded = base64.b64encode(auth_string.encode('ascii'))
- headers = {
- "authorization" : 'Basic ' + encoded.decode('ascii'),
- "Accept" : "application/vnd.github+json",
- }
-
- # first fetch the git commit history
- api_url = 'https://api.github.com/repos/{}/{}/commits'.format(user_name, repo)
- r = requests.get(api_url, headers=headers)
- requests_remaining = int(r.headers['X-RateLimit-Remaining'])
- if requests_remaining == 0:
- logging.error("no API requests remaining")
- exit(1)
-
- commits = r.json()
-
- # now get the artifacts
- api_url = 'https://api.github.com/repos/{}/{}/actions/artifacts'.format(user_name, repo)
- r = requests.get(api_url, headers=headers)
- data = r.json()
-
- # check there are some artifacts
- if 'artifacts' not in data:
- logging.error("no artifact found for {}".format(self))
- exit(1)
+ def get_macro_instance(self):
+ print(self)
+ if self.wokwi_id == 0:
+ return self.top_module
else:
- # only get artifacts called GDS
- artifacts = [a for a in data['artifacts'] if a['name'] == 'GDS']
- logging.debug("found {} artifacts".format(len(artifacts)))
+ return f"user_module_{self.wokwi_id}"
- if len(artifacts) == 0:
- logging.error("no artifacts for this project")
- exit(1)
+ def get_scanchain_instance(self):
+ return f"scanchain_{self.index}"
- download_url = Projects.get_most_recent_action_url(commits, artifacts)
- logging.debug("download url {}".format(download_url))
+ def get_index(self):
+ return self.index
- # need actions access on the token to get the artifact
- # won't work on a pull request because they won't have the token
- r = requests.get(download_url, headers=headers)
- z = zipfile.ZipFile(io.BytesIO(r.content))
- z.extractall(tmp_dir)
+ def get_wokwi_id(self):
+ return self.wokwi_id
- # get the wokwi id
- info_yaml = Projects.load_yaml(os.path.join(tmp_dir, 'src/info.yaml'))
- wokwi_id = Projects.get_wokwi_id_from_yaml(info_yaml)
+ def get_macro_gds_name(self):
+ if self.wokwi_id == 0:
+ return f"{self.top_module}.gds"
+ else:
+ return f"user_module_{self.wokwi_id}.gds"
- logging.info("wokwi id {} github url {}".format(wokwi_id, url))
+ def get_macro_lef_name(self):
+ if self.wokwi_id == 0:
+ return f"{self.top_module}.lef"
+ else:
+ return f"user_module_{self.wokwi_id}.lef"
- # copy all important files to the correct places. Everything is dependent on the id
- files = [
- ("/tmp/tt/runs/wokwi/results/final/gds/user_module_{}.gds".format(wokwi_id), "gds/user_module_{}.gds".format(wokwi_id)),
- ("/tmp/tt/runs/wokwi/results/final/lef/user_module_{}.lef".format(wokwi_id), "lef/user_module_{}.lef".format(wokwi_id)),
- ("/tmp/tt/runs/wokwi/results/final/verilog/gl/user_module_{}.v".format(wokwi_id), "verilog/gl/user_module_{}.v".format(wokwi_id)),
- ("/tmp/tt/src/user_module_{}.v".format(wokwi_id), "verilog/rtl/user_module_{}.v".format(wokwi_id)),
- ]
+ def get_verilog_include(self):
+ if self.wokwi_id == 0:
+ # wrong
+ return f'`include "{self.top_module}.v"\n'
+ else:
+ return f'`include "user_module_{self.wokwi_id}.v"\n'
+
+ def get_gl_verilog_names(self):
+ if self.wokwi_id == 0:
+ return [f"{self.top_module}.v"]
+ else:
+ return [f'user_module_{self.index}.v']
+
+ def get_verilog_names(self):
+ if self.wokwi_id == 0:
+ files = []
+ for src in self.src_files:
+ files.append(src)
+ return files
+ else:
+ return [f'user_module_{self.wokwi_id}.v']
+
+ def get_giturl(self):
+ return self.git_url
+
+ # todo, do the source and GL files as well
+ def copy_files_to_caravel(self):
+ if self.wokwi_id == 0:
+ files = [
+ (f"projects/{self.index}/runs/wokwi/results/final/gds/{self.top_module}.gds", f"gds/{self.top_module}.gds"),
+ (f"projects/{self.index}/runs/wokwi/results/final/lef/{self.top_module}.lef", f"lef/{self.top_module}.lef"),
+ (f"projects/{self.index}/runs/wokwi/results/final/verilog/gl/{self.top_module}.v", f"verilog/gl/{self.top_module}.v"),
+ ]
+# Tholin has used * in src
+ for src in self.src_files:
+ print(src)
+ files.append((f"projects/{self.index}/src/{src}", f"verilog/rtl/{src}"))
+ else:
+ # copy all important files to the correct places. Everything is dependent on the id
+ files = [
+ (f"projects/{self.index}/runs/wokwi/results/final/gds/user_module_{self.wokwi_id}.gds", f"gds/user_module_{self.wokwi_id}.gds"),
+ (f"projects/{self.index}/runs/wokwi/results/final/lef/user_module_{self.wokwi_id}.lef", f"lef/user_module_{self.wokwi_id}.lef"),
+ (f"projects/{self.index}/runs/wokwi/results/final/verilog/gl/user_module_{self.wokwi_id}.v", f"verilog/gl/user_module_{self.wokwi_id}.v"),
+ (f"projects/{self.index}/src/user_module_{self.wokwi_id}.v", f"verilog/rtl/user_module_{self.wokwi_id}.v"),
+ ]
logging.debug("copying files into position")
for from_path, to_path in files:
@@ -243,13 +206,10 @@
shutil.copyfile(from_path, to_path)
# Uniquify the Verilog for this project
- self.uniquify_project(wokwi_id, [
- f"verilog/rtl/user_module_{wokwi_id}.v",
- ])
+# self.uniquify_project(wokwi_id, [
+# f"verilog/rtl/user_module_{wokwi_id}.v",
+# ])
- # unlink temp directory
- shutil.rmtree(tmp_dir)
- return wokwi_id
def uniquify_project(self, wokwi_id : str, rtl_files : List[str]) -> None:
"""
@@ -310,7 +270,6 @@
with open(path, "w", encoding="utf-8") as fh:
fh.writelines(new_txt)
-
class CaravelConfig():
def __init__(self, projects, num_projects):
@@ -338,7 +297,7 @@
scanchain_w = 36
num_macros_placed = 0
-
+ logging.info(self.num_projects)
# macro.cfg: where macros are placed
logging.info("creating macro.cfg")
with open("openlane/user_project_wrapper/macro.cfg", 'w') as fh:
@@ -359,26 +318,26 @@
if num_macros_placed < self.num_projects:
if orientation == 'N':
# scanchain first
- macro_instance = self.projects.get_scanchain_instance(num_macros_placed)
+ macro_instance = self.projects[num_macros_placed].get_scanchain_instance()
instance = "{} {:<4} {:<4} {}\n".format(
macro_instance, start_x + col * step_x, start_y + row * step_y, orientation
)
fh.write(instance)
- macro_instance = self.projects.get_macro_instance(num_macros_placed)
+ macro_instance = self.projects[num_macros_placed].get_macro_instance()
instance = "{} {:<4} {:<4} {}\n".format(
macro_instance, start_x + scanchain_w + col * step_x, start_y + row * step_y, orientation
)
fh.write(instance)
else:
# macro first
- macro_instance = self.projects.get_macro_instance(num_macros_placed)
+ macro_instance = self.projects[num_macros_placed].get_macro_instance()
instance = "{} {:<4} {:<4} {}\n".format(
macro_instance, start_x + col * step_x, start_y + row * step_y, orientation
)
fh.write(instance)
- macro_instance = self.projects.get_scanchain_instance(num_macros_placed)
+ macro_instance = self.projects[num_macros_placed].get_scanchain_instance()
instance = "{} {:<4} {:<4} {}\n".format(
macro_instance, start_x + (step_x - scanchain_w) + col * step_x, start_y + row * step_y, orientation
)
@@ -386,7 +345,7 @@
num_macros_placed += 1
- logging.info("total user macros placed: {}".format(num_macros_placed))
+ logging.info(f"total user macros placed: {num_macros_placed}")
# macro_power.tcl: extra file for macro power hooks
logging.info("creating macro_power.tcl")
@@ -398,10 +357,10 @@
fh.write(", \\\n")
for i in range(self.num_projects):
fh.write(" ")
- fh.write(self.projects.get_scanchain_instance(i))
+ fh.write(self.projects[i].get_scanchain_instance())
fh.write(" vccd1 vssd1 vccd1 vssd1, \\\n")
fh.write(" ")
- fh.write(self.projects.get_macro_instance(i))
+ fh.write(self.projects[i].get_macro_instance())
fh.write(" vccd1 vssd1 vccd1 vssd1")
if i != self.num_projects - 1:
fh.write(", \\\n")
@@ -412,8 +371,8 @@
lefs = []
gdss = []
for i in range(self.num_projects):
- lefs.append(self.projects.get_macro_lef_name(i))
- gdss.append(self.projects.get_macro_gds_name(i))
+ lefs.append(self.projects[i].get_macro_lef_name())
+ gdss.append(self.projects[i].get_macro_gds_name())
# can't have duplicates or OpenLane crashes at PDN
lefs = CaravelConfig.unique(lefs)
@@ -492,10 +451,9 @@
pfx = f"sw_{idx:03d}"
prev_pfx = f"sw_{idx-1:03d}" if idx > 0 else "sc"
# Pickup the Wokwi design ID and github URL for the project
- wk_id = self.projects.get_wokwi_id(idx)
- giturl = self.projects.get_giturl(idx)
- logging.debug("instance %(idx)d user_module_%(wk_id)s", { "idx" : idx,
- "wk_id": wk_id })
+ index = self.projects[idx].get_index()
+ giturl = self.projects[idx].get_giturl()
+
# Append the instance to the body
body += [
"",
@@ -503,7 +461,7 @@
f"wire {pfx}_clk_out, {pfx}_data_out, {pfx}_scan_out, {pfx}_latch_out;",
f"wire [7:0] {pfx}_module_data_in;",
f"wire [7:0] {pfx}_module_data_out;",
- f"scanchain #(.NUM_IOS(8)) {self.projects.get_scanchain_instance(idx)} (",
+ f"scanchain #(.NUM_IOS(8)) {self.projects[idx].get_scanchain_instance()} (",
f" .clk_in ({prev_pfx}_clk_out),",
f" .data_in ({prev_pfx}_data_out),",
f" .scan_select_in ({prev_pfx}_scan_out),",
@@ -520,7 +478,7 @@
# Append the user module to the body
body += [
"",
- f"user_module_{wk_id} {self.projects.get_macro_instance(idx)} (",
+ f"user_module_{index} {self.projects[idx].get_macro_instance()} (",
f" .io_in ({pfx}_module_data_in),",
f" .io_out ({pfx}_module_data_out)",
");"
@@ -551,7 +509,7 @@
# build the user_project_includes.v file - used for blackboxing when building the GDS
verilogs = []
for i in range(self.num_projects):
- verilogs.append(self.projects.get_verilog_include(i))
+ verilogs.append(self.projects[i].get_verilog_include())
verilogs = CaravelConfig.unique(verilogs)
with open('verilog/rtl/user_project_includes.v', 'w') as fh:
@@ -563,7 +521,7 @@
# build complete list of filenames for sim
verilog_files = []
for i in range(self.num_projects):
- verilog_files += self.projects.get_verilog_names(i)
+ verilog_files += self.projects[i].get_verilog_names()
verilog_files = CaravelConfig.unique(verilog_files)
with open('verilog/includes/includes.rtl.caravel_user_project', 'w') as fh:
fh.write('-v $(USER_PROJECT_VERILOG)/rtl/user_project_wrapper.v\n')
@@ -576,7 +534,7 @@
# build GL includes
verilog_files = []
for i in range(self.num_projects):
- verilog_files += self.projects.get_gl_verilog_names(i)
+ verilog_files += self.projects[i].get_gl_verilog_names()
verilog_files = CaravelConfig.unique(verilog_files)
with open('verilog/includes/includes.gl.caravel_user_project', 'w') as fh:
fh.write('-v $(USER_PROJECT_VERILOG)/gl/user_project_wrapper.v\n')
@@ -586,6 +544,8 @@
fh.write('-v $(USER_PROJECT_VERILOG)/gl/{}\n'.format(verilog))
def build_docs(self):
+ pass
+ """
logging.info("building doc index")
with open("README_init.md") as fh:
readme = fh.read()
@@ -593,21 +553,7 @@
fh.write(readme)
for wokwi_id, project_url in zip(self.projects.get_wokwi_ids(), self.projects.get_project_urls()):
fh.write("* [{}]({}) {}\n".format(wokwi_id, Projects.build_wokwi_url(wokwi_id), project_url))
-
- # requires tinytapeout_scan repo to be installed - use --recursive when cloning this repo
- # also needs a mod to sby, so probably ignore this unless you're Matt
- def formal_scan(self):
- cwd = os.getcwd()
- gl_dir = 'verilog/gl/'
- formal_dir = 'tinytapeout_scan'
- for i in range(self.num_projects):
- gl_filename = self.projects.get_gl_verilog_names(i)[0]
- shutil.copyfile(os.path.join(gl_dir, gl_filename), os.path.join(formal_dir, gl_filename))
- os.chdir(formal_dir)
- commands = ['sby', '-f', 'tinytapeout_scan.sby', gl_filename.rstrip('.v')]
- logging.info(commands)
- subprocess.run(commands, check=True)
- os.chdir(cwd)
+ """
def list(self):
count = 0
@@ -615,22 +561,18 @@
logging.info("{:3} {:20} {}".format(count, wokwi_id, project_url))
count += 1
-
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="TinyTapeout")
parser.add_argument('--list', help="list projects", action='store_const', const=True)
- parser.add_argument('--update-projects', help='fetch the project data', action='store_const', const=True)
- parser.add_argument('--update-single', help='only fetch a single repo for debug')
+ parser.add_argument('--clone-all', help="clone all projects", action="store_const", const=True)
+ parser.add_argument('--clone-single', help='only fetch a single repo for debug', type=int)
+ parser.add_argument('--fetch-gds', help='fetch gds', action='store_const', const=True)
parser.add_argument('--update-caravel', help='configure caravel for build', action='store_const', const=True)
parser.add_argument('--limit-num-projects', help='only configure for the first n projects', type=int, default=DEFAULT_NUM_PROJECTS)
parser.add_argument('--test', help='use test projects', action='store_const', const=True)
parser.add_argument('--debug', help="debug logging", action="store_const", dest="loglevel", const=logging.DEBUG, default=logging.INFO)
- # stuff for help with applying patches to all designs and re-hardening
- # parser.add_argument('--clone-all', help="clone all projects", action="store_const", const=True)
- parser.add_argument('--formal', help="formal scan proof", action="store_const", const=True)
- # parser.add_argument('--summary', help="summary", action="store_const", const=True)
args = parser.parse_args()
@@ -650,13 +592,11 @@
ch.setFormatter(log_format)
log.addHandler(ch)
- projects = Projects(update_cache=args.update_projects, test=args.test, update_single=args.update_single)
+ projects = Projects(args)
projects.check_dupes()
- caravel = CaravelConfig(projects, num_projects=args.limit_num_projects)
+ caravel = CaravelConfig(projects.projects, num_projects=args.limit_num_projects)
- if args.formal:
- caravel.formal_scan()
if args.list:
caravel.list()
diff --git a/git_utils.py b/git_utils.py
new file mode 100644
index 0000000..00c8c0d
--- /dev/null
+++ b/git_utils.py
@@ -0,0 +1,112 @@
+import base64
+from urllib.parse import urlparse
+import logging
+import requests
+import os
+import zipfile, io
+
+
+def fetch_file_from_git(git_url, path):
+ # get the basics
+ user_name, repo = split_git_url(git_url)
+
+ # authenticate for rate limiting
+ auth_string = os.environ['GH_USERNAME'] + ':' + os.environ['GH_TOKEN']
+ encoded = base64.b64encode(auth_string.encode('ascii'))
+ headers = {
+ "authorization" : 'Basic ' + encoded.decode('ascii'),
+ "Accept" : "application/vnd.github+json",
+ }
+ encoded = base64.b64encode(auth_string.encode('ascii'))
+
+ api_url = 'https://api.github.com/repos/%s/%s/contents/%s' % (user_name, repo, path)
+
+ logging.debug(api_url)
+ r = requests.get(api_url, headers=headers)
+ requests_remaining = int(r.headers['X-RateLimit-Remaining'])
+ if requests_remaining == 0:
+ logging.error("no API requests remaining")
+ exit(1)
+
+ logging.debug("API requests remaining %d" % requests_remaining)
+
+ data = r.json()
+ if 'content' not in data:
+ return None
+
+ file_content = data['content']
+
+ file_content_encoding = data.get('encoding')
+ if file_content_encoding == 'base64':
+ file_content = base64.b64decode(file_content)
+
+ return file_content
+
+# the latest artifact isn't necessarily the one related to the latest commit, as github
+# could have taken longer to process an older commit than a newer one.
+# so iterate through commits and return the artifact that matches
+def get_most_recent_action_url(commits, artifacts):
+ release_sha_to_download_url = {artifact['workflow_run']['head_sha']: artifact['archive_download_url'] for artifact in artifacts}
+ for commit in commits:
+ if commit['sha'] in release_sha_to_download_url:
+ return release_sha_to_download_url[commit['sha']]
+
+def split_git_url(url):
+ res = urlparse(url)
+ try:
+ _, user_name, repo = res.path.split('/')
+ except ValueError:
+ logging.error(f"couldn't split repo from {url}")
+ exit(1)
+ repo = repo.replace('.git', '')
+ return user_name, repo
+
+# download the artifact for each project to get the gds & lef
+def install_artifacts(url, directory):
+ logging.debug(url)
+ user_name, repo = split_git_url(url)
+
+ # authenticate for rate limiting
+ auth_string = os.environ['GH_USERNAME'] + ':' + os.environ['GH_TOKEN']
+ encoded = base64.b64encode(auth_string.encode('ascii'))
+ headers = {
+ "authorization" : 'Basic ' + encoded.decode('ascii'),
+ "Accept" : "application/vnd.github+json",
+ }
+
+ # first fetch the git commit history
+ api_url = f'https://api.github.com/repos/{user_name}/{repo}/commits'
+ r = requests.get(api_url, headers=headers)
+ requests_remaining = int(r.headers['X-RateLimit-Remaining'])
+ if requests_remaining == 0:
+ logging.error("no API requests remaining")
+ exit(1)
+
+ commits = r.json()
+
+ # now get the artifacts
+ api_url = f'https://api.github.com/repos/{user_name}/{repo}/actions/artifacts'
+ r = requests.get(api_url, headers=headers)
+ data = r.json()
+
+ # check there are some artifacts
+ if 'artifacts' not in data:
+ logging.error(f"no artifact found for {url}")
+ exit(1)
+ else:
+ # only get artifacts called GDS
+ artifacts = [a for a in data['artifacts'] if a['name'] == 'GDS']
+ logging.debug(f"found {len(artifacts)} artifacts")
+
+ if len(artifacts) == 0:
+ logging.error("no artifacts for this project")
+ exit(1)
+
+ download_url = get_most_recent_action_url(commits, artifacts)
+ logging.debug(f"download url {download_url}")
+
+ # need actions access on the token to get the artifact
+ # won't work on a pull request because they won't have the token
+ r = requests.get(download_url, headers=headers)
+ z = zipfile.ZipFile(io.BytesIO(r.content))
+ z.extractall(directory)
diff --git a/project_urls.py b/project_urls.py
index 03a0f11..5cc2e5f 100644
--- a/project_urls.py
+++ b/project_urls.py
@@ -1,156 +1,25 @@
-filler_project_url = 'https://github.com/mattvenn/wokwi_filler'
+filler_project_url = 'https://github.com/TinyTapeout/tt02-test-straight'
project_urls = [
- 'https://github.com/mattvenn/tinytapeout_m_segments', # already patched
- 'https://github.com/gregdavill/tinytapeout_spin0',
- 'https://github.com/mole99/wokwi-1bit-alu',
- 'https://github.com/ericsmi/tinytapeout_popcnt.git',
- 'https://github.com/krasin/wokwi-guess-my-number',
- 'https://github.com/johshoff/barrelshifter-wokwi-gds',
- 'https://github.com/pretentious7/tinytapeout',
- 'https://github.com/GuzTech/wokwi-ripple-carry-adder',
- 'https://github.com/kbeckmann/tinytapeout_kbeckmann1',
- 'https://github.com/H-S-S-11/tinytapeout-verilog-test',
- 'https://github.com/skerr92/tinytapeout_frequency_div',
- 'https://github.com/argunda/tinytapeout_dualedgedetector',
- 'https://github.com/libokuohai/tinytapeout-2022-08',
- 'https://github.com/jglim/tinytapeout_bcd-dec',
- 'https://github.com/jglim/tinytapeout_bcd-7seg',
- 'https://github.com/tkuester/wokwi-directghost',
- 'https://github.com/shahzaibk23/tinytapeout-barrel-shifter',
- 'https://github.com/tcptomato/tinytapeout',
- 'https://github.com/DaveyPocket/chaser',
- 'https://github.com/GuzTech/tinytapeout-4x4-multiplier',
- 'https://github.com/derhexenmeister/tinytapeout_nco',
- 'https://github.com/mbalestrini/tinytapeout_rgb_lut_test',
- 'https://github.com/derhexenmeister/tinytapeout_updwnbcd',
- 'https://github.com/bradysalz/pll_tiny_tapeout_demo',
- 'https://github.com/pramitpal/tinytapeout_pramit',
- 'https://github.com/gregdavill/tinytapeout-verilog-fifo',
- 'https://github.com/gregdavill/tinytapeout-wokwi-74x1G00',
- 'https://github.com/gregdavill/tinytapeout-wokwi-74x1G02',
- 'https://github.com/gregdavill/tinytapeout-wokwi-74xG198',
- 'https://github.com/gregdavill/tinytapeout-verilog-7seg-clock',
- 'https://github.com/alanvgreen/tinytapeout4bitadder',
- 'https://github.com/benlaurie/twistedringcounter',
- 'https://github.com/sureshsugumar/tinytapeout_counter',
- 'https://github.com/daniestevez/tinytapeout-verilog',
- 'https://github.com/pkuligowski/tinytapeout_tmr',
- 'https://github.com/chiplet/tinytapeout-snake',
- 'https://github.com/derhexenmeister/tinytapeout_pwm',
- 'https://github.com/raha96/tinycharacters-locked',
- 'https://github.com/nathancheek/tinytapeout-loop',
- 'https://github.com/andars/universal-turing-machine-w5s8', # dup id
- 'https://github.com/vmunoz82/tinytapeout_euler1',
- 'https://github.com/mikenet213/mikenet213-tt1-verilog',
- 'https://github.com/veremenko-y/tinytapeout-ue14500',
- 'https://github.com/mikenet213/mikenet213-tt2-verilog',
- 'https://github.com/aiunderstand/tinytapeout_asyncbinterconvcomp.git',
- 'https://github.com/smunaut/tinytapeout-fifo', # already patched
- 'https://github.com/nwtechguy/tinytapeout_BCD_counter',
- 'https://github.com/kambadur/bcd_to_7seg',
- 'https://github.com/bieganski/tinytapeout_bieganski',
- 'https://github.com/TomKeddie/tinytapeout-2022-1',
- 'https://github.com/r-a-hoggarth/tinytapeGaloisLFSR',
- 'https://github.com/adamgreig/tinytapeout-prn',
- 'https://github.com/ianloic/tinytapeout-1',
- 'https://github.com/sad-electronics/tinytapeout-clock-divider-asic',
- 'https://github.com/gatecat/tinytapeout-lutram-test',
- 'https://github.com/tommythorn/tinytapeout-4-bit-cpu',
- 'https://github.com/wokwi/tt-game-of-life-cell-popcnt',
- 'https://github.com/gatecat/tinytapeout-srlut-test',
- 'https://github.com/AdDraw/tinytapeout_demo',
- 'https://github.com/cpldcpu/tinydice',
- 'https://github.com/cpldcpu/tinytapeout_mcpu6bit',
- 'https://github.com/azzeloof/tinytapeout-counter',
- #'https://github.com/georgerennie/tinytapeout-verilog-async-arb', # can't build - replace
- 'https://github.com/mattvenn/tinytapeout-341802655228625490',
- 'https://github.com/mwelling/led-blaster',
- 'https://github.com/mwelling/figure-8',
- 'https://github.com/gatecat/tinytapeout-fpga-test', # already patched
- 'https://github.com/cfib/trafficlight-fsm',
- 'https://github.com/clj/tinytapeout-verilog-7seg-figure-eight',
- 'https://github.com/smunaut/tinytapeout-misc-1', # already patched
- 'https://github.com/regymm/tinytapeout-funnyblinky', # fails openlane doesn't fit, has messed with config.tcl
- 'https://github.com/Sirawit7205/tinytapeout-2G57-2G58',
- 'https://github.com/Sirawit7205/tinytapeout-2G97-2G98',
- 'https://github.com/hosein-mokarian/tinytapeout_counter_3to8_decoder',
- 'https://github.com/burtyb/srld',
- 'https://github.com/Mahnoor-ismail01/tinytapeout-chromatic-3-to-8-Decoder',
- 'https://github.com/Shahzaib2028/tinytapeout-4to2Encoder-2to4Decoder',
- 'https://github.com/sfmth/tinytapeout-tinycordic',
- 'https://github.com/mm21/tinytapeout-led-matrix',
- 'https://github.com/jeanthom/tinytapout-lock',
- 'https://github.com/AidanMedcalf/tinytapeout-tinyio',
- 'https://github.com/ElectricPotato/tinytapeout-hello-world-uart',
- 'https://github.com/abdullahkhalids/TinyTapeout-hamming-code',
- 'https://github.com/hossein1387/tinytapeout-verilog-test',
- 'https://github.com/ChrisPVille/tinytapeout-FROG4bitCPU', # fails patch, easy fix
- 'https://github.com/Talha-Ahmed-1/tinytapeout_flop_regfile',
- 'https://github.com/skylersaleh/tinytapeout-hello',
- 'https://github.com/proppy/tinytapeout-xls-popcount',
- 'https://github.com/proppy/tinytapeout-xls-popcount-bithacks',
- 'https://github.com/proppy/tinytapeout-xls-inverter',
- 'https://github.com/mark64/tinytapeout',
- 'https://github.com/dave-roo/ddcomparatorandro',
- 'https://github.com/splinedrive/tinytapeout-verilog-4x4-multiplier',
- 'https://github.com/ThorKn/tinytapeout_shiftregister_8bit',
- 'https://github.com/UDXS/tinytapeout-sqrt',
- 'https://github.com/coralmw/tinytapeout-css-feedback',
- 'https://github.com/ericsmi/tinytapeout-verilog-div3', # fails openlane hangs forever, works with pdk7 and updated area
- 'https://github.com/fluxfocus/jdtt-logic1.git',
- 'https://github.com/anm/nyasic',
- 'https://github.com/aiunderstand/tinytapeout_bintristateloadablecounter',
- 'https://github.com/ThorKn/tinytapeout_shiftregister_challenge',
- 'https://github.com/regymm/tinytapeout-mcpi', # openlane fail density error, has messed with config.tcl
- 'https://github.com/todd1251/tinytapeout-figure8',
- 'https://github.com/CyberGai/tinytapeout-bcd-counter',
- 'https://github.com/georgeyhere/tinytapeout-dice-roller',
- 'https://github.com/nayanesh-reddy/2-Bit_Add_Mul_Comp',
- 'https://github.com/ryancor/half_addr_asic',
- 'https://github.com/hovind/tinytapeout-verilog-test',
- 'https://github.com/siriusm46/tinytapeout_bcd_decimal',
- 'https://github.com/cpldcpu/tinytapeout_mcpu5',
- 'https://github.com/goran-mahovlic/tinytapeout-verilog-piano',
- 'https://github.com/andars/universal-turing-machine-aw7s8',
- 'https://github.com/marcusmueller/hamming74-tapeout',
- 'https://github.com/13arn/tinytapeout_counter_steamdeck',
- 'https://github.com/johshoff/tinytapeout-verilog',
- 'https://github.com/cy384/seven-segment-with-adder',
- 'https://github.com/georgerennie/tinytapeout-wokwi-cd4518',
- 'https://github.com/ElectricPotato/tinytapeout-picture-printer-b',
- 'https://github.com/theFestest/tinytapeout-simple-invert8',
- 'https://github.com/ArsenioDev/CustomSiliconTest',
- 'https://github.com/mgargano/tinytapeout_alu_with_4bit_7segmetdisplay_decoder',
- 'https://github.com/theFestest/tinytapeout-4x4-ram',
- 'https://github.com/michael-christen/wokwi-verilog-asic-experiment',
- 'https://github.com/craigcc-frii/tinytapeout_craig',
- 'https://github.com/youngpines/r2rdac_tinytapeout_demo',
- 'https://github.com/toybuilder/learn-tinytapeout',
- 'https://github.com/eggsactly/tinytapeout_demo',
- 'https://github.com/gsegura96/tinytapeout-chisel',
- 'https://github.com/abf149/fbna_like_verilog_abf149',
- 'https://github.com/MC-SecPat/tinytapeout_chi2shares',
- 'https://github.com/MC-SecPat/tinytapeout_chi3shares',
- 'https://github.com/Adil8442/tiny_tapeout_test',
- #'https://github.com/MC-SecPat/tinytapeout_chiDOM', # remove, - empty project
- 'https://github.com/mattvenn/tinytapeout-341802448429515346', # filler
- 'https://github.com/r4d10n/tinytapeout-HELLo-3orLd-7seg',
- 'https://github.com/proppy/tinytapeout-xls-graydec',
- 'https://github.com/prabaldutta/tinytapeout_adi',
- 'https://github.com/maehw/wokwi-verilog-gds-wolf-goat-cabbage',
- 'https://github.com/ThorKn/tinytapeout_pattern_player',
- 'https://github.com/rigobertoruiz98/cts_fsm',
- 'https://github.com/rajarshiroy/tinytapout0_rajarshi',
- 'https://github.com/BarsMonster/MicroASIC',
-# 'https://github.com/kammoh/tinytapeout-chisel', # wrong id, hasn't merged PR
- 'https://github.com/cpldcpu/TinyTapeout_TrainLED',
- 'https://github.com/malkam03/tinytapeout-game-of-life',
- 'https://github.com/BarsMonster/MicroAsicV',
- 'https://github.com/maehw/wokwi-verilog-gds-lowspeed-tiny-uart',
- 'https://github.com/smunaut/tinytapeout-smolram', # already patched
- 'https://github.com/sirejdua/6bit-cellular-automata-tinytapeout',
- 'https://github.com/DuaneSand/TinyTapeout-Hello',
- 'https://github.com/tzachari/tinytapeout-lab11',
- 'https://github.com/mattvenn/tinytapeout-marc',
- 'https://github.com/mattvenn/tinytapeout-laura', # already patched
+ "https://github.com/maehw/tt02-bcd-7segment-encoder",
+ "https://github.com/ekliptik/tt02-chase-the-beat",
+ "https://github.com/leardilap/tt02-LUTRAM",
+ "https://github.com/steieio/tt02-submission-universal-sr",
+ "https://github.com/Tschucker/tt02-submission-tiny-fir",
+ "https://github.com/moyesw/tt02-moyesw-StreamIntegrator",
+ "https://github.com/RiceShelley/tiny-fft",
+ #"https://github.com/89Mods/tt2-AvalonSemi-5401",
+ 'https://github.com/TinyTapeout/tt02-test-straight',
+ "https://github.com/svd321/tt02-Ising",
+ "https://github.com/JensIMS/tt02-trafficlight",
+ "https://github.com/jar/tt02_sram",
+ "https://github.com/justinP-wrk/tt02-TinySensor",
+ "https://github.com/azdle/binary-clock-asic",
+ "https://github.com/AidanGood/tt02-McCoy",
+ "https://github.com/ryancor/tt02-submission-template",
+ "https://github.com/grayresearch/tt02-s4ga",
+ "https://github.com/migcorre/tt02-dc",
+ "https://github.com/loxodes/tt02-submission-loxodes",
+ "https://github.com/chrisruk/matrixchip",
+ "https://github.com/TomKeddie/tinytapeout-2022-2",
+ "https://github.com/Fraserbc/tt02-simon",
]