Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

changed LVS config from shell to json #204

Merged
merged 7 commits into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions check_manager/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def run(self):
return self.result


class lvs(CheckManager):
class Lvs(CheckManager):
__ref__ = 'lvs'
__surname__ = 'LVS'
__supported_pdks__ = ['sky130A', 'sky130B']
Expand All @@ -147,7 +147,7 @@ def __init__(self, precheck_config, project_config):
self.design_name = "user_analog_project_wrapper"
else:
self.design_name = "user_project_wrapper"
self.config_file = self.precheck_config['input_directory'] / f"lvs/{self.design_name}/sky130A.lvs_config.sh"
self.config_file = self.precheck_config['input_directory'] / f"lvs/{self.design_name}/lvs_config.json"
self.pdk_root = precheck_config['pdk_path'].parent
self.pdk = precheck_config['pdk_path'].name

Expand Down Expand Up @@ -424,7 +424,7 @@ def run(self):
(KlayoutMetalMinimumClearAreaDensity.__ref__, KlayoutMetalMinimumClearAreaDensity),
(KlayoutPinLabelPurposesOverlappingDrawing.__ref__, KlayoutPinLabelPurposesOverlappingDrawing),
(KlayoutZeroArea.__ref__, KlayoutZeroArea),
(lvs.__ref__, lvs),
(Lvs.__ref__, Lvs),
])

# Note: list of checks for a private project
Expand All @@ -440,7 +440,7 @@ def run(self):
(KlayoutMetalMinimumClearAreaDensity.__ref__, KlayoutMetalMinimumClearAreaDensity),
(KlayoutPinLabelPurposesOverlappingDrawing.__ref__, KlayoutPinLabelPurposesOverlappingDrawing),
(KlayoutZeroArea.__ref__, KlayoutZeroArea),
(lvs.__ref__, lvs),
(Lvs.__ref__, Lvs),
])


Expand Down
97 changes: 82 additions & 15 deletions checks/lvs_check/lvs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,92 @@
import os
from pathlib import Path
from datetime import datetime
import json
import re

def is_valid(string):
if string.startswith("/"):
return False
else:
return True

def is_path(string):
if "/" in string:
return True
else:
return False

def replace_env_var(value, lvs_env):
words = re.findall(r'\$\w+', value)
for w in words:
env_var = w.split("$")[1]
if env_var in lvs_env:
if not is_path(value):
value = value.replace(w, lvs_env.get(env_var))
else:
value = os.path.join(os.path.dirname(value), os.path.splitext(value)[0].replace(w, lvs_env.get(env_var)) + os.path.splitext(value)[1])
else:
logging.error(f"Could not resolve environment variable {w}")
return False
return value

def parse_config_file(json_file, lvs_env):
with open(json_file, "r") as f:
data = json.load(f)
for key, value in data.items():
if type(value) == list:
exports = []
for val in value:
if is_valid(val):
if "$" in val:
val = replace_env_var(val, lvs_env)
if val is False:
return False
exports.append(val)
else:
logging.error(f"{val} is an absolute path, paths must start with $PDK_ROOT or $UPRJ_ROOT")
return False
lvs_env[key] = ' '.join(exports)
else:
if is_valid(value):
if "$" in value:
value = replace_env_var(value, lvs_env)
if value is False:
return False
lvs_env[key] = value
else:
logging.error(f"{val} is an absolute path, paths must start with $PDK_ROOT or $UPRJ_ROOT")
return False
return True

def run_lvs(design_directory, output_directory, design_name, config_file, pdk_root, pdk):
tag = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
if not os.path.isdir(f"{design_directory}/lvs/{design_name}/lvs_results"):
os.mkdir(f"{design_directory}/lvs/{design_name}/lvs_results")
if not os.path.isdir(f"{design_directory}/lvs/{design_name}/lvs_results/{tag}"):
os.mkdir(f"{design_directory}/lvs/{design_name}/lvs_results/{tag}")
logs_directory = output_directory / 'logs'
log_file_path = logs_directory / 'be_check.log'
if not os.path.isdir(logs_directory):
os.mkdir(logs_directory)
os.environ['UPRJ_ROOT'] = f"{design_directory}"
os.environ['LVS_ROOT'] = f'{os.getcwd()}/checks/lvs_check/'
os.environ['WORK_ROOT'] = f"{design_directory}/lvs/{design_name}/lvs_results/{tag}"
os.environ['LOG_ROOT'] = f"{design_directory}/lvs/{design_name}/lvs_results/{tag}/logs"
os.environ['SIGNOFF_ROOT'] = f"{design_directory}/lvs/{design_name}/lvs_results/{tag}/output"
os.environ['PDK'] = f'{pdk}'
os.environ['PDK_ROOT'] = f'{pdk_root}'
output_directory = f"{design_directory}/lvs/{design_name}/lvs_results/{tag}"
if not os.path.isdir(f"{output_directory}"):
os.mkdir(f"{output_directory}")
log_file_path = f"{output_directory}/be_check.log"
if not os.path.isdir(f"{output_directory}/log"):
os.mkdir(f"{output_directory}/log")
lvs_env = dict()
lvs_env['UPRJ_ROOT'] = f"{design_directory}"
lvs_env['LVS_ROOT'] = f'{os.getcwd()}/checks/lvs_check/'
lvs_env['WORK_ROOT'] = f"{output_directory}"
lvs_env['LOG_ROOT'] = f"{output_directory}/log"
lvs_env['SIGNOFF_ROOT'] = f"{output_directory}/output"
lvs_env['PDK'] = f'{pdk}'
lvs_env['PDK_ROOT'] = f'{pdk_root}'
if not os.path.exists(f"{config_file}"):
logging.error(f"ERROR LVS FAILED, Could not find LVS configuration file {config_file}")
return False
if not parse_config_file(config_file, lvs_env):
return False
lvs_cmd = ['bash', f'{os.getcwd()}/checks/lvs_check/run_be_checks', f'{config_file}', f'{design_name}']

