blob: 93b48e8c8f75ed7e2f60261c3afd60143717cfab [file] [log] [blame]
#!/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 json
import os
import pprint
import re
import subprocess
from collections import defaultdict
from pathlib import Path
from common import lib_extract_from_path, version_extract_from_path, lib_extract_from_name, extract_version_and_lib_from_path, copy_file_to_output
from common import convert_libname, convert_cell_fullname, convert_pinname
Copyright_header = (
'# Copyright 2020 The SkyWater PDK Authors\n'
'#\n'
'# Licensed under the Apache License, Version 2.0 (the "License");\n'
'# you may not use this file except in compliance with the License.\n'
'# You may obtain a copy of the License at\n'
'#\n'
'# https://www.apache.org/licenses/LICENSE-2.0\n'
'#\n'
'# Unless required by applicable law or agreed to in writing, software\n'
'# distributed under the License is distributed on an "AS IS" BASIS,\n'
'# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
'# See the License for the specific language governing permissions and\n'
'# limitations under the License.\n'
'\n')
def rewrite_lef(lef_file, out_file):
lef_file = os.path.abspath(lef_file)
p = subprocess.Popen(['lefrw', '-o', out_file , lef_file], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout , stderr = p.communicate()
ret = p.wait()
if stderr != b'':
print(stderr)
return ret
#RE_MACRO = re.compile('^MACRO (!?CLASS)(?P<cellname>[\\n]*)$(?P<content>.*)^END(?P=cellname)$', re.DOTALL|re.MULTILINE)
RE_MACRO = re.compile('MACRO (?P<cellname>[^\\n]*?)$\\n(?P<content>.*?\\n?)END (?P=cellname)\\n', re.DOTALL|re.MULTILINE)
RE_FOREIGN = re.compile('^ FOREIGN (?P<cellname>.*?) ;$', re.MULTILINE)
RE_PIN = re.compile(' PIN (?P<pinname>[^\\n]*)\\n(?P<content>.*?\\n) END (?P=pinname)\\n', re.DOTALL|re.MULTILINE)
RE_PIN_TYPE = re.compile("USE ([^;]*);")
RE_PIN_DIR = re.compile("DIRECTION ([^;]*);")
def change_names_lef(new_lib, contents, extra=''):
output = []
info = {}
last_macro_endpos = 0
for macro in RE_MACRO.finditer(contents):
between = contents[last_macro_endpos:macro.start(0)]
if between.strip():
output.append(('???', between))
last_macro_endpos = macro.end(0)
old_macroname = macro.group('cellname')
old_lib, old_cellname = lib_extract_from_name(old_macroname)
if not old_cellname.startswith(extra):
assert extra not in old_cellname or 'probe' in old_cellname, (old_cellname, extra, old_macroname)
assert old_lib, (old_macroname, old_lib, old_cellname)
old_macroname = old_lib+'_'+extra+'_'+old_cellname
new_macroname = convert_cell_fullname(old_macroname, new_lib)
ext_lib, new_cell = new_macroname.split('__')
assert new_lib == ext_lib, (new_lib, ext_lib)
content = macro.group('content')
output_macro = []
output.append((new_macroname, output_macro))
output_macro.append(f'\nMACRO {new_macroname}\n')
macro_info = {}
assert new_macroname not in info, (new_macroname, info[newmacro_name])
info[new_macroname] = macro_info
last_pin_endpos = 0
for pin in RE_PIN.finditer(content):
between = content[last_pin_endpos:pin.start(0)]
if between.strip():
output_macro.append(between)
last_pin_endpos = pin.end(0)
old_pinname = pin.group('pinname')
new_pinname = convert_pinname(old_pinname)
pin_content = pin.group('content')
pin_info = {}
assert new_pinname not in pin_info, (new_pinname, pin_info[new_pinname])
macro_info[new_pinname] = pin_info
pin_dir = RE_PIN_DIR.search(pin_content)
if pin_dir:
pin_info['direction'] = pin_dir.group(1).strip()
pin_type = RE_PIN_TYPE.search(pin_content)
if pin_type:
pin_info['type'] = pin_type.group(1).strip()
if pin_info['type'] in ('POWER', 'GROUND') and 'direction' in pin_info:
del pin_info['direction']
output_macro.append(f" PIN {new_pinname}\n")
output_macro.append(pin_content)
output_macro.append(f" END {new_pinname}\n")
output_macro.append(content[last_pin_endpos:])
output_macro.append(f'END {new_macroname}\n')
return output, info
def filemain(input_path, temp_dir, final_dir):
assert not os.path.exists(temp_dir), temp_dir+" exists!"
os.makedirs(temp_dir)
global debug_print
input_file = os.path.basename(input_path)
if '_' in input_file:
input_base, _ = input_file.split('.', 1)
libname, extra = input_base.split('_', 1)
if extra == 's8p':
extra = 'lpflow'
print("Extra:", extra)
else:
extra = ''
tmp_lef = os.path.join(temp_dir, input_file)
rewrite_lef(input_path, tmp_lef)
with open(tmp_lef) as in_f:
contents = in_f.read()
out = contents
out = re.sub(r"Parsed [0-9]* number of lines!![\n]", "", out)
out = re.sub(r"MACRO\s+CLASS\s+[A-Z ]*[\n]", "", out)
out = re.sub(r"DIVIDER (?P<div>.) ;", "DIVIDERCHAR \"\g<div>\" ;", out)
out.replace(Copyright_header, '')
out = Copyright_header + out
old_lib, new_lib, ver = extract_version_and_lib_from_path(input_path)
output, info = change_names_lef(new_lib, out, extra)
header = output.pop(0)
assert header[0] == '???', header
header = header[1]
header = "\n".join(x for x in header.splitlines() if x.strip() and not x.strip().startswith('#'))
print("LEFs ----")
for m in output:
macroname, macrocontent = m
assert macroname != '???', m
tmp_file = os.path.join(temp_dir, macroname+'.lef')
assert not os.path.exists(tmp_file), tmp_file
with open(tmp_file, 'w') as f:
f.write(Copyright_header)
f.write(header)
for c in macrocontent:
f.write(c)
copy_file_to_output(tmp_file, final_dir, new_lib, ver, macroname)
print("---------")
print()
print("Pins ----")
for macroname, macrodata in sorted(info.items()):
tmp_file = os.path.join(temp_dir, macroname+'.lef.pins')
assert not os.path.exists(tmp_file), tmp_file
with open(tmp_file, 'w') as f:
for pinname, pinprop in sorted(macrodata.items()):
f.write(f"{pinname} ")
f.write(" ".join(f"{a}={b}" for a, b in sorted(pinprop.items())))
f.write("\n")
copy_file_to_output(tmp_file, final_dir, new_lib, ver, macroname)
print("---------")
def main(args, infile):
if not os.path.isfile(infile):
all_input_files = sorted(infile.rglob('*.lef'))
for f in all_input_files:
main(args, os.path.join(infile, f))
else:
path = os.path.abspath(infile)
ver = version_extract_from_path(path)
if ver is None:
ver = 'XXXX'
else:
ver = "v{}.{}.{}".format(*ver)
filename = os.path.basename(path)
tempdir = os.path.join(args.temp, 'lef_split', filename, ver)
print()
print()
print("Processing", path, "in", tempdir)
print('-'*75)
filemain(path, tempdir, str(args.output))
print('-'*75)
if __name__ == "__main__":
import doctest
fails, _ = doctest.testmod()
if fails>0:
exit()
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(
"temp",
help="The path to the temp directory",
type=Path)
args = parser.parse_args()
main(args, args.input)