blob: e5b05252817b672a11d4b997e8360504d50a24e9 [file] [log] [blame] [edit]
#!/usr/bin/env python3
# Copyright 2020 The Skywater PDK Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
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", "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/tech/logic_p.Cat", "s8phirs_10_r", "skywater_130_fd_pr", "output")
'output/skywater-pdk/libraries/skywater_130_fd_pr/V1.0.0/tech/logic_p.Cat'
>>> remap_path("../skywater-src-nda/s8/V1.0.0/VirtuosoOA/libs/technology_library/logic_p.Cat", "s8phirs_10_r", "skywater_130_fd_pr", "output")
'output/skywater-pdk/libraries/skywater_130_fd_pr/V1.0.0/technology_library/logic_p.Cat'
>>> remap_path("../skywater-src-nda/s8/V1.0.0/VirtuosoOA/libs/s8phirs_10r/s8phirs_10r.TopCat", "s8phirs_10r", "skywater_130_fd_pr", "output")
'output/skywater-pdk/libraries/skywater_130_fd_pr/V1.0.0/cells/skywater_130_fd_pr.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)
if old_path.find('/tech/') != -1:
new_path = f"{output_dir}/skywater-pdk/libraries/{new_lib}/{ver}/tech/{f_name}"
elif old_path.find('/technology_library') != -1:
new_path = f"{output_dir}/skywater-pdk/libraries/{new_lib}/{ver}/technology_library/{f_name}"
else:
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"'
"""
# debug_print("# Before")
# debug_print(contents)
contents = contents.replace(lib+'_', '')
contents = contents.replace(lib, replace_with)
# debug_print("# After")
# debug_print(contents)
# debug_print("##")
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('tech/spam', '../s8iom0s8/tech/Capacitors.Cat')
('tech/spam', 'tech/spam')
>>> remap_cells_dir_path('technology_library/spam', '../s8iom0s8/technology_library/Capacitors.Cat')
('technology_library/spam', 'technology_library/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')
new_path = "/".join(deconstr_path)
return new_path, old_path
def move_to_dir(src_dir, dest):
contents = os.listdir(src_dir)
dest = os.path.abspath(dest)
if not os.path.exists(dest):
print(f"# Creating dir: {dest}")
os.makedirs(dest)
for element in contents:
move(src_dir + '/' + 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')
''
"""
global debug
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:
if line.find('type="cell"') != -1:
line = line.lower()
old_path = line.split(" ")[0]
new_path, _ = remap_cells_dir_path(old_path, cat_path)
old_path = os.path.join(parent, old_path)
new_path = os.path.join(parent, new_path)
if not os.path.exists(old_path):
debug_print("# Line dropped as target cell was not found")
debug_print(f"# line: {line}")
debug_print(f"# path: {old_path}")
drop_line = True
else:
if old_path != new_path:
line = line.replace(old_path, new_path)
if os.path.isdir(new_path) != -1:
pass
else:
print(f"# Moving {old_path} to {new_path}")
move_to_dir(old_path, 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_base'
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)
if contents.find('type="cell"') == -1:
print("# No cells in this file, Dropping")
return
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_drc/' in f:
# print("unable to figure new_path :" + f)
#
continue
print("File: "+f)
main(f, args.output)
with open(args.sourcestodest, 'w') as srctodest:
json.dump(sourcestodest, srctodest, indent = 2)