Source code for bag.verification.icv

# SPDX-License-Identifier: BSD-3-Clause AND Apache-2.0
# Copyright 2018 Regents of the University of California
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Copyright 2019 Blue Cheetah Analog Design Inc.
#
# 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.

"""This module implements LVS/RCX using ICV and stream out from Virtuoso.
"""

from typing import TYPE_CHECKING, Optional, List, Tuple, Dict, Any, Sequence

import os

from .virtuoso import VirtuosoChecker
from ..io import read_file, open_temp

if TYPE_CHECKING:
    from .base import FlowInfo


# noinspection PyUnusedLocal
[docs]def _all_pass(retcode: int, log_file: str) -> bool: return True
# noinspection PyUnusedLocal
[docs]def lvs_passed(retcode: int, log_file: str) -> Tuple[bool, str]: """Check if LVS passed Parameters ---------- retcode : int return code of the LVS process. log_file : str log file name. Returns ------- success : bool True if LVS passed. log_file : str the log file name. """ dirname = os.path.dirname(log_file) cell_name = os.path.basename(dirname) lvs_error_file = os.path.join(dirname, cell_name + '.LVS_ERRORS') # append error file at the end of log file with open(log_file, 'a') as logf: with open(lvs_error_file, 'r') as errf: for line in errf: logf.write(line) if not os.path.isfile(log_file): return False, '' cmd_output = read_file(log_file) test_str = 'Final comparison result:PASS' return test_str in cmd_output, log_file
[docs]class ICV(VirtuosoChecker): """A subclass of VirtuosoChecker that uses ICV for verification. Parameters ---------- tmp_dir : string temporary directory to save files in. lvs_run_dir : str the LVS run directory. lvs_runset : str the LVS runset filename. rcx_run_dir : str the RCX run directory. rcx_runset : str the RCX runset filename. source_added_file : str the source.added file location. Environment variable is supported. Default value is '$DK/Calibre/lvs/source.added'. rcx_mode : str the RC extraction mode. Defaults to 'starrc'. """ def __init__(self, tmp_dir: str, lvs_run_dir: str, lvs_runset: str, rcx_run_dir: str, rcx_runset: str, source_added_file: str = '$DK/Calibre/lvs/source.added', rcx_mode: str = 'pex', **kwargs): max_workers = kwargs.get('max_workers', None) cancel_timeout = kwargs.get('cancel_timeout_ms', None) rcx_params = kwargs.get('rcx_params', {}) lvs_params = kwargs.get('lvs_params', {}) rcx_link_files = kwargs.get('rcx_link_files', None) lvs_link_files = kwargs.get('lvs_link_files', None) if cancel_timeout is not None: cancel_timeout /= 1e3 VirtuosoChecker.__init__(self, tmp_dir, max_workers, cancel_timeout, source_added_file, **kwargs) self.default_rcx_params = rcx_params self.default_lvs_params = lvs_params self.lvs_run_dir = os.path.abspath(lvs_run_dir) self.lvs_runset = lvs_runset self.lvs_link_files = lvs_link_files self.rcx_run_dir = os.path.abspath(rcx_run_dir) self.rcx_runset = rcx_runset self.rcx_link_files = rcx_link_files self.rcx_mode = rcx_mode
[docs] def get_rcx_netlists(self, lib_name: str, cell_name: str) -> List[str]: """Returns a list of generated extraction netlist file names. Parameters ---------- lib_name : str library name. cell_name : str cell_name Returns ------- netlists : List[str] a list of generated extraction netlist file names. The first index is the main netlist. """ # PVS generate schematic cellviews directly. if self.rcx_mode == 'starrc': return ['%s.spf' % cell_name] else: pass
[docs] def setup_lvs_flow(self, lib_name: str, cell_name: str, sch_view: str = 'schematic', lay_view: str = 'layout', gds: str = '', netlist = '', params: Optional[Dict[str, Any]] = None) -> Sequence[FlowInfo]: if netlist: netlist = os.path.abspath(netlist) run_dir = os.path.join(self.lvs_run_dir, lib_name, cell_name) os.makedirs(run_dir, exist_ok=True) lay_file, sch_file = self._get_lay_sch_files(run_dir, netlist) # add schematic/layout export to flow flow_list = [] if not gds: cmd, log, env, cwd = self.setup_export_layout(lib_name, cell_name, lay_file, lay_view, None) flow_list.append((cmd, log, env, cwd, _all_pass)) if not netlist: cmd, log, env, cwd = self.setup_export_schematic(lib_name, cell_name, sch_file, sch_view, None) flow_list.append((cmd, log, env, cwd, _all_pass)) lvs_params_actual = self.default_lvs_params.copy() if params is not None: lvs_params_actual.update(params) with open_temp(prefix='lvsLog', dir=run_dir, delete=False) as logf: log_file = logf.name # set _drPROCESS dr_process_str = '_drPROCESS=' + lvs_params_actual['_drPROCESS'] cmd = ['icv', '-D', dr_process_str, '-i', lay_file, '-s', sch_file, '-sf', 'SPICE', '-f', 'GDSII', '-c', cell_name, '-vue', '-I'] for f in self.lvs_link_files: cmd.append(f) flow_list.append((cmd, log_file, None, run_dir, lvs_passed)) return flow_list
[docs] def setup_rcx_flow(self, lib_name: str, cell_name: str, sch_view: str = 'schematic', lay_view: str = 'layout', gds: str = '', netlist: str = '', params: Optional[Dict[str, Any]] = None) -> Sequence[FlowInfo]: # update default RCX parameters. rcx_params_actual = self.default_rcx_params.copy() if params is not None: rcx_params_actual.update(params) run_dir = os.path.join(self.rcx_run_dir, lib_name, cell_name) os.makedirs(run_dir, exist_ok=True) lay_file, sch_file = self._get_lay_sch_files(run_dir, netlist) with open_temp(prefix='rcxLog', dir=run_dir, delete=False) as logf: log_file = logf.name flow_list = [] if not gds: cmd, log, env, cwd = self.setup_export_layout(lib_name, cell_name, lay_file, lay_view, None) flow_list.append((cmd, log, env, cwd, _all_pass)) if not netlist: cmd, log, env, cwd = self.setup_export_schematic(lib_name, cell_name, sch_file, sch_view, None) flow_list.append((cmd, log, env, cwd, _all_pass)) if self.rcx_mode == 'starrc': # first: run Extraction LVS lvs_params_actual = self.default_lvs_params.copy() dr_process_str = '_drPROCESS=' + lvs_params_actual['_drPROCESS'] cmd = ['icv', '-D', '_drRCextract', '-D', dr_process_str, '-D', '_drICFOAlayers', '-i', lay_file, '-s', sch_file, '-sf', 'SPICE', '-f', 'GDSII', '-c', cell_name, '-I'] for f in self.lvs_link_files: cmd.append(f) # hack the environment variables to make sure $PWD is the same as current working directory env_copy = os.environ.copy() env_copy['PWD'] = run_dir flow_list.append((cmd, log_file, env_copy, run_dir, lvs_passed)) # second: setup CCP # make symlinks if self.rcx_link_files: for source_file in self.rcx_link_files: targ_file = os.path.join(run_dir, os.path.basename(source_file)) if not os.path.exists(targ_file): os.symlink(source_file, targ_file) # generate new cmd for StarXtract cmd_content, result = self.modify_starrc_cmd(run_dir, lib_name, cell_name, rcx_params_actual, sch_file) # save cmd for StarXtract with open_temp(dir=run_dir, delete=False) as cmd_file: cmd_fname = cmd_file.name cmd_file.write(cmd_content) cmd = ['StarXtract', '-clean', cmd_fname] else: cmd = [] # noinspection PyUnusedLocal def rcx_passed(retcode, log_fname): dirname = os.path.dirname(log_fname) cell_name = os.path.basename(dirname) results_file = os.path.join(dirname, cell_name + '.RESULTS') # append error file at the end of log file with open(log_fname, 'a') as logf: with open(results_file, 'r') as errf: for line in errf: logf.write(line) if not os.path.isfile(log_fname): return None, '' cmd_output = read_file(log_fname) test_str = 'DRC and Extraction Results: CLEAN' if test_str in cmd_output: return results_file, log_fname else: return None, log_fname flow_list.append((cmd, log_file, None, run_dir, rcx_passed)) return flow_list
@classmethod
[docs] def _get_lay_sch_files(cls, run_dir, netlist=''): lay_file = os.path.join(run_dir, 'layout.gds') sch_file = netlist if netlist else os.path.join(run_dir, 'schematic.net') return lay_file, sch_file
[docs] def modify_starrc_cmd(self, run_dir, lib_name, cell_name, starrc_params, sch_file): # type: (str, str, str, Dict[str, Any], str) -> Tuple[str, str] """Modify the cmd file. Parameters ---------- run_dir : str the run directory. lib_name : str the library name. cell_name : str the cell name. starrc_params : Dict[str, Any] override StarRC parameters. sch_file : str the schematic netlist Returns ------- starrc_cmd : str the new StarXtract cmd file. output_name : str the extracted netlist file. """ output_name = '%s.spf' % cell_name if 'CDSLIBPATH' in os.environ: cds_lib_path = os.path.abspath(os.path.join(os.environ['CDSLIBPATH'], 'cds.lib')) else: cds_lib_path = os.path.abspath('./cds.lib') content = self.render_string_template(read_file(self.rcx_runset), dict( cell_name=cell_name, extract_type=starrc_params['extract'].get('type'), netlist_format=starrc_params.get('netlist_format', 'SPF'), sch_file=sch_file, cds_lib=cds_lib_path, lib_name=lib_name, run_dir=run_dir, )) return content, os.path.join(run_dir, output_name)