# 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 DbAccess, the base class for CAD database manipulation.
"""
from __future__ import annotations
from typing import TYPE_CHECKING, List, Dict, Tuple, Optional, Sequence, Any, Mapping
import abc
import importlib
import traceback
from pathlib import Path
from pybag.core import gds_equal
from ..io.file import make_temp_dir, open_file, read_file, write_file
from ..verification import make_checker
from ..layout.routing.grid import RoutingGrid
from ..concurrent.core import batch_async_task
from .base import InterfaceBase
if TYPE_CHECKING:
from .zmqwrapper import ZMQDealer
from ..verification import Checker
[docs]def dict_to_item_list(table: Dict[str, Any]) -> List[List[str]]:
"""Given a Python dictionary, convert to sorted item list.
Parameters
----------
table :
a Python dictionary where the keys are strings.
Returns
-------
assoc_list :
the sorted item list representation of the given dictionary.
"""
return [[key, table[key]] for key in sorted(table.keys())]
[docs]def handle_reply(reply):
"""Process the given reply."""
if isinstance(reply, dict):
if reply.get('type') == 'error':
if 'data' not in reply:
raise Exception('Unknown reply format: %s' % reply)
raise VirtuosoException(reply['data'])
else:
try:
return reply['data']
except Exception:
raise Exception('Unknown reply format: %s' % reply)
else:
raise Exception('Unknown reply format: %s' % reply)
[docs]class VirtuosoException(Exception):
"""Exception raised when Virtuoso returns an error."""
def __init__(self, *args, **kwargs):
# noinspection PyArgumentList
Exception.__init__(self, *args, **kwargs)
[docs]class DbAccess(InterfaceBase, abc.ABC):
"""A class that manipulates the CAD database.
Parameters
----------
dealer : Optional[ZMQDealer]
an optional socket that can be used to communicate with the CAD database.
tmp_dir : str
temporary file directory for DbAccess.
db_config : Dict[str, Any]
the database configuration dictionary.
lib_defs_file : str
name of the file that contains generator library names.
"""
def __init__(self, dealer: ZMQDealer, tmp_dir: str, db_config: Dict[str, Any],
lib_defs_file: str) -> None:
InterfaceBase.__init__(self)
self.handler: ZMQDealer = dealer
self.tmp_dir: str = make_temp_dir('dbTmp', parent_dir=tmp_dir)
self.db_config: Dict[str, Any] = db_config
# noinspection PyBroadException
try:
check_kwargs = self.db_config['checker'].copy()
check_kwargs['tmp_dir'] = self.tmp_dir
self.checker: Optional[Checker] = make_checker(**check_kwargs)
except Exception:
stack_trace = traceback.format_exc()
print('*WARNING* error creating Checker:\n%s' % stack_trace)
print('*WARNING* LVS/RCX will be disabled.')
self.checker: Optional[Checker] = None
# set default lib path
self._default_lib_path: str = self.get_default_lib_path(db_config)
# get yaml path mapping
self.lib_path_map: Dict[str, str] = {}
with open_file(lib_defs_file, 'r') as f:
for line in f:
lib_name = line.strip()
self.add_sch_library(lib_name)
self._close_all_cv: bool = db_config.get('close_all_cellviews', True)
@classmethod
[docs] def get_default_lib_path(cls, db_config: Dict[str, Any]) -> str:
default_lib_path = Path(db_config.get('default_lib_path', '.'))
if not default_lib_path.is_dir():
default_lib_path = Path.cwd()
return str(default_lib_path.resolve())
@property
[docs] def default_lib_path(self) -> str:
"""str: The default directory to create new libraries in.
"""
return self._default_lib_path
@property
[docs] def has_bag_server(self) -> bool:
"""bool: True if the BAG server is up."""
return self.handler is not None
@abc.abstractmethod
[docs] def get_exit_object(self) -> Any:
"""Returns an object to send to the server to shut it down.
Return None if this option is not supported.
"""
return None
@abc.abstractmethod
[docs] def get_cells_in_library(self, lib_name: str) -> List[str]:
"""Get a list of cells in the given library.
Returns an empty list if the given library does not exist.
Parameters
----------
lib_name : str
the library name.
Returns
-------
cell_list : List[str]
a list of cells in the library
"""
return []
@abc.abstractmethod
[docs] def create_library(self, lib_name: str, lib_path: str = '') -> None:
"""Create a new library if one does not exist yet.
Parameters
----------
lib_name : str
the library name.
lib_path : str
directory to create the library in. If Empty, use default location.
"""
pass
@abc.abstractmethod
@abc.abstractmethod
[docs] def get_testbench_info(self, tb_lib: str, tb_cell: str
) -> Tuple[List[str], List[str], Dict[str, str], Dict[str, str]]:
"""Returns information about an existing testbench.
Parameters
----------
tb_lib : str
testbench library.
tb_cell : str
testbench cell.
Returns
-------
cur_envs : List[str]
the current simulation environments.
envs : List[str]
a list of available simulation environments.
parameters : Dict[str, str]
a list of testbench parameter values, represented as string.
outputs : Dict[str, str]
a list of testbench output expressions.
"""
return [], [], {}, {}
@abc.abstractmethod
[docs] def update_testbench(self, lib: str, cell: str, parameters: Dict[str, str],
sim_envs: Sequence[str], config_rules: Sequence[List[str]],
env_parameters: Sequence[List[Tuple[str, str]]]) -> None:
"""Update the given testbench configuration.
Parameters
----------
lib : str
testbench library.
cell : str
testbench cell.
parameters : Dict[str, str]
testbench parameters.
sim_envs : Sequence[str]
list of enabled simulation environments.
config_rules : Sequence[List[str]]
config view mapping rules, list of (lib, cell, view) rules.
env_parameters : Sequence[List[Tuple[str, str]]]
list of param/value list for each simulation environment.
"""
pass
@abc.abstractmethod
[docs] def instantiate_layout_pcell(self, lib_name: str, cell_name: str, view_name: str,
inst_lib: str, inst_cell: str, params: Dict[str, Any],
pin_mapping: Dict[str, str]) -> None:
"""Create a layout cell with a single pcell instance.
Parameters
----------
lib_name : str
layout library name.
cell_name : str
layout cell name.
view_name : str
layout view name, default is "layout".
inst_lib : str
pcell library name.
inst_cell : str
pcell cell name.
params : Dict[str, Any]
the parameter dictionary.
pin_mapping: Dict[str, str]
the pin mapping dictionary.
"""
pass
@abc.abstractmethod
[docs] def create_schematics(self, lib_name: str, sch_view: str, sym_view: str,
content_list: Sequence[Any], lib_path: str = '') -> None:
"""Create the given schematics in CAD database.
Precondition: the library already exists, all cellviews are writable (i.e. they have been
closed already).
Parameters
----------
lib_name : str
name of the new library to put the concrete schematics.
sch_view : str
schematic view name.
sym_view : str
symbol view name.
content_list : Sequence[Any]
list of schematics to create.
lib_path : str
the path to create the library in. If empty, use default location.
"""
pass
@abc.abstractmethod
[docs] def create_layouts(self, lib_name: str, view: str, content_list: Sequence[Any]) -> None:
"""Create the given layouts in CAD database.
Precondition: the library already exists, all cellviews are writable (i.e. they have been
closed already).
Parameters
----------
lib_name : str
name of the new library to put the concrete schematics.
view : str
layout view name.
content_list : Sequence[Any]
list of layouts to create.
"""
pass
@abc.abstractmethod
[docs] def close_all_cellviews(self) -> None:
"""Close all currently opened cellviews in the database."""
pass
@abc.abstractmethod
[docs] def release_write_locks(self, lib_name: str, cell_view_list: Sequence[Tuple[str, str]]) -> None:
"""Release write locks from all the given cells.
Parameters
----------
lib_name : str
the library name.
cell_view_list : Sequence[Tuple[str, str]]
list of cell/view name tuples.
"""
pass
@abc.abstractmethod
[docs] def refresh_cellviews(self, lib_name: str, cell_view_list: Sequence[Tuple[str, str]]) -> None:
"""Refresh the given cellviews in the database.
Parameters
----------
lib_name : str
the library name.
cell_view_list : Sequence[Tuple[str, str]]
list of cell/view name tuples.
"""
pass
@abc.abstractmethod
@abc.abstractmethod
[docs] def create_schematic_from_netlist(self, netlist: str, lib_name: str, cell_name: str,
sch_view: str = '', **kwargs: Any) -> None:
"""Create a schematic from a netlist.
This is mainly used to create extracted schematic from an extracted netlist.
Parameters
----------
netlist : str
the netlist file name.
lib_name : str
library name.
cell_name : str
cell_name
sch_view : str
schematic view name. The default value is implemendation dependent.
**kwargs : Any
additional implementation-dependent arguments.
"""
pass
@abc.abstractmethod
[docs] def create_verilog_view(self, verilog_file: str, lib_name: str, cell_name: str, **kwargs: Any
) -> None:
"""Create a verilog view for mix-signal simulation.
Parameters
----------
verilog_file : str
the verilog file name.
lib_name : str
library name.
cell_name : str
cell name.
**kwargs : Any
additional implementation-dependent arguments.
"""
pass
@abc.abstractmethod
[docs] def import_sch_cellview(self, lib_name: str, cell_name: str, view_name: str) -> None:
"""Recursively import the given schematic and symbol cellview.
Parameters
----------
lib_name : str
library name.
cell_name : str
cell name.
view_name : str
view name.
"""
pass
@abc.abstractmethod
[docs] def import_design_library(self, lib_name: str, view_name: str) -> None:
"""Import all design templates in the given library from CAD database.
Parameters
----------
lib_name : str
name of the library.
view_name : str
the view name to import from the library.
"""
pass
@abc.abstractmethod
[docs] def import_gds_file(self, gds_fname: str, lib_name: str, layer_map: str, obj_map: str,
grid: RoutingGrid) -> None:
pass
[docs] def send(self, obj: Any) -> Any:
"""Send the given Python object to the server, and return result."""
if self.handler is None:
raise Exception('BAG Server is not set up.')
self.handler.send_obj(obj)
reply = self.handler.recv_obj()
return reply
[docs] def close(self) -> None:
"""Terminate the database server gracefully.
"""
if self.handler is not None:
exit_obj = self.get_exit_object()
if exit_obj is not None:
self.handler.send(exit_obj)
self.handler.close()
self.handler = None
[docs] def _eval_skill(self, expr: str, input_files: Optional[Dict[str, Any]] = None,
out_file: Optional[str] = None) -> str:
"""Send a request to evaluate the given skill expression.
Because Virtuoso has a limit on the input/output data (< 4096 bytes),
if your input is large, you need to write it to a file and have
Virtuoso open the file to parse it. Similarly, if you expect a
large output, you need to make Virtuoso write the result to the
file, then read it yourself. The parameters input_files and
out_file help you achieve this functionality.
For example, if you need to evaluate "skill_fun(arg fname)", where
arg is a file containing the list [1 2 3], and fname is the output
file name, you will call this function with:
expr = "skill_fun({arg} {fname})"
input_files = { "arg": [1 2 3] }
out_file = "fname"
the bag server will then a temporary file for arg and fname, write
the list [1 2 3] into the file for arg, call Virtuoso, then read
the output file fname and return the result.
Parameters
----------
expr :
the skill expression to evaluate.
input_files :
A dictionary of input files content.
out_file :
the output file name argument in expr.
Returns
-------
result :
a string representation of the result.
Raises
------
VirtuosoException :
if virtuoso encounters errors while evaluating the expression.
"""
request = dict(
type='skill',
expr=expr,
input_files=input_files,
out_file=out_file,
)
reply = self.send(request)
return handle_reply(reply)
[docs] def get_python_template(self, lib_name: str, cell_name: str, primitive_table: Dict[str, str]
) -> str:
"""Returns the default Python Module template for the given schematic.
Parameters
----------
lib_name : str
the library name.
cell_name : str
the cell name.
primitive_table : Dict[str, str]
a dictionary from primitive cell name to module template file name.
Returns
-------
template : str
the default Python Module template.
"""
param_dict = dict(lib_name=lib_name, cell_name=cell_name)
if lib_name == 'BAG_prim':
if cell_name in primitive_table:
# load template from user defined file
template = self._tmp_env.from_string(read_file(primitive_table[cell_name]))
return template.render(**param_dict)
else:
if cell_name.startswith(('nmos', 'pmos')):
# transistor template
module_name = 'MosModuleBase'
elif cell_name.startswith('ndio_') or cell_name.startswith('pdio_'):
# diode template
module_name = 'DiodeModuleBase'
elif cell_name.startswith('res_metal_'):
module_name = 'ResMetalModule'
elif cell_name.startswith('res_'):
# physical resistor template
module_name = 'ResPhysicalModuleBase'
elif cell_name.startswith('esd_'):
# static esd template
module_name = 'ESDModuleBase'
elif cell_name.startswith('mim_'):
# mim template
module_name = 'MIMModuleBase'
else:
raise Exception('Unknown primitive cell: %s' % cell_name)
param_dict['module_name'] = module_name
return self.render_file_template('PrimModule.pyi', param_dict)
else:
# use default empty template.
return self.render_file_template('Module.pyi', param_dict)
[docs] def _create_sch_templates(self, cell_list: List[Tuple[str, str]]) -> None:
for lib, cell in cell_list:
python_file = Path(self.lib_path_map[lib]) / (cell + '.py')
if not python_file.exists():
content = self.get_python_template(lib, cell,
self.db_config.get('prim_table', {}))
write_file(python_file, content, mkdir=False)
[docs] def instantiate_schematic(self, lib_name: str, content_list: Sequence[Any], lib_path: str = '',
sch_view: str = 'schematic', sym_view: str = 'symbol') -> None:
"""Create the given schematics in CAD database.
Parameters
----------
lib_name : str
name of the new library to put the concrete schematics.
content_list : Sequence[Any]
list of schematics to create.
lib_path : str
the path to create the library in. If empty, use default location.
sch_view : str
schematic view name.
sym_view : str
symbol view name.
"""
cell_view_list = []
if self._close_all_cv:
self.close_all_cellviews()
else:
for cell_name, _ in content_list:
cell_view_list.append((cell_name, sch_view))
cell_view_list.append((cell_name, sym_view))
self.release_write_locks(lib_name, cell_view_list)
self.create_library(lib_name, lib_path=lib_path)
self.create_schematics(lib_name, sch_view, sym_view, content_list, lib_path)
if cell_view_list:
self.refresh_cellviews(lib_name, cell_view_list)
[docs] def instantiate_layout(self, lib_name: str, content_list: Sequence[Any], lib_path: str = '',
view: str = 'layout') -> None:
"""Create a batch of layouts.
Parameters
----------
lib_name : str
layout library name.
content_list : Sequence[Any]
list of layouts to create
lib_path : str
the path to create the library in. If empty, use default location.
view : str
layout view name.
"""
cell_view_list = []
if self._close_all_cv:
self.close_all_cellviews()
else:
for cell_name, _ in content_list:
cell_view_list.append((cell_name, view))
self.release_write_locks(lib_name, cell_view_list)
self.create_library(lib_name, lib_path=lib_path)
self.create_layouts(lib_name, view, content_list)
if cell_view_list:
self.refresh_cellviews(lib_name, cell_view_list)
[docs] def run_drc(self, lib_name: str, cell_name: str, **kwargs: Any) -> Tuple[bool, str]:
"""Run DRC on the given cell.
Parameters
----------
lib_name : str
library name.
cell_name : str
cell_name
**kwargs :
optional keyword arguments. See DbAccess class for details.
Returns
-------
value : bool
True if DRC succeeds.
log_fname : str
name of the DRC log file.
"""
coro = self.async_run_drc(lib_name, cell_name, **kwargs)
results = batch_async_task([coro])
if results is None:
return False, ''
ans = results[0]
if isinstance(ans, Exception):
raise ans
return ans
[docs] def run_lvs(self, lib_name: str, cell_name: str, **kwargs: Any) -> Tuple[bool, str]:
"""Run LVS on the given cell.
Parameters
----------
lib_name : str
library name.
cell_name : str
cell_name
**kwargs :
optional keyword arguments. See DbAccess class for details.
Returns
-------
value : bool
True if LVS succeeds
log_fname : str
name of the LVS log file.
"""
coro = self.async_run_lvs(lib_name, cell_name, **kwargs)
results = batch_async_task([coro])
if results is None:
return False, ''
ans = results[0]
if isinstance(ans, Exception):
raise ans
return ans
[docs] def run_rcx(self, lib_name: str, cell_name: str,
params: Optional[Mapping[str, Any]] = None,
**kwargs: Any) -> Tuple[str, str]:
"""run RC extraction on the given cell.
Parameters
----------
lib_name : str
library name.
cell_name : str
cell name.
params : Optional[Dict[str, Any]]
optional RCX parameter values.
**kwargs :
optional keyword arguments. See DbAccess class for details.
Returns
-------
netlist : str
The RCX netlist file name. empty if RCX failed.
log_fname : str
RCX log file name.
"""
coro = self.async_run_rcx(lib_name, cell_name, params=params, **kwargs)
results = batch_async_task([coro])
if results is None:
return '', ''
ans = results[0]
if isinstance(ans, Exception):
raise ans
return ans
[docs] def import_layout(self, in_file: str, lib_name: str, cell_name: str, **kwargs: Any) -> str:
"""Import layout.
Parameters
----------
in_file : str
input file name.
lib_name : str
library name.
cell_name : str
cell name.
**kwargs : Any
optional keyword arguments. See Checker class for details.
Returns
-------
log_fname : str
log file name. Empty if task cancelled.
"""
self.create_library(lib_name)
coro = self.async_import_layout(in_file, lib_name, cell_name, **kwargs)
results = batch_async_task([coro])
if results is None:
return ''
ans = results[0]
if isinstance(ans, Exception):
raise ans
return ans
[docs] def export_layout(self, lib_name: str, cell_name: str, out_file: str, **kwargs: Any) -> str:
"""Export layout.
Parameters
----------
lib_name : str
library name.
cell_name : str
cell name.
out_file : str
output file name.
**kwargs : Any
optional keyword arguments. See Checker class for details.
Returns
-------
log_fname : str
log file name. Empty if task cancelled.
"""
coro = self.async_export_layout(lib_name, cell_name, out_file, **kwargs)
results = batch_async_task([coro])
if results is None:
return ''
ans = results[0]
if isinstance(ans, Exception):
raise ans
return ans
[docs] def export_schematic(self, lib_name: str, cell_name: str, out_file: str, **kwargs: Any) -> str:
"""Export schematic.
Parameters
----------
lib_name : str
library name.
cell_name : str
cell name.
out_file : str
output file name.
**kwargs : Any
optional keyword arguments. See Checker class for details.
Returns
-------
log_fname : str
log file name. Empty if task cancelled.
"""
coro = self.async_export_schematic(lib_name, cell_name, out_file, **kwargs)
results = batch_async_task([coro])
if results is None:
return ''
ans = results[0]
if isinstance(ans, Exception):
raise ans
return ans
[docs] async def async_run_drc(self, lib_name: str, cell_name: str, **kwargs: Any) -> Tuple[bool, str]:
"""A coroutine for running DRC.
Parameters
----------
lib_name : str
library name.
cell_name : str
cell_name
**kwargs : Any
optional keyword arguments. See Checker class for details.
Returns
-------
value : bool
True if DRC succeeds
log_fname : str
name of the DRC log file.
"""
if self.checker is None:
raise Exception('DRC/LVS/RCX is disabled.')
return await self.checker.async_run_drc(lib_name, cell_name, **kwargs)
[docs] async def async_run_lvs(self, lib_name: str, cell_name: str, **kwargs: Any) -> Tuple[bool, str]:
"""A coroutine for running LVS.
Parameters
----------
lib_name : str
library name.
cell_name : str
cell_name
**kwargs : Any
optional keyword arguments. See Checker class for details.
Returns
-------
value : bool
True if LVS succeeds
log_fname : str
name of the LVS log file.
"""
if self.checker is None:
raise Exception('DRC/LVS/RCX is disabled.')
return await self.checker.async_run_lvs(lib_name, cell_name, **kwargs)
[docs] async def async_run_rcx(self, lib_name: str, cell_name: str, **kwargs: Any) -> Tuple[str, str]:
"""A coroutine for running RCX.
Parameters
----------
lib_name : str
library name.
cell_name : str
cell name.
**kwargs : Any
optional keyword arguments. See Checker class for details.
Returns
-------
netlist : str
The RCX netlist file name. empty if RCX failed.
log_fname : str
RCX log file name.
"""
if self.checker is None:
raise Exception('DRC/LVS/RCX is disabled.')
return await self.checker.async_run_rcx(lib_name, cell_name, **kwargs)
[docs] async def async_run_lvl(self, gds_file: str, ref_file: str, **kwargs: Any) -> Tuple[bool, str]:
"""A coroutine for running LVL with gds files.
Parameters
----------
gds_file : str
name of the current gds to be compared.
ref_file : str
name of the reference gds file.
**kwargs : Any
optional keyword arguments. See Checker class for details.
Returns
-------
value : bool
True if LVL succeeds
log_fname : str
name of the LVL log file.
"""
if self.checker is None:
return gds_equal(gds_file, ref_file), ''
return await self.checker.async_run_lvl(gds_file, ref_file, **kwargs)
[docs] async def async_import_layout(self, in_file: str, lib_name: str, cell_name: str, **kwargs: Any) -> str:
"""Import layout.
Parameters
----------
in_file : str
input file name.
lib_name : str
library name.
cell_name : str
cell name.
**kwargs : Any
optional keyword arguments. See Checker class for details.
Returns
-------
log_fname : str
log file name. Empty if task cancelled.
"""
if self.checker is None:
raise Exception('layout import is disabled.')
return await self.checker.async_import_layout(in_file, lib_name, cell_name, **kwargs)
[docs] async def async_export_layout(self, lib_name: str, cell_name: str, out_file: str, **kwargs: Any) -> str:
"""Export layout.
Parameters
----------
lib_name : str
library name.
cell_name : str
cell name.
out_file : str
output file name.
**kwargs : Any
optional keyword arguments. See Checker class for details.
Returns
-------
log_fname : str
log file name. Empty if task cancelled.
"""
if self.checker is None:
raise Exception('layout export is disabled.')
return await self.checker.async_export_layout(lib_name, cell_name, out_file, **kwargs)
[docs] async def async_export_schematic(self, lib_name: str, cell_name: str, out_file: str, **kwargs: Any) -> str:
if self.checker is None:
raise Exception('schematic export is disabled.')
return await self.checker.async_export_schematic(lib_name, cell_name, out_file, **kwargs)
[docs] def add_sch_library(self, lib_name: str) -> Path:
try:
lib_module = importlib.import_module(lib_name)
except ModuleNotFoundError:
raise ModuleNotFoundError(f'Cannot find python package {lib_name}. '
'You can only add schematic library if the corresponding '
'package is on your PYTHONPATH')
if not hasattr(lib_module, '__file__'):
raise ImportError(f'{lib_name} is not a normal python package '
'(no __file__ attribute). Did you create a proper python '
'schematic library?')
if lib_module.__file__ is None:
raise ImportError(f'{lib_name} has None __file__ attribute. Did you create a proper '
'python schematic library?')
lib_path: Path = Path(lib_module.__file__).parent
sch_lib_path = lib_path / 'schematic'
if not sch_lib_path.is_dir():
sch_lib_path.mkdir()
init_file = sch_lib_path / '__init__.py'
with open_file(init_file, 'w'):
pass
netlist_info_path = sch_lib_path / 'netlist_info'
if not netlist_info_path.is_dir():
netlist_info_path.mkdir()
sch_lib_path = sch_lib_path.resolve()
self.lib_path_map[lib_name] = str(sch_lib_path)
return sch_lib_path
[docs] def exclude_model(self, lib_name: str, cell_name: str) -> bool:
"""True to exclude the given schematic generator when generating behavioral models."""
sch_config = self.db_config['schematic']
lib_list = sch_config.get('model_exclude_libraries', None)
lib_cell_dict = sch_config.get('model_exclude_cells', None)
if lib_list and lib_name in lib_list:
return True
if lib_cell_dict:
cell_list = lib_cell_dict.get(lib_name, None)
return cell_list and cell_name in cell_list
return False