From 5e9fff900e8d30d3fb538f55b3f0f765208e2d43 Mon Sep 17 00:00:00 2001 From: Uri Shaked Date: Mon, 8 Sep 2025 16:02:33 +0300 Subject: [PATCH] cleanup: remove legacy code --- discord_bot.py | 101 ------------------- gds_compare.py | 27 ----- markdown_utils.py | 26 ----- reharden.py | 250 ---------------------------------------------- tt_annotate.py | 38 ------- 5 files changed, 442 deletions(-) delete mode 100755 discord_bot.py delete mode 100644 gds_compare.py delete mode 100644 reharden.py delete mode 100755 tt_annotate.py diff --git a/discord_bot.py b/discord_bot.py deleted file mode 100755 index 367097f..0000000 --- a/discord_bot.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys - -import discord -import yaml - -TOKEN = os.environ["DISCORD_TOKEN"] -TARG = os.environ["TARGET_SERVER"] -ROLE_ID = os.environ["ROLE_ID"] - -intents = discord.Intents.default() -intents.members = True - -client = discord.Client(intents=intents) -exit_code = 0 - - -@client.event -async def on_ready(): - global exit_code - try: - print(f"{client.user} has connected to Discord!") - server = None - for g in client.guilds: - if g.name == TARG: - server = g - break - if server is None: - print(f"Error: Bot is not in a server named {TARG}") - exit_code = 1 - await client.close() - - role = server.get_role(int(ROLE_ID)) - if not role: - print("Error: requested role does not exist on server!") - exit_code = 1 - await client.close() - - participants = [] - for project in os.listdir("./projects"): - info_file = os.path.join("projects", project, "info.yaml") - if not os.path.exists(info_file): - print(f"info.yaml does not exist for project {project}. Skipping.") - else: - try: - stream = open(info_file, "r") - data = yaml.load(stream, Loader=yaml.FullLoader) - stream.close() - if "discord" in data["documentation"]: - dname = data["documentation"]["discord"] - if len(dname): - if "#" in dname: - dname = dname.split("#")[0] - # print(f'adding {dname}') - participants.append(dname) - else: - print( - f"Project {project} has no discord username listed. Skipping." - ) - except Exception as ex: - print(f"Error parsing info.yaml for project {project}. Skipping.") - print(f"Error was: {ex}") - - members = [] - for p in participants: - for m in server.members: - if m.name == p: - members.append(m) - break - if m.global_name == p: - members.append(m) - break - else: - print(f"{p} not found in members") - - new_role_members = [] - for m in members: - if len([d for d in m.roles if d.id == role.id]) == 0: - print(f"Giving the role to {m.name}") - new_role_members.append(m.name) - await m.add_roles( - role, reason="Automatic role assignment by GitHub actions pipeline." - ) - else: - print(f"{m.name} already has role") - - print("All done!") - print("gave new role to:") - print("@" + " @".join(new_role_members)) - await client.close() - except Exception as ex: - print("Exception was thrown!") - print(ex) - exit_code = 1 - await client.close() - - -client.run(TOKEN) -sys.exit(exit_code) diff --git a/gds_compare.py b/gds_compare.py deleted file mode 100644 index 4a5644f..0000000 --- a/gds_compare.py +++ /dev/null @@ -1,27 +0,0 @@ -# Usage: -# klayout -b -r gds_compare.py -rd gds1=file1.gds -rd gds2=file2.gds - -import sys - -import pya - - -def compare_gds(file1, file2): - diff = pya.LayoutDiff() - - # Load the layouts - layout1 = pya.Layout() - layout1.read(file1) - - layout2 = pya.Layout() - layout2.read(file2) - - # Check if the layouts are identical - return diff.compare(layout1, layout2) - - -if __name__ == "__main__": - if compare_gds(gds1, gds2): # noqa: F821 - sys.exit(0) - else: - sys.exit(-1) diff --git a/markdown_utils.py b/markdown_utils.py index 970715b..332b49e 100644 --- a/markdown_utils.py +++ b/markdown_utils.py @@ -1,6 +1,5 @@ import math import os -import shutil from typing import Any, Dict import mistune @@ -53,28 +52,3 @@ class ImagePathRewriterRenderer(MarkdownRenderer): def rewrite_image_paths(source: str, prefix: str) -> str: markdown = mistune.create_markdown(renderer=ImagePathRewriterRenderer(prefix)) return unescape_braces(str(markdown(source))) - - -class WebsiteImagePathRewriterRenderer(MarkdownRenderer): - def __init__(self, source_dir: str, target_dir: str): - super().__init__() - self.source_dir = source_dir - self.target_dir = target_dir - - def image(self, token, state): - url = token["attrs"]["url"] - if "%7B" in url: - pass - elif ".." in url: - url = "" - elif "://" not in url and not url.startswith("/"): - filename = os.path.basename(url) - if url.startswith("/"): - url = "../" + url[1:] - shutil.copyfile( - os.path.join(self.source_dir, url), - os.path.join(self.target_dir, filename), - ) - url = f"images/{filename}" - token["attrs"]["url"] = url - return super().image(token, state) diff --git a/reharden.py b/reharden.py deleted file mode 100644 index dbe5ef7..0000000 --- a/reharden.py +++ /dev/null @@ -1,250 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import csv -import datetime -import glob -import json -import logging -import os -import re -import sys -from typing import List - -import git - -ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) -PROJECTS_DIR = os.path.join(ROOT, "projects") -REHARDEN_DIR = os.path.join(ROOT, "reharden") - - -def load_metrics(path): - try: - with open(os.path.join(path, "runs/wokwi/reports/metrics.csv")) as fh: - metrics = next(csv.DictReader(fh)) - return metrics - except FileNotFoundError: - logging.warning(f"no metrics found for {path}") - return None - - -# get cell count from synth report -def get_cell_count_from_synth(path): - num_cells = 0 - try: - yosys_report = ( - f"{path}/runs/wokwi/reports/synthesis/1-synthesis.AREA_0.stat.rpt" - ) - with open(yosys_report) as fh: - for line in fh.readlines(): - m = re.search(r"Number of cells:\s+(\d+)", line) - if m is not None: - num_cells = int(m.group(1)) - - except IndexError: - logging.warning(f"couldn't open yosys cell report for cell checking {path}") - return 0 - except FileNotFoundError: - logging.warning(f"no cell count found for {path}") - return 0 - - return num_cells - - -def get_cell_counts_from_gl(path): - cell_count = {} - total = 0 - - try: - gl_path = glob.glob( - os.path.join(path, "runs/wokwi/results/final/verilog/gl/*.nl.v") - )[0] - except IndexError: - logging.warning("no gl cell count found") - return 0 - with open(gl_path) as fh: - for line in fh.readlines(): - m = re.search(r"sky130_(\S+)__(\S+)_(\d+)", line) - if m is not None: - total += 1 - cell_lib = m.group(1) - cell_name = m.group(2) - cell_drive = m.group(3) - assert cell_lib in ["fd_sc_hd", "ef_sc_hd"] - assert int(cell_drive) > 0 - try: - cell_count[cell_name] += 1 - except KeyError: - cell_count[cell_name] = 1 - return cell_count - - -def build_metrics(shuttle_index): - total_seconds = 0 - total_wire_length = 0 - total_wires_count = 0 - total_physical_cells = 0 - max_cells = 0 - min_cells = 1000 - max_cell_project = None - max_util = 0 - min_util = 100 - max_util_project = None - - for project in shuttle_index["projects"]: - macro: str = project["macro"] - repo_dir = os.path.join(REHARDEN_DIR, macro) - metrics = load_metrics(repo_dir) - if metrics is None: - continue - - try: - dt = datetime.datetime.strptime(metrics["total_runtime"][:-3], "%Hh%Mm%Ss") - except KeyError: - continue - - delt = datetime.timedelta(hours=dt.hour, minutes=dt.minute, seconds=dt.second) - total_seconds += delt.total_seconds() - - total_wire_length += int(metrics["wire_length"]) - total_wires_count += int(metrics["wires_count"]) - util = float(metrics["OpenDP_Util"]) - num_cells = get_cell_count_from_synth(repo_dir) - total_physical_cells += num_cells - - if num_cells > max_cells: - max_cells = num_cells - max_cell_project = project - if num_cells < min_cells: - min_cells = num_cells - - if util > max_util: - max_util = util - max_util_project = project - if util < min_util: - min_util = util - - logging.info(f"build time for all projects {total_seconds / 3600} hrs") - logging.info(f"total wire length {total_wire_length} um") - logging.info(f"total cells {total_physical_cells}") - logging.info(f"max cells {max_cells} for project {max_cell_project}") - logging.info(f"min cells {min_cells}") - logging.info(f"max util {max_util} for project {max_util_project}") - logging.info(f"min util {min_util}") - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="TT reharden tool") - parser.add_argument( - "--debug", - help="debug logging", - action="store_const", - dest="loglevel", - const=logging.DEBUG, - default=logging.INFO, - ) - parser.add_argument( - "--clone", help="clone the repos", action="store_const", const=True - ) - parser.add_argument( - "--harden", help="harden the projects", action="store_const", const=True - ) - parser.add_argument("--start-from", help="start from", type=int, default=0) - parser.add_argument("--end-at", help="end at", type=int, default=100000) - parser.add_argument("--build-metrics", action="store_const", const=True) - - 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) - - shuttle_index = json.load(open("shuttle_index.json")) - differences: List[str] = [] - for number, project in enumerate(shuttle_index["projects"]): - if number < args.start_from: - continue - if number > args.end_at: - continue - - repo = project["repo"] - commit = project["commit"] - macro = project["macro"] - repo_dir = os.path.join(REHARDEN_DIR, macro) - - if macro == "tt_um_chip_rom": - logging.info(f"[{number :03}] skipping chip ROM") - continue - - if "analog_pins" in project: - logging.info(f"[{number :03}] skipping analog project {macro}") - continue - - if args.clone: - if not os.path.exists(repo_dir): - logging.info(f"cloning {number :03} {repo}") - git.Repo.clone_from(repo, repo_dir) - cloned_repo = git.Repo(repo_dir) - cloned_repo.git.submodule("update", "--init") - cloned_repo.git.checkout(commit) - else: - cloned_repo = git.Repo(repo_dir) - if commit != cloned_repo.commit().hexsha: - logging.info(f"updating {number :03} {repo} to commit {commit}") - cloned_repo.git.fetch("origin") - cloned_repo.git.checkout(commit) - cloned_repo.git.submodule("update", "--init") - - # get tt tools setup. - # can't run tt from another directory because gitpython fails - # can't run symlinked because openlane fails - # simple copy won't work because of tt is a submodule and not a standalone repo. - tt_tools_path = os.path.join(repo_dir, "tt") - if not os.path.exists(tt_tools_path): - logging.info(f"cloning tt to {tt_tools_path}") - git.Repo.clone_from("tt", tt_tools_path, depth=1) - - if args.harden: - logging.info(f"hardening {number :03} {repo_dir}") - cwd = os.getcwd() - os.chdir(repo_dir) - new_gds = f"runs/wokwi/results/final/gds/{macro}.gds" - prev_commit = None - if os.path.isfile("commit.txt"): - with open("commit.txt", "r") as f: - prev_commit = f.read().strip() - if not os.path.exists(new_gds) or prev_commit != commit: - os.system(f"tt/tt_tool.py --create-user-config > reharden.log") - res = os.system(f"tt/tt_tool.py --harden --debug >> reharden.log") - if res != 0: - logging.warning(f"failed to harden {macro}") - else: - with open("commit.txt", "w") as f: - f.write(commit) - orig_gds = os.path.join(PROJECTS_DIR, f"{macro}/{macro}.gds") - logging.info(f"Comparing {orig_gds} with {new_gds}") - res = os.system( - f"klayout -b -r tt/gds_compare.py -rd 'gds1={orig_gds}' -rd 'gds2={new_gds}'" - ) - if res != 0: - logging.warning(f"GDS compare failed for {macro}") - differences.append(macro) - os.chdir(cwd) - - if args.harden: - print(f"Found total of {len(differences)} different projects: ") - for diff in differences: - print("- ", diff) - - if args.build_metrics: - build_metrics(shuttle_index) diff --git a/tt_annotate.py b/tt_annotate.py deleted file mode 100755 index 14faae8..0000000 --- a/tt_annotate.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 - -from PIL import Image, ImageDraw, ImageFont - -img = Image.open("pics/tinytapeout.png").convert("RGBA") -overlay = Image.new("RGBA", img.size, (0, 0, 0, 0)) -draw = ImageDraw.Draw(overlay) -font = ImageFont.truetype("/usr/share/fonts/dejavu/DejaVuSans.ttf", 30) - -# coordinate mapping -xmap = ((88, 272), (2676, 1646)) # two pairs of (chip x coord, picture x coord) -ymap = ((95, 1879), (3240, 213)) # two pairs of (chip y coord, picture y coord) -xshift, yshift = 40, -43 # text position relative to macro origin - -for line in open("openlane/user_project_wrapper/macro.cfg"): - macro, x, y, orient = line.split() - if macro == "scan_controller": - continue - elif macro.startswith("scanchain_"): - continue - name, num = macro.rsplit("_", 1) - num, x, y = int(num), int(x), int(y) - x = int( - (x - xmap[0][0]) / (xmap[1][0] - xmap[0][0]) * (xmap[1][1] - xmap[0][1]) - + xmap[0][1] - ) - y = int( - (y - ymap[0][0]) / (ymap[1][0] - ymap[0][0]) * (ymap[1][1] - ymap[0][1]) - + ymap[0][1] - ) - x += xshift - y += yshift - msg = str(num) - w, h = draw.textsize(msg, font=font) - draw.text((x - w / 2, y - h / 2), msg, fill=(0, 0, 0, 255), font=font) - -combined = Image.alpha_composite(img, overlay).convert("RGB") -combined.save("pics/tinytapeout_numbered.png")