blob: 28c8133f256bfeac3107cf1cb927953c0acb11c5 [file] [log] [blame] [edit]
#!/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)