Source code for bag.verification.base

# 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 defines Checker, an abstract base class that handles LVS/RCX."""

from __future__ import annotations

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

import abc
from pathlib import Path

from pybag.core import gds_equal

from ..io.template import new_template_env
from ..concurrent.core import SubProcessManager
from ..util.importlib import import_class

if TYPE_CHECKING:
    from ..concurrent.core import FlowInfo, ProcInfo


[docs]class Checker(abc.ABC): """A class that handles DRC/LVS/RCX/LVL. Parameters ---------- tmp_dir : str temporary directory to save files in. """ def __init__(self, tmp_dir: str) -> None: self.tmp_dir = tmp_dir self._tmp_env = new_template_env('bag.verification', 'templates') @abc.abstractmethod
[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. """ return []
@abc.abstractmethod
[docs] async def async_run_drc(self, lib_name: str, cell_name: str, lay_view: str = 'layout', layout: str = '', params: Optional[Dict[str, Any]] = None, run_dir: Union[str, Path] = '', **kwargs: Any) -> Tuple[bool, str]: """A coroutine for running DRC. Parameters ---------- lib_name : str library name. cell_name : str cell name. lay_view : str layout view name. Optional. layout : str the layout file name. If not empty, will not try to generate the layout file. params : Optional[Dict[str, Any]] optional DRC parameter values. run_dir : Union[str, Path] Defaults to empty string. The run directory, use empty string for default. Returns ------- success : bool True if DRC succeeds. log_fname : str DRC log file name. """ return False, ''
@abc.abstractmethod
[docs] async def async_run_lvs(self, lib_name: str, cell_name: str, sch_view: str = 'schematic', lay_view: str = 'layout', layout: str = '', netlist: str = '', params: Optional[Dict[str, Any]] = None, run_rcx: bool = False, run_dir: Union[str, Path] = '', **kwargs: Any) -> Tuple[bool, str]: """A coroutine for running LVS. Parameters ---------- lib_name : str library name. cell_name : str cell name. sch_view : str schematic view name. Optional. lay_view : str layout view name. Optional. layout : str the layout file name. If not empty, will not try to generate the layout file. netlist : str the CDL netlist name. If provided, will not try to call tools to generate netlist. params : Optional[Dict[str, Any]] optional LVS parameter values. run_rcx : bool True if extraction will be ran after LVS. run_dir : Union[str, Path] Defaults to empty string. The run directory, use empty string for default. Returns ------- success : bool True if LVS succeeds. log_fname : str LVS log file name. """ return False, ''
@abc.abstractmethod
[docs] async def async_run_rcx(self, lib_name: str, cell_name: str, sch_view: str = 'schematic', lay_view: str = 'layout', layout: str = '', netlist: str = '', params: Optional[Dict[str, Any]] = None, run_dir: Union[str, Path] = '', **kwargs: Any) -> Tuple[str, str]: """A coroutine for running RCX. Parameters ---------- lib_name : str library name. cell_name : str cell name. sch_view : str schematic view name. Optional. lay_view : str layout view name. Optional. layout : str the layout file name. If not empty, will not try to generate the layout file. netlist : str the CDL netlist name. If provided, will not try to call tools to generate netlist. params : Optional[Dict[str, Any]] optional RCX parameter values. run_dir : Union[str, Path] Defaults to empty string. The run directory, use empty string for default. Returns ------- netlist : str The RCX netlist file name. empty if RCX failed. log_fname : str RCX log file name. """ return '', ''
@abc.abstractmethod
[docs] async def async_run_lvl(self, gds_file: str, ref_file: str, run_dir: Union[str, Path] = '', **kwargs: Any) -> Tuple[bool, str]: """A coroutine for running LVL with two gds files. Parameters ---------- gds_file : str name of the current gds to be compared. ref_file : str name of the reference gds file. run_dir : Union[str, Path] Defaults to empty string. The run directory, use empty string for default. Returns ------- success : bool True if LVL succeeds. log_fname : str LVL log file name. """ return False, ''
@abc.abstractmethod
[docs] async def async_import_layout(self, in_file: str, lib_name: str, cell_name: str, view_name: str = 'layout', params: Optional[Dict[str, Any]] = None, **kwargs: Any) -> str: """A coroutine for importing layout. Parameters ---------- in_file : str input file name. lib_name : str library name. cell_name : str cell name. view_name : str layout view name. params : Optional[Dict[str, Any]] optional export parameter values. Returns ------- log_fname : str log file name. """ return ''
@abc.abstractmethod
[docs] async def async_export_layout(self, lib_name: str, cell_name: str, out_file: str, view_name: str = 'layout', params: Optional[Dict[str, Any]] = None, **kwargs: Any) -> str: """A coroutine for exporting layout. Parameters ---------- lib_name : str library name. cell_name : str cell name. view_name : str layout view name. out_file : str output file name. params : Optional[Dict[str, Any]] optional export parameter values. Returns ------- log_fname : str log file name. """ return ''
@abc.abstractmethod
[docs] async def async_export_schematic(self, lib_name: str, cell_name: str, out_file: str, view_name: str = 'schematic', params: Optional[Dict[str, Any]] = None, **kwargs: Any) -> str: """A coroutine for exporting schematic. Parameters ---------- lib_name : str library name. cell_name : str cell name. view_name : str schematic view name. out_file : str output file name. params : Optional[Dict[str, Any]] optional export parameter values. Returns ------- log_fname : str log file name. """ return ''
[docs] def render_file_template(self, temp_name: str, params: Dict[str, Any]) -> str: """Returns the rendered content from the given template file.""" template = self._tmp_env.get_template(temp_name) return template.render(**params)
[docs] def render_string_template(self, content: str, params: Dict[str, Any]) -> str: """Returns the rendered content from the given template string.""" template = self._tmp_env.from_string(content) return template.render(**params)
[docs]class SubProcessChecker(Checker, abc.ABC): """An implementation of :class:`Checker` using :class:`SubProcessManager`. Parameters ---------- tmp_dir : str temporary file directory. max_workers : int maximum number of parallel processes. cancel_timeout : float timeout for cancelling a subprocess. mgr_class: Type[SubProcessManager] class for subprocess manager. Default is SubProcessManager. mgr_kwargs: Optional[Dict[str, Any]] constructor arguments for subprocess manager class. If None, no arguments are passed in. Default is None. """ def __init__(self, tmp_dir: str, max_workers: int, cancel_timeout: float, mgr_class: Type[SubProcessManager] = SubProcessManager, mgr_kwargs: Optional[Dict[str, Any]] = None) -> None: Checker.__init__(self, tmp_dir) mgr_class: Type[SubProcessManager] = import_class(mgr_class) mgr_kwargs: Dict[str, Any] = mgr_kwargs or {} self._manager: SubProcessManager = mgr_class(max_workers=max_workers, cancel_timeout=cancel_timeout, **mgr_kwargs) @abc.abstractmethod
[docs] def setup_drc_flow(self, lib_name: str, cell_name: str, lay_view: str = 'layout', layout: str = '', params: Optional[Dict[str, Any]] = None, run_dir: Union[str, Path] = '') -> Sequence[FlowInfo]: """This method performs any setup necessary to configure a DRC subprocess flow. Parameters ---------- lib_name : str library name. cell_name : str cell name. lay_view : str layout view name. layout : str the layout file name. If not empty, will not try to generate the layout file. params : Optional[Dict[str, Any]] optional DRC parameter values. run_dir : Union[str, Path] Defaults to empty string. The run directory, use empty string for default. Returns ------- flow_info : Sequence[FlowInfo] the DRC flow information list. Each element is a tuple of: args : Union[str, Sequence[str]] command to run, as string or list of string arguments. log : str log file name. env : Optional[Dict[str, str]] environment variable dictionary. None to inherit from parent. cwd : Optional[str] working directory path. None to inherit from parent. vfun : Sequence[Callable[[Optional[int], str], Any]] a function to validate if it is ok to execute the next process. The output of the last function is returned. The first argument is the return code, the second argument is the log file name. """ return []
@abc.abstractmethod
[docs] def setup_lvs_flow(self, lib_name: str, cell_name: str, sch_view: str = 'schematic', lay_view: str = 'layout', layout: str = '', netlist: str = '', params: Optional[Dict[str, Any]] = None, run_rcx: bool = False, run_dir: Union[str, Path] = '') -> Sequence[FlowInfo]: """This method performs any setup necessary to configure a LVS subprocess flow. Parameters ---------- lib_name : str library name. cell_name : str cell name. sch_view : str schematic view name. lay_view : str layout view name. layout : str the layout file name. If not empty, will not try to generate the layout file. netlist : str the CDL netlist name. If provided, will not try to call tools to generate netlist. params : Optional[Dict[str, Any]] optional LVS parameter values. run_rcx : bool True if extraction will follow LVS. run_dir : Union[str, Path] Defaults to empty string. The run directory, use empty string for default. Returns ------- flow_info : Sequence[FlowInfo] the LVS flow information list. Each element is a tuple of: args : Union[str, Sequence[str]] command to run, as string or list of string arguments. log : str log file name. env : Optional[Dict[str, str]] environment variable dictionary. None to inherit from parent. cwd : Optional[str] working directory path. None to inherit from parent. vfun : Sequence[Callable[[Optional[int], str], Any]] a function to validate if it is ok to execute the next process. The output of the last function is returned. The first argument is the return code, the second argument is the log file name. """ return []
@abc.abstractmethod
[docs] def setup_rcx_flow(self, lib_name: str, cell_name: str, sch_view: str = 'schematic', lay_view: str = 'layout', layout: str = '', netlist: str = '', params: Optional[Dict[str, Any]] = None, run_dir: Union[str, Path] = '', ) -> Sequence[FlowInfo]: """This method performs any setup necessary to configure a RCX subprocess flow. Parameters ---------- lib_name : str library name. cell_name : str cell name. sch_view : str schematic view name lay_view : str layout view name layout : str layout netlist netlist : str schematic netlist params : Optional[Dict[str, Any]] optional RCX parameter values. run_dir : Union[str, Path] Defaults to empty string. The run directory, use empty string for default. Returns ------- flow_info : Sequence[FlowInfo] the RCX flow information list. Each element is a tuple of: args : Union[str, Sequence[str]] command to run, as string or list of string arguments. log : str log file name. env : Optional[Dict[str, str]] environment variable dictionary. None to inherit from parent. cwd : Optional[str] working directory path. None to inherit from parent. vfun : Sequence[Callable[[Optional[int], str], Any]] a function to validate if it is ok to execute the next process. The output of the last function is returned. The first argument is the return code, the second argument is the log file name. """ return []
# noinspection PyMethodMayBeStatic
[docs] def setup_lvl_flow(self, gds_file: str, ref_file: str, run_dir: Union[str, Path] = '') -> Sequence[FlowInfo]: """This method performs any setup necessary to configure a LVL subprocess flow. Parameters ---------- gds_file : str name of the current gds to be compared. ref_file : str name of the reference gds file. run_dir : Union[str, Path] Defaults to empty string. The run directory, use empty string for default. Returns ------- flow_info : Sequence[FlowInfo] the LVL flow information list. Each element is a tuple of: args : Union[str, Sequence[str]] command to run, as string or list of string arguments. log : str log file name. env : Optional[Dict[str, str]] environment variable dictionary. None to inherit from parent. cwd : Optional[str] working directory path. None to inherit from parent. vfun : Sequence[Callable[[Optional[int], str], Any]] a function to validate if it is ok to execute the next process. The output of the last function is returned. The first argument is the return code, the second argument is the log file name. """ return []
@abc.abstractmethod
[docs] def setup_import_layout(self, in_file: str, lib_name: str, cell_name: str, view_name: str = 'layout', params: Optional[Dict[str, Any]] = None ) -> ProcInfo: """This method performs any setup necessary to import layout. Parameters ---------- in_file : str input file name. lib_name : str library name. cell_name : str cell name. view_name : str layout view name. params : Optional[Dict[str, Any]] optional export parameter values. Returns ------- args : Union[str, Sequence[str]] command to run, as string or list of string arguments. log : str log file name. env : Optional[Dict[str, str]] environment variable dictionary. None to inherit from parent. cwd : Optional[str] working directory path. None to inherit from parent. """ return '', '', None, None
@abc.abstractmethod
[docs] def setup_export_layout(self, lib_name: str, cell_name: str, out_file: str, view_name: str = 'layout', params: Optional[Dict[str, Any]] = None ) -> ProcInfo: """This method performs any setup necessary to export layout. Parameters ---------- lib_name : str library name. cell_name : str cell name. out_file : str output file name. view_name : str layout view name. params : Optional[Dict[str, Any]] optional export parameter values. Returns ------- args : Union[str, Sequence[str]] command to run, as string or list of string arguments. log : str log file name. env : Optional[Dict[str, str]] environment variable dictionary. None to inherit from parent. cwd : Optional[str] working directory path. None to inherit from parent. """ return '', '', None, None
@abc.abstractmethod
[docs] def setup_export_schematic(self, lib_name: str, cell_name: str, out_file: str, view_name: str = 'schematic', params: Optional[Dict[str, Any]] = None) -> ProcInfo: """This method performs any setup necessary to export schematic. Parameters ---------- lib_name : str library name. cell_name : str cell name. out_file : str output file name. view_name : str layout view name. params : Optional[Dict[str, Any]] optional export parameter values. Returns ------- args : Union[str, Sequence[str]] command to run, as string or list of string arguments. log : str log file name. env : Optional[Dict[str, str]] environment variable dictionary. None to inherit from parent. cwd : Optional[str] working directory path. None to inherit from parent. """ return '', '', None, None
[docs] async def async_run_drc(self, lib_name: str, cell_name: str, lay_view: str = 'layout', layout: str = '', params: Optional[Dict[str, Any]] = None, run_dir: Union[str, Path] = '', subproc_options: Optional[Dict[str, Any]] = None, **kwargs: Any) -> Tuple[bool, str]: flow_info = self.setup_drc_flow(lib_name, cell_name, lay_view, layout, params, run_dir) return await self._manager.async_new_subprocess_flow(flow_info, **(subproc_options or {}))
[docs] async def async_run_lvs(self, lib_name: str, cell_name: str, sch_view: str = 'schematic', lay_view: str = 'layout', layout: str = '', netlist: str = '', params: Optional[Dict[str, Any]] = None, run_rcx: bool = False, run_dir: Union[str, Path] = '', subproc_options: Optional[Dict[str, Any]] = None, **kwargs: Any) -> Tuple[bool, str]: flow_info = self.setup_lvs_flow(lib_name, cell_name, sch_view, lay_view, layout, netlist, params, run_rcx, run_dir) return await self._manager.async_new_subprocess_flow(flow_info, **(subproc_options or {}))
[docs] async def async_run_rcx(self, lib_name: str, cell_name: str, sch_view: str = 'schematic', lay_view: str = 'layout', layout: str = '', netlist: str = '', params: Optional[Dict[str, Any]] = None, run_dir: Union[str, Path] = '', subproc_options: Optional[Dict[str, Any]] = None, **kwargs: Any) -> Tuple[str, str]: flow_info = self.setup_rcx_flow(lib_name, cell_name, sch_view, lay_view, layout, netlist, params, run_dir) return await self._manager.async_new_subprocess_flow(flow_info, **(subproc_options or {}))
[docs] async def async_run_lvl(self, gds_file: str, ref_file: str, run_dir: Union[str, Path] = '', subproc_options: Optional[Dict[str, Any]] = None, **kwargs: Any) -> Tuple[bool, str]: flow_info = self.setup_lvl_flow(gds_file, ref_file, run_dir) if flow_info: return await self._manager.async_new_subprocess_flow(flow_info, **(subproc_options or {})) else: return gds_equal(gds_file, ref_file), ''
[docs] async def async_import_layout(self, in_file: str, lib_name: str, cell_name: str, view_name: str = 'layout', params: Optional[Dict[str, Any]] = None, subproc_options: Optional[Dict[str, Any]] = None, **kwargs: Any) -> str: proc_info = self.setup_import_layout(in_file, lib_name, cell_name, view_name, params) await self._manager.async_new_subprocess(*proc_info, **(subproc_options or {})) return proc_info[1]
[docs] async def async_export_layout(self, lib_name: str, cell_name: str, out_file: str, view_name: str = 'layout', params: Optional[Dict[str, Any]] = None, subproc_options: Optional[Dict[str, Any]] = None, **kwargs: Any) -> str: proc_info = self.setup_export_layout(lib_name, cell_name, out_file, view_name, params) await self._manager.async_new_subprocess(*proc_info, **(subproc_options or {})) return proc_info[1]
[docs] async def async_export_schematic(self, lib_name: str, cell_name: str, out_file: str, view_name: str = 'schematic', params: Optional[Dict[str, Any]] = None, subproc_options: Optional[Dict[str, Any]] = None, **kwargs: Any) -> str: proc_info = self.setup_export_schematic(lib_name, cell_name, out_file, view_name, params) await self._manager.async_new_subprocess(*proc_info, **(subproc_options or {})) return proc_info[1]
[docs]def get_flow_config(root_dir: Dict[str, str], template: Dict[str, str], env_vars: Dict[str, Dict[str, str]], link_files: Dict[str, List[str]], params: Dict[str, Dict[str, Any]]) -> Dict[str, Dict[str, Any]]: ans = {name: dict(root_dir=Path(root_dir[name]).resolve(), template=str(Path(template[name]).resolve()), env_vars=env_vars[name], link_files=_process_link_files(link_files[name]), params=params[name]) for name in ['drc', 'lvs', 'rcx']} ext_lvs = 'lvs_rcx' ans[ext_lvs] = dict(root_dir=Path(root_dir.get(ext_lvs, root_dir['rcx'])).resolve(), template=str(Path(template.get(ext_lvs, template['lvs'])).resolve()), env_vars=env_vars.get(ext_lvs, env_vars['lvs']), params=params.get(ext_lvs, params['lvs'])) test = link_files.get(ext_lvs, None) if test is None: ans[ext_lvs]['link_files'] = ans['lvs']['link_files'] else: ans[ext_lvs]['link_files'] = _process_link_files(test) return ans