os.environ.update(lvs_env)
with open(log_file_path, 'w') as lvs_log:
logging.info("run: run_be_checks") # helpful reference, print long-cmd once & messages below remain concise
logging.info(f"LVS output directory: {output_directory}")
p = subprocess.run(lvs_cmd, stderr=lvs_log, stdout=lvs_log)
# Check exit-status of all subprocesses
stat = p.returncode
Expand Down Expand Up @@ -53,4 +117,7 @@ def run_lvs(design_directory, output_directory, design_name, config_file, pdk_ro
pdk = pdk_path.name
pdk_root = pdk_path.parent

run_lvs(design_directory, output_directory, design_name, config_file, pdk_root, pdk)
if not run_lvs(design_directory, output_directory, design_name, config_file, pdk_root, pdk):
logging.error("LVS Failed.")
else:
logging.info("LVS Passed!")
2 changes: 1 addition & 1 deletion checks/lvs_check/run_be_checks
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ if [[ ! -f $CONFIG_FILE ]]; then
fi

#get_lvs_config $CONFIG_FILE |
source $CONFIG_FILE
# source $CONFIG_FILE

echo "TOP SOURCE: $TOP_SOURCE"
echo "SOURCE FILE(S): $(echo $LVS_SPICE_FILES $LVS_VERILOG_FILES | sed -e 's/#[^ ]*//g' -e 's/ /\n/g')"
Expand Down
3 changes: 1 addition & 2 deletions checks/lvs_check/run_full_lvs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ if [[ ! -f $CONFIG_FILE ]]; then
fi

#get_lvs_config $CONFIG_FILE |
source $CONFIG_FILE
# source $CONFIG_FILE
if [[ $EXTRACT_LAYOUT = no ]]; then
export LAYOUT_FILE=
fi
Expand All @@ -61,7 +61,6 @@ echo "LOG_ROOT : ${LOG_ROOT:=$WORK_ROOT}"
echo "SIGNOFF_ROOT: ${SIGNOFF_ROOT:=$WORK_ROOT}"
export LOG_ROOT SIGNOFF_ROOT WORK_ROOT


mkdir -p $LOG_ROOT
mkdir -p $SIGNOFF_ROOT
mkdir -p $WORK_ROOT
Expand Down
3 changes: 1 addition & 2 deletions checks/lvs_check/run_scheck
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
# 5. Compare
#


# Use cases
# run_scheck [--noextract] lvs_config_file [top_block [gds_file]]

Expand Down Expand Up @@ -77,7 +76,7 @@ if [[ ! -f $CONFIG_FILE ]]; then
exit 1
fi

source $CONFIG_FILE
# source $CONFIG_FILE
export TOP=$TOP_LAYOUT
if [[ $EXTRACT_LAYOUT = no ]]; then
export LAYOUT_FILE=
Expand Down
4 changes: 0 additions & 4 deletions mpw_precheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ def main(*args, **kwargs):
parser.add_argument('-p', '--pdk_path', required=True, help="PDK_PATH, points to the installation path of the pdk (variant specific)")
parser.add_argument('-o', '--output_directory', required=False, help="OUTPUT_DIRECTORY, default=<input_directory>/precheck_results/DD_MMM_YYYY___HH_MM_SS.")
parser.add_argument('--private', action='store_true', help=f"If provided, precheck skips {open_source_checks.keys() - private_checks.keys()} checks that qualify the project to be Open Source")
parser.add_argument('--disable-lvs', action='store_true', default=False, help="If provided, precheck skips LVS - CVC - Soft connections checks that qualify the project to be Open Source")
parser.add_argument('checks', metavar='check', type=str, nargs='*', choices=list(open_source_checks.keys()).append([]), help=f"Checks to be run by the precheck: {' '.join(open_source_checks.keys())}")
args = parser.parse_args()

Expand All @@ -132,9 +131,6 @@ def main(*args, **kwargs):

sequence = args.checks if args.checks else [x for x in private_checks.keys()] if args.private else [x for x in open_source_checks.keys()]

if args.disable_lvs:
sequence.remove('lvs')

main(input_directory=args.input_directory,
output_directory=output_directory,
caravel_root=os.environ['GOLDEN_CARAVEL'],
Expand Down