| #!/bin/python3 |
| |
| import argparse |
| import os |
| import re |
| import json |
| from collections import defaultdict |
| from shutil import move |
| from pathlib import Path |
| from common import lib_extract_from_path, convert_libname, version_extract_from_path |
| |
| debug = False |
| |
| debug_print = lambda x: print(x) if debug else 0 |
| |
| sourcestodest = defaultdict(list) |
| |
| def remap_path(old_path, lib, new_lib, output_dir, additional = ""): |
| """ |
| >>> remap_path("../skywater-src-nda/s8/V1.0.0/VirtuosoOA/libs/s8phirs_10r/Capacitors.Cat", "s8phirs_10r", "skywater_130_fd_pr_base", "output") |
| 'output/skywater-pdk/libraries/skywater_130_fd_pr/V1.0.0/cells/Capacitors.Cat' |
| >>> remap_path("../skywater-src-nda/s8/V1.0.0/VirtuosoOA/libs/s8phirs_10r/s8phirs_10r.TopCat", "s8phirs_10r", "skywater_130_fd_pr_base", "output") |
| 'output/skywater-pdk/libraries/skywater_130_fd_pr/V1.0.0/cells/skywater_130_fd_pr_base.TopCat' |
| """ |
| output_dir = str(output_dir) |
| try: |
| ver = "V" + ".".join([str(v) for v in version_extract_from_path(old_path)]) |
| except TypeError: |
| # Version not found in path |
| ver = "" |
| |
| assert lib is not None |
| assert new_lib is not None |
| assert lib != "???" |
| assert ver != "" |
| |
| rest, f_name = os.path.split(old_path) |
| f_name = f_name.replace(lib, new_lib) |
| |
| new_path = f"{output_dir}/skywater-pdk/libraries/{new_lib}/{ver}/cells/{f_name}" |
| return new_path |
| |
| def change_naming(contents, lib, replace_with): |
| """ |
| >>> change_naming('TDMCHECKPOINT="1.0"\\nkeepEmptyCategory type="unknown"\\ns8phirs_10r/cap2 type="cell"\\ns8phirs_10r/cmimc type="cell"', 's8phirs_10r', 'cells') |
| 'TDMCHECKPOINT="1.0"\\nkeepEmptyCategory type="unknown"\\ncells/cap2 type="cell"\\ncells/cmimc type="cell"' |
| |
| """ |
| return contents.replace(lib, replace_with) |
| |
| def remap_cells_dir_path(old_path, cat_path, forbidden = []): |
| """ |
| >>> remap_cells_dir_path('cells/eg', '../s8iom0s8/Diodes.Cat') |
| ('cells/eg', 'cells/eg') |
| >>> remap_cells_dir_path('cells/eg', '../s8iom0s8_dv/Diodes.Cat') |
| ('cells/eg/dv', 'cells/eg') |
| >>> remap_cells_dir_path('cells/eg', '../s8iom0s8_dv/DoNotUse.Cat') |
| ('cells/DoNotUse/eg/dv', 'cells/eg') |
| >>> remap_cells_dir_path('cells/spam', '../s8iom0s8_dv/Capacitors.Cat', ['spam', 'eggs']) |
| ('', 'cells/spam') |
| """ |
| deconstr_path = old_path.split('/') |
| cat_name = cat_path.split('/')[-1].split(".")[-2] |
| # cell names shoulld be all lowercase |
| deconstr_path[-1] = deconstr_path[-1].lower() |
| if deconstr_path[-1] in forbidden: |
| #this was in one of DONotUse, Unsupported, Unknown, Obsoloete and should removed from normal .Cat files |
| return "", old_path |
| if cat_name.lower() in ('donotuse', 'unsupported', 'unknown', 'obsolete'): |
| deconstr_path.insert(-1, cat_name) |
| if cat_path.find('_dv/') != -1: |
| deconstr_path.append('dv') |
| return "/".join(deconstr_path), old_path |
| |
| def move_to_dir(src_dir, dest): |
| contents = os.listdir(src_dir) |
| for element in contents: |
| move(element, dest) |
| |
| |
| def check_and_move_cells(contents, cat_path, parent): |
| """ |
| >>> check_and_move_cells('cells/egg type="cell"\\ncells/spam type="cells"\\nkeepThisLine', "oa/EggAndSpam_dv/Dinner.Cat", 'V0.0.0') |
| Moving V0.0.0/cells/egg to V0.0.0/cells/egg/dv |
| Moving V0.0.0/cells/spam to V0.0.0/cells/spam/dv |
| 'cells/egg/dv type="cell"\\ncells/spam/dv type="cells"\\nkeepThisLine' |
| >>> check_and_move_cells('cells/egg type="cell"\\ncells/spam type="cells"\\nkeepThisLine', "oa/EggAndSpam/Dinner.Cat", 'V0.0.0') |
| 'cells/egg type="cell"\\ncells/spam type="cells"\\nkeepThisLine' |
| >>> check_and_move_cells('cells/Dinner.Cat type="category"\\ncells/Breakfast.Cat type="category"\\nkeepThisLine', "oa/EggAndSpam/Meals.TopCat", 'V0.0.0') |
| '' |
| """ |
| out_lines = [] |
| if cat_path.split('.')[-1] == "TopCat": |
| return '' |
| lines = contents.split('\n') |
| for line in lines: |
| drop_line = False |
| if line.find('type="') == -1 or line.find("keepEmptyCategory") != -1: |
| out_lines.append(line) |
| else: |
| old_path = line.split(" ")[0] |
| new_path, _ = remap_cells_dir_path(old_path, cat_path) |
| if old_path != new_path: |
| line = line.replace(old_path, new_path) |
| old_path = os.path.join(parent, old_path) |
| new_path = os.path.join(parent, new_path) |
| if os.path.isdir(new_path) or line.find('type="category"') != -1: |
| pass |
| else: |
| if not debug: |
| try: |
| print(old_path, new_path) |
| move_to_dir(old_path, new_path) |
| except FileNotFoundError: |
| drop_line = True |
| else: |
| print(f" Moving {old_path} to {new_path}") |
| if not drop_line: |
| out_lines.append(line) |
| return "\n".join(out_lines) |
| |
| |
| def main(cat_file, output_dir = None): |
| global sourcestodest |
| create_subdir = False |
| rest, filename = os.path.split(cat_file) |
| filename = filename.split(".")[0] |
| lib = lib_extract_from_path(cat_file) |
| new_lib = convert_libname(lib) |
| if lib == '???' and cat_file.find('/s8/') != -1: |
| lib = 's8' |
| new_lib = 'sky130_fd_pr' |
| |
| with open(cat_file, 'r') as in_f: |
| contents = in_f.read() |
| # correct old convention |
| contents = change_naming(contents, lib, 'cells') |
| if output_dir is not None: |
| new_path = remap_path(cat_file, lib, new_lib, output_dir) |
| sourcestodest[cat_file].append(new_path) |
| dir, _ = os.path.split(new_path) |
| parent, _ = os.path.split(dir) |
| contents = check_and_move_cells(contents, cat_file, parent) |
| dest_dir = os.path.split(new_path)[0] |
| if not os.path.isdir(dest_dir): |
| os.makedirs(dest_dir) |
| with open(new_path, 'w') as out_f: |
| out_f.write(contents) |
| else: |
| print(">>> Output:\n" + "_"*60 +"\n" + out) |
| |
| |
| if __name__ == "__main__": |
| import doctest |
| debug = True |
| fails, _ = doctest.testmod() |
| if fails>0: |
| exit(1) |
| else: |
| print("Tests Passed") |
| debug = False |
| parser = argparse.ArgumentParser() |
| parser.add_argument( |
| "input", |
| help="The path to the source directory/file", |
| type=Path) |
| parser.add_argument( |
| "output", |
| help="The path to the output directory", |
| type=Path) |
| parser.add_argument( |
| "sourcestodest", |
| help="output file mapping input to output", |
| type=Path) |
| args = parser.parse_args() |
| if not os.path.isdir(args.input): |
| debug = True |
| main(str(args.input)) |
| else: |
| files = sorted(args.input.rglob('*Cat')) |
| for f in files: |
| f = str(f) |
| if 'sram' in f or '_dv/' in f or '/QA_s8_rdc/' in f: |
| # print("unable to figure new_path :" + f) |
| # |
| continue |
| print("## "+f) |
| main(f, args.output) |
| with open(args.sourcestodest, 'w') as srctodest: |
| json.dump(sourcestodest, srctodest, indent = 2) |