blob: adf1d6fb0e3d051a24b6a1bf327d824ef89b7f05 [file] [log] [blame]
# SPDX-FileCopyrightText: 2020 Efabless Corporation
#
# 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
#
# http://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.
# SPDX-License-Identifier: Apache-2.0
import argparse
import logging
import os
import subprocess
from pathlib import Path
from checks.utils import utils
def gds_xor_check(input_directory, output_directory, magicrc_file_path, gds_golden_wrapper_file_path, project_config):
parent_directory = Path(__file__).parent
logs_directory = output_directory / 'logs'
outputs_directory = output_directory / 'outputs'
gds_ut_path = input_directory / 'gds' / f"{project_config['user_module']}.gds"
xor_log_file_path = logs_directory / 'xor_check.log'
if not gds_ut_path.exists():
logging.error("GDS not found")
return False
with open(xor_log_file_path, 'w') as xor_log:
rb_gds_size_file_path = parent_directory / 'gds_size.rb'
rb_gds_size_cmd = ['ruby', rb_gds_size_file_path, gds_ut_path, project_config['user_module']]
rb_gds_size_process = subprocess.run(rb_gds_size_cmd, stderr=xor_log, stdout=xor_log)
if rb_gds_size_process.returncode != 0:
logging.error(f"Top cell name {project_config['user_module']} not found.")
return False
# TODO: Try to pass the MAGTYPE as a commandline argument
os.environ['MAGTYPE'] = 'mag'
# Erase box
gds_ut_box_erased_path = outputs_directory / f"{project_config['user_module']}_erased.gds"
tcl_erase_box_file_path = parent_directory / 'erase_box.tcl'
magic_gds_erase_box_ut_cmd = ['magic', '-dnull', '-noconsole', '-rcfile', magicrc_file_path, tcl_erase_box_file_path,
gds_ut_path, gds_ut_box_erased_path, project_config['user_module']]
subprocess.run(magic_gds_erase_box_ut_cmd, stderr=xor_log, stdout=xor_log)
gds_golden_wrapper_box_erased_file_path = outputs_directory / f"{project_config['golden_wrapper']}_erased.gds"
magic_gds_erase_box_golden_wrapper_cmd = ['magic', '-dnull', '-noconsole', '-rcfile', magicrc_file_path,
tcl_erase_box_file_path, gds_golden_wrapper_file_path,
gds_golden_wrapper_box_erased_file_path, project_config['user_module']]
subprocess.run(magic_gds_erase_box_golden_wrapper_cmd, stderr=xor_log, stdout=xor_log)
# Check if the two resulting GDSes have any differences and write them to a file
klayout_rb_drc_xor_file_path = parent_directory / 'xor.rb.drc'
xor_resulting_shapes_gds_file_path = outputs_directory / f"{project_config['user_module']}.xor.gds"
xor_total_file_path = logs_directory / 'xor_check.total'
xor_command = ['klayout', '-b', '-r', klayout_rb_drc_xor_file_path,
'-rd', 'ext=gds',
'-rd', 'top_cell=xor_target',
'-rd', f'thr={os.cpu_count()}',
'-rd', f'a={gds_ut_box_erased_path}',
'-rd', f'b={gds_golden_wrapper_box_erased_file_path}',
'-rd', f'o={xor_resulting_shapes_gds_file_path}',
'-rd', f'ol={xor_resulting_shapes_gds_file_path}',
'-rd', f'xor_total_file_path={xor_total_file_path}']
subprocess.run(xor_command, stderr=xor_log, stdout=xor_log)
try:
with open(xor_total_file_path) as xor_total:
xor_cnt = xor_total.read()
logging.info(f"{{XOR CHECK UPDATE}} Total XOR differences: {xor_cnt}, for more details view {xor_resulting_shapes_gds_file_path}")
if xor_cnt == '0':
return True
else:
return False
except FileNotFoundError:
logging.error(f"XOR CHECK FILE NOT FOUND in {xor_total_file_path}")
return False
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG, format=f"%(asctime)s | %(levelname)-7s | %(message)s", datefmt='%d-%b-%Y %H:%M:%S')
parser = argparse.ArgumentParser(description='Runs a magic xor check on a given GDS.')
parser.add_argument('--input_directory', '-i', required=True, help='Design Path')
parser.add_argument('--output_directory', '-o', required=False, default='.', help='Output Directory')
parser.add_argument('--magicrc_file_path', '-mrc', required=True, help='magicrc file path')
args = parser.parse_args()
output_directory = Path(args.output_directory)
project_config = utils.get_project_config(Path(args.input_directory))
empty_wrapper_url = f"{project_config['link_prefix']}/gds/{project_config['golden_wrapper']}.gds.gz"
gds_golden_wrapper_file_path = output_directory / 'outputs' / f"{project_config['golden_wrapper']}.gds"
utils.download_gzip_file_from_url(empty_wrapper_url, gds_golden_wrapper_file_path)
if gds_xor_check(Path(args.input_directory), output_directory, Path(args.magicrc_file_path), gds_golden_wrapper_file_path, project_config):
logging.info("XOR Check Clean")
else:
logging.info("XOR Check Dirty